W przypadku języków maszyn wirtualnych opartych na kodzie bajtowym, takich jak Java, VB.NET, C #, ActionScript 3.0 itp., Czasami słyszysz o tym, jak łatwo jest pobrać dekompilator z Internetu, uruchomić kod bajtowy za jednym razem, i często, wymyślić coś nie za daleko od oryginalnego kodu źródłowego w ciągu kilku sekund. Podobno ten rodzaj języka jest na to szczególnie podatny.
Niedawno zacząłem się zastanawiać, dlaczego nie słyszysz więcej na ten temat o natywnym kodzie binarnym, kiedy przynajmniej wiesz, w jakim języku został napisany (a więc w jakim języku próbować się dekompilować). Przez długi czas myślałem, że to dlatego, że natywny język maszynowy jest bardziej szalony i bardziej złożony niż typowy kod bajtowy.
Ale jak wygląda kod bajtowy? To wygląda tak:
1000: 2A 40 F0 14
1001: 2A 50 F1 27
1002: 4F 00 F0 F1
1003: C9 00 00 F2
A jak wygląda natywny kod maszynowy (szesnastkowo)? Oczywiście wygląda to tak:
1000: 2A 40 F0 14
1001: 2A 50 F1 27
1002: 4F 00 F0 F1
1003: C9 00 00 F2
Instrukcje pochodzą z nieco podobnego sposobu myślenia:
1000: mov EAX, 20
1001: mov EBX, loc1
1002: mul EAX, EBX
1003: push ECX
Biorąc pod uwagę język, w którym próbujemy dekompilować jakiś natywny plik binarny, powiedzmy C ++, co jest w tym takiego trudnego? Jedyne dwa pomysły, które od razu przychodzą mi na myśl, to 1) tak naprawdę jest to o wiele bardziej skomplikowane niż kod bajtowy, lub 2) coś w tym, że systemy operacyjne mają tendencję do dzielenia programów na części i rozpraszania ich elementów, powoduje zbyt wiele problemów. Jeśli jedna z tych możliwości jest prawidłowa, proszę wyjaśnić. Ale tak czy inaczej, dlaczego tak naprawdę nigdy o tym nie słyszysz?
UWAGA
Zaraz przyjmuję jedną z odpowiedzi, ale najpierw chciałbym coś wspomnieć. Prawie wszyscy odwołują się do faktu, że różne fragmenty oryginalnego kodu źródłowego mogą być mapowane na ten sam kod maszynowy; nazwy zmiennych lokalnych zostały utracone, nie wiesz, jakiego rodzaju pętli pierwotnie użyto itp.
Jednak przykłady takie jak dwa, które właśnie zostały wspomniane, są dla mnie trochę banalne. Niektóre odpowiedzi twierdzą jednak, że różnica między kodem maszynowym a oryginalnym źródłem jest znacznie większa niż coś tak trywialnego.
Ale na przykład, jeśli chodzi o takie rzeczy, jak lokalne nazwy zmiennych i typy pętli, kod bajtowy również traci tę informację (przynajmniej w przypadku ActionScript 3.0). Wcześniej przeciągałem to z powrotem przez dekompilator i tak naprawdę nie obchodziło mnie, czy zmienna została wywołana strMyLocalString:String
czy loc1
. Nadal mogłem zajrzeć do tego małego, lokalnego zasięgu i zobaczyć, jak jest używany bez większych problemów. A for
pętla jest dokładnie tą samą dokładną rzeczą cowhile
pętla, jeśli się nad tym zastanowić. Również nawet gdybym uruchomił źródło za pomocą funkcji irFuscator (która, w przeciwieństwie do secureSWF, nie robi nic więcej niż tylko losowe nazwy zmiennych i funkcji), nadal wyglądało to tak, jakbyś mógł po prostu zacząć izolować niektóre zmienne i funkcje w mniejszych klasach, rysunek dowiedz się, jak są używane, przypisz im własne imiona i pracuj stamtąd.
Aby to była wielka sprawa, kod maszynowy musiałby stracić o wiele więcej informacji, a niektóre odpowiedzi na to idą.