Tarantula
Non serve neanche il softice!

Data

by "AndreaGeddon"

 

17/08/2002

UIC's Home Page

Published by Quequero


zanne e artigli acuminati lacerano secoli di cadaveri

Qualche mio eventuale commento sul tutorial :)))

:-P

non potevi sperare che non scrivessi qui

cavalcando uno stormo di vampiri la morte viene distillata dalla notte

....

Home page: http://www.andreageddon.8m.com
E-mail: andreageddon@hotmail.com
irc.azzurra.org   #crack-it
 

....

Difficoltà

( )NewBies (x)Intermedio (x)Avanzato ( )Master

 

Il tarantula di Spider, un simpatico crackme che non avevo finito :) e che mi costava perenni sbeffeggiamenti dallo spiderino! Ora è tempo di vendetta! Ho sempre odiato i ragni! All'epoca Spider sottopose il Tarantula a Kill3xx, che manco a dirlo lo risolse solo con ida. L'altra sera stavo parlando con Ntoskrnl e mi ha detto che lo stava facendo, così mi è preso lo schizzo e mi ci sono messo :-) Ovviamente batterlo con il softice sarebbe troppo facile... userò solo ida :-) se c'è riuscito quel leim di killo perchè non posso riuscirci io? (e fu così che fu lunciato :-P). A parte gli scherzi l'ho fatto tanto per giocare un pò con l'idc scripting di IDA. Mi sento un pò in colpa per aver fregato il crackme a Ntoskernel che ci voleva scrivere il tutorial, vabbè i credits glieli ho dati visto che anche lui l'aveva finito considerate questo tutorial come se fosse anche suo :-)


Tarantula
Non Serve neanche il Softice!
Written by AndreaGeddon

Introduzione

Usiamo gli IDC in ida per decriptarci il codice e poi ce lo analizziamo con calma :-)

Tools usati

SoftIce? non stavolta!
IDA (4.17, ma qualunque versione va bene)
un HexEditor

URL o FTP del programma

www.bigspider.cjb.net

Notizie sul programma

Un piccolo crackme con registrazione del seriale, lo scopo è quello di patcharlo per fargli accettare tutti i serial.

Essay

 

Eccomi qui di nuovo a scrivere un tutorial finalmente! E' passato un pò di tempo ma non mi sono arrugginito, stavo solo cercando il mio neurone. Dunque qui abbiamo il crackme di Spider, ispirandomi a Kill3xx lo risolveremo usando soltanto ida. Prendiamo il crackme e disassembliamolo. L'entry point è a 00401000 e vediamo che ida non applica nessuna signature, questo significa che il crackme è stato scritto direttamente in asm. Partiamo dall'entry point: dopo un paio di cicli vediamo

 

.text:00401036 loc_0_401036: ; DATA XREF: .text:00401002o
.text:00401036 lds edx, [eax-13h]
.text:00401039 lodsb
.text:0040103A mov ch, 0F9h
.text:0040103A ; -----------------------------------------------------------
.text:0040103C dd 9CD78FD9h, 3F9B4EC9h, 0F177BFE9h, 3FB5E02h, 0C2D93221h
.text:0040103C dd 0BD99DDDDh, 0FE3B5628h, 0E11B4E61h, 0CF164FCBh, 479B4F07h

cioè robaccia alquanto indefinita. Il crackme è chiaramente criptato, e i cicli prima di questa parte sono appunto i cicli di decrypt. Ma se è criptato dobbiamo andare in softice e dumparcelo? No! Vi ho detto che softice non lo usiamo! Possiamo invece usare l'idc scrpiting di ida: quello che ci serve di sapere è solo l'algoritmo di decrypt per poi replicarlo. Cominciamo con il primo ciclo:

 

