Reversiamo l'AciD Crypt v0.1.2

Data

by ::[ active85k ]::

 

15/10/2006

UIC's Home Page

Published by Quequero


Al mondo ci sono 10 tipi di persone: chi conosce il sistema di numerazione binario e chi non lo conosce affatto!

Bravo act hai fatto davvero un ottimo lavoro!!! Hai analizzato per bene il crypter e hai fatto anche bene il decrypter, complimenti!

Non so che cosa scrivere... que.. al massimo fai di testa tua :-)
Non preoccuparti, mi sono vendicato nella sezione a sinistra ;p NdQue

....

Home page se presente: http://www.active85k.da.ru 
E-mail: [email protected]
active85k, #crack-it | #fuckinworld @ Azzurra.org

....

Difficoltà

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

 

Che cosa c'è da dire su un crypter di eseguibili oltre al fatto che è un crypter di eseguibili? :S

Introduzione

Introduciamo il reversing del programma :)

Tools usati

Un buon cervello (consigliato ottimo cervello)
Una buona conoscenza del formato PE (consigliata anche una buona conoscenza sul funzionamento di un crypter)
IDA Pro v4.17 (ma dovrebbe andare bene qualsiasi versione... e per favore non dite la parola W32Dasm! :)
Microsoft Visual C++ v6.0... ebbè, si, avete capito bene! Scriviamoci il decrypter automatico! :)
Cavia.exe - Il programma che useremo come cavia per i nostri esperimenti (scaricabile da qui)
Un editor esadecimale (io uso l'Hex Workshop, ma va bene qualsiasi programma)
AciD Crypt v0.1.2 (scaricabile da qui oppure da www.lalucedelsud.cjb.net)
Assolutamente non si usa il SoftICE (poi vedremo perché)!
Un qualsiasi PE-Editor

URL o FTP del programma

www.lalucedelsud.cjb.net 

Essay

Prima di tutto, dovete sapere che il programma in questione è un'invenzione di un mio caro amico (ovvero AciD_LeO), che non è stato contrario a farmi scrivere questo tutorial (gliel'ho chiesto perché solitamente non vuole che un suo programma venga data la spiegazione o i sorgenti, e io sono d'accordo con lui. Ma in questo caso ha fatto un'eccezione :). Oltre a questo, colgo l'occasione per farvi sapere che voi state qua a leggere questo tutorial soprattutto grazie a lui: se non l'avessi conosciuto, a quest'ora sarei in mezzo alla strada :).
Come crypter per eseguibili, l'ho trovato molto interessante, sia dal punto dell'algo di crittografia che dal punto di vista del reverser. Grazie a questo programma, imparerete a fare attenzione a molti particolari che io stesso avevo inizialmente trascurato. Allora: se siete arrivati fin qua, significa che vi siete procurati tutti i tools e i file che vi servono. Mi raccomando, durante lo svolgimento di questo tutorial, vi prego di prestare molta attenzione, perché stiamo sempre parlando di un codice polimorfe di un crypter... e anche nel più stupido dei casi, non è da sottovalutare.
Iniziamo a "dichiarare" un paio di punti "cardine" (che parole "arcane" che uso :). Vediamo velocemente (per chi non ne avesse la minima idea) come funziona un pe-crypter generico. Partendo dall'eseguibile originale, si ottiene un eseguibile crittografato: questo contiene (generalmente) una nuova sezione al suo interno (che può assumere nomi a piacimento) che contiene il codice di decrypting per l'intero eseguibile. In questo nuovo exe, viene modificato l'entry point, e viene settato all'inizio della sezione di decrypting. In questo modo, l'eseguibile, quando parte, si auto-decrypta in memoria per poi essere eseguito. Una volta che questo viene decrittato, si ha un salto (o una chiamata, insomma, qualsiasi cosa) che ci porta all'esecuzione del codice originale decrittato (molte volte si può raggiungere perché il salto avviene con un bel "jmp EAX", ma non sempre).
Devo dire che in giro ho trovato un altro tool in grado di decryptare (se così si può dire) un file generato con l'AciD Crypt. Questo tool si chiama "UnAcid Crypt", e l'ho trovato su www.protools.cjb.net. Nel file zip scaricabile c'era anche il sorgente di questo programmino (scritto in TASM). Mi son guardato quel codice e non è stato difficile capire che quel programmatore, non aveva capito affatto il funzionamento di questo crypter (ciò non troglie che la strada da lui seguita vada benissimo!). Di fatto, quel programma non decritta il file: crea un processo, lo esegue e aspetta che si auto-decrypta in memoria. Fatto questo, prende il file dalla memoria e lo riscrive su disco facendogli qualche piccola aggiustatina (questa operazione si chiama "dumping"). Noi non seguiremo questa strada, ma ricostruiremo il nostro eseguibile partendo da quello crittato! :).
Iniziamo adesso con il reversing! La prima cosa da fare è quella di analizzare un po' l'eseguibile dell'acid crypt. Se lo apriamo con un PE-Editor e ci osserviamo la section table, possiamo notare che ha due sezioni alquanto strane :). Ci sono due sezioni che si chiamano "AciDLeO"! Questo significa che il crypter stesso è stato cryptato due volte con sè stesso! Così ci viene difficile disassemblarlo per vedere le operazioni che fa. Però, riflettete un momento: quando noi criptiamo un file, questo deve essere in grado di auto-decriptarsi, giusto? Questo significa che noi possiamo capire l'algo di critto-analisi a partire da un eseguibile già crittato, senza osservare il crypter! Per rendere le cose più facili, dobbiamo utilizzare un eseguibile di piccole dimensioni. Così, siccome tutti gli exe che avevano non mi piacevano, ho pensato di farne uno io! Scaricate Cavia.exe (è un programma scritto in assembly che fa una MessageBox del ca**o... solo 2,50 kb! meglio? :). Mettetelo nella stessa cartella dell'acid crypt e criptatelo! Benissimo! E' uscito fuori Cavia2.exe (notate che le dimensioni del file sono leggermente cambiate). Adesso vediamo che diavolo dobbiamo fare: prendiamo IDA e disasmiamo Cavia2.exe per vedere che cosa ci offre la casa:

