Aspack, UnAspack(già sentito??)

Data

by "Pnluck"

 

29/10/2004

UIC's Home Page

Published by Quequero


Ke bello riempire gli spazzi vuoti

Complimenti pn, ti sei dedicato al UIC Packers Project e ne hai fatto un ottimo tute, bravo davvero, continua cosi

Ke bello riempire gli spazzi vuoti

....

E-mail: [email protected]

Pnluck,#crack-it,#pmode,#assembly,#iglug

....

Difficoltà

(X)NewBies ( )Intermedio ( )Avanzato ( )Master

 

Manual Unpacking

di

Aspack
Written by Pnluck


Introduzione

Analiziamo il packer: Aspack. Troviamo OEP  e l’ IT. Cmq un minimo sul PE lo dovreste sapere. Vedete il tute di Nt è ottimo :D

Tools usati

Tools usati: Ollydbg ,Wark o LordPe

URL o FTP del programma

Non me lo ricordo, vedete nel forum :D

Notizie sul programma

Un packer semplice semplice, forse + di upx :D.

Essay

Analiziamo il prg (qui gli opcode sono importanti):

00438000   90               NOP              ; nop

00438001   60    PUSHAD  ;salva i seguenti registri eax, ecx, edx, ebx, esp, ebpesi, edi

00438002   E8 03000000      CALL uic_test.0043800A ;chiama la funzione a 43800A

00438007  -E9 EB045D45      JMP 45A084F7; ke strano jmp

0043800C   55               PUSH EBP  ;salva il registro  ebp

0043800D   C3               RETN ;ritorna

Ora analizziamo tutto da capo, la call punta dentro il codice di quel jmp, quindi qui c’è un piccolo trucco, cmq steppiamo dentro et voilà:

00438008   EB 04            JMP SHORT uic_test.0043800E    ;salta

0043800A   5D               POP EBP           ; preleva uic_test.00438007

0043800B   45               INC EBP                       ebx++

0043800C   55               PUSH EBP                ,salva ebp

0043800D   C3               RETN                       ,va a 438008   

La call ci porta a 43800A, qui si prende dallo stack l’indirizzo 438007, all’istruzione successiva si aumenta questo valore di uno (ebp=43008), e fa andare quel retn a 43800D, e poi con quel jmp ci troviamo a  43800E:

0043800E   E8 01000000      CALL uic_test.00438014  ;di nuovo lo stesso skerzetto

00438013   EB 5D            JMP SHORT uic_test.00438072 ;chi sa ke uscirà fuori  :D

00438015   BB EDFFFFFF      MOV EBX,-13  ;toglie a ebx  -13h

Ok steppiamo dentro :D

00438014   5D               POP EBP      ;EBP =00438013

00438015   BB EDFFFFFF      MOV EBX,-13  ;questo,

0043801A   03DD             ADD EBX,EBP  ;questo

0043801C   81EB 00800300    SUB EBX,38000 ;e questo, restituiscono in ebx l’image-base

00438022   83BD 22040000 00 CMP DWORD PTR SS:[EBP+422],0  ;verifica se è vuoto

00438029   899D 22040000    MOV DWORD PTR SS:[EBP+422],EBX  ; gli mette  ebx

0043802F   0F85 65030000    JNZ uic_test.0043839A   ;se non era vuoto salta

Ok, questo processo calcola l’image-base. Andiamo avanti :

00438035   LEA EAX,DWORD PTR SS:[EBP+42E]  ;eax = kernel32.dll

0043803B    PUSH EAX   ;carica eax

0043803C   CALL DWORD PTR SS:[EBP+F4D] ;e chiama GetModuleHandle

00438042   MOV DWORD PTR SS:[EBP+426],EAX ;mette il risultato in [ebp+426]

00438048   MOV EDI,EAX  ;e in edx

0043804A   LEA EBX,DWORD PTR SS:[EBP+5E] ;ebx=VirtualAlloc

0043804D   PUSH EBX ;prende ebx

0043804E    PUSH EAX ;eax = handle della dll