.text:00401000 start: ; DATA XREF: .text:004012C2o
.text:00401000 xor eax, eax
.text:00401002 mov ecx, offset CryptedBlock1
.text:00401007 mov ebx, offset EndCryptedBlock1
.text:0040100C 
.text:0040100C CODE_Decrypt: ; CODE XREF: .text:0040101Fj
.text:0040100C mov edx, [ecx]           ;prendi la dword criptata
.text:0040100E xor edx, CODE_Mask       ;xor con la mask
.text:00401014 rol edx, cl              ;rol della dword con il LSB del suo VA
.text:00401016 add eax, edx             ;eax = checksummer che sarà usato come DATA_Mask
.text:00401018 mov [ecx], edx           ;mette a posto la dword decriptata
.text:0040101A add ecx, 4
.text:0040101D cmp ecx, ebx
.text:0040101F jb short CODE_Decrypt    ;ripeti per 0x207 dwords

ho aggiunto qualche commento e qualche nome tanto per rendere più comprensibilie il tutto. CryptedBlock1 e EndCryptedBlock1 rappresentano i limiti inferiore e superiore dell'area di codice da decriptare. I loro valori numeri sono 0x00401036 e 0x00401853, che di danno un delta di 0x81D bytes, ovvero 0x207 dwords (+1 byte di resto). Come vediamo nel ciclo il decrypt avviene dword per dword, prima con uno XOR con la CODE_Mask (il cui valore numerico è 0x4FC9FD9B) e poi con un ROL della dword per il less significant byte dell'indirizzo virtuale corrispondente all'istruzione. Bene, it's time to decrypt. Scriviamo con il notepad un semplice file di testo che sarà il nostro codice IDC. L'IDC è in tutto simile al C, per cui non sarà difficile implementare questo decrypt. Le reference del linguaggio IDC le trovate nell'help di ida ed anche online sul sito della datarescue. Ecco il codice che dobbiamo scrivere per il decrypt della CODE:

 

static decrypt()
{
  auto i, quattro, temppp, blocco;   // definizione variabili
  blocco = 0x00401036;               // address di partenza
  quattro = 0x36;                    // LSB dell'address di partenza
  for(i=0; i<519; i++)               // ciclo per tutte le dwords (519 dec)
  {
    temppp = Dword(blocco);          // prende la dword dall'address
    temppp = temppp^(0x4FC9FD9B);    // la xora
    temppp = rotate_left(temppp, quattro);   // la rolla
    PatchDword(blocco, temppp);      // e la rimette a posto decriptata
    blocco = blocco + 4;             // aggiorna l'address
    quattro = (quattro + 4) & 0x000000FF;  // aggiorna il LSB
  }
}

static rotate_left(target, rotandi)
{
  auto x, flag;
  flag = 0;
  for(x=0; x<rotandi; x++)            // esegue uno shift bit per bit
  {
    flag = target & 0x80000000;       // e di volta in volta se il MSBit è settato...
    target = target << 1;             // ...dopo lo shift...
    if(flag) target = target | 1;     // ...lo reinserisce a destra
    flag = 0;                         // uhm questo è inutile che ce l'ho messo a fare?
  }
  return target;                      // ritorna la dword rollata (ma non fumata)
}

 

non sono riuscito ad usare l'inline assembler con l'IDC per cui l'istruzione ROL ho dovuto emularla con la funzione rotate_left, si lo so è orribile ma funziona :). Adesso salvate questo file di testo, andiamo in ida, menu, File->Idc File... e caricate il vostro IDC. Vi apparirà la floating bar con i due pulsanti, uno per aprire ed editare lo script e uno per compilarlo. Se lo compilate e non vi dà nessun errore allora lo potete eseguire  perchè la sintassi è corretta (per la semantica sta a voi controllare :-P). Ok ora il nostro script è pronto, prima di usarlo però facciamo l'undefine (tasto u oppure usate il trackpopup menu) di tutto il codice criptato dall'address 0040136 in poi, tanto per comodità. Ora la funzione del nostro script è "decrypt()", per cui per usarla andiamo nel menu File->Idc Command... e ci scriviamo un bel

decrypt();

