|
Kwasek6 polymorphic code, modified Ruby Hash,
TEA (modified + mmx version)
|
|
Data
|
by Evilcry
|
|
|
02/1/2006
|
UIC's Home Page
|
Published by Quequero
|
|
Where ones sees a limit
|
Qualche mio...eventuale
commento sul tutorial :)))
|
the others sees an opporunity
|
|
Prepare for what you cannot see, expect
the unexpected, a waiting game waiting to see......
|
- WebSite:
http://evilcry.altervista.org
- www.cryptorev.org
- E-mail:
evilcry@virgilio.it / evilcry@gmail.com
- IRC
frequentato irc.azzurranet.org
#cryptorev #crack-it - irc.efnet.nl #cryptography
-
|
|
|
Difficoltà
|
NewBies () Intermedio (X) Avanzato (X)
Master ()
|
|
- Kwasek6 polymorphic code, modified Ruby Hash, TEA (modified + mmx version)
- Written by Evilcry.
- Kwasek6 è un crackme che basa la sua protezione su un keyfile, ed
implementa contemporaneamente, codice polimorfico normale (soliti mov, add,
sub, etc) ed MMX (paddx, psubd, pxor) oltre ad una versione modificata del
RubyHash, e del TEA (Tiny Encryption Algorithm) riscritto in versione MMX.
- IDA
- OllyDbg
- UnFSG (unpacker per FSG)
- MASM
- www.google.it
Troverete (crackme + keygen) nell' allegato.
Il crackme in questione è protetto con FSG (si può notare immediatamente
dalla signature "FSG!" presente fra i primi bytes del file), per
risolvere il problema basta cercare su internet un qualsiasi unpacker per
FSG. Come si può vedere non esistono editbox, ma analizzando la ImportTable
possiamo trovare alcune funzioni interessanti: CreateFile, GetFileSize etc.
(si tratta quindi di una keyfile protection). Passiamo a vedere il listato:
00401AFD push
0
00401AFF push
0
00401B01 push
3 ;OPEN_EXISTING
00401B03 push
0
00401B05 push
0
00401B07 push
80000000h ;GENERIC_READ
00401B0C push
offset aKwasek_htb ;nome del keyfile -> Kwasek.htb
00401B11 call
CreateFileA
00401B16 mov
ds:hFile, eax
00401B1B inc
eax
00401B1C jz
loc_401EEA ;Se il file non esiste salta ad unregistered
00401B22 push
0
00401B24 push
ds:hFile
00401B2A call
GetFileSize ;Determina la grandezza del file
00401B2F cmp
eax, 80h ;Se diverso da 80h (128 caratteri) salta ad unregistered
00401B34 jnz
loc_401EEA
00401B3A push
0
00401B3C push
offset NumberOfBytesRead
00401B41 push
eax
00401B42 push
offset Buffer
00401B47 push
ds:hFile
00401B4D call
ReadFile
00401B52 mov
esi, offset Buffer ;Buffer con il contenuto del file
00401B57 mov
edi, offset dword_4177D6 ;buffer di destinazione
00401B5C push
edi
00401B5D mov
ecx, 10h
00401B62 repe
movsb
00401B64 pop
edi
00401B65 push edi ;Nel
buffer di destinazione ci sono i primi 16 bytes del keyfile
La routine che abbiamo appena visto è molto semplice, non fa altro
che andare ad aprire un file già esistente, che dev'essere di 128 bytes,
e ne legge il contenuto, estrapolandone da esso i primi 16 bytes che andranno
a finire in un buffer specifico. Continuiamo con l'analisi:
00401B66 mov
eax, [edi] ;Mette in eax una dword per volta, del buffer
di destinazione
00401B68 bswap
eax
00401B6A xor
eax, 12E4B8Ch
00401B6F mov
[edi], eax ;mette la dword 'decryptata' in edi
00401B71 inc
edi
00401B72 inc
ecx ;incrementa il loop counter
00401B73 cmp
ecx, 0Dh
00401B76 jnz
short loc_401B66 ;ripete per 13 volte
00401B78 pop
edi
00401B79 cmp
byte ptr [edi], 0 ;controlla che il primo byte non sia
0
00401B7C jz
loc_401EEA
00401B82 cmp
byte ptr [edi+0Fh], 0 ;e che l'ultimo (il sedicesimo)
sia 0
00401B86 jnz
loc_401EEA
I primi 16 bytes del nostro keyfile vengono decriptati (attraverso uno bswap
ed uno xor), e alla fine la routine controlla che il primo byte non sia 0
ed il sedicesimo sia NULL (questo ci fa pensare che i primi 16 bytes opportunamente
trattati devono darci una stringa NULL-Terminated)
00401B8C mov
edx, 2 ;Index counter (fa si che la call 00401206 sia
chiamata 2 volte)
00401B91 mov
eax, offset dword_4177D6 ;Buffer che contiene la stringa
decryptata
00401B96 mov
ebx, offset dword_4177E7 ;Output buffer (In seguito chiamato
Hash)
00401B9B mov
cl, 10h
00401B9D push
eax
00401B9E push
ecx
00401B9F push
ebx
00401BA0 call
sub_401206 ;Questa call accetta 3 parametri: OutputBuff,
10h, DecryptBuff
00401BA5 mov
eax, ebx
00401BA7 add
ebx, 8
00401BAA mov
cl, 8
00401BAC dec
edx
00401BAD test
edx, edx
00401BAF jnz short loc_401B9D
Adesso andiamo ad analizzare la subroutine 00401206
00401256 mov
edi, A
00401258 rol
edi, cl
0040125A mov
esp, B
0040125C mov
ecx, C
0040125E shr
ecx, 1Bh
...
00401276 xor
edi, esi
00401278 mov
A, edi
0040127A add
A, 456C6091h ;Magic Value 1
0040127F xchg
A, B
00401280 xchg
B, C
00401282 xchg
C, D
...
004012A8 mov
A, edi
004012AA imul
A, 0AA7110C3h ;Magic Value 2
004012B0 xchg
A, B
004012B1 xchg
B, C
Come si può vedere, nella routine sono utilizzati due MagicValues
(classica definizione delle costanti crittografiche) 456C6091h
e 0AA7110C3h che con
una piccola ricerca ci indicano che siamo in presenza di un Ruby Hash,
da notare però che in questo caso la routine è leggermente modificata,
se ricordate edx ha un counter settato a 2, e di conseguenza otteremo un hash
di 16d bytes. Passiamo alla prossima porzione di routine, che implementa un
semplicissimo Engine Polimorfico, che genera un set limitato di istruzioni
basandosi sull'hash appena determinato:
00401BB1 mov
edi, offset dword_40407B ;Buffer del codice
00401BB6 mov
esi, offset dword_4177E7 ;Hash
00401BBB push
esi
00401BBC lodsb
00401BBD cmp
al, 0 ;se l'i-esimo carattere del nostro hash è
0
00401BBF jb
short loc_401BE8
00401BC1 cmp
al, 20h
00401BC3 ja
short loc_401BE8 ;se superiore a 20h salta alla prossima
routine
...
00401BD0 lodsb
00401BD1 call
sub_401AA6 ;Call che analizzeremo inseguito
00401BD6 test
cl, cl
00401BD8 jz
loc_401CD7
00401BDE add
al, 10h
00401BE0 jmp
short loc_401BD1
00401BE2 mov
al, 3 ;il nostro byte viene bilanciato poi viene chiamata
la call 00401BD1h
00401BE4 stosb
00401BE5 lodsb
;Mette nel buffer del codice l'opcode ottenuto
00401BE6 jmp
short loc_401BD1
Ho saltato molte parti di codice, perchè non vale la pena andare a
commentare ogni singola linea codice, ma solamente capire come funziona il
nostro Engine:
00401BE8 cmp
al, 21h
00401BEA jb
short loc_401C13 ;Salta al prossimo set di byte (Opcode
nel nostro caso)
00401BEC cmp
al, 2Fh
00401BEE ja
short loc_401C13
stosb
lodsb ;Mette
nel buffer del codice l'opcode ottenuto
...
00401C13 cmp
al, 30h
00401C15 jb
short loc_401C3E ;Salta al prossimo set di byte (Opcode
nel nostro caso)
00401C17 cmp
al, 80h
00401C19 ja
short loc_401C3E
stosb
lodsb ;Mette
nel buffer del codice l'opcode ottenuto
...
00401C3E cmp
al, 81h
00401C40 jb
short loc_401C5F ;Salta al prossimo set
00401C42 cmp
al, 0A0h
00401C44 ja
short loc_401C5F
...
00401C5F cmp
al, 0A1h
00401C61 jb
short loc_401C9C ;Salta al prossimo set
00401C63 cmp
al, 0E0h
00401C65 ja
short loc_401C9C
...
00401C9C cmp
al, 0E1h
00401C9E jb
short loc_401CD7 ;Salta al prossimo set
00401CA0 cmp
al, 0FFh
00401CA2 ja
short loc_401CD7
...
00401C9C cmp
al, 0E1h
00401C9E jb
short loc_401CD7 ;Salta al prossimo set
00401CA0 cmp
al, 0FFh
00401CA2 ja
short loc_401CD7
...
00401CD7 pop
esi
00401CD8 inc
esi
00401CD9 inc
edx
00401CDA cmp
edx, 0Dh ;Se non siamo al 13esimo carattere del nostro
hash
00401CDD jnz
loc_401BBB ;salta all'inizio dell'Engine
00401CE3 mov
edx, 4
00401CE8 mov
esi, offset dword_4177E7 ;esi punta al nostro Hash
00401CED loc_401CED:
00401CED xor
dword ptr [esi], 499602D2h ;queste istruzioni modificano
4 bytes del nostro 00401CF3 ror dword ptr [esi], 7 ;hash
00401CF6 add
esi, 4
00401CF9 dec
edx
00401CFA jnz
short loc_401CED
00401CFC add
ds:dword_41774A, 1 ;incrementa il contatore
00401D03 cmp
ds:dword_41774A, 10h
00401D0A jnz
loc_401BB6 ;salta all'Engine che avrà come input
l'hash modificato
00401D10 xor
ds:dword_41774A, 10h ;azzera il counter
00401D17 mov
al, 0C3h ;chiude il codice generato con un RET
00401D19 stosb
Come potete vedere il funzionamento dell'Engine non è molto complesso,
ad ogni ciclo prende un carattere del nostro hash e lo filtra attraverso una
struttura decisionale che contiene un ben definito range di opcodes. Dopo
aver iterato l'intero hash, a 00401CEDh abbiamo una banale routine che modifica
il nostro hash 4 bytes per volta, che vanno poi a costituire un nuovo input
per l'Engine. Se andiamo a fare un pò di conti vediamo, che dal nostro
hash vengono generati 1C6h bytes di codice, ed osservando approfonditamente
gli opcodes usati nella struttura dell'engine, possiamo notare che il nuovo
codice userà un set limitato di istruzioni, e cioè:
add reg,
reg
sub reg,
reg
rol reg,
int
ror reg,
int
xchg reg,
reg
xor reg,
reg
not reg
Andiamo ad analizzare adesso la prossima porzione di codice, che costituisce
la prima applicazione del codice appena generato:
00401D1A mov
esi, offset dword_417762 ;Buffer del nostro keyfile+16
00401D1F mov
eax, [esi] ;carica 24 bytes presi dal nostro keyfile
00401D21 mov
ebx, [esi+4] ;|
00401D24 mov
ecx, [esi+8] ;|
00401D27 mov
edx, [esi+0Ch] ;|
00401D2A mov
edi, [esi+10h] ;|
00401D2D mov
esi, [esi+14h] ;|
00401D30 push
ebp
00401D31 mov
ebp, offset dword_40407B
00401D36 call
ebp ;chiama il codice polimorfico
00401D38 mov
ebp, offset dword_404062 ;ebp contiene la nostra stringa
decriptata
00401D3D mov
[ebp+0], eax
00401D40 mov
[ebp+4], ebx
00401D43 mov
[ebp+8], ecx
00401D46 mov
[ebp+0Ch], edx
00401D49 mov
[ebp+10h], edi
00401D4C mov
[ebp+14h], esi
00401D4F mov
eax, ebp
00401D51 pop
ebp
00401D52 mov
ecx, 18h ;ecx verrà usato come counter, ed è
settato a 24
00401D57 mov
esi, eax
00401D59 mov
edi, offset aPolymorphisByK ;"Polymorphis by Kwasek :)"
00401D5E call
sub_401F08 ;in questa call vengono confrontate la nostra
stringa con quella
00401D63 or
eax, eax ;in decriptata
00401D65 jnz
short loc_401D6C ;vai alla prossima parte della routine
di protezione
00401D67 jmp
loc_401EEA ;salta ad UNREGISTERED
Il codice è molto semplice, vengono presi 24 bytes dal nostro keyfile
e decriptati attraverso l'algoritmo polimorfico, ed infine confrontati con
la stringa "Polymorphis by
Kwasek :)" se sono uguali, possiamo passare alla prossima parte del
crackme, altrimenti l'esecuzione salta ad unregistered. Il nostro compito
è quindi generare un algoritmo polimorfico con le istruzioni reversate,
ed applicare la stringa "Poly...." in modo da ottenere una serie
di bytes, che decriptati ci danno la stessa "Poly...", come reversare
le istruzioni lo vedremo in fase di keygenning, ora possiamo tranquillamente
procedere con la nostra analisi..
00401D7D mov
esi, offset dword_4177E7 ;esi continene il puntatore al
nostro Hash
00401D82 loc_401D82:
00401D82 push
esi
00401D83 mov
al, 0Fh
00401D85 stosb
00401D86 lodsb
00401D87 cmp
al, 0
00401D89 jb
short loc_401DA0
00401D8B cmp
al, 0EFh
00401D8D ja
short loc_401DA0
00401D8F mov
al, 0EFh
00401D91 stosb
00401D92 lodsb
...
0401DD2 pop
esi
00401DD3 inc
esi
00401DD4 inc
edx
00401DD5 cmp
edx, 0Dh
00401DD8 jnz
short loc_401D82
00401DDA mov
edx, 4
00401DDF mov
esi, offset dword_4177E7 ;esi continene il puntatore al
nostro Hash
00401DE4 xor
dword ptr [esi], 3ADE68B1h
00401DEA ror
dword ptr [esi], 3
00401DED add
esi, 4
00401DF0 dec
edx
00401DF1 jnz
short loc_401DE4 ;queste istruzioni modificano 4 bytes
del nostro hash
00401DF3 add
ds:dword_41774A, 1 ;incrementa il contatore
00401DFA cmp
ds:dword_41774A, 10h
00401E01 jnz
loc_401D7D ;salta all'Engine che avrà come input
l'hash modificato
00401E07 xor
ds:dword_41774A, 10h ;azzera il counter
00401E0E mov
al, 0C3h ;chiude il codice generato con un RET
00401E10 stosb
Questa parte dovrebbe risultarvi molto familiare, si tratta sempre del nostro
Engine, che stavolta utilizza opcodes che rientrano nelle istruzioni MMX.
La solita routine, modifica il nostro hash, per un totale di 1C6h bytes di
istruzioni mmx polimorfiche. Il set di istruzioni in questo caso è:
paddd mmx,mmx
psubd mmx,mmx
pxor mmx,mmx
00401E13 mov
esi, offset dword_41777A ;Buffer del nostro keyfile+40
00401E18 movq
mm0, qword ptr [esi] ;carica 64 bytes presi dal nostro
keyfile
00401E1B movq
mm1, qword ptr [esi+8] ;|
00401E1F movq
mm2, qword ptr [esi+10h] ;|
00401E23 movq
mm3, qword ptr [esi+18h] ;|
00401E27 movq
mm4, qword ptr [esi+20h] ;|
00401E2B movq
mm5, qword ptr [esi+28h] ;|
00401E2F movq
mm6, qword ptr [esi+30h] ;|
00401E33 movq
mm7, qword ptr [esi+38h] ;|
00401E37 mov
ebp, offset dword_40447B
00401E3C call
ebp ; dword_40447B ;Codice polimorfico MMX appena generato
00401E3E mov
esi, offset dword_404021 ;Stringa di 64 bytes decriptata
00401E43 movq
qword ptr [esi], mm0
00401E46 movq
qword ptr [esi+8], mm1
00401E4A movq
qword ptr [esi+10h], mm2
00401E4E movq
qword ptr [esi+18h], mm3
00401E52 movq
qword ptr [esi+20h], mm4
00401E56 movq
qword ptr [esi+28h], mm5
00401E5A movq
qword ptr [esi+30h], mm6
00401E5E movq
qword ptr [esi+38h], mm7
00401E62 mov
ecx, 40h ;Lunghezza della stringa
00401E67 mov
edi, offset aPolymorphismmx ;"PolymorphisMMX by Kwasek
2oo3 kwasek2@w..."
00401E6C call
sub_401F08 ;Confronta le due stringhe
00401E71 or
eax, eax
00401E73 jnz
short loc_401E77 ;Se sono uguali procedi con la prossima
routine
00401E75 jmp
short loc_401EEA ;Altrimenti salta ad UNREGISTERED
Lo schema di protezione è praticamente identico a quello già
visto, con la sola accezione che qui vengono usate istruzioni mmx e la stringa
è lunga 64 bytes. La tecnica di reverse è sempre la stessa,
dobbiamo elaborare un engine che generi le istruzioni mmx reversed. Procediamo
con la prossima parte di codice:
00401E77 push
offset dword_4177BA ;Buffer del nostro keyfile+104
00401E7C push
offset aPolymorphisByK ;"Polymorphis by Kwasek :)"
00401E81 call
sub_401F33 ;Call molto importante, che analizzeremo in
seguito
00401E86 mov
ecx, 8 ;Lunghezza della stringa
00401E8B mov
esi, offset dword_4177E7 ;Hash
00401E90 mov
edi, offset dword_4177BA ;keyfile+104
00401E95 call
sub_401F08 ;Confrontali
00401E9A or
eax, eax
00401E9C jnz
short loc_401EA0 ;Se sono uguali procedi con la prossima
routine
00401E9E jmp
short loc_401EEA ;Altrimenti
salta ad UNREGISTERED
00401EA0 loc_401EA0:
00401EA0 movd
eax, mm7
00401EA3 psllq
mm7, 20h ;Equivalente dell'istruzione SHL
00401EA7 movd
ebx, mm7
00401EAA test
eax, eax
00401EAC jnz
short loc_401EEA ;Se eax è diverso da 0 salta ad
UNREGISTERED
00401EAE test
ebx, ebx
00401EB0 jnz
short loc_401EEA ;Se eax è diverso da 0 salta ad
UNREGISTERED
00401EB2 mov
ecx, 8
00401EB7 mov
esi, offset dword_4177EF keyfile+08
00401EBC mov
edi, offset dword_404010
00401EC1 call
sub_401F08
00401EC6 or
eax, eax
00401EC8 jnz
short loc_401ECC ;Se sono uguali procedi vai a REGISTERED
00401ECA jmp
short loc_401EEA ;Altrimenti salta ad UNREGISTERED
Questa che abbiamo visto, è l'ultima parte di protezione, la struttura
rimane invariata, abbiamo infatti delle call che decriptano porzioni di keyfile,
che poi vengono confrontate con le solite stringhe. Andiamo quindi ad analizzare
queste call, e più precisamente la call 00401F33h:
00401F33 emms
0401F39 mov
esi, offset dword_4177C2 ;keyfile+112
00401F3E mov
edi, offset aWitamWszystkic ; "Witam wszystkich crackersow
a szczegoln"
00401F43 movd
mm4, dword ptr [edx] ;in edx keyfile+104
00401F46 call
sub_40208D
00401F4B movq
mm7, qword ptr [esi];in esi keyfile+112
00401F4E call
sub_40208D
...
Ho tagliato molta parte del codice, ma possiamo una cosa molto strana, ci
sono numerose chiamate a 0040208Dh, andiamo a vedere cosa succede in questa
call:
00402093 mov
esi, offset dword_4177CA ;keyfile+120
00402098 mov
edi, offset dword_404010 ;buffer finale
0040209D mov
ebx, offset aUnregistered ;"UNREGISTERED"
004020A2
lodsb
004020A3 xor
al, [ecx+ebx]
004020A6 xor
al, 7
004020A8 add
al, 6
004020AA sub
al, [ecx+ebx]
004020AD stosb
004020AE loop
loc_4020A2
004020B0 mov
ecx, 8
004020B5 mov
esi, offset dword_404010
004020BA mov
edi, offset dword_4177CA
004020BF repe
movsb ;copia il buffer finale in keyfile+120
004020C1 popa
004020C2 retn
Il codice è abbastanza semplice, i bytes del keyfile vengono combinati
con la stringa unregistered attraverso una serie di istruzioni come add, sub
e xor che come sapete possono essere tranquillamente reversate, poichè
non comportano perdite di bit (e quindi di informazione), ed il nostro compito
è proprio questo, dobbiamo \notate all'uscita della call a 0040208Dh
i valori contenuti nei vari registri rimangono invariati, e la routine (escluse
appunto le chiamate a 0040208Dh) fa qualcos'altro..come avrete intuito, la
seconda funzione di tutte queste call è confonderci le idee! proviamo
infatti a riscrivere l'intera routine, eliminando tutte queste call:
00401F39 mov
esi, offset dword_4177C2 ;keyfile+112
00401F3E mov
edi, offset aWitamWszystkic
00401F43 movd
mm4, dword ptr [edx] ;in mm4 keyfile+104
00401F4B movq
mm7, qword ptr [esi] ;in mm7 keyfile+104
00401F53 movd
mm1, dword ptr [edx+4]
00401F5C mov
ecx, ds:dword_401F1B ;ecx=32375E7Eh
00401F67 movd
mm0, ds:dword_401F23 ;MM0=12345678h
00401F73 movd
mm6, ecx
00401F7B pxor
mm6, mm0
00401F83 pxor
mm5, mm5
00401F8B mov
ecx, 20h ;Contatore settato a 32d
00401F95 push
edx
00401F9B paddd
mm5, mm6 ;MM5 + 020030806h
00401FA3 movq
mm0, mm1
00401FAB movq
mm3, mm1
00401FB3 call
sub_402075 ;Equivale a shl MM0,4 cioè MM0*16
00401FBD psrld
mm3, 5
00401FC6 paddd
mm0, qword ptr [eax] ;"Polymorp" + MM0
00401FCE paddd
mm3, qword ptr [eax+4];"hisMMX b" + MM3
00401FDB pxor
mm0, mm1
00401FE9 paddd
mm0, mm5
00401FF0 pxor
mm0, mm3
00401FFE paddd
mm0, mm4
00402006 movq
mm4, mm0
0040200E call
sub_402075 ;MM0*16
00402018 movq
mm3, mm4
00402020 psrld
mm3, 5
00402029 paddd
mm0, qword ptr [eax+8] ;"y Kwasek" + MM0
00402036 pxor
mm0, mm4
00402044 paddd
mm3, qword ptr [eax+0Ch] " 2oo3 kw" + MM3
0040204D paddd
mm0, mm5
00402054 pxor
mm0, mm3
00402057 paddd
mm7, qword ptr [edi]
00402062 paddd
mm1, mm0
00402065 dec
ecx
00402066 jnz
loc_401F9B
0040206C pop
edx ;Buffer del nostro keyfile+104
0040206D movd
dword ptr [edx], mm4 ;Inserisci i valori appona trovati
nel buffer
00402070 movd
dword ptr [edx+4], mm1 ;|
00402074 retn
Proviamo a riscrivere in C questo pezzetto di codice:
void code(long*
v, long* k) {
unsigned long
y=v[0],z=v[1], sum=0, delta=0x20030806, n=32 ;
while (n-->0)
{
sum += delta
;
y += (z>5)+k[1];
z += (y>5)+k[3];
}
v[0]=y ; v[1]=z;
}
Come per magia scopriamo, che si tratta semplicemente della procedura di
Encryption del TEA, solo che riscritto in istruzioni MMX e con il delta cambiato
:) Quello che dobbiamo fare è semplicemente reversare questo algoritmo,
scrivendo cioè la routine di Decryption.
Piccola nota sul TEA:
|
TEA
Il TEA (Tiny Encryption Alorithm) e' stato creato da David Wheeler
e Roger Needham. Questo algoritmo ha il pregio di essere abbastanza
veloce e di offrire una "sicurezza-media", si basa principalmente su
un'architettura di tipo Feistel Network, perciò la cifratura/decifratura
di dati avviene "a blocchi" solitamente di 64 bits. E' un sistema di
cifratura simmetrica quindi se invertiamo l'intero algoritmo
ed usiamo la stessa chiave utilizzata per crittare, ed applichiamo
in entrata il CipherText, in uscita otterremo il PlainText.
|
Un ultimo piccolo reverse ed abbiamo finalmente finito, se ricordate all'uscita
della call 00401F33h, viene controllato se MM7 è uguale a zero, se
non lo è, l'esecuzione salta ad Unregistered. La routine responsabile
del valore di MM7 in uscita è contenuta all'interno della procedura
del TEA, ben nascosta in modo da essere confusa con l'intero altro codice,
eccola:
00401FD7 push
ecx
00401FD8 push
0Bh
00401FDA pop
ecx
00401FDB pxor
mm0, mm1
00401FDE paddd
mm7, qword ptr [edi] ;edi punta alla stringa "Witam
wszystkich" la quale viene
00401FE1 loop
loc_401FDB ;sommata a keyfile+112 che è contenuta
in MM7
00401FE3 pop
ecx 00402032 push ecx
00402033 push
29h
00402035 pop
ecx
00402036 pxor
mm0, mm4
00402039 paddd
mm7, qword ptr [edi]
0040203C loop
loc_402036
Anche qui si tratta di un banalissimo reverse, che vi riporto qui sotto:
pxor mm7, mm7
push 2688 ;(0Bh+0Bh+29h+15h)*20h
pop ecx mov
edi, offset Witam
LOOP:
psubd mm7,
qword ptr [edi]
loop LOOP
mov edi, offset
RisultatoMM7
movq qword
ptr [edi], mm7
E con questo abbiamo finalmente finito il reverse di Kwasek6! :)
Note per un eventuale keygen
Un imbocca al lupo per chi volesse cimentarsi nel keygenning
di questo crackme, non perchè sia eccessivamente difficile, ma necessita
sicuramente di un buon ordine di coding ed una gran pazienza :), credo che
possa aiutarvi, suddividere il crackme in fasi successive:
-
Nome
- Ruby_Hash(Nome)
- Engine Polimorfico 1
- Engine Polimorfico 2 (MMX)
- TEA (Decrypter)
- MM7 Check
- Unreg. loop
Ci vediamo al prossimo tute!!! :))
Thanks to Badsector, per avermi risparmiato mooolte linee di codice nella fase
di codice :)
Noi reversiamo al solo scopo informativo
e di miglioramento del linguaggio Assembly.
Capitoooooooo????? Bhè credo di si ;))))