Jak wykorzystac TR do rozpakowywania DOS exe-packerow by GustawKit

TeMaT      : Jak rozpakowac lub rozszyfrowac dosowe exe pliki.
NARZEDZIA  : The SUPER TRACER Version 2.50 (http://www.netease.com/~ayliutt)

Jak zapewne kazdy sie zorientowal, wiele dosowych plików exe spakowanych jest przez tzw.exe pakery. Przykladami takich pakerów sa PKLite, Diet, WWPack itp. Podobnie duzo plików exe blokowanych jest roznego rodzaju szyfratorami, blokerami z sztuczkami anty-debug.

Co z takimi plikami poczac, jezeli chcemy je modyfikowac. Ano pasuje je rozpakowac lub zdeszyfrowac. Sa do tego specjalne programiki rozpakowujace dany exe-paker badz tez uniwersalne rozpakowujace co sie tylko da. Jednak nalezy pamietac, ze rozpakowuja one z reguly tylko znane im pakery. Przy nowych wersjach po prostu wymiekaja. Co wtedy mozemy zrobic. Ano pasuje zabrac sie za reczne rozpakowywanie za pomoca debuggera.

Kazdy jak przypuszczam z czasem przyzwyczaja sie do swojego ulubionego debuggera i na nim wykonuje wiekszosc czynnosci. Chcialbym jednak polecic wszystkim bardzo dobry debugger pewnego faceta ze wschodu LiuTaoTao. Program ten nazywa sie The SUPER TRACER i obecna jego wersja to 2.50. Umownie przyjmijmy nazywac ten debugger TR. A co w nim takiego ciekawego, a no posiada bardzo dobre funkcje umozliwiajace zapisywanie programów z pamieci do plików i konstruowanie z nich plików wykonalnych.

Najpierw pasowaloby sie zapoznac z owym debuggerem. Mysle jednak, ze kazdy kto mial do czynienia z innym np. SoftICE itp nie bedzie mial z nim zadnego problemu. Nie bede go opisywal bo nie jest to naszym celem a potrzebne funkcje debuggera wyjasnie w ponizszym tekscie.

Aby zabrac sie za rozpakowywanie plików com i exe nalezy zrozumiec na czym polega caly proces i jaki problem jest z plikami exe.

Otóz pliki com, sa prostymi plikami uruchomieniowymi o max. dlugosci 64kb. Pliki com ladowane sa do pamieci i traktowane przez nia jako jeden ciagly blok pamieci. Sprowadza sie to do tego ze w pamieci jest po prostu obraz pliku com bez jakichkolwiek modyfikacji. Taki przypadek umozliwia nam skopiowanie pliku z pamieci (np. po rozpakowaniu w pamieci) i przeniesieniu go do pliku na dysku. Inaczej sie maja pliki exe, które posiadaja inna strukture i moga posiadac teoretycznie dowolne rozmiary. Powoduje to jednak, ze w pamieci umieszczane sa one w odpowiednich blokach i przy kazdym zaladowaniu do pamieci adresy poszczególnych fragmentów kodu moga byc inne.

Skopiowanie obraz pliku exe z pamieci na dysk nie jest jego poprawnym odpowiednikiem, co wynika wlasnie z natury plików exe. W celu poprawnego skonstruowania pliku exe z pamieci wymagane jest powtórne jego skopiowanie z innych lokacji pamieci i dopiero porównanie obu zrzutów pozwoli nam znalezc tzw. relokacje potrzebne do skonstruowania poprawnego pliku exe.

Na poczatek za przyklad posluzy nam maly pliczek pk150.exe zawarty w tym tutorze. Znalazlem go gdzies w sieci i dokladnie sie do tego nadaje. Jest on spakowany exe-pakerem PKLite v1.50.

Ladujemy pliczek do debuggera czyli --> tr pk150.exe

Pojawia sie nam ekran na którym mamy trzy glówne sekcje, u góry jest stan poszczególnych rejestrów, po srodku mamy kod aktualnie sledzonego programu natomiast na dole mamy linie komend. Kod jaki nam sie pojawi to :

    PUSH      AX                       ;2978:0100  50              
    MOV       AX,01B9                  ;2978:0101  B8B901          
    MOV       DX,000B                  ;2978:0104  BA0B00          
    ADD       AX,2988                  ;2978:0107  058829          
    CMP       AX,[0002]                ;2978:010A  3B060200        
    JB        013A                     ;2978:010E  722A            
    MOV       AH,09                    ;2978:0110  B409            
    MOV       DX,011C                  ;2978:0112  BA1C01          
    INT       21                       ;2978:0115  CD21            
    MOV       AX,4C01                  ;2978:0117  B8014C          
    ..................
    ..................
    PUSH      CX                       ;2978:0140  51              
    SUB       AX,0019                  ;2978:0141  2D1900     ---> AX = 2B08
    MOV       ES,AX                    ;2978:0144  8EC0            adres rozpakowywanej proc.
    PUSH      AX                       ;2978:0146  50              
    MOV       CX,00C5                  ;2978:0147  B9C500          
    XOR       DI,DI                    ;2978:014A  33FF            
    PUSH      DI                       ;2978:014C  57              
    MOV       SI,0154                  ;2978:014D  BE5401          
    CLD                                ;2978:0150  FC              
    REPZ                               ;2978:0151  F3              
    MOVSW                              ;2978:0152  A5              
    RETF                               ;2978:0153  CB              

Widzimy, ze nasz CS:IP czyli entry point wynosi 2978:0100. Nie jest to teraz wazne ale pózniej sie przyda gdy bedziemy musieli skopiowac program z innych lokacji. Interesuje nas na razie pierwszy fragment kodu az do instrukcji powrotu RETF. Kod ten jak dobrze mysle powoduje rozpakowanie glównego kodu algorytmu dekompresji Pklite w jakies miejsce w pamieci. Nie bedziemy tu analizowac algorytmów Pklite bo nie jest to naszym celem.

Interesuje nas dostanie sie do momentu powrotu (RETF). TR posiada instrukcje sledzenia programu az do momentu powrotu, jest to 'pret' a wiec wpisujemy pret i program nam sie wykona i kursor zatrzyma na linijce kodu pret. Jestesmy ciekawi co dalej program robi, naciskamy sledzenie jednej instrukcji czyli F8 lub wpisujemy 't' (trace) i oto mamy :

    STD                                ;2B08:0000  FD              
    MOV       BX,DS                    ;2B08:0001  8CDB            
    PUSH      BX                       ;2B08:0003  53              
    ADD       BX,2E                    ;2B08:0004  83C32E          
    NOP                                ;2B08:0007  90              
    ADD       BX,DX                    ;2B08:0008  03DA            
    MOV       BP,CS                    ;2B08:000A  8CCD            
    MOV       AX,DX                    ;2B08:000C  8BC2            
    AND       AH,0F                    ;2B08:000E  80E40F          
    MOV       CL,04                    ;2B08:0011  B104            
    MOV       SI,DX                    ;2B08:0013  8BF2            
    SHL       SI,CL                    ;2B08:0015  D3E6            
    ...................
    ...................
    XOR       BX,BX                    ;2B08:0155  33DB            
    MOV       CX,BX                    ;2B08:0157  8BCB            
    MOV       DX,BX                    ;2B08:0159  8BD3            
    MOV       BP,BX                    ;2B08:015B  8BEB            
    MOV       SI,BX                    ;2B08:015D  8BF3            
    MOV       DI,BX                    ;2B08:015F  8BFB            
    RETF                               ;2B08:0161  CB          

Powyzszy algorytm pklite dekompresuje pod pewne adresy pamieci glówny program, do którego chcemy sie dorwac. Sledzenie tego kodu moze byc troche meczace bo jest tam wiele petli, ale predzej czy pózniej dojdziemy do konca algorytmu i instrukcji powrotu RETF.

Jezeli nie chcecie analizowac algorytmu krok po kroku to dajemy pret i TR ustawi kursor na instrukcji powrotu RETF. Not idziemy dalej czyli t i wchodzimy do naszego glównego juz rozpakowanego programu :

    PUSH      AX                       ;2988:0000  50              
    PUSH      BX                       ;2988:0001  53              
    MOV       AX,DS                    ;2988:0002  8CD8            
    MOV       BX,CS                    ;2988:0004  8CCB            
    SUB       BX,10                    ;2988:0006  83EB10          
    CMP       AX,BX                    ;2988:0009  3BC3            
    JNE       0016                     ;2988:000B  7509            
    MOV       AX,2988                  ;2988:000D  B88829          
    MOV       DS,AX                    ;2988:0010  8ED8            
    INC       [BYTE 00A7]              ;2988:0012  FE06A700        
    MOV       AX,2988                  ;2988:0016  B88829          
    MOV       DS,AX                    ;2988:0019  8ED8            
    POP       BX                       ;2988:001B  5B              
    POP       AX                       ;2988:001C  58              

Czyli w 2988:0000 jest poczatek naszego glównego programu. Skoro doszlismy juz do tego momentu bez zadnych klopotów (nie bylo nic anty-debug -:)) to powinno nam sie juz wszystko udac. Musimy zapamietac jak doszlismy do poczatku naszego rozpakowanego glównego kodu, czyli ze robilismy dwa razy wykonanie do powrotu 'pret' i pózniej sledzenie jednej linijki 't'.