e facciamo eseguire. Tempo un secondo e i bytes che abbiamo undefinito cambieranno. Ora vi basta premere "C" sull'address 00401036 e vedrete comparire il codice decriptato! Wow! Attenti!! Non tutti bytes vengono disassemblati, per cui scendete e cercate le zone che sono ancora definite con "db", e con C fatele disassemblare. Attenti2!!! C'è del codice polimorfico (o smc come amate chiamarlo), e questo porta ad uno scorretto disassemblaggio, ad esempio

 

.text:00401097 jnz short loc_0_40108C
.text:00401099 jz short near ptr loc_0_40109B+1           ;uhmmmmm
.text:0040109B 
.text:0040109B loc_0_40109B: ; CODE XREF: .text:00401099j
.text:0040109B add [ecx+40339C3Dh], al                    ;qui bisogna undefinire
.text:004010A1 add bl, al
.text:004010A3 pop es
.text:004010A3 ; -----------------------------------------------------
.text:004010A4 db 0 ; 
.text:004010A5 db 0 ; 
.text:004010A6 ; -----------------------------------------------------
.text:004010A6 
.text:004010A6 loc_0_4010A6: ; CODE XREF: .text:004010BCj
.text:004010A6 jnz locret_0_4011E4

il salto alla riga 00401099 salta a 0040109C, ma ida disassembla a partire da 0040109B, un byte prima, per cui per risolverlo andate su 0040109B, fate l'undefine, e poi premete C sul byte a 0040109C. Ripetete questo per tutti i casi simili nel codice (non ce ne sono molti). Fatto ciò la sezione CODE è completamente in chiaro. Ci serve la sezione data. Tornando ai cicli iniziali subito dopo il ciclo di decrypt del CODE c'è il ciclo di decrypt della DATA:

 

.text:00401021 mov ecx, offset DATA_Section      ;crypted data start address

.text:00401026 mov ebx, (offset Buffer+7)        ;crypted data end address
.text:0040102B 
.text:0040102B DATA_Decrypt: ; CODE XREF: .text:00401034j
.text:0040102B mov edx, [ecx]                    ;prendi dword criptata
.text:0040102D xor edx, eax                      ;xora con la DATA_Mask ricavata prima
.text:0040102F mov [ecx], edx                    ;salva dword decriptata
.text:00401031 inc ecx
.text:00401032 cmp ecx, ebx
.text:00401034 jnz short DATA_Decrypt            ;ripeti byte per byte

stavolta è molto più semplice, c'è solo un ciclo di xor, la DATA_Mask se ve la calcolate trovate che vale 0x6B650DB5. Scriviamo quindi la funzione relativa al data decrypt:

 

static datadecrypt()
{
  auto temp2, i, bloccodata;
  bloccodata = 0x00403000;
  for(i=0; i<0xb2; i++)
  {
    temp2 = Dword(bloccodata);
    temp2 = temp2 ^ 0x6B650DB5;
    PatchDword(bloccodata, temp2);
    bloccodata++;
  }
}

 

really simple. Come prima, carichiamo l'idc, andiamo in idc command e diamo il comando

datadecrypt();

et voilà, vedrete apparire tante stringhe sensate dall'address 00403000, tra cui anche le stringhe di errata e corretta registrazione. Fatto ciò abbiamo il file completamente decriptato. Attenzione! Abbiamo decriptato solo il disassemblato, il file exe è sempre quello originale. Quel che importa è che ora possiamo iniziare a vedere come funge il crackme. Subito dopo il data decrypt abbiamo il seguente pezzetto:

 

.text:00401036 sub ecx, ecx
.text:00401038 mov eax, offset ChecksumFromHere
.text:0040103D mov ebx, offset EndCryptedBlock1
.text:00401042 
.text:00401042 Checksum1: ; CODE XREF: .text:00401049j
.text:00401042 add ecx, [eax]
.text:00401044 add eax, 4
.text:00401047 cmp eax, ebx
.text:00401049 jb short Checksum1
.text:0040104B cmp ecx, 0D0A485A3h ; check checksum :)
.text:00401051 jnz _ExitProcess

