[ INPUT ]
I.1- Name :
First i seek for GetDlgItemTextA
:
_text:00402FD7 GetName:
_text:00402FD7
push 63h
; nMaxCount
_text:00402FD9
push offset Name ; lpString
_text:00402FDE
push 0C8h
; nIDDlgItem
_text:00402FE3
push [ebp+hDlg] ; hDlg
_text:00402FE6
call GetDlgItemTextA
_text:00402FEB
mov ds:SerialSize, eax
_text:00402FF0
cmp ds:SerialSize, 0
_text:00402FF7
jnz short GetSerial
_text:00402FF9
push offset aEnterName ; lpString
_text:00402FFE
push 0C9h
; nIDDlgItem
_text:00403003
push [ebp+hDlg]
; hDlg
_text:00403006
call SetDlgItemTextA
_text:0040300B
jmp EndCheckSerial
So the length of the "Name" has to be > 0... ;p
I.2- Serial :
_text:00403038 GetSerial:
_text:00403038
push 23h
; nMaxCount
_text:0040303A
push offset Serial ; lpString
_text:0040303F
push 0C9h
; nIDDlgItem
_text:00403044
push [ebp+hDlg] ; hDlg
_text:00403047
call GetDlgItemTextA
_text:0040304C
mov ds:SerialSize, eax
_text:00403051
cmp ds:SerialSize, 20h
_text:00403058
jnb short Next
_text:0040305A
push offset aMaybeAskJoe? ; lpString
_text:0040305F
push 0C9h
; nIDDlgItem
_text:00403064
push [ebp+hDlg] ; hDlg
_text:00403067
call SetDlgItemTextA
_text:0040306C
jmp EndCheckSerial
... and length(Serial) = 32...
_text:00403071 Next:
_text:00403071
mov ds:FlagValidCar, 0
_text:0040307B
mov ds:CurrentCar, 0
_text:00403085
jmp short O9AF
_text:00403087 ; ---------------------------------------------------------------------------
_text:00403087
_text:00403087 LoopO9AF:
_text:00403087
mov edx, ds:CurrentCar
_text:0040308D
movzx edi, Serial[edx]
_text:00403095
cmp edi, 30h
_text:00403098
jge short loc_0_4030A4
_text:0040309A
mov ds:FlagValidCar, 1
_text:004030A4
_text:004030A4 loc_0_4030A4:
_text:004030A4
mov edx, ds:CurrentCar
_text:004030AA
movzx edi, Serial[edx]
_text:004030B2
cmp edi, 39h
_text:004030B5
jle short loc_0_4030F1
_text:004030B7
mov edx, ds:CurrentCar
_text:004030BD
movzx edi, Serial[edx]
_text:004030C5
cmp edi, 46h
_text:004030C8
jle short loc_0_4030D4
_text:004030CA
mov ds:FlagValidCar, 1
_text:004030D4
_text:004030D4 loc_0_4030D4:
_text:004030D4
mov edx, ds:CurrentCar
_text:004030DA
movzx edi, Serial[edx]
_text:004030E2
cmp edi, 41h
_text:004030E5
jge short loc_0_4030F1
_text:004030E7
mov ds:FlagValidCar, 1
_text:004030F1
_text:004030F1 loc_0_4030F1:
_text:004030F1
inc ds:CurrentCar
_text:004030F7
_text:004030F7 O9AF:
_text:004030F7
mov edi, ds:SerialSize
_text:004030FD
cmp ds:CurrentCar, edi
_text:00403103
jb short LoopO9AF
_text:00403105
cmp ds:FlagValidCar, 0
_text:0040310C
jnz short ErrorSerialCar
Then it checks if the serial is composed only of [0-9A-F]...
_text:0040310E
push offset Serial
_text:00403113
call ascii2hex
_text:00403118
add esp, 4
...and it converts ascii to hexadecimal value ( ie '0' =>
00h, ..., '9' => 09h, 'A' => 0Ah, ... ,'F' => 0Fh )
_text:00403120
push ds:SerialSize
_text:00403126
push offset Serial
_text:0040312B
push offset Name
_text:00403130
call CheckSerial
_text:00403135
add esp, 0Ch
_text:00403138
cmp eax, 0
_text:0040313B
jz short BadSerial
...and checks if the serial is valid....
[ ANALYSE ]
II.1- Square
The procedure called (00402F49) uses 5 lookup-tables
which seems to be the ones used by : Square
So we can guess the "meaning" of these 2 call:
_text:00402F0A
push ebp
_text:00402F0B
mov ebp, esp
_text:00402F0D
sub esp, 168h
_text:00402F13
push esi
_text:00402F14
push edi
_text:00402F15
lea edi, [ebp+lpPhrase]
_text:00402F18
lea esi, [ keyword ]
_text:00402F1E
mov ecx, 47h
_text:00402F23
repe movsb
_text:00402F25
lea edi, [ebp+RoundKey_d]
_text:00402F2B
push edi
_text:00402F2C
lea edi, [ebp+RoundKey_e]
_text:00402F32
push edi
_text:00402F33
lea edi, [ebp+lpPhrase]
_text:00402F36
push edi
_text:00402F37
call GenerateRoundKeys_e
_text:00402F3C
add esp, 0Ch
_text:00402F3F
lea edi, [ebp+RoundKey_e]
_text:00402F45
push edi
_text:00402F46
push [ebp+Serial_hex]
_text:00402F49
call squareEncrypt
_text:00402F4E
add esp, 8
So now we have :
squareGenerateRoundKeys (keyword, roundKeys_e, roundKeys_d);
Serial_hex := squareEncrypt (Serial_hex,
roundKeys_e);
II.2- Magic
Then a "transformation" it is performed on each Serial_hex dword...
_text:00403250
mov ecx, 4
_text:00403255
mov ebx, 4078BCh
_text:0040325A
mov esi, [ebp+Serial_hex]
_text:0040325D
mov edi, esi
_text:0040325F
_text:0040325F LoopC:
_text:0040325F
lodsd
_text:00403260
push eax
_text:00403261
add ebx, 80h
_text:00403267
push ebx
_text:00403268
call LinearMat
_text:0040326D
add esp, 8
_text:00403270
stosd
_text:00403271
loop LoopC
In other words :
Serial_hex[0] := LinearMat(Serial_hex[0],Mat[0])
Serial_hex[1] := LinearMat(Serial_hex[1],Mat[1])
Serial_hex[2] := LinearMat(Serial_hex[2],Mat[2])
Serial_hex[3] := LinearMat(Serial_hex[3],Mat[3])
II.3- "MD5"
Using PEiD we can guess that it is MD5 but it seems that it is
modified ( initialisation, tables...)
_text:00403233
push offset Buffer
_text:00403238
call Ini
_text:0040323D
add esp, 4
_text:00403240
push offset Buffer
_text:00403245
push [ebp+Name]
_text:00403248
call Hash
Now:
[Buffer] := modified_MD5[Buffer];
( 4 dword )
II.4- CPUiD
_text:00403273
mov esi, offset Buffer
_text:00403278
xor eax, eax
_text:0040327A
cpuid
_text:0040327C
xor [esi], ebx
_text:0040327E
rol ebx, 0Bh
_text:00403281
xor [esi+4], ebx
_text:00403284
rol ebx, 0Bh
_text:00403287
xor [esi+8], ebx
_text:0040328A
rol ebx, 11h
_text:0040328D
xor [esi+0Ch], ebx
It performs a "xor cpuid" on each dd of the [Buffer]
Buffer[0] := Buffer[0] xor cpuid;
Buffer[1] := Buffer[1] xor cpuid;
Buffer[2] := Buffer[2] xor cpuid;
Buffer[3] := Buffer[3] xor cpuid;
II.5- Final Cmp
_text:00403273
mov esi, offset Buffer
[ .................................................. ]
text:00403290
xor eax, eax
_text:00403292
mov edi, [ebp+Serial_hex]
_text:00403295
mov ecx, 5
_text:0040329A
repe cmpsd
At last it checks if [Buffer] is equal to [Serial_hex]
[ KEYGEN ]
To summarize:
squareGenerateRoundKeys (keyword, roundKeys_e, roundKeys_d);
Serial_hex := squareEncrypt (Serial_hex,
roundKeys_e);
Serial_hex[0] := LinearMat(Serial_hex[0],Mat[0]);
Serial_hex[1] := LinearMat(Serial_hex[1],Mat[1]);
Serial_hex[2] := LinearMat(Serial_hex[2],Mat[2]);
Serial_hex[3] := LinearMat(Serial_hex[3],Mat[3]);
[Buffer] := modified_MD5[Buffer];
Buffer[0] := Buffer[0] xor cpuid;
Buffer[1] := Buffer[1] xor cpuid;
Buffer[2] := Buffer[2] xor cpuid;
Buffer[3] := Buffer[3] xor cpuid;
[Buffer] ?= [Serial_hex]
So the algo of the keygen is:
[Buffer] := modified_MD5[Buffer];
[Buffer + 0] := [Buffer + 0] xor cpuid;
[Buffer + 1] := [Buffer + 1] xor cpuid;
[Buffer + 2] := [Buffer + 2] xor cpuid;
[Buffer + 3] := [Buffer + 3] xor cpuid;
Buffer[0] := LinearMat(Buffer[0],Matt[0]);
Buffer[1] := LinearMat(Buffer[1],Matt[1]);
Buffer[2] := LinearMat(Buffer[2],Matt[2]);
Buffer[3] := LinearMat(Buffer[3],Matt[3]);
squareGenerateRoundKeys (keyword, roundKeys_e, roundKeys_d);
Serial_hex := squareDecrypt (Buffer, roundKeys_d);
The first part ( MD5 and "xor cpuid" ) isn't difficult...
III.1 - LinearMat
_text:004032A7 LinearMat proc
near
_text:004032A7
_text:004032A7 lpMat
= dword ptr 10h
_text:004032A7 lpDD
= dword ptr 14h
_text:004032A7
_text:004032A7
push ecx
_text:004032A8
push esi
_text:004032A9
push ebp
_text:004032AA
mov esi, [esp+lpMat]
_text:004032AE
mov eax, [esp+lpDD]
_text:004032B2
or ebp, 0FFFFFFFFh
_text:004032B5
mov ecx, 20h
_text:004032BA
xor edx, edx
_text:004032BC
test eax, eax
_text:004032BE
jnz short ForEachBit
_text:004032C0
xor eax, eax
_text:004032C2
jmp short end_LinearMat
_text:004032C4 ; ---------------------------------------------------------------------------
_text:004032C4
_text:004032C4 ForEachBit:
_text:004032C4
inc ebp
_text:004032C5
test eax, 1
_text:004032CA
jz short DontAdd
_text:004032CC
xor edx, [esi+ebp*4]
_text:004032CF
_text:004032CF DontAdd:
_text:004032CF
shr eax, 1
_text:004032D1
loop ForEachBit
_text:004032D3
xchg eax, edx
_text:004032D4
_text:004032D4 end_LinearMat:
_text:004032D4
pop ebp
_text:004032D5
pop esi
_text:004032D6
pop ecx
_text:004032D7
retn
_text:004032D7 LinearMat endp
We can take this part of the code as an endomorphism on GF(2^32)
( so xor is regarded as + ) :
LinearMat(DD) = LinearMat(DD and 2^0) + ... + LinearMat(DD and 2^31)
= DD'
...and the table [lpMat] = Mat as the matrix of the endomorphism,
so to be able to compute DD thanks to DD ( ie LinearMat-1(DD')
= DD ) we have to find Mat-1 such as:
Mat * Mat-1 = I32
How find Mat-1 ?
Build the matrix T = ( I32 | Mat ) and then performs
Gauss-Jourdan algo ( very easy on Z/2Z ;p)
we will obtain T = ( Mat-1 | I32 )....
III.2- SquareDecrypt
We can use squareGenerateRoundKeys to compute roundKeys_d
but where can we find "squareDecrypt" ? Are we
obliged to code "squareDecrypt" ? No... ;)
A part of the crackme seems to be useless ( never called )
_text:004022BA
push ebp
_text:004022BB
mov ebp, esp
_text:004022BD
sub esp, 10h
_text:004022C0
push ebx
_text:004022C1
push esi
_text:004022C2
push edi
_text:004022C3
mov edi, [ebp+8]
_text:004022C6
mov esi, [ebp+0Ch]
_text:004022C9
mov esi, ds:0[esi]
_text:004022D0
xor ds:0[edi], esi
_text:004022D7
mov edi, [ebp+8]
_text:004022DA
add edi, 4
[...................................................]
_text:00402E7E
movzx ebx, Sd[ebx]
_text:00402E86
shl ebx, 10h
_text:00402E89
xor esi, ebx
_text:00402E8B
mov ebx, [ebp-4]
_text:00402E8E
shr ebx, 18h
_text:00402E91
and ebx, 0FFh
_text:00402E97
movzx ebx, Sd[ebx]
_text:00402E9F
shl ebx, 18h
_text:00402EA2
xor esi, ebx
_text:00402EA4
mov ebx, [ebp+0Ch]
_text:00402EA7
xor esi, [ebx+8Ch]
_text:00402EAD
mov [edi+0Ch], esi
_text:00402EB0
pop edi
_text:00402EB1
pop esi
_text:00402EB2
pop ebx
_text:00402EB3
leave
_text:00402EB4
retn
Moreover it seems that this procedure plays with 5 lookup table which
are used in "squareDecrypt"... ;) I don't know why "squareDecrypt"
is here but can take advantage of this fact and uses the code in our keygen...
;)
[ END ]
There is a bug in the CrackMe ( my keygen has the same ) so you
have to restart the Crackme after each serial check...
Name |
Amenesia |
Serial |
D929B716E5C044B00D6714E25549D914 |
Despite my bad english feel free to contact me : tchernozium@yahoo.fr
|