Risolviamo il BruteMe 2 di Spider (la semplicità innanzitutto)
Pensavi di esserti liberato di me spido? Io brutalizzerò anche il tuo bruteme2, che tu lo voglia oppure no, ihihih.
[Si ma non ti montare la testa... NdSpider]
Questa volta nel readme spider ha pure scritto:
Ecco il mio secondo BruteMe... Se la prima volta un volontario buco rendeva la soluzione piuttosto semplice, stavolta le cose sono più complicate... L'idea di fondo è la stessa, ma in questo caso non si ha praticamente nessun riferimento a ciò che potrebbe essere contenuto nella parte di codice criptata, tanto più che nel codice criptato vi è un secondo layer di decrypt... ve lo dico per risparmiarvi un po' di tempo :)
Solo ??? Spider mi deludi, stai diventando prevedibile, sarà l'età...
[Troppo tardi, ti sei già montato la testa... :P NdSpider]
Ok, basta scherzi, mano a IDA e iniziamo a guardarci il codice, come sempre il punto da brute forcare lo troviamo subito:
:00401135 push 3E8h
:0040113A push dword ptr [ebp+8]
:0040113D call GetDlgItemInt ; prende il testo come numero
:00401142 push eax ; pusha eax
:00401143 movzx ecx, ax ; ne prende la prima word e la mette in ecx
:00401146 shr eax, 10h ; shifta eax di 16 bit
:00401149 mov edx, eax ; edx = eax
:0040114B add edx, ecx ; edx = edx + ecx
:0040114D pop eax ; recupera il numero originale
:0040114E jz loc_401227 ; salta a errore se = 0
:00401154 test dx, dx ; dx = 0 ?
:00401157 jnz loc_401227 ; se no, allora salta a errore
:0040115D pusha ; salva il contenuto dei registri
:0040115E mov edx, offset loc_401175 ; ecx = offset byte crittati
:00401163 mov ecx, 26h ; ecx = 38d
:00401168
:00401168 loc_401168:
:00401168
:00401168 xor [edx], eax ; xora i byte con la cifra da noi inserita
:0040116A sub dword ptr [edx], 5C5893h ; sottrae 5C5893h
:00401170 add edx, 4 ; incrementa l'offset
:00401173 loop loc_401168 ; continua la decrittzione
Le istruzioni mi sembrano chiare, spider ci semplifica un poco le cose per quanto riguarda il fattore tempo per la decrittazione, infatti il range che ci consente di usare per la decrittazione si limita appunto ad una word, spiegazione: noi inseriamo un numero, il programma considera la dword xxxxyyyy che è il nostro numero e mette la low word in ecx, quindi avremo ecx = yyyy, dopodiché shifta il numero di 16 bit quindi avrà in eax la cifra xxxx, quindi controlla che xxxx + yyyy sia uguale a 0 (in word intendiamoci, in dword avremo 10000h), altrimenti la cifra immessa non è valida. Fatto questo controllo limitando la cifra da noi immessa, il programma procede col decrypting.
Prima di parlare come risolvere facciamo una cosina:
:00401075 push offset sub_0_4012C1
:0040107A push 64h
:0040107C push 0
:0040107E push 0
:00401080 call SetTimer ; noppiamo queste istruzioni
E rinominiamo il titolo della main window in "BruteMe2" (per questo ci avvaliamo del resource hacker).
Questo perché nel brute mi baso sul titolo della window e non sul nome della classe, e purtroppo spider ha messo un effetto grafico che a tempo cambia il titolo della window. Fatta questa piccola modifica iniziamo a vedere come brute forcare.
L'idea che mi è venuta consiste nel caricare il bruteme, immettere il seriale, mandare il messaggio di check del seriale e controllare se il prog ci mostra il msgbox di seriale corretto, se no allora ricomincia il tutto incrementando il seriale. Ah ovviamente questa soluzione è possibile effettuarla solo su sistemi NT, su architetture 9x il pc vi freeza se viene eseguito un comando come ad esempio outsd in modo scorretto (il che con la decrittazione è quasi sicuro). Ho molta poca stima nei confronti degli os che non differenziano tra codice eseguito in user o in kernel mode diciamolo subito.
Vediamo adesso il codice che assicuro mi è costato solo 10 minuti appena (anzi mi scuso se non fosse proprio stupendo).
; bm2.asm -----------------------------------------------
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
include \masm32\include\gdi32.inc
includelib \masm32\lib\gdi32.lib
.const
ID_OPENDLG equ 40002d
ID_REGISTER equ 1
.data
Bruteme2 db "BruteMe2.exe", 0
Main db "BruteMe2", 0
Registra db "Registra", 0
Sfotti db "Sfotti?", 0
Titolo db "Seriale BruteMe2:", 0
ws db "%u", 0
Serial db 11 dup(?)
sinfo STARTUPINFO <>
pinfo PROCESS_INFORMATION <>
.data?
hWnd dd ?
hReg dd ?
count dd ?
.code
start:
; fa in modo che in caso di crash del processo
; noi non veniamo avvertiti tramite dlgbox dall'os
push SEM_NOGPFAULTERRORBOX
call SetErrorMode
; azzera eax
xor eax, eax
next:
; incrementa eax e lo mette in count
inc eax
mov count, eax
; crea il processo
push offset pinfo
push offset sinfo
push NULL
push NULL
push HIGH_PRIORITY_CLASS
push FALSE
push 0
push 0
push NULL
push offset Bruteme2
call CreateProcess
; se non è stato creato chiude
test eax, eax
jz close
repeat_f:
; dorme 150 millisecondi
push 150d
call Sleep
; cerca la main window
push offset Main
push NULL
call FindWindow
; se non la trova, riprova
test eax, eax
jz repeat_f
; apre la dlg di registrazione
; uso postmessage dato che mando
; un msg ad window che non fa parte
; del mio processo
push NULL
push ID_OPENDLG
push WM_COMMAND
push eax
call PostMessage
; calcola il seriale
mov ecx, count
mov esi, 10000h
sub esi, ecx
shl esi, 10h
mov si, cx
; lo stampa come stringa
push esi
push offset ws
push offset Serial
call wsprintf
; dorme 20 millisecondi
push 20d
call Sleep
; cerca la dlg di registrazione
push offset Registra
push NULL
call FindWindow
; se non la trova chiude il prog
test eax, eax
jz close
; mette in hReg l'handle ricavato
mov hReg, eax
; prende l'handle dell'edit box della
; window di registrazione
push 1000d
push eax
call GetDlgItem
; se l'handle è nullo, chiude
test eax, eax
jz close
; mette l'handle in hWnd
mov hWnd, eax
; controlla la grandezza del buffer/seriale
push offset Serial
call lstrlen
; da qua in poi stampa carattere per carattere
; nella edit box della dlg di registrazione
mov esi, eax
lea edi, Serial
add esi, edi
post:
xor ebx, ebx
mov bl, byte ptr [edi]
push 0
push ebx
push WM_CHAR
push hWnd
call PostMessage
inc edi
cmp edi, esi
jl post
; dorme 120 millisecondi
push 120d
call Sleep
repeat_r:
mov eax, hReg
; manda il msg di controllare il seriale
; alla dlg di registrazione
push NULL
push ID_REGISTER
push WM_COMMAND
push eax
call PostMessage
test eax, eax
jz close
; dorme 140 millisecondi
push 140d
call Sleep
; controlla che la window di registrazione
; sia ancora presente sullo schermo
push offset Registra
push NULL
call FindWindow
; se non lo è ricomincia il ciclo e incrementa il serial
test eax, eax
jz get_next
; controlla che non sia arrivata il msgbox
; che ci dice che il seriale non è corretto
; questo può accadere se non è passato abbastanza
; tempo fra l'input del seriale e il getdlgitemint che
; prende il serial
push offset Sfotti
push NULL
call FindWindow
; se ha trovato la window allora ripete il comando register
test eax, eax
jnz repeat_r
; se tutte queste cose non si sono verificate
; allora è possibile che il seriale sia giusto
; e il prog lascia al bruteme tutto il tempo
; per decrittazione e esecuzione del codice
; 1 secondo netto per la precisione
; (non si sa mai)
push 1000d
call Sleep
; prende la finestra col focus
call GetForegroundWindow
mov edx, hReg
; controlla se la finestra col focus è
; la dlg di registrazione, se così non fosse
; allora il seriale è corretto e quindi
; salta al msgbox
cmp eax, edx
jnz msgbox
get_next:
; termina il processo
push 0
push pinfo.hProcess
call TerminateProcess
; dorme per 30 millisecondi
push 30d
call Sleep
; controlla che il count sia minore o uguale
; a FFFFh se è così può ricominciare il ciclo
; altrimenti esce (messo per sicurezza il check)
mov eax, count
cmp eax, 0FFFFh
jle next
jmp close
msgbox:
; ci mostra il seriale corretto
push NULL
push offset Titolo
push offset Serial
push NULL
call MessageBox
close:
push NULL
call ExitProcess
end start
; -------------------------------------------------------
Chiaramente i tempi prestabiliti di attesa sono impostati per la mia cpu, diversa velcità del pc -> diversi tempi di attesa.
Lascio eseguire il brute per un po' (manco pochissimo eh), e dopo qualche tempo mi arriva il msgbox di congratulazioni e il seriale valido in un msgbox, il seriale sarebbe: 1468901491 che in hex sarebbe 578DA873h.
Ma ora voglio vedermi il codice del bruteme2 su IDA (non ho voglia di caricare il sice) quindi mi faccio il solito idc script. Facciamo l'undefine sui bytes crittati e poi applichiamo lo script:
; decrypt.idc -------------------------------------------
static decrypt1()
{
auto x, mod;
for(x = 0x00401175; x <= 0x00401175 + (0x26 * 4); x = x + 4)
{
mod = Dword(x);
mod = mod ^ 1468901491;
mod = mod - 0x5C5893;
PatchDword(x, mod);
}
}
; -------------------------------------------------------
Definiamo i byte decrittati come code e ricaveremo il secondo layer di decrypt:
:00401175 mov ecx, 36DB6DB6h
:0040117A push 0
:0040117C pop eax
:0040117D xchg eax, ecx ; eax = 36DB6DB6h && ecx = 0
:0040117E add ecx, 5Ah ; ecx = 5Ah
:00401184 push 371B7F4Ah
:00401189 pop ebx
:0040118A sub ebx, eax ; ebx = 371B7F4Ah - 36DB6DB6h
:0040118A ; ebx = offset decrittazione
:0040118C
:0040118C loc_0_40118C:
:0040118C
:0040118C xor [ebx], al ; xora il byte puntato da ebx con al
:0040118E ror eax, 8 ; ruota i bit di eax di 8
:00401191 inc ebx ; incrementa l'offset
:00401192 loop loc_0_40118C ; ripete il ciclo
E me scrivo il dovuto idc script:
; decrypt.idc -------------------------------------------
static decrypt2()
{
auto count, mod, calc, x;
x = 0;
for(count = 0x00401194; count <= 0x00401194 + 0x5A; count++)
{
mod = Byte(count);
if(x == 0) calc = 0xB6; // tanto per non usare il ror
if(x == 1) calc = 0x6D; // fa schifo ma tanto non ce ne importa
if(x == 2) calc = 0xDB;
if(x == 3) calc = 0x36;
if(x == 3) x = 0;
else x++;
mod = mod ^ calc;
PatchByte(count, mod);
}
}
; -------------------------------------------------------
E vediamo il codice decrittato:
:00401194 push 40h
:00401196 call sub_0_4011A1 ; salta..
:0040119B push edi
:0040119C outsd
:0040119D ja short near ptr loc_0_4011BE+2
:0040119F and [eax], eax
:004011A1
:004011A1 sub_0_4011A1 proc near
:004011A1 call sub_0_4011E6 ; qua e risalta...
:004011A6 inc ebx
:004011A7 outsd
:004011A8 ins dword ptr es:[edi], dx
:004011A9 jo short near ptr loc_0_401212+5
:004011AB imul ebp, [ebp+65h], 2169746Eh
:004011B2 and [eax+61h], cl
:004011B5 imul esp, [eax], 766F7274h
:004011BB popa
:004011BC jz short near ptr loc_0_401227+6
:004011BE
:004011BE loc_0_4011BE:
:004011BE and [ecx+6Ch], ch
:004011C1 and [ebx+65h], dh
:004011C4 jb short loc_0_40122F
:004011C6 popa
:004011C7 ins byte ptr es:[edi], dx
:004011C8
:004011C8 loc_0_4011C8:
:004011C8 and gs:[ebx+6Fh], ah
:004011CC jb short near ptr loc_0_40123A+6
:004011CE db 65h
:004011CE jz short near ptr loc_0_401241+4
:004011D1 outsd
:004011D2 and [ecx+ebp*2+20h], ah
:004011D6 jno short near ptr loc_0_40124B+2
:004011D8 db 65h
:004011D8 jnb short near ptr loc_0_40124B+4
:004011DB outsd
:004011DC and [ebx+72h], ah
:004011DF popa
:004011E0 arpl [ebx+6Dh], bp
:004011E3 and gs:[eax], eax
:004011E3 sub_0_4011A1 endp
:004011E3
:004011E6
:004011E6 sub_0_4011E6 proc near
:004011E6 push dword ptr [ebp+8] ; qua
:004011E9 call MessageBoxA ; chiama il msg box di congratulazioni
Ok, direi che abbiamo finito. Come potete notare la soluzione è molto semplice, quella che aveva in mente spider era un po' più complessa, praticamente si trattava di trasformare il crackme in un autokeygen, quindi sarebbe stato doveroso eseguire il codice in un thread a parte, mettere seh ecc. ecc. Io il codice per brute forcare l'ho scritto in 10 minuti, quindi credo che valutando il tempo necessario per l'implementazione della soluzione di spider e la sua esecuzione, sia nel complesso più lungo della mia idea (ebete).
Alla prossima.
Ntoskrnl