è un checksum per controllare l'integrità della sezione di codice, per cui tenelo a mente se dopo volete patchare :-). Poco dopo c'è solo del codice di inizializzazione che serve per chiamare la WinMain alla riga 00401087, all'interno della quale inizia il codice vero e proprio: alla riga 00401261 vediamo una chiamata a RegisterClassA e poi alla riga 00401295 vediamo la chiamata a CreateWindowExA che crea la finestra principale. Quindi possiamo segnarci l'address della Window Procedure nella WNDCLASS:

 

.text:004011FC mov dword ptr [ebp-28h], offset WindowProcedure ; WndProc

 

così poi esaminiamo con calma cosa fa la finestra. Cmq proseguendo dopo la creazione della finestra troviamo un altro checksum

 

.text:004012BB Checksum2: ; CODE XREF: .text:004012C0j
.text:004012BB add ecx, [eax]
.text:004012BD cmp eax, ebx
.text:004012BF inc eax
.text:004012C0 jb short Checksum2
.text:004012C2 mov eax, offset start ; on exit ECX = CheckSum
.text:004012C2 ; EBX = WinMain
.text:004012C7 
.text:004012C7 CheckSum2a: ; CODE XREF: .text:004012CDj
.text:004012C7 xor ecx, [eax] ; eax = Start = 00401000
.text:004012C9 inc eax ; xor previous checksum with code bytes
.text:004012CA inc eax
.text:004012CB cmp eax, ebx
.text:004012CD jb short CheckSum2a
.text:004012CF sub CODE_Mask, ecx ; update decrypt mask

e dopo, cosa molto più importante, c'è il set di un timer:

 

.text:004012D8 push offset TimerProcedure
.text:004012DD push 19h ; timeout
.text:004012DF push eax ; timer ID = 1
.text:004012E0 push dword ptr [ebp-50h] ; hWnd
.text:004012E3 jnz short loc_0_4012E6
.text:004012E3 ; -----------------------------------------------
.text:004012E5 db 77h ; w
.text:004012E6 ; -----------------------------------------------
.text:004012E6 
.text:004012E6 loc_0_4012E6: ; CODE XREF: .text:004012E3j
.text:004012E6 call SetTimer

ricordatevi di questo più tardi, per ora proseguiamo con l'analisi. Subito dopo  il SetTimer c'è il ciclo di vita della finestra principale, con GetMessage etc etc, per cui ora ci dobbiamo spostare alla WindowProcedure della finestra principale

 

.text:00401319 WindowProcedure: ; DATA XREF: .text:004011FCo
.text:00401319 push ebp ; (HWND, UMSG, WPARAM, LPARAM)
.text:00401319 ; +8 +C +10 +14
.text:0040131A mov ebp, esp
.text:0040131C xor esi, esi
.text:0040131E cmp dword ptr [ebp+0Ch], 2 ; WM_DESTROY
.text:00401322 jnz short WM_COMMAND
.text:00401324 push esi ; quit = 0
.text:00401325 call _PostQuitMessage
.text:0040132A jmp ZeroQuit
.text:0040132F ; MACRO_WM WM_COMMAND
.text:0040132F WM_COMMAND: ; CODE XREF: .text:00401322j
.text:0040132F cmp dword ptr [ebp+0Ch], 111h ; WM_COMMAND
.text:00401336 jnz DEFAULT
.text:0040133C mov eax, [ebp+10h] ; control ID
.text:0040133F cmp ax, 1
.text:00401343 jnz short ID_2_Registra
.text:00401345 
.text:00401345 ID_1_Esci:
.text:00401345 push esi ; NULL
.text:00401346 push esi ; NULL
.text:00401347 push 10h ; WM_CLOSE
.text:00401349 push dword ptr [ebp+8] ; HWND
.text:0040134C call SendMessageA
.text:00401351 jmp ZeroQuit
.text:00401356 ; --------------------------------------------------
.text:00401356 
.text:00401356 ID_2_Registra: ; CODE XREF: .text:00401343j

