niedziela, 15 czerwca 2008

dekompilacja kodu Java w locie i w Eclipsie

Dzisiaj kontynuuję moje przygody z najnowszym Eclipse Ganymede. Zainstaluję do niego wtyczkę (w końcu to Eclipse) która mam nadzieję rozwiąże przynajmniej część moich problemów z brakującymi źródłami zewnętrznych bibliotek. O ile źródła wielu z nich, jak np. Spring są łatwe do znalezienia, o tyle żeby znaleźć źródła JavaMail musiałem się kiedyś nieźle na pocić. Czasami nie mam na to czasu ani ochoty, a jakieś źródła fajnie zobaczyć.
Stąd mój zwrot w kierunku dekompilacji kodu i programu Jad - the fast JAva Decompiler do którego istnieje wtyczka JadClipse przeznaczona dla środowiska Eclipse. Kod po dekompilacji zawsze będzie gorszy niż pierwotny kod źródłowy. Nie będzie komentarzy ani adnotacji ale lepszy rydz niż nic.
Zatem do dzieła.
Pobieram Jad 1.5.8e for Linux on Intel platform
rozpakowuję do katalogu /opt/jad i nadaję prawa do wykonania pliku jad
mateusz@mateusz-laptop:~$ chmod +x /opt/jad/jad

krótki sprawdzian, czy działa:

mateusz@mateusz-laptop:~$ /opt/jad/jad
/opt/jad/jad: error while loading shared libraries: libstdc++-libc6.2-2.so.3: cannot open shared object file: No such file or directory

i tu klops, nie działa... Niespełnione zależności, czyli coś co w Linuksie lubię najbardziej.
Wkładam gogle, martwa cisza. W Ubuntu 8.04 nikt się nie chwali, że to rozwiązał. Ja się pochwalę. Pobieram i instaluję paczkę libstdc++2.10-glibc2.2 dla Ubuntu 7.10. O dziwo zainstalowała się bez przeszkód. Sprawdzam czy działa Jad:
mateusz@mateusz-laptop:~$ /opt/jad/jad
Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov (kpdus@yahoo.com).
Usage: jad [option(s)] <filename(s)>
Options: -a - generate JVM instructions as comments (annotate)
-af - output fully qualified names when annotating
-b - generate redundant braces (braces)
-clear - clear all prefixes, including the default ones
-d <dir> - directory for output files
-dead - try to decompile dead parts of code (if there are any)
-dis - disassembler only (disassembler)
-f - generate fully qualified names (fullnames)
-ff - output fields before methods (fieldsfirst)
-i - print default initializers for fields (definits)
-l<num> - split strings into pieces of max <num> chars (splitstr)
-lnc - output original line numbers as comments (lnc)
-lradix<num>- display long integers using the specified radix
-nl - split strings on newline characters (splitstr)
-noconv - don't convert Java identifiers into valid ones (noconv)
-nocast - don't generate auxiliary casts
-noclass - don't convert .class operators
-nocode - don't generate the source code for methods
-noctor - suppress the empty constructors
-nodos - turn off check for class files written in DOS mode
-nofd - don't disambiguate fields with the same names (nofldis)
-noinner - turn off the support of inner classes
-nolvt - ignore Local Variable Table entries (nolvt)
-nonlb - don't insert a newline before opening brace (nonlb)
-o - overwrite output files without confirmation
-p - send all output to STDOUT (for piping)
-pa <pfx>- prefix for all packages in generated source files
-pc <pfx>- prefix for classes with numerical names (default: _cls)
-pe <pfx>- prefix for unused exception names (default: _ex)
-pf <pfx>- prefix for fields with numerical names (default: _fld)
-pi<num> - pack imports into one line using .* (packimports)
-pl <pfx>- prefix for locals with numerical names (default: _lcl)
-pm <pfx>- prefix for methods with numerical names (default: _mth)
-pp <pfx>- prefix for method parms with numerical names (default:_prm)
-pv<num> - pack fields with the same types into one line (packfields)
-r - restore package directory structure
-radix<num>- display integers using the specified radix (8, 10, or 16)
-s <ext> - output file extension (default: .jad)
-safe - generate additional casts to disambiguate methods/fields
-space - output space between keyword (if, while, etc) and expression
-stat - show the total number of processed classes/methods/fields
-t<num> - use <num> spaces for indentation (default: 4)
-t - use tabs instead of spaces for indentation
-v - show method names while decompiling

Teraz lepiej.
Otwieram Eclipse a w nim ostatnio stworzony projekt aquarium.
Przechodzę do klasy InfoController. Klikam myszką na nazwę interfejsu Controller i naciskam klawisz F3



Source not found czyli źródła niepodpięte.
Czas zainstalować wtyczkę JadClipse. Pobieram jadclipse3.3 i kopiuję do katalogu plugins Eclipse Ganymede. Restart IDE.
W menu Window/Preferences... pojawiła się pozycja Java/JadClipse czyli wtyczka wydaje się, że działa. Wskazuję położenie pliku wykonywalnego jad na /opt/jad/jad


Ponownie naciskam F3 kiedy mam zaznaczoną nazwę interfejsu InfoController.


Widzę kod, czyli działa. Przechodzę dalej do klasy ModelAndView


No, pełen sukces.

2 komentarze:

a pisze...

Korzystam z takiego cudeńka w pracy od pół roku :)
Generalnie rewelacja.
W trakcie dekompilacja samym jadem polecam dodać opcję 'output original line numbers as comments' a w przypadku jadclipse'a - tożsamo oraz 'align code for debugging' jeśli by kiedykolwiek przyszła chęć na debuggowanie takiej klasy :).

Jest tylko jeden szkopuł! (a właściwie dwa, czyli cały szkop!)Dla dużych i skomplikowanych klas jad:
a) nie radzi sobie z obsługą wyjątków i niektórymi instrukcjami sterującymi, oraz powoływaniem klas po nazwie, generuje wtedy kod, który się nie kompiluje i czasami nie wiadomo jak go naprawić. Nie wiem czemu tak jest. wszak bajtkod powinien mieć wsteczne odzwierciedlenie w kodzie. Kompilacja chyba nie jest jednokierunkową funkcją haszującą :D (ale nie znam się wystarczająco).

b) gdy kod jest zagmatwany - jadclipse nie jest w stanie wstawiać linijki pochodzącej z 50 linii do 50 linii, tylko je przesuwa. a wtedy debugger staje się baaardzo uciążliwy :)

----------
Racjonalny Developer

MZ pisze...

dzięki za komentarz. Align code for debugging faktycznie jest bardzo przydatne przy debugowaniu dekompilowanego kodu, choć i tak daleko się na tym nie zajedzie.