AciDLeO:00404000 start proc near
AciDLeO:00404000     pusha
AciDLeO:00404001     mov ecx, 200h
AciDLeO:00404006     mov edx, offset dword_0_401000
AciDLeO:0040400B     mov esi, 3Fh

AciDLeO:00404010 loc_0_404010:                 ; CODE XREF: start+14 j
AciDLeO:00404010     add bh, [eax]
AciDLeO:00404012     inc eax
AciDLeO:00404013     dec esi
AciDLeO:00404014     jnz short loc_0_404010
AciDLeO:00404016     mov eax, edx
 
AciDLeO:00404018 loc_0_404018:                 ; CODE XREF: start+26 j
AciDLeO:00404018     mov bl, [eax]
AciDLeO:0040401A     xor bl, bh
AciDLeO:0040401C     ror bl, 3
AciDLeO:0040401F     xor bl, 10h
AciDLeO:00404022     mov [eax], bl
AciDLeO:00404024     inc eax
AciDLeO:00404025     dec ecx
AciDLeO:00404026     jnz short loc_0_404018
AciDLeO:00404028     mov al, 0
AciDLeO:0040402A     mov edi, offset dword_0_402010
AciDLeO:0040402F     mov ecx, 28h
AciDLeO:00404034     repe stosb
AciDLeO:00404036     popa
AciDLeO:00404037     mov eax, offset dword_0_401000
AciDLeO:0040403C     jmp eax
AciDLeO:0040403C start endp

Toh, vi ho messo anche i colori :). Adesso memorizzate bene questa schermata, perché tutto quello che ci serve si trova qua dentro! Come avete notato, l'entry point del file è chiaramente cambiato! Infatti IDA, quando finisce il disassembly dell'eseguibile, ci fa partire direttamente dall'entry point dello stesso. Facciamo finta di debuggare a mente l'eseguibile per vedere in che condizione si trova il programma. Appena questo eseguibile parte, noi abbiamo il registro EAX che contiene l'entry point dell'eseguibile in memoria. In poche parole, se vediamo EAX come un puntatore, esso punta direttamente a questo codice. Poi abbiamo il registro EBX che è uguale a zero. Adesso iniziamo ad analizzare il codice (come indicazioni userò il numero del virtual address delle righe). Iniziamo da 00404001: come vedete c'è una "mov" che setta ECX a 0x200. Se devo essere sincero, a me la domanda è uscita spontanea: guardando un po' così al volo il codice successivo, mi son chiesto da dove avesse "cacciato" quel 0x200! In effetti, il mio presentimento era giusto: più in là scopriremo che 0x200 è la raw size della sezione che aveva l'entry point prima del decrypting; per adesso accontentiamoci del fatto che 0x200 è comunque la dimensione della sezione che viene criptata. Prendete il vostro PE-Editor e date di nuovo uno sguardo alla section table, questa volta, del file Cavia2.exe (ovvero quello crittato):