0043804F   CALL DWORD PTR SS:[EBP+F49] ;chiama GetProcessAddress(restituisce l’indirizzo dell’api in memoria)

00438055   MOV DWORD PTR SS:[EBP+54D],EAX ;ebp+54d = Kernel32.VirtualAlloc

0043805B   LEA EBX,DWORD PTR SS:[EBP+6B] ;ebx = VirtualFree

0043805E   PUSH EBX ;prende ebx

0043805F   PUSH EDI ;handle della dll

00438060   CALL DWORD PTR SS:[EBP+F49] ;chiama di nuovo GetProcessAddress

00438066   MOV DWORD PTR SS:[EBP+551],EAX  ;ebx = kernel32.VirtualFree

0043806C   LEA EAX,DWORD PTR SS:[EBP+77] ;mette in eax 0x40808A

0043806F   JMP EAX  ;e salta proprio lì

Questa parte di codice ricava l’indirizzo in memoria di due api: VirtualAlloc e VirtualFree.

La prima riserva una zona in memoria per il processo

La seconda libera una zona virtuale di memoria

Ora se andiamo a vedere l’indirizzo di 40808A, c’è sempre il solito trucchetto, perciò vi riporto direttamente il codice:

0043808A   MOV EBX,DWORD PTR SS:[EBP+531]  ;ebx=0

00438090   OR EBX,EBX   ;se ebx

00438092   JE SHORT uic_test.0043809E ;è zero salta

00438094    MOV EAX,DWORD PTR DS:[EBX] ;se no eax=ebx

00438096   XCHG DWORD PTR SS:[EBP+535],EAX ;ebp+535= eax

0043809C   MOV DWORD PTR DS:[EBX],EAX ;ebx = eax

0043809E   LEA ESI,DWORD PTR SS:[EBP+569] ;esi= VOffset di .text

004380A4   CMP DWORD PTR DS:[ESI],0  ;se esi = 0

004380A7   JE uic_test.004381CE ;zompa

Si prepara x chiamare una dll

004380AD   PUSH 4  ;il tipo di protezione

004380AF   PUSH 1000;il tipo di allocazione

004380B4   PUSH 1800 ;la grandezza

004380B9   PUSH 0 ;passa l’indirizzo

004380BB   CALL DWORD PTR SS:[EBP+54D] chiama VirtualAlloc

004380C1   MOV DWORD PTR SS:[EBP+156],EAX  ;ebp+156 = spazio allocato(da 860000)

004380C7   MOV EAX,DWORD PTR DS:[ESI+4]  ;eax = VSize di una sezione

004380CA   ADD EAX,10E  ;eax += 0x10e

004380CF   PUSH 4 ;come prima

004380D1   PUSH 1000  ;tipo allocazione

004380D6  PUSH EAX  ;grandezza

004380D7   PUSH 0  ;indirizzo

004380D9   CALL DWORD PTR SS:[EBP+54D] ;chiama VirtualAlloc

004380DF  MOV DWORD PTR SS:[EBP+152],EAX  ;ebp+152 =spazio allocato (da 870000)

Queste chiamate a VirtualAlloc, creano dello spazio in memoria, dove, vi sarà il codice decriptato, di ogni sezione del prg (formato PE si intende :). Continuiamo

004380E5   PUSH ESI  ;

004380E6   MOV EBX,DWORD PTR DS:[ESI] ;ebx = VOffset della sezione

004380E8   ADD EBX,DWORD PTR SS:[EBP+422] ;ebx =imagebase + VOffset

004380EE   PUSH DWORD PTR SS:[EBP+156] passa 86000

004380F4   PUSH DWORD PTR DS:[ESI+4]    passa la grandezza della sezione, cioè il VSize

004380F7   PUSH EAX  passa 87000 cioè dove deve mettere il codice decriptato

004380F8   PUSH EBX  e passa dove, deve iniziare a decriptare

004380F9   CALL uic_test.0043866C   ;call che decripta una sezione del PE

004380FE   MOV BL,0  ;bl = 0  (all’inizio)

