| [
Jak debugować i rozpakować Crunch1.0 ]
Minęło trochę czasu odkąd pisałem
o rozpakowywaniu plików uruchomieniowych spakowanych różnymi
exe-pakerami. Za ostanie miesiące, lata pojawiło się wiele nowych
pakerów i crypterów. Pojawiło się także dużo narzędzi
wspomagających rozpakowywanie. IMHO najbardziej uniwersalnymi i
praktycznymi narzędziami są nadal SoftIce i ProcDump..., dołączył
do nich także znakomity debuger TRW2000. Ostatnio pojawił się
ciekawy paker/crypter firmy www.bit-arts.com
o nazwie Crunch 1.0. Pierwsze spotkanie z nim sprawiło, że
postanowiłem się nim zająć i sprobować go rozpakować ponieważ
zauważyłem kilka ciekawostek w kodzie i z biegu nie udało mi się
go rozpakować uniwersalnymi narzędziami. Za przykład użyjemy
samego Crunch'a, który jest spakowany sobą.
[ Tutorial ]
Postaram się wam przedstawić jak
zbudować uniwersalny skrypt do ProcDump'a rozpakowujący Crunch'a
gdyż narazie nie ma żadnego dedykowanego unpackera a uniwersalne
nie radzą sobie z nim. Z uwagi na to, że program wywala się w
obecności SoftIce (ma to robić wg autorów) do debugowania użyjemy
TRW2000. Podczas debugowania okazuję się, że w kodzie jest kilka
interesujących miejsc, które mocno utrudniają jego debugowanie i
muszą być uwzględnion przy tworzeniu skryptu do ProcDumpa.
Dlatego też proces tworzenia skryptu będę przeplatał z
fragmentami kodu crunch'a w celu lepszego zrozumienia zagadnienia i
ewentualnego szybkiego znalezienia tego kodu na podstawie adresów
czy ciągu bajtów podczas debugowania. Polecam także zmienić od
razu nazwy sekcji spakowanego Cruncha (wszystkie mają tą samą
nazwe) ponieważ będzie nam łatwiej się zorientować kiedy
przeskakujemy z jednej sekcji do drugiej,
Oki.. startujemy śledzenie
crunch.exe. Na starcie EIP=0067E00 pojawia się na mniej więcej
taki kod:
017F:0067E000 55 PUSH EBP
017F:0067E001 E800000000 CALL 0067E006
017F:0067E006 5D POP EBP
017F:0067E007 83ED06 SUB EBP,00000006
.............
017F:0067E0C0 8BC8 MOV ECX,EAX
017F:0067E0C2 F3A4 REP MOVSB !!!!!
017F:0067E0C4 8B07 MOV EAX,[EDI]
017F:0067E0C6 D5 AAD
017F:0067E0C7 81C2F3080E00 ADD EDX,000E08F3
017F:0067E0CD 52 PUSH EDX
017F:0067E0CE 33C0 XOR EAX,EAX
Tu już pojawia się pierwsza zasadzka w
instrukcji REP MOVSB. Otóż ta instrukcja powoduje kopiowanie
fragmentu kodu akurat w miejsce następnej instrukcji i po jej
wykonaniu kod bedzie wyglądac tak:
017F:0067E0C0 8BC8 MOV ECX,EAX
017F:0067E0C2 F3A4 REP MOVSB
017F:0067E0C4 8BD5 MOV EDX,EBP
017F:0067E0C6 81C2F3080000 ADD EDX,000008F3
017F:0067E0CC 52 PUSH EDX
017F:0067E0CD 33C0 XOR EAX,EAX
Tu jest pierwszy moment, który musimy uwzględnić
przy tworzeniu skryptu. Ponieważ po instrukcji REP MOVSB dalszy kod
się zmienia należy pozwolić ProcDumpowi znaleˇć miejsce kiedy
to nastąpi :
LOOK F3,A4 / ADD 2 / BP / OBJR
ProcDump przeszuka kod za bajtami REP MOVSB i do
EIP ze znalezionymi bajtami doda 2 i na takim adresie założy pułapke.
Musimy to zrobić w ten sposób ponieważ ProcDump musie się
zatrzymać już po wykonaniu REP MOVSB. Dalej znajdujemy podobny
kod, który kopiuje fragmenty kodu tym razem w procedurze CALL.
017F:0067E0FE FFD2 CALL Near EDX
017F:0067E100 CC INT 03
017F:0067E101 CC INT 03
017F:0067E102 E8A6080000 CALL 0067E9AD
017F:0067E107 E81C060000 CALL 0067E728
....................
a po wywołaniu CALL kod wygląda:
017F:0067E0FE FFD2 CALL Near EDX
017F:0067E100 EB05 JMP 0067E107
017F:0067E102 E8A6080000 CALL 0067E9AD
017F:0067E107 E81C060000 CALL 0067E728
Jeżeli prześledzimy procedurę w wywołaniu CALL
znajdziemy ciekawy kod, który wykłada program, jeżeli SoftICe
jest w pamięci. Otóż ten fragment kodu: JMP 0067E107 generowany
jest na podstawie wartości z tablicy IDT. Jak wiemy gdy SI jest
zainstalowany w tablicy są inne wpisy dla przerwań (INT1/IN3) niż
w czystym systemie. To właśnie sprawdza Crunch, i jeżeli są tam
wartości wpisane przez SI generuje bzdurne wartości powodując nie
działanie programu. TRW nie zmienia wpisów w IDT więc nim możemy
spokojnie śledzić taki kod. Ten fragment kodu musimy także uwzględnić
w naszym skrypcie:
LOOK FF,D2 / BP / WALK /OBJR /
LOOK 74,02,58,c3 /ADD 3 /BP / WALK /OBJR
Znajdujemy kod CALL'a i na nim zakładamy pułapkę.
Póˇniej wykonujemy jedną instrukcję i przypisujemy bazowy EIP do
dalszego działania skryptu. Ponieważ jednak ProcDump sam nie chciał
mi się zatrzymać po powrocie z CALL więc poszuka moment powrotu
RET z Call i na nim się zatrzyma, wykona jedną instrukcję czyli
wyjdzie z Call'a i przypisze bazowy EIP już po tym.
Dalej debugując kod programu po wielu różnych
cudach z kodem dochodzimy do:
017F:00680DA3 E900FFFFFF JMP 00680CA8
017F:00680DA8 8B8522130000 MOV EAX,[EBP+00001322]
017F:00680DAE 01858F220000 ADD [EBP+0000228F],EAX
017F:00680DB4 FFA5F22A0000 JMP Near [EBP+00002AF2] !!!!
017F:00680DBA C3 RET
017F:0067E107 E81C060000 CALL 0067E728
gdzie interesująco wygląda skok JMP [EBP+2AF2] i
skacze do :
017F:0067E0C4 61 POPAD
017F:0067E0C5 5D POP EBP
017F:0067E0C6 8B858F220000 MOV EAX,[EBP+0000228F]
017F:0067E0CC 5D POP EBP
017F:0067E0CD FFE0 JMP Near EAX !!!
A ten jump EAX jest do :-)) cs:401000 czyli kodu głównego programu.
017F:00401000 A19CA54E00 MOV EAX,[004EA59C]
017F:00401005 C1E002 SHL EAX,02
017F:00401008 A3A0A54E00 MOV [004EA5A0],EAX
017F:0040100D 57 PUSH EDI
017F:0040100E 51 PUSH ECX
017F:0040100F 33C0 XOR EAX,EAX
017F:00401011 BF686C4F00 MOV EDI,004F6C68
Spróbujmy dopisać do skryptu szukanie tych
jump'ow ( LOOK FF,A5,F2,2A,00,00 / BP).
Szukanie jednak bajtów kodów tych skoków przez ProcDumpa w
momencie ostatniego zatrzymania skryptu (po wyjściu z CALL) nie
daje rezultatu. A raczej daje tylko okazuje się, że kody tych
jump'ów znajdują się pod innymi adresami ( kod pierwszego jump
jest pod cs:0067F062) niż wynika to z debugera no i mimo ich
znalezienia pułapka BP nie zadziała. Co wiec robimy ?.. będąc w
debugerze w czasie śledzenia jeszcze pod adresem 017F:0067E100
(czyli w momencie kiedy ProcDump zacznie szukać tego jmp) zaglądamy
co jest tam gdzie ma być ten JMP czyli pod adres 017F:00680DB4.
Okazuje się, że jest co innego. No wiec zakładamy pułapkę na
zapisie pod ten adres (BP 017F:00680DB4 W) i puszczamy program
dalej. Okazuje się, że ten kod jest kopiowany w to miejsce z
innego w tym fragmencie:
017F:0067E2B0 F3A4 REP MOVSB !!!!
017F:0067E2B2 5F POP EDI
017F:0067E2B3 89BD022B0000 MOV [EBP+00002B02],EDI
017F:0067E2B9 33D2 XOR EDX,EDX
Wniosek z tego, że skrypt musi najpierw znaleˇć
miejsce kopiowania, zatrzymać się na nim i szukać JMP'ów dopiero
po ich skopiowaniu. A więc :
LOOK F3,A4 / ADD 2 / BP /OBJR
I dopiero po tym poszukać końcowych jmp'ów.
LOOK FF,A5,F2,2A,00,00 / REPL
90,90,90,90,90,90 / LOOK FF,A5,F2,2A,00,00 /BP /WALK /OBJR
LOOK FF,E0 / BP / STEP
ProcDump najpierw znajdzie to pierwszą kopie bajtów
(którą wcześniej znajdywał), które są już zbędne bo
skopiowane pod właściwy adres, więc aby je pominąć zastępujemy
je NOP'ami i szukamy ponownie. I to by było praktycznie wszystko.
Końcowy pełny skrypt wygląda następująco:
[Crunch]
L1=LOOK F3,A4
L2=ADD 2
L3=BP
L4=OBJR
L5=LOOK FF,D2
L6=BP
L7=WALK
L8=OBJR
L9=LOOK 74,02,58,c3
LA=ADD 3
LB=BP
LC=WALK
LD=OBJR
LE=LOOK F3,A4
LF=ADD 2
L10=BP
L11=OBJR
L12=LOOK FF,A5,F2,2A,00,00
L13=REPL 90,90,90,90,90,90
L14=LOOK FF,A5,F2,2A,00,00
L15=BP
L16=WALK
L17=OBJR
L18=LOOK FF,E0
L19=BP
L1A=STEP
OPTL1=00000000
OPTL2=01010001
OPTL3=01010001
OPTL4=00030000
OPTL5=00000000
[ Wnioski i uwagi ]
Prawdopodobnie istnieją inne drogi śledzenia i stworzenia skryptu
w inny sposób. To co przedstawiłem jest końcowym efektem mojej
pracy bez żadnych optymalizacji. Skrypt działa poprawnie na
programach spakowanych Crunch'em wię nie ma co już go poprawiać.
O czym świadczy cały ten przykład.., a no o tym, że każdy
spakowany czy zaszyfrowany program uruchomieniowy można rozpakować,
bez względu na ilość i jakość zabezpieczeń prze tym. Crunch
nie jest złym pakerem.., myślę, że w większości przypadków mógłby
uniemożliwić i zniechęcić do modyfikacji i łamania programów
nim zabezpieczonych. Mógłby gdyby nie ProcDump, TRW i CrackPL :-).
Przykro mi, że tak bezwzględnie potraktowałem Crunch i chłopaków
z BitArts ale zrobiłem to tylko dlatego, że ich produkt uznałem
za ciekawy i warty poświęcenia mojego czasu.
|