SECTION VIRTUAL SIZE VIRTUAL ADDRESS RAW SIZE RAW ADDRESS CHARACTERISTICS
.text 0x00000026 0x00001000 0x00000200 0x00000400 0xE0000020
.rdata 0x00000092 0x00002000 0x00000200 0x00000600 0xE0000020
.data 0x00000033 0x00003000 0x00000200 0x00000800 0xE0000020
AciDLeO 0x00001000 0x00004000 0x00000200 0x00000A00 0xE0000020

Da questa tabellina si può capire qualcosa: tutte le sezioni hanno la dimensione di 0x200, ma non dovete spaventarvi, non sarà difficile capire quale sezione ci interessa :). Sempre con un PE-Editor, adesso, troviamoci l'image base di questo file. Come potete constatare, questa è pari a 0x00400000. Con IDA abbiamo potuto vedere che l'entryu point attuale del file criptato è 0x00404000. Attenzione: questo è l'entry point rispetto all'image base (e se no perché ve l'ho fatta trovare? :). Per ottenere il reale entry point del file basta fare 0x00404000 - ImageBase (0x00400000). Il risultato è 0x00004000 (ovvero il virtual address della sezione AciDLeO). Tutta 'sta storia a che serve? Serve a farvi capire che quando vi serve qualcosa da verificare sulla section table, dovete sottrargli l'image base. Passiamo avanti con le istruzioni: alla riga 0x00404006 troviamo un'altra mov "interessante". EDX assume un valore molto interessante (ovvero 0x00401000). Non vi è strano 'sto numero? Se togliamo l'image base a questo numero otteniamo 0x00001000. Che cos'è questo? Non ci viene difficile capire che questo è il virtual address della sezione che dovrà auto-decriptarsi! Siamo già a boun punto :). Da qua abbiamo la conferma che lo 0x200 di prima rappresenta la dimensione della sezione che deve essere decrittata! ;) A questo punto, deve saltarvi in testa un dato molto utile: la routine di decrypting dell'eseguibile criptato, è stata in parte formulata in base ai dati dell'eseguibile di partenza! Questo significa che l'acid crypt, al momento della creazione del file criptato, modifica alcuni opcode della routine in base ad informazioni che preleva dal file di origine! Questo significa che, se criptiamo un altro file, il listato assembly di IDA non sarà perfettamente identico a questo, ma cambierà in alcuni valori (che dopo analizzeremo). Passiamo avanti. Dopo una mov di 0x3F (...) in ESI, vediamo un primo ciclo importante. A BH viene aggiunto un byte puntato da EAX: fate attenzione! Ricordate quello che vi ho detto prima? Che alla partenza di questo eseguibile, EAX punta all'entry point, ovvero al codice stesso della routine! Significa che a BH viene aggiunto, byte dopo byte, il valore dei byte che compongono le istruzioni della stessa routine! :S E' per questo che non possiamo utilizzare il SoftICE! Se noi debuggassimo questo eseguibile, ci verrebbe spontaneo inserire qualche breakpoint all'interno della routine! Ma così facendo, inseriremo degli "int 03h" all'interno del codice! Per questo, alla fine, BH sarà generato male in quanto gli verranno addizionati anche i byte del nostro brackpoint! Devo dire che questo crypter è davvero molto interessante! Subito, il puntatore EAX viene incrementato (per addizionare il byte successivo), mentre ESI viene decrementato... e poi, alla riga successiva c'è un jump condizionato: se ESI è uguale a zero, non salta, altrimenti riprende il ciclo. Vediamo che cosa potrebbe dire... beh... non è difficile, basta pensarci un po' su. Il ciclo viene ripetuto per ESI volte. Siccome, quattro righe prima, ESI è diventato 0x3F, e siccome abbiamo capito che a BH vengono addizionati i byte del codice di decrypting, non ci viene difficile che 0x3F è la dimensione della funzione di decrypting! 'Sto crypter diventa sempre più bello! Adesso facciamo un piccolo saltello: andiamo a guardare qualche riga più avanti, quando BL viene xorato con BH. Se fate caso, potete notare che questa è l'unica volta (dopo il ciclo di prima) che BH viene toccato! Viene utilizzato come chiave di decrypting per l'eseguibile! In poche parole, dopo dovremo generarci questo BH in base ai dati della routine di decrypting (non è molto difficile, guardando gli opcodes si arriva facilmente ad una soluzione ;). Adesso ritorniamo indietro e ricominciamo l'analisi del codice. Stavolta EAX prende il valore di EDX. Torniamo indietro fino a quando EDX prende il valore 0x00401000 e capiamo subito che EAX, stavolta, punta alla sezione da decryptare. Il ciclo di decrypting è veramente facile da capire:

AciDLeO:00404018 loc_0_404018:                 ; CODE XREF: start+26 j
AciDLeO:00404018     mov bl, [eax] ;EAX punta alla sezione
AciDLeO:0040401A     xor bl, bh    ;BL viene xorato con la somma dei bytes (BH)
AciDLeO:0040401C     ror bl, 3     ;BL viene ruotato a destra di 3
AciDLeO:0040401F     xor bl, 10h   ;BL viene xorato xon 0x10
AciDLeO:00404022     mov [eax], bl ;Il nuovo byte viene salvato
AciDLeO:00404024     inc eax       ;Passa al byte successivo
AciDLeO:00404025     dec ecx       ;Il ciclo è finito?
AciDLeO:00404026     jnz short loc_0_404018 ;NO: salta a 0x00404018

Se avete capito come avviene questo decrypting, continuate a leggere, altrimenti, prendete 'na video cassetta porno e passatevi meglio tutto questo tempo! Passando avanti, la cosa diventa ancora più carina:

AciDLeO:00404028     mov al, 0
AciDLeO:0040402A     mov edi, offset dword_0_402010
AciDLeO:0040402F     mov ecx, 28h
AciDLeO:00404034     repe stosb

Vediamo di capire di cosa si tratta. Alla riga 0x00404028 AL viene azzerato. Poi EDI diventa puntatore di qualcosa: come facciamo a scoprire a che cosa punta senza usare il SoftICE? Niente di più semplice: basta un po' di matematica!

  0x00402010 -  Prendiamo il relative virtual address in questione
  0x00400000 =  e gli togliamo l'image base dell'eseguibile.
---------------
  0x00002010    Il risultato è il virtual address puntato.

Prendiamo questo virtual address e decrementiamolo di uno fino a quando non otteniamo un numero che rispetta il section alignement di questo eseguibile (sarebbe, in questo caso, 0x00001000). Il risultato? Non ci vuole un genio! 0x00002000!

*** Nel caso qualcuno non lo sapesse, ne approfitto per spiegare che cosa significa "rispettare il section alignement". Un qualsiasi editor di eseguibili, ci mostra in chiaro il section alignement di un qualsiasi eseguibile (in questo caso 0x00001000). Le sezioni di un eseguibile devono tutte rispettare questo valore. Che cosa vuol dire? Vuol dire che il virtual address di ogni sezione deve essere multiplo del section alignement. Per capire se questo avviene, basta vedere se la divisione tra il nostro valore e il section alignement è intera o meno. Se la divisione ha il resto, ancora non ci siamo. Quindi, per rispettare il nostro section alignement, in questo caso, l'operazione da fare (in C) è questa:

void alignement()
{
    DWORD myvalue, section_align;

    myvalue = 0x00002010;
    section_align = 0x00001000;

    while(myvalue % section_align != 0)
        myvalue--

    return;
}

Riprendiamo il discorso: abbiamo ottenuto il virtual address della sezione puntata da EDI. Adesso, guardando la section table, quale sezione ha il virtual address uguale a 0x00002000? La sezione ".rdata"! Di questa sezione prendiamo nota del suo raw address (che sarebbe il suo indirizzo all'interno del file), che in questo caso è 0x00000600. A questo punto facciamo ancora un po' di matematica:

  0x00002010 -   Prendiamo il virtual address puntato
  0x00002000 =   Togliamogli il virtual address della sua sezione per ottenere il numero di byte
---------------
  0x00000010 +   Al risultato, basterà aggiungere
  0x00000600 =   il raw address della sezione in questione... 
---------------
  0x00000610     Per ottenere il punto, all'interno del file, puntato da EDI!

Non dite che sono pignolo... ma faccio i calcoli per farvi capire di che cosa stiamo parlando! Naturalmente queste, per i più esperti, sono conclusioni visibili "ad occhio nudo" :). Vabbè, abbiamo ottenuto l'indirizzo fisico. Con un editor esadecimale apriamo il file ed andiamo all'indirizzo calcolato (0x00000610) per vedere che cosa c'è:

00000610
00000620
00000630
00000640
00000650
00000660
00000670
00000680
4C20 0000 0000 0000 0000 0000 6A20 0000
0020 0000 5420 0000 0000 0000 0000 0000
8620 0000 0820 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 5C20 0000
0000 0000 7820 0000 0000 0000 7500 4578
6974 5072 6F63 6573 7300 4B45 524E 454C
3332 2E64 6C6C 0000 BB01 4D65 7373 6167
6542 6F78 4100 5553 4552 3332 2E64 6C6C
L ..........j ..
. ..T ..........
. ... ..........
............\ ..
....x ......u.Ex
itProcess.KERNEL
32.dll....Messag
eBoxA.USER32.dll

Madò, oggi mi sento particolarmente generoso! Vi ho anche postato quello che vedo io nel mio editor esadecimale! Ok te la do buona perche' questa tabellina mi e' piaciuta parecchio ;p NdQue, Comunque, sapete che cos'è questa, no? NOO??!!!? Beh, allora seguite il suggerimento che vi ho dato prima (quello della video cassetta!). Siamo sulla IT (import table. Ne parlava anche il readme.txt contenuto insieme all'acid crypt). E' questo il momento del "trick" sulla IT di cui parlava: AL è uguale a zero, EDI punta alla import table, ECX è il contatore per l'istruzione "repe stosb". Vi spiego quest'istruzione anche se non dovrei farlo :-\
Quest'istruzione qua, mette in [EDI] il byte contenuto da AL (ovvero zero) per ECX volte, incrementando EDI ogni volta di uno. Avete capito? Male per voi! Ritornate alla video cassetta! :@ In poche parole, dopo aver decriptato tutta l'area codice, il programma azzera la import table. In questo modo, se si dumpa il file, la import table viene dumpata completamente vuota. Di conseguenza il programma non parte! A noi questo non interessa perché l'algoritmo di crtitto-analisi lo eliminiamo completamente :).
Siamo quasi alla fine. Il codice lo abbiamo tutto analizzato. Adesso, vi ricordate il fatto che la sezione di codice aveva qualche valore che cambiava in base all'eseguibile di partenza? Bene, adesso dobbiamo trovare quali sono quei valori che cambiano. Procuratevi un eseguibile diverso da Cavia2.exe (magari un eseguibile particolare, chessò, scritto in delphi, così contiene un gran numero di sezioni rispetto a quelli "tradizionali") e cryptatelo con l'acid-crypt. Poi disasmatelo con IDA e prendete nota dey byte che cambiano rispetto al listato assembly di cavia2.exe. I byte che non corrispondono sono quelli che vengono modificati durante la creazione stessa dell'exe criptato. Ve li evidenzio per farvi venire il lavoro meno faticoso:

AciDLeO:00404000 start proc near
AciDLeO:00404000     pusha
AciDLeO:00404001     mov ecx, 200h
AciDLeO:00404006     mov edx, offset dword_0_401000
AciDLeO:0040400B     mov esi, 3Fh

AciDLeO:00404010 loc_0_404010:                 ; CODE XREF: start+14 j
AciDLeO:00404010     add bh, [eax]
AciDLeO:00404012     inc eax
AciDLeO:00404013     dec esi
AciDLeO:00404014     jnz short loc_0_404010
AciDLeO:00404016     mov eax, edx
 
AciDLeO:00404018 loc_0_404018:                 ; CODE XREF: start+26 j
AciDLeO:00404018     mov bl, [eax]
AciDLeO:0040401A     xor bl, bh
AciDLeO:0040401C     ror bl, 3
AciDLeO:0040401F     xor bl, 10h
AciDLeO:00404022     mov [eax], bl
AciDLeO:00404024     inc eax
AciDLeO:00404025     dec ecx
AciDLeO:00404026     jnz short loc_0_404018
AciDLeO:00404028     mov al, 0
AciDLeO:0040402A     mov edi, offset dword_0_402010
AciDLeO:0040402F     mov ecx, 28h
AciDLeO:00404034     repe stosb
AciDLeO:00404036     popa
AciDLeO:00404037     mov eax, offset dword_0_401000
AciDLeO:0040403C     jmp eax
AciDLeO:0040403C start endp