Ok. Mozemy zaczac wszystko od nowa. Najlepiej wyjsc z debuggera 'q' i zaladowac ponownie program do niego.

TR posiada komendy ulatwiajace nam a wrecz umozliwiajace zapisanie plików z pamieci na dysk.

Najpierw musimy przygotowac program w pierwszych lokacjach (jak pamietamy beda nam potrzebne dwie rozne lokacje). Robimy to za pomoca komendy EXE1 która przygotowuje pamiec na 1 exe a nastepnie komenda RELOAD która przeladowuje program do tej lokacji.

Nastepnie sledzimy program za do momentu gdzie zaczyna sie zdekompresowany kod, który chcemy zapisac na dysku. Gdy znajdziemy sie w pierwszej linijce tego kodu to wykonujemy zapisanie pliku z pamieci na dysk za pomoca komendy WEXE1 czyli po prostu write exe1. Jak wszystko poszlo dobrze program napisze nam ok.

Czyli wykonalismy :

EXE1
RELOAD
pret
pret
t
WEXE1

Teraz jak pamietamy musimy umiescic program w innych lokacjach pamiec i ponownie go zapisac na dysk. Pozwólmy aby program uruchomil sie do konca czyli 'g'.

Nastepnie deklarujemy przydzielenie nowych adresów pamieci dla pliku czyli dajemy EXE2 a pózniej RELOAD. Jezeli uwaznie przygladniemy sie np. segmentowi kodu CS, to okaze sie, ze jest on inny niz poprzednio i oto nam wlasnie chodzilo. Dalej wykonujemy juz tak samo jak przedtem.