vedete il processing dei messaggi, il parsing del WM_COMMAND relativo alle voci di menu, cmq a noi interessa ovviamente la parte relativa al "registra", vediamo bene che succede:

 

.text:00401360 sidt qword ptr SIDT_LIMIT_Int1Addr
.text:00401367 mov eax, dword ptr SIDT_BASE
.text:0040136C add eax, 8 ; goto INT 1
.text:0040136F mov ebx, [eax] ; ebx = first 4 bytes
.text:00401371 shl ebx, 10h ; get low address bits
.text:00401374 or bx, [eax+6] ; add high address bits
.text:00401378 ror ebx, 10h ; obatin address of int descriptor
.text:0040137B mov dword ptr SIDT_LIMIT_Int1Addr, ebx
.text:00401381 add eax, 10h ; goto INT 3
.text:00401384 mov ebx, [eax] ;as above
.text:00401386 shl ebx, 10h
.text:00401389 or bx, [eax+6]
.text:0040138D ror ebx, 10h
.text:00401390 mov dword ptr Int3Address, ebx
.text:00401396 xor eax, eax
.text:00401398 jz short loc_0_40139C

qui vediamo semplicemente del codice che con SIDT prende address e size della IDT, dopodichè salva gli address dei vettori dell'interrupt 1 e dell'interrupt 3, che guarda caso sono il single_step e il break_point :-) che sia un debug tampering?? :-P Proseguiamo

 

.text:0040139C call _GetCurrentThread
.text:004013A1 xchg eax, ecx
.text:004013A2 mov ebx, offset CONTEXT_ContextFlags
.text:004013A7 mov CONTEXT_ContextFlags, 10010h ; context flags = CONTEXT_DEBUG_REGISTERS | CONTEXT_i486
.text:004013B1 inc eax
.text:004013B2 jnz short loc_0_4013B6
.text:004013B2 ; --------------------------------------------
.text:004013B4 db 0Fh ; 
.text:004013B5 db 8Ah ; è
.text:004013B6 ; --------------------------------------------
.text:004013B6 loc_0_4013B6: ; CODE XREF: .text:004013B2j
.text:004013B6 ; .text:004013E8j
.text:004013B6 push ebx ; pcontext
.text:004013B7 push ecx ; hthread
.text:004013B8 call GetThreadContext
.text:004013BD mov eax, dword ptr SIDT_LIMIT_Int1Addr
.text:004013C2 mov CONTEXT_DR0, eax 

.text:004013C7 mov CONTEXT_DR2, eax
.text:004013CC mov eax, dword ptr Int3Address
.text:004013D1 mov CONTEXT_DR1, eax
.text:004013D6 mov CONTEXT_DR3, eax
.text:004013DB mov CONTEXT_DR7, 155h ; CONTEXT.DR7 = L0 + L1 + L2 + L3 + LE
.text:004013E5 dec eax
.text:004013E6 jnz short loc_0_4013F0
.text:004013E8 jz short loc_0_4013B6
.text:004013EA jmp loc_0_401790
.text:004013EA ; ---------------------------------------------
.text:004013EF db 0E9h ; Ú
.text:004013F0 ; ---------------------------------------------
.text:004013F0 loc_0_4013F0: ; CODE XREF: .text:004013E6j
.text:004013F0 push ebx ; pcontext
.text:004013F1 push ecx ; hthread
.text:004013F2 call SetThreadContext

 

bene, che succede qui? Gli address dei vettori dell'interrupt 1 e dell'interrupt 3 vengono messi nei debug registers 0/2 e 1/3, e poi viene aggiornato il debug register 7 settando i flag L0 L1 L2 L3 e LE che servono ad abilitare le breakpoint condition, in pratica con questo trick il programma usa dei BPM per controllare quando vengono eseguiti gli int1 e int3 (e quindi si accorge se lo si sta tracciando), simpatico vero? Continuando si arriva alla

 