Bella la vita, eh? Adesso, secondo voi, perché ci servono questi byte che cambiano? Ci servono per diversi motivi: per capire qual'è la sezione da decryptare, per capire con quale valore bisogna xorare il carattere elaborato della sezione, per capire qual'è l'entry point da ripristinare, e soprattutto per calcolare il famoso BH (ricordate? Fa la somma dei bytes della routine di codice!). Minghia, 'sto programma mi affascina! Per capire quali byte dobbiamo prendere, occorre visualizzare questa routine come opcode (gli opcode li potete vedere con IDA selezionando, dal menu, Options -> General: nella schermata che appare, nella pagina "Disassembly" bisogna inserire il numero di byte da dedicare per gli opcode: 10 va benissimo). Naturalmente, anche qua vi evidenzio i byte che ci interessano:

60
B9 00 02 00 00  Dopo il secondo byte dobbiamo prendere la dimensione della sezione di codice
BA 00 10 40 00  Dopo il settimo byte dobbiamo ritirare l'address della sezione da decriptare
BE 3F 00 00 00

02 38
40 4E
75 FA
8B C2

8A 18
32 DF
C0 CB 03
80 F3 10  Dopo il trentatreesimo byte dobbiamo prendere il valore di xoring
88 18
40
49
75 F0
B0 00
BF 10 20 40 00  Dopo il quarantatreesimo byte dobbiamo ritirare l'address della IT
B9 28 00 00 00
F3 AA
61
B8 00 10 40 00  Dopo il cinquantaseesimo byte ritiriamo (l'entry point del file originale + imagebase)
FF E0

Una volta visto dove si trovano questi byte, basta ritirarli e calcolare il valore del famigerato BH. Non avrete intenzione di copiarvi tutti questi bytes per farlo spero! Ragioniamo un po': se i byte evidenziati fossero tutti zero... sommando i byte rimanenti che cosa avverrebbe? Uscirebbe 0x147B. Quindi, il valore di BH esce sempre maggiore di questo numero, in quanto, per ottenerlo, basterà sommare a questo, i bytes evidenziati! Del risultato bisogna prendere solo la parte bassa :) Daaaai su, non fate così! Forse è un po' lungo, ma è bello, ne vale la pena =)! Una volta ottenuto tutto il materiale che ci serve, basta metterlo insieme ed eliminare i byte della sezione AciDLeO perché ormai occupano solo spazio dentro al nostro HDD!
Ok, amici il reversing è finito! Hey! Ho detto il reversing, non il lavoro! Non avrete mica intenzione di lasciare tutto 'sto lavoro così, campato in aria! Adesso ci tocca scrivere un decrypter automatico per questi filez, no? Prima però vediamo di definire meglio qualche punto: dopo aver crittato il file, ho controllato eventuali differenze negli header tra il file originale e quello criptato, e ho notato che in quello criptato, il size of image era stato incrementato di 0x1000. Adesso è finito :). Vediamo allora quali sono le operazioni di base da fare per una corretta ricostruzione:

Molto bene! Siamo arrivati alla fine! Adesso scriviamo il decrypter automatico ed abbiamo davvero finito (il codice postato riguarda soltanto la fase di decrypting. Il resto non lo posto anche perché sarebbe inutile :)! Vediamo come possiamo fare:

void DecryptFile()
{
    HANDLE hFile;
    DWORD file_size, dd;
    IMAGE_DOS_HEADER* mz;
    IMAGE_NT_HEADERS* pe;
    IMAGE_SECTION_HEADER* sections;

    WORD number_of_sections;
    DWORD values[4];
    BYTE xoring;

    hFile = CreateFile("cavia2.exe", GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        MessageBox(0, "Impossibile aprire il file!", "Errore", MB_ICONERROR | MB_OK);
        return;
    }

    file_size = GetFileSize(hFile, 0);
    mz = (IMAGE_DOS_HEADER*) VirtualAlloc(0, file_size, MEM_COMMIT,

        PAGE_EXECUTE_READWRITE);
    ReadFile(hFile, mz, file_size, &dd, 0);
    CloseHandle(hFile);
    pe = (IMAGE_NT_HEADERS*) ((DWORD) mz + mz->e_lfanew);

    number_of_sections = pe->FileHeader.NumberOfSections;
    number_of_sections--;  // Decrementiamo il numero di sezioni

    sections = (IMAGE_SECTION_HEADER*) ((DWORD) pe + 0xF8);  // section table
    if(strcmp((char*) sections[number_of_sections].Name, "AciDLeO") != 0)
    {
        MessageBox(0, "Il file non è stato cryptato con l'AciDCrypt v0.1.2!",

            "Errore", MB_ICONERROR | MB_OK);
        return;  // Il file non è crittato con l'acidcrypt
    }

    pe->OptionalHeader.SizeOfImage -= 0x1000;
    pe->FileHeader.NumberOfSections = number_of_sections;
    values[0] = *(DWORD*)((sections[number_of_sections].PointerToRawData +

        (DWORD) mz + 2));
    values[1] = *(DWORD*)((sections[number_of_sections].PointerToRawData +

        (DWORD) mz + 7));
    xoring = *(BYTE*)((sections[number_of_sections].PointerToRawData +

        (DWORD) mz + 33));
    values[2] = *(DWORD*)((sections[number_of_sections].PointerToRawData +

        (DWORD) mz + 43));
    values[3] = *(DWORD*)((sections[number_of_sections].PointerToRawData +

        (DWORD) mz + 56));

    pe->OptionalHeader.AddressOfEntryPoint = (values[3] -

        pe->OptionalHeader.ImageBase);

    int i, n;
    DWORD global = 0x147B;
    DWORD temp;
    BYTE myBH;

    for(i=0; i<4; i++)
    {
        temp = values[i];
        for(n=0; n<5; n++)
        {
            global += BYTE(temp);
            temp >>= 8;
        }
    }

    global += xoring;
    myBH = BYTE(global); // calcola il famoso BH

    BYTE* lpsection;
    lpsection = (BYTE*) (values[1] - pe->OptionalHeader.ImageBase);
    for(i=0; i<number_of_sections; i++)
    {
        if(sections[i].VirtualAddress == (DWORD) lpsection)
        {
            lpsection = (BYTE*) ((DWORD) mz + sections[i].PointerToRawData);
            break;
        }
    }

    BYTE b;
    for(i=0; i<=values[0]; i++) // questo è l'algoritmo di decrypting della sezione
    {
        b = *(lpsection+i);
        b ^= myBH;
        _asm
        {
            mov cl, b
            ror cl, 3
            mov b, cl
        }

        b ^= xoring;
        *(lpsection+i) = b;
    }

    lpsection = (BYTE*)((DWORD) pe + 0xF8 + (number_of_sections*40));
    DWORD section_size;

    section_size = sections[number_of_sections].SizeOfRawData;
    memset(lpsection, 0, 40); // elimina l'header della sezione del crypter

    hFile = CreateFile("decrypted.exe", GENERIC_READ | GENERIC_WRITE,

        0, 0, CREATE_ALWAYS, 0, 0);
    WriteFile(hFile, mz, file_size - section_size, &dd, 0);
    CloseHandle(hFile);
    VirtualFree(mz, file_size, MEM_DECOMMIT);

    MessageBox(0, "File decryptato correttamente!", "Enjoy :)",

        MB_ICONINFORMATION | MB_OK);
    return;
}

ABBIAMO FINITOOOOO! :D Fine del reversing! Dite la verità, non è affascinante? A me è piaciuto un casino! Se adesso, per curiosità, volete vedere come funziona il crypter, dovete decrittare anche quello. Fate attenzione: come abbiamo visto all'inizio, nel crypter ci sono due sezioni che si chiamano "AciDLeO". Questo significa che è stato cryptato due volte. Quindi, per decryptarlo, bisogna decryptarlo due volte! :) Poi potete disasmarlo tranquillamente. Il codice è abbastanza chiaro (naturalmente io mi sono già fatto un giro :).
Spero di aver scritto correttamente questo mega tutorial e di esser stato chiaro al massimo. Se trovate qualcosa che non quadra, scrivetemi pure una mail piena di insulti e minacce che penserò a correggere subito l'errore! Ci si vede (?) al prossimo reversing! ;)

 

::[ active85k ]::

 

Note finali

Mi sembra di aver scritto tutto sopra...

Disclaimer

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 che ogni sviluppatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.

Reversiamo al solo scopo informativo e per migliorare la nostra conoscenza del linguaggio Assembly.