Czyli po kolei:

EXE2
RELOAD
pret
pret
t
WEXE2

I jestesmy juz po robocie. Mamy dwa obrazy rozpakowanego kodu programu z roznych lokacji pamieci. Mozemy teraz spokojnie wyjsc z TR czyli 'q'.

Podczas tej pracy na dysku utworzyly nam sie dwa pliki : mem1.dat i mem2.dat. Oczywiscie wiemy skad one sie wziely. Teraz uruchamiamy programik mkexe.exe, który z tych dwóch plików utworzy nam mem.exe, a który jest juz naszym poprawnym rozpakowanym plikiem pk150.exe. Wystarczy podgladnac oba pliki pk150.exe i mem.exe zeby zobaczyc, ze rozpakowywanie nam sie udalo.

Mysle, ze kazdy kto przerobil powyzszy tekst zalapie o co chodzi i bedzie potrafil rozpakowac takze programy spakowane innymi pakerami np. WWPack, LZexe itp. Wszystkie te pakery dzialaja na podobnej zasadzie wiec nie powinno byc problemu. Nalezy jednak pamietac, ze najnowsze wersje szyfratorow maja pulapki wykrywajace TR i wtedy nalezy dokladnie analizowac kod programu i w razie czego go zmodyfikowac.

P.S. WAZNE!!!, przed kazdym nowym rozpakowywaniem usunmy z katalogu stare pliki mem1.dat i mem2.dat bo inaczej nowe zostana dopisane do starych.