.text:004013F8 push offset ReigstraDialogProc ; pDlgProc
.text:004013FD clc
.text:004013FE push dword ptr [ebp+8] ; hParentWnd
.text:00401401 jb loc_0_4017ED
.text:00401407 push offset aDlg_register ; TemplateName
.text:0040140C push hInstance ; hInstance
.text:00401412 jnb short loc_0_401415
.text:00401412 ; --------------------------------------------
.text:00401414 db 8Eh ; Ä
.text:00401415 ; --------------------------------------------
.text:00401415 loc_0_401415: ; CODE XREF: .text:00401412j
.text:00401415 call DialogBoxParamA

e qui viene creato il Dialog di registrazione, quello dove chiede il seriale. E' d'uopo spostarci nella relativa dialog procedure. Tagliamo corto: il codice relativo alla pressione del bottone OK è quello alla riga 004014A8, vedete che c'è l'installazione di un paio di seh frames e poi di nuovo il giochetto con i debug registers, fino ad arrivare a:

 

.text:0040152E push 0Bh ; StrSize
.text:00401530 push offset Buffer ; Buffer
.text:00401535 push 65h ; Control ID
.text:00401537 push dword ptr [ebp+8] ; HWND
.text:0040153A jb short loc_0_40153D
.text:0040153A ; ---------------------------------

.text:0040153C db 76h ; v
.text:0040153D ; ---------------------------------
.text:0040153D loc_0_40153D: ; CODE XREF: .text:0040153Aj
.text:0040153D call GetDlgItemTextA
.text:00401542 pushf
.text:00401543 or byte ptr [esp], 1   ;set trap flag
.text:00401547 popf
.text:00401548 pushf
.text:00401549 or byte ptr [esp+1], 1
.text:0040154E popf
.text:0040154F pushf
.text:00401550 xor byte ptr [esp], 7
.text:00401554 popf
.text:00401555 pushf
.text:00401556 or dword ptr [esp], 0FFFFFFFFh
.text:0040155A popf
.text:0040155B push 40h
.text:0040155D push offset aBravo ; "Bravo! :-)"
.text:00401562 push offset aComplimentiNonEr ; "Complimenti, non era per niente facile!"
.text:00401567 push dword ptr [ebp+8]
.text:0040156A dec esp
.text:0040156B call MessageBoxA

dopo che prende il testo troviamo il trick del trap flag, cioè una volta settato il TF in EFL l'istruzione dopo popf provocherà una eccezione di tipo SINGLE_STEP che verrà notificata al debugger. Essendoci softice la notifica viene dispatchata da quest'ultimo e se lo steppate finirete nella riga successiva, invece in condizioni di assenza di debugger l'eccezione non sarebbe stata dispatchata da nessun debugger e quindi sarebbe scattato il seh handler (previamente installato). Anche questa è una contromisura anti tracing: se finite alla riga 00401556 dopo il popf avrete probabilmente un crash, infatti l'esecuzione alla riga 00401547 doveva deviare verso il seh handler all'address 00401606. Ora vi risparmio la fatica di cercare, perchè non troverete niente di utile che si riferisca al salto alla messagebox di errata se non del codice che vi farà girare un pò in tondo. Torniamo quindi all'inizio. Vi ricordate della famosa TimerProcedure all'address 00401092? Bene! E' il momento di setacciarla :-) All'inizio avete di nuovo il giochetto dei debug registers, per cui scendiamo sotto il SetThreadContext e vediamo cosa abbiamo:

(le "J" indicano quale jump deve essere eseguito)

 