00438100   CMP BL,0 ;se non è la prima sezione analizzata

00438103   JNZ SHORT uic_test.00438163 ;salta, se è la sezione .text continua

00438105   INC BYTE PTR SS:[EBP+EC]   aumentando il valore del mov con bl. Cioè dopo quel moc bl,0 diviene mov bl,1 etc a seconda delle sezioni

Ora quella call, non ve la riporto tutta, xkè uscirebbe un libro di 130 pagine, vi dico solo una cosa, vedete 870000 prima e dopo,giusto x vedere se dico cazzate.

Questa call viene richiamata x ogni sezione da decriptare. Avanti:

0043810B   MOV EDI,DWORD PTR DS:[ESI]    edi = VOffset di .text

0043810D   ADD EDI,DWORD PTR SS:[EBP+422]  e gli aggiunge l’image base

00438113   PUSH DWORD PTR DS:[EDI]  salva edi

00438115   MOV BYTE PTR DS:[EDI],0C3 gli muove c3 cioè un ret

00438118   CALL EDI  chiama 401000

0043811A  POP DWORD PTR DS:[EDI]  e rimette a posto tutto

Questo è un altro trucchetto, che serve nei casi che impostiamo il rage tra VOffset di .text e .rdata, per trovare l’Entrypoint. Continuiamo:

0043811C   PUSH EAX         grandezza .text

0043811D   PUSH ECX    ecx = 1

0043811E   PUSH ESI    43857c

0043811F   PUSH EBX   imagebase+VOffset di .text

00438120   MOV ECX,EAX   ecx = eax

00438122   SUB ECX,6   ecx - 6

00438125   MOV ESI,DWORD PTR SS:[EBP+152]  esi = 870000

0043812B   XOR EBX,EBX  ebx = 0

0043812D   /OR ECX,ECX  se ecx

0043812F   |JE SHORT uic_test.0043815F risulta 0x00 o

00438131   |JS SHORT uic_test.0043815F  è negative esce,

00438133   |LODS BYTE PTR DS:[ESI]  se no, carica in al il valore di esi

00438134   |CMP AL,0E8  se è uguale a E8(call)

00438136   |JE SHORT uic_test.00438142  salta

00438138   |JMP SHORT uic_test.0043813A 

0043813A   |CMP AL,0E9  se è uguale a E9(long jmp)

0043813C   |JE SHORT uic_test.00438142 salta

0043813E   |INC EBX  se no, incrementa ebx

0043813F   |DEC ECX  e decremente ecx

00438140   \JMP SHORT uic_test.0043812D e reinizia

00438142   |MOV EAX,DWORD PTR DS:[ESI]  mette in eax la dword puntata da esi

00438144   |JMP SHORT uic_test.00438146 (non fa niente)

00438146   |CMP BYTE PTR DS:[ESI],7    se dopo E8 o E9 non c’è 0x07

00438149   |JNZ SHORT uic_test.0043813E salta

0043814B   |AND AL,0  altrimenti fa un AND tra al con 0x00

0043814D   |ROL EAX,18  fa un rotate left di 18

00438150   |SUB EAX,EBX  eax -= ebx

00438152   |MOV DWORD PTR DS:[ESI],EAX  sostituisce il risultato in memoria

00438154   |ADD EBX,5 ebx+5

00438157   |ADD ESI,4 esi+4

0043815A   |SUB ECX,5  ecx-5

0043815D   \JMP SHORT uic_test.0043812D reinizia

0043815F   POP EBX imagebase+VOffset

00438160   POP ESI   esi = 43857c

00438161   POP ECX  rimette a posto ecx

00438162   POP EAX rimette a posto il VSize

00438163   JMP SHORT uic_test.0043816D e salta

Questa procedura si ripete finché ecx(che conteniene il VSize di .text) non è zero, poi come capito verifica se c’è un E807 o E907.

Questo controllo avviene solo x la sezione .text.Ora viene il bello dehhihihihho :D

0043816D   MOV ECX,EAX  ecx = VSize