Autor przygotowal dla nas takze automatyzacje powyzszego procesu. Jezeli dojdziemy do momentu naszego rozpakowanego kodu wystarczy napisac getknl [count] gdzie count oznacza ile TR shell'i ma wykonac i wykonuje automatycznie nastepujace instrukcje :

 exe1
 reload
 goknl count
 wexe1
 exe2
 reload
 goknl count
 wexe2
 q

a pózniej wystarczy uruchomic mkexe.exe i mamy rozpakowany plik.

Czasami okazuje sie, ze pliki exe sa nie tylko spakowane exe-pakerem ale jeszcze na dodatek zaszyfrowane jakims szyfratorem lub protektorem aby uniemozliwic ich rozpakowanie. Zasada dzialania protektorów jest w zasadzie podobna do dzialania kompresorów, tyle ze glownie nastawione one sa nie na kompresje a na funkcje anty-debug i anty-unpack. Program chroniacy dopisuje swój kod na koncu pliku, ewentualnie jeszcze szyfruje glówny program a entry point ustawiony jest na wlasnie te funkcje dolaczonego protektora. Idea usuniecia takiego protektora bedzie wiec polegac na przejsciu funkcji protektora i znalezieniu oryginalnego programu i jego zapisowi z pamieci na dysk.

Posluzymy sie przykladem pliku zawartego w tutorze - wwpack.exe. Pliczek, który poprzednio rozpakowywalismy spakowalem pakerem WWPack 3.04a Enh a nastepnie zablokowalem protektorem Secure 0.19 Piotra Warezaka (jednego z autorów WWPack). Autor TR, kody takich protektorów i pakerow dzialajacych w danym pliku okresla shell'ami. Przygotowal nam tez, jak juz wczesniej wspomnialem komendy ulatwiajace usuwanie takich zabezpieczen.

Jedna z takich komend jest getknl, która wyjasnialem powyzej. Powoduje ona automatyczne uruchamianie pliku i zapisu jego obrazu na dysku z pominieciem danej ilosci shell'i. Mozna ten proces zrobic recznie analizujac kod ale akurat w naszym przypadku automatyczna komenda dziala bardzo dobrze.

A wiec ladujemy tr wwpack.exe i mamy :

    CALL      0143                     ;2D88:000F  E83101          
    CMP       CX,DX                    ;2D88:0012  39D1            
    SUB       [BYTE DI+0031],82        ;2D88:0014  826D3182        
    XCHG      AX,DX                    ;2D88:0018  92              
    OR        [WORD DI+8248],6A        ;2D88:0019  838D48826A      
    INC       DI                       ;2D88:001E  47              
    STOSB                              ;2D88:001F  AA              
    XCHG      AX,SI                    ;2D88:0020  96              
    ADC       [WORD SI+BP+6596],8F6B   ;2D88:0021  819296656B8F    

dajemy teraz getknl a program automatycznie zrobi caly proces, a pózniej tylko mkexe i mamy plik mem.exe w którym pominiety zostal kod protektora Secure. Glebsza analiza powstalego pliku pokaze, ze jednak kod nie zostal usuniety a jedynie pominiety, co jednak nie wplywa nasza prace.