.text:00401154 loc_0_401154: ; CODE XREF: .text:0040115Aj
.text:00401154 ; .text:00401161j
.text:00401154 mov ebx, 169830Eh
.text:00401159 inc eax
.text:0040115A jz short loc_0_401154
.text:0040115C jmp short loc_0_40115F ; j
.text:0040115C ; ---------------------------------------------------------------------------
.text:0040115E db 0D0h ; ð
.text:0040115F ; ---------------------------------------------------------------------------
.text:0040115F loc_0_40115F: ; CODE XREF: .text:00401151j
.text:0040115F ; .text:0040115Cj
.text:0040115F jnz short loc_0_401164 ; j
.text:00401161 jz short loc_0_401154
.text:00401161 ; ---------------------------------------------------------------------------
.text:00401163 db 1Bh ; 
.text:00401164 ; ---------------------------------------------------------------------------
.text:00401164 loc_0_401164: ; CODE XREF: .text:0040115Fj
.text:00401164 sub ebx, 1295263h
.text:0040116A jnz short loc_0_40116D
.text:0040116A ; ---------------------------------------------------------------------------
.text:0040116C db 7Ch ; |
.text:0040116D ; ---------------------------------------------------------------------------
.text:0040116D loc_0_40116D: ; CODE XREF: .text:0040116Aj
.text:0040116D ; .text:0040116Fj
.text:0040116D mov eax, [ebx] ; ebx now = 004030AB = Buffer

vediamo che tramite una sottrazione calcola l'address del buffe (per poterlo usare indirettamente), qui inizia a controllare cosa c'è nel buffer che ha preso il testo, vuol dire che ci stiamo avvicinando :) continuiamo:

 

.text:00401177 mov ecx, 833631h
.text:0040117C loc_0_40117C: ; CODE XREF: .text:00401173j
.text:0040117C xor edx, ecx
.text:0040117E jz short loc_0_401177
.text:00401180 loc_0_401180: ; CODE XREF: .text:00401189j
.text:00401180 jnz short loc_0_401183 ; j
.text:00401180 ; ------------------------------------

.text:00401182 db 68h ; h
.text:00401183 ; ------------------------------------
.text:00401183 loc_0_401183: ; CODE XREF: .text:00401180j
.text:00401183 sub ecx, 431EA1h
.text:00401189 js short loc_0_401180
.text:0040118B jmp short loc_0_40118E ; j
.text:0040118B ; ------------------------------------
.text:0040118D db 0A3h ; ú
.text:0040118E ; ------------------------------------
.text:0040118E loc_0_40118E: ; CODE XREF: .text:0040118Bj
.text:0040118E mov edx, 6A1A4h
.text:00401193 jo short loc_0_401199 ; j
.text:00401195 jno short loc_0_401199
.text:00401195 ; ------------------------------------
.text:00401197 db 0Fh ; 
.text:00401198 db 23h ; #
.text:00401199 ; ------------------------------------
.text:00401199 loc_0_401199: ; CODE XREF: .text:00401193j
.text:00401199 ; .text:00401195j
.text:00401199 call CheckRoutine  ;on entry ecx=401790  edx=6A1A4h

bene entriamo nella routine di check; per comodità elimino le righe inutili (smc):

 

.text:0040119F loc_0_40119F: ; CODE XREF: .text:004011B5j
.text:0040119F add edx, 397649h
.text:004011A5 push offset LOCRET2
.text:004011AA push offset LOCRET1
.text:004011AF retn ; to LOCRET1

.text:004011B2 add esp, 4             ; <---- la call ci porta qui!!!!
.text:004011B5 jnz short loc_0_40119F ; j
.text:004011B8 LOCRET1: ; CODE XREF: .text:004011AFj
.text:004011B8 ; DATA XREF: .text:004011AAo
.text:004011B8 mov ebx, [ebx+4]
.text:004011BB retn ; TO LOCRET2
.text:004011BD LOCRET2: ; CODE XREF: .text:004011C9j
.text:004011BD ; DATA XREF: .text:004011A5o
.text:004011BD add eax, ebx
.text:004011BF jz short loc_0_4011C4 ; j
.text:004011C1 jnz short loc_0_4011C4
.text:004011C3 scasd
.text:004011C4 loc_0_4011C4: ; CODE XREF: .text:004011BFj
.text:004011C4 ; .text:004011C1j
.text:004011C4 shr eax, 1
.text:004011C6 stc
.text:004011C7 jb short loc_0_4011CC ; j
.text:004011C9 jmp short near ptr LOCRET2+1
.text:004011CC loc_0_4011CC: ; CODE XREF: .text:004011C7j
.text:004011CC shl eax, 1
.text:004011CE pusha
.text:004011CF jmp short loc_0_4011D3
.text:004011D3 loc_0_4011D3: ; CODE XREF: .text:004011CFj
.text:004011D3 popa
.text:004011D4 jnb short loc_0_4011DE   ;<- eccolo!
.text:004011D6 jb short loc_0_4011D9 ; j
.text:004011D9 loc_0_4011D9: ; CODE XREF: .text:004011D6j
.text:004011D9 xor eax, eax
.text:004011DB jz short loc_0_4011E1 ; j
.text:004011DE loc_0_4011DE: ; CODE XREF: .text:004011D4j
.text:004011DE jmp edx ; jumps to 004017ED : BAD CODE!
.text:004011E1 loc_0_4011E1: ; CODE XREF: .text:004011DBj
.text:004011E1 jmp ecx ; jumps to 00401790 : GOOD!
.text:004011E4 locret_0_4011E4: ; CODE XREF: .text:004010A6j
.text:004011E4 leave
.text:004011E5 retn 10h ; EndTimerProcedure

questi due ultimi salti sono quelli decisivi: uno salta alla messagebox di errore e uno salta alla messagebox di avvenuta registrazione. Il JNB alla riga 004011D4 è quello che decide quale di questi due jump usare, per cui abbiamo trovato quello che ci serve! Gli opcode dei due jump sono relativamente FF E2 e FF E1. Ci basta trasformare il primo in JUMP ECX! Quindi basta un solo byte :) Il programma è cmq criptato, ma non c'è problema, basta calcolarci la relativa dword criptata. Innanzitutto troviamo l'allineamento della dword: il jump che vogliamo patchare è quello all'address 004011DE, per cui dobbiamo vedere come era allineata la dword criptata. Il codice di decrypt partiva da 00401036 ed avanzava di 4 bytes alla volta, per cui è immediato vedere che 004011DE è proprio l'inizio di una dword. Ottimo. I bytes dunque sono

FF E2    jump  edx

D4         (byte di disturbo)

FF E1    jump ecx

corrispondenti alla dword 0xFFD4E2FF, mentre i corrispondenti byte criptati erano la dword 0xB09A7664. Ora la nuova dword che dobbiamo inserire sarà 0xFFD4E1FF. Ricordate però i checksum? Ci sono i controlli di integrità, ed inoltre il checksum era usato anche per il data decrypt, per cui se vogliamo svangarcela basta sfruttare l'assorbimento del checkusm, come per il krypton2 :-). Se in una dword togliamo nella successiva aggiungiamo, così il bilancio totale sarà invariato. Per cui i calcoli che dobbiamo fare sono:

 

004011DE  ->  (FFD4E1FF ror DE) xor 4FC9FD9B = B09A7A64

004011E2  ->  (C2C9D6E1 ror E2) xor 4FC9FD9B = 3F7B8823

 

nella prima dword E2 è diventato E1, quindi nella seguente D5 è diventato D6 e siamo pari :-). Ovviamente se andate a guardare il disasm il byte che abbiamo usato per bilanciare la perdita era fortunatamente

.text:004011E3 D5 db 0D5h ; i

un byte di disturbo (smc), altrimenti avremmo dovuto trovare qualche altra dword da qualche altra parte da poter utilizzare per il bilanciamento. Adesso potete fare la modifica come più vi aggrada e vedrete che il crackme è risolto (per inciso l'offset di 004011DE è 5DE e quello di 004011E2 è 5E2).

Ecco qui risolto il ragnetto di Spider, ci vediamo alla proxima!

Ciauz

AndreaGeddon

 

Note finali

Ammettiamo pure che il risultato sia antiestetico, dal nostro limitato punto di vista umano. E allora? Non è umano e non pretende di esserlo.

Disclaimer

Noi reversiamo perchè non abbiamo nulla di meglio da fare :P.

Capitoooooooo????? Bhè credo di si ;)))) 


Home