0043816F   MOV EDI,DWORD PTR DS:[ESI]  edi = VOffset

00438171   ADD EDI,DWORD PTR SS:[EBP+422]  edi += imagebase

00438177   MOV ESI,DWORD PTR SS:[EBP+152] esi = 870000(ci sono i file decifrati)

0043817D   SAR ECX,2   sar è uno shift aritmetico a destra. Qui divide x 100(10^2)

00438180   REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] qui il codice decriptato di una sezione, va a sovrascrivere il codice criptato della stessa. Dopo ciò edi punta al VOffset della prossima sezione.(Raga è un giro di parole, ma abbiate pazienza :)

00438182   MOV ECX,EAX ecx = VSize sezione

00438184   AND ECX,3  azzera ecx

00438187   REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]  non fa niente

00438189   POP ESI  rimette a posto 43857c

0043818A   PUSH 8000

0043818F   PUSH 0

00438191   PUSH DWORD PTR SS:[EBP+152]   zona memoria da liberare con la

00438197   CALL DWORD PTR SS:[EBP+551] chiamata a VirtualFree

0043819D   ADD ESI,8 esi +8

004381A0   CMP DWORD PTR DS:[ESI],0 se abbiamo finito  continua con l’IT,

004381A3  JNZ uic_test.004380C7 se no, decripta la prossima sezione

Questa procedura decripta ogni sezione PE del file in questo modo:

1.      Passa alla call in 4380f9, le informazioni della sezione da decriptare cioè dove inizia decriptare(ImageBase+VOffet),la grandezza della sezione(VSize)

2.      e dove mettere il codice da decriptare 870000(nel mio caso)

3.      Nella call vengono decriptate tutte le sezioni del prg(se decriptiamo la prima sezione cioè .text, verifica la presenza di E807 o E907),e passa alla prossima sezione

4.      Se la prossima sezione del prg, ci restituisce valori null, significa che le sezioni sono finite,e passa al prossimo passaggio; altrimenti continua a decriptare.

Continuiamo con la spiegazione:

004381A9   PUSH 8000

004381AE   PUSH 0

004381B0   PUSH DWORD PTR SS:[EBP+156]  prende la zona in memoria: 860000

004381B6   CALL DWORD PTR SS:[EBP+551]  e la libera con VirtualFree

004381BC   MOV EBX,DWORD PTR SS:[EBP+531] ebx = 0

004381C2   OR EBX,EBX   se ebx = 0

004381C4   JE SHORT uic_test.004381CE  salta 2 righe + giù              \

004381C6   MOV EAX,DWORD PTR DS:[EBX]                                   |

004381C8   XCHG DWORD PTR SS:[EBP+535],EAX                              |

004381CE   MOV EDX,DWORD PTR SS:[EBP+422]  edx = imagebase(del prg)     /

004381D4   MOV EAX,DWORD PTR SS:[EBP+52D] eax = imagebase(data da win)

004381DA   SUB EDX,EAX  se edx - eax

004381DC   JE SHORT uic_test.00438257 è uguale a zero salta

……………(altro codice che serve x le dll)

Quel sub controllo se si agisce su un prg, altrimenti è una dll ed il codice che ho lasciato serve (penso) x le dll. Ora inizia il bello l’IT del prg iniziale :D

00438257   MOV EDX,DWORD PTR SS:[EBP+422]       edx = imagebase

0043825D   MOV ESI,DWORD PTR SS:[EBP+541]    esi = 0

00438263   OR ESI,ESI  se esi = 0

00438265   JE SHORT uic_test.00438278 salta

……………(altro codice non indispensabile)

00438278   MOV ESI,7F88     0x7f88 è il  VirtualAddess dell’IT

0043827D   MOV EDX,DWORD PTR SS:[EBP+422]  edi = imagebase

00438283   ADD ESI,EDX   calcola l’indirizzo dell’IT (rva)

00438285   MOV EAX,DWORD PTR DS:[ESI+C]   EAX = Nome della dll analizare

00438288   TEST EAX,EAX  se è zero