Kopiujemy plik mem.exe na wwpack.exe i ponownie ladujemy do Tr. Teraz bedziemy musieli usunac kompresora WWPack. Mozna spróbowac ponownie automatycznie za pomoca komend getknl itp. Zróbmy jednak (tak dla wprawy) to recznie. Po wgraniu programu mamy taki oto fragment kodu :

    MOV       AX,0024                  ;2CFE:0001  B82400          
    MOV       DX,CS                    ;2CFE:0004  8CCA            
    ADD       DX,AX                    ;2CFE:0006  03D0            
    MOV       CX,CS                    ;2CFE:0008  8CC9            
    ADD       CX,0087                  ;2CFE:000A  81C18700        
    PUSH      CX                       ;2CFE:000E  51              
    ......................
    REPZ                               ;2CFE:0031  F3              
    MOVSW                              ;2CFE:0032  A5              
    DEC       AX                       ;2CFE:0033  48              
    JNS       0024                     ;2CFE:0034  79EE            
    MOV       DS,BP                    ;2CFE:0036  8EDD            
    PUSH      CS                       ;2CFE:0038  0E              
    POP       ES                       ;2CFE:0039  07              
    XOR       DI,DI                    ;2CFE:003A  33FF            
    MOV       SI,0008                  ;2CFE:003C  BE0800          
    RETF                               ;2CFE:003F  CB             

Tak jak poprzednio, pomijamy analize kodu, i przechodzimy do miejsca powrotu funkcji RETF. Czyli dajemy pret. Nastepnie znajdujemy sie w nowym fragmencie kodu, który rozpakowywuje nasz glówny program do pamieci. Poniewaz program wywoluje kilka funkcji a zatem tez kilka powrotów z funkcji RETF po chwilowej analizie znajdziemy koncowy RETF. Bedzie on w adresie CS:017D. Proponuje wiec od razu g 17d co powoduje wykonanie programu do linii cs:17d. Mamy tam funkcje powrotu a wiec jedna linijke F8 i jestesmy znów w nowym fragmencie kodu. Nie jest to jeszcze nasz glówny program wiec znów dajemy pret i wreszcie znajdujemy:

    PUSH      AX                       ;2988:0000  50              
    PUSH      BX                       ;2988:0001  53              
    MOV       AX,DS                    ;2988:0002  8CD8            
    MOV       BX,CS                    ;2988:0004  8CCB            
    SUB       BX,10                    ;2988:0006  83EB10          
    CMP       AX,BX                    ;2988:0009  3BC3            
    JNE       0016                     ;2988:000B  7509            
    MOV       AX,2988                  ;2988:000D  B88829          
    MOV       DS,AX                    ;2988:0010  8ED8            
    INC       [BYTE 00A7]              ;2988:0012  FE06A700        
    MOV       AX,2988                  ;2988:0016  B88829          
    MOV       DS,AX                    ;2988:0019  8ED8            
    POP       BX                       ;2988:001B  5B              
    POP       AX                       ;2988:001C  58              

Teraz wiedzac juz gdzie zaczyna sie nasz glówny kod wystarczy wykonac opisywana juz procedure zrzutów obrazu programu do pliku.

A wiec :

EXE1
RELOAD
pret
g 17d
t
pret
WEXE1

a pózniej

EXE2
RELOAD
g 17d
t
pret
WEXE2

Teraz wychodzimy z TR i tylko mkexe i mamy rozpakowany wwpack.exe w pliku mem.exe. Po dluzszej zabawie TR z plikami spakowanymi innymi pakerami lub protektorami okazuje sie ze prawie wszystko mozna takim sposobem rozpakowac. Nalezy tylko umiejetnie znajdywac odpowiednie fragmenty kodu i w razie potrzeby usuwac zbedne (np. wywalajace debugerra itp).

GustawKit [CrackPl]