0043828A   JE uic_test.0043839A  jmp

00438290    ADD EAX,EDX      EAX= imagebase+dll_name

00438292   MOV EBX,EAX   ebx = eax

00438294   PUSH EAX             Prende EAX

00438295   CALL DWORD PTR SS:[EBP+F4D] e chiama GetModuleHandle

0043829B   TEST EAX,EAX  se non è zero

0043829D   JNZ SHORT uic_test.004382A6 jmp due righe più sotto

0043829F   PUSH EBX                                 

004382A0   CALL DWORD PTR SS:[EBP+F51]

004382A6   MOV DWORD PTR SS:[EBP+545],EAX    risultato della chiamata: salvato

004382AC   MOV DWORD PTR SS:[EBP+549],0      [ebp+549] = 0

004382B6   /MOV EDX,DWORD PTR SS:[EBP+422]    EDX = imagebase

004382BC   |MOV EAX,DWORD PTR DS:[ESI]     EAX = ESI = OriginalFirstThunk della dll

004382BE   |TEST EAX,EAX       se è diverso da zero

004382C0   |JNZ SHORT uic_test.004382C5  jmp

004382C2   |MOV EAX,DWORD PTR DS:[ESI+10]

004382C5   |ADD EAX,EDX               EAX = imagebase+OriginalFirstThunk della dll

004382C7   |ADD EAX,DWORD PTR SS:[EBP+549]   EAX += valore di [EBP+549]

004382CD   |MOV EBX,DWORD PTR DS:[EAX]    EBX = [EAX]  = nome della call

004382CF   |MOV EDI,DWORD PTR DS:[ESI+10]   EDI = FirstThunk dell’api

004382D2   |ADD EDI,EDX   EDI = imagebase + FirstThunk

004382D4   |ADD EDI,DWORD PTR SS:[EBP+549]  EDI  += [ebp+549]

004382DA   |TEST EBX,EBX            se EBX = 0

004382DC   |JE uic_test.00438384   passa alla prossima dll da analizzare

004382E2   |TEST EBX,80000000  se non è uguale a 80000000

004382E8   |JNZ SHORT uic_test.004382EE  jmp, altrimenti

004382EA   |ADD EBX,EDX      EBX = imagebase + call_name                       

004382EC   |INC EBX   EBX++

004382ED   |INC EBX    EBX++

004382EE   |PUSH EBX  passa alla prossima call

004382EF   |AND EBX,7FFFFFFF  gli fa un and

004382F5   |PUSH EBX  prende EBX

004382F6   |PUSH DWORD PTR SS:[EBP+545]   prende l’indirizzo in memoria della dll, ke richiama la call

004382FC   |CALL DWORD PTR SS:[EBP+F49]  e chiama GetProcAddr

00438302   |TEST EAX,EAX    se il risultato nam_dll.call_nam(es: user32.EnableWindow) non è zero

00438304   |POP EBX       

00438305   |JNZ SHORT uic_test.00438376 salta

…………………..(codice inutile x decriptare un prg)

00438376   |MOV DWORD PTR DS:[EDI],EAX         sovrascrive il valore di edi(punta all’IAT)   

00438378   |ADD DWORD PTR SS:[EBP+549],4        [ebp +594]  += 4 : prossima call

0043837F   \JMP uic_test.004382B6     e re-inizia

00438384   MOV DWORD PTR DS:[ESI],EAX  OFT= 0

00438386   MOV DWORD PTR DS:[ESI+C],EAX  Name_dll = 0          

00438389   MOV DWORD PTR DS:[ESI+10],EAX FT = 0

0043838C   ADD ESI,14  passa alla prossima dll

0043838F   MOV EDX,DWORD PTR SS:[EBP+422]  edx = imagebase

00438395   JMP uic_test.00438285 ritorna a 438285

Quest’ultima parte di codice lavora con l’ IT del prg packato, ho intuito ciò xkè vedevo i nomi delle dll :P, cmq poi andando all’indirizzo dell’IT ho visto che quei byte potevano essere  quelli dell’Import Table, dato ke ci sono 2 dword vuote(TimeDateStamp e FowarderChain) . Cmq credo che il procedimento sia chiaro. Prende dall’IT l’indirizzo della prima dll, poi man mano lavora con le call, prendedo gli indirizzi in memoria di queste e le sostituisce con quelli dell ‘IAT originale.

Quando arriva ad una dword vuota passa, alla prossima dll, e fa lo stesso lavore con le call di questa, fino a quando trova una dword vuota, lì dove ci dovrebbero essere le informazioni della Import Table di una dll, e va avanti nel decriptino del prg. Ora troviamo l’OEP (Original Entry Point).

0043839A   MOV EAX,12A5     0x12A5 è il VA dell’entry point

0043839F   PUSH EAX  salva eax

004383A0   ADD EAX,DWORD PTR SS:[EBP+422]  gli aggiunge l’imagebase

004383A6   POP ECX  carica ecx

004383A7   OR ECX,ECX e fa un bel OR

004383A9   MOV DWORD PTR SS:[EBP+3A8],EAX  mette l’OEP al posto di quell “push 0” a 4383BA

004383AF  POPAD rimette a posto i valore che c’erano quando è stato lanciato il prg

004383B0   JNZ SHORT uic_test.004383BA   Se ecx è diverso da zero continua(dovrebbe jmpare sempre)

004383B2   MOV EAX,1

004383B7   RETN 0C

004383BA   PUSH 0  questo push diventa: “PUSH uic_test.004012A5”, e mette in ESP l’indirizzo dell’OEP

004383BF   RETN e ritorna, all’indirizzo indicato da ESP.

Da dopo il RET, ci dovrebbe essere il prg unpackato, ora in olly, rianalizatte tutto e dovreste vedere il codice in chiaro :D

Evvai, abbiamo finito =); ora facciamo un breve sommario

  1. Il prg decripta una sezione PE alla volta.
  2. Poi tramite l’indirizzo dell’IT lavora con le DLL e quindi modifica IAT.
  3. poi fa un bel RET all’OEP.

Manual Unpacking

Ora sappiamo dov’è l’ OEP e l’indirizzo dell’IT, ora non ci resta che dumparlo prima che lavori con l’IT.

Quindi lo dobbiamo far “freezare”, prima che avvenga ciò, lo possiamo anche appena finisce il decript,io ho deciso di farlo freezare a 00438278.

Se aveto il plug-in commandline di quequero (lo trovate sul forum che e' ancora in fase beta NdQue), non vi basta che andare a quest’indirizzo e  digitare “eip eip”, ora premete F9 in Olly, x lanciate il prg, , lanciate wark o lordPE, e dumpate il programma.

Ora aprite il file con un editor PE, e cambiate EntryPoint con 12A5, poi andate alla Directory Table, e azzerate il Relocation, e cambiate IT con il valore trovato, cioè 7F88, c’è pure la grandezza, come la calcoliamo??? Semplice Import Table, e composta da 5 Dword.

Ora x vedere quante dll ci sono, basta prendere il RA del name della prima dll, e cercare in quel gruppo, nomi che terminano con “.DLL”.

Quindi basta fare dalla calcolatrice 4*5*(numero delle dll) e convertire tutto in hex, ora in questo caso è 4*5*2 = 40 = 0x28. Ora salviamo tutto, e lanciamo il prg, e tatà parte :D

Ora alleggeriamolo un po’, cancelliamo la sezione .aspack e .adata.

Finito

                                          Pnluck

Note finali

Grazie ai ragazzi della uic, B0, Geo, Fego, Ged x il suo tute su aspack ke mi ha aiutato un po’, Saturn e tutti gli altri che mi hanno aiutato.                                  Poi i miei boss Nt e Quake :D

Disclaimer

Qui inserirete con questo carattere il vostro piccolo disclaimer, non è obbligatorio però è meglio per voi se c'è. Dovete scrivere qualcosa di simile a: vorrei ricordare che il software va comprato e  non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.

Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly