First we can see that the CrackMe is packed with
FSG (using PEiD):
We can easily find OEP with the OllyDbg option: "Trace real entry blockwise"
seg001:00413831 loc_0_413831:
seg001:00413831
dec byte ptr [esi]
seg001:00413833
jz near ptr dword_0_402D71
; OEP
seg001:00413839
push esi
seg001:0041383A
seg001:0041383A loc_0_41383A:
seg001:0041383A
push ebp
seg001:0041383B
call dword ptr [ebx+4]
seg001:0041383E
stosd
seg001:0041383F
jmp short loc_0_413821
seg001:0041383F start
endp
Then dump and rebuild the IAT...
1-Input
It doesn't use GetDlgItemText ,neither GetWindowTextA...
so I seek for the string "Invalid user data."
seg000:00403991
mov ebx, offset aInvalidUserData
seg000:00403996
mov esi, offset Serial
seg000:0040399B
push esi
; lParam
seg000:0040399C
push 41h
; wParam
seg000:0040399E
push 0Dh
; Msg
seg000:004039A0
push 3EAh
; nIDDlgItem
seg000:004039A5
push [ebp+hDlg] ; hDlg
seg000:004039A8
call SendDlgItemMessageA
seg000:004039AD
cmp eax, 40h
seg000:004039B0
jnz short End
seg000:004039B2
mov edi, offset Name
seg000:004039B7
push edi
; lParam
seg000:004039B8
push 41h
; wParam
seg000:004039BA
push 0Dh
; Msg
seg000:004039BC
push 3E9h
; nIDDlgItem
seg000:004039C1
push [ebp+hDlg] ; hDlg
seg000:004039C4
call SendDlgItemMessageA
seg000:004039C9
cmp eax, 5
seg000:004039CC
jl short End
So, Serial length = 40h and Name length > 4
Then the Name is used to creat a 256 bits key (NameKey)...
seg000:004039CE
push edi
seg000:004039CF
push eax
; Name length
seg000:004039D0
push edi
; Name
seg000:004039D1
call GenSubKey
... and the serial string is converted: '0' -> 0, '1' ->1, ...,
'F' -> F
seg000:004039D6
push esi
seg000:004039D7
push esi
; serial
seg000:004039D8
call AsciiToHex
seg000:004039DD
jb End
2- Serial
Then NameKey and a constant ( MagicKey ) are used to modify the
Serial (ModifiedSerial):
seg000:004039E2 Loop:
seg000:004039E2
push esi
; NameKey
seg000:004039E3
push offset MagicKey ; a constant
seg000:004039E8
push edi
; Serial
seg000:004039E9
call GenereKey
seg000:004039EE
add esi, 10h
seg000:004039F1
add edi, 10h
seg000:004039F4
dec ecx
seg000:004039F5
jnz short Loop
seg000:00403A45 GenereKey proc
near
seg000:00403A45
seg000:00403A45 Name
= dword ptr 8
seg000:00403A45 MagicKey
= dword ptr 0Ch
seg000:00403A45 Serial
= dword ptr 10h
seg000:00403A45
seg000:00403A45
push ebp
seg000:00403A46
mov ebp, esp
seg000:00403A48
pusha
seg000:00403A49
mov esi, [ebp+Serial]
seg000:00403A4C
mov edi, [ebp+Name]
seg000:00403A4F
xor eax, eax
seg000:00403A51
mov ecx, 80h
seg000:00403A56
seg000:00403A56 LoopGenKey:
seg000:00403A56
shl dword ptr [edi+0Ch], 1
seg000:00403A59
rcl dword ptr [edi+8], 1
seg000:00403A5C
rcl dword ptr [edi+4], 1
seg000:00403A5F
rcl dword ptr [edi], 1
seg000:00403A61
setb al
seg000:00403A64
or [edi+0Ch], eax
seg000:00403A67
push esi
seg000:00403A68
push [ebp+MagicKey]
seg000:00403A6B
call [off_0_403A79 + eax*4]
seg000:00403A72
loop LoopGenKey
seg000:00403A74
popa
seg000:00403A75
leave
seg000:00403A76
retn 0Ch
seg000:00403A76 GenereKey endp
seg000:00403A79 off_0_403A79 dd offset Cas1
seg000:00403A7D
dd offset Cas2
So if the current bit is 0 GenereKey call Cas1 and if the current
bit is 1 it calls Cas2...
seg000:00403B87 Cas1
proc near
seg000:00403B87
seg000:00403B87 MagicKey
= dword ptr 8
seg000:00403B87 Serial
= dword ptr 0Ch
seg000:00403B87
seg000:00403B87
push ebp
seg000:00403B88
mov ebp, esp
seg000:00403B8A
pusha
seg000:00403B8B
xor ecx, ecx
seg000:00403B8D
push ecx
seg000:00403B8E
push ecx
seg000:00403B8F
push ecx
seg000:00403B90
push ecx
seg000:00403B91
mov esi, [ebp+MagicKey]
seg000:00403B94
mov edi, [ebp+Serial]
seg000:00403B97
mov edx, 80h
seg000:00403B9C
seg000:00403B9C LoopCase1:
seg000:00403B9C
shr dword ptr [edi], 1
seg000:00403B9E
rcr dword ptr [edi+4], 1
seg000:00403BA1
rcr dword ptr [edi+8], 1
seg000:00403BA4
rcr dword ptr [edi+0Ch], 1
seg000:00403BA7
jnb short loc_0_403BB7
seg000:00403BA9
seg000:00403BA9 Loop64_C1:
seg000:00403BA9
mov eax, [ecx+esi]
seg000:00403BAC
xor [esp+ecx], eax
seg000:00403BAF
add ecx, 4
seg000:00403BB2
and ecx, 0Fh
seg000:00403BB5
jnz short Loop64_C1
seg000:00403BB7
seg000:00403BB7 loc_0_403BB7:
seg000:00403BB7
add esi, 10h
seg000:00403BBA
dec edx
seg000:00403BBB
jnz short LoopCase1
seg000:00403BBD
cld
seg000:00403BBE
pop eax
seg000:00403BBF
stosd
seg000:00403BC0
pop eax
seg000:00403BC1
stosd
seg000:00403BC2
pop eax
seg000:00403BC3
stosd
seg000:00403BC4
pop eax
seg000:00403BC5
stosd
seg000:00403BC6
popa
seg000:00403BC7
leave
seg000:00403BC8
retn 8
seg000:00403BC8 Cas1
endp
seg000:00403BC8
How determine Serial with Cas1(Serial) ?
If "Y1 = Cas1(Serial1)" and "Y2 = Cas1(Serial2)" then "Y2
xor Y1 = Cas1(Serial2 xor Serial1)"
Moreover a Serial can be consider as:
BnBn-1...B1B0 =000...001*B0 xor 000...010*B1 xor ...
xor 010...000*B(n-1) xor 100...000*Bn
so Cas1(BnBn-1...B1B0 ) = Cas1(000...001)*B0
xor Cas1(000...010)*B1 xor ... xor Cas1(010...000)*B(n-1) xor
Cas1(100...000)*Bn
So AnAn-1...A1A0 = Cas1(BnBn-1...B1B0 ) = BnBn-1...B1B0
*M with M a matrix:
|
B0 |
B1 |
... |
Bn-1 |
Bn |
|
|
|
|
\/ |
\/ |
\/ |
\/ |
\/ |
|
|
|
( |
Cas1(B0 )n |
Cas1(B1 )n |
... |
Cas1(Bn-1 )n |
Cas1(Bn )n |
) |
> |
An |
( |
Cas1(B0 )n-1 |
Cas1(B1 )n-1 |
... |
Cas1(Bn-1 )n-1 |
Cas1(Bn )n-1 |
) |
> |
An-1 |
( |
... |
... |
... |
... |
... |
) |
> |
... |
( |
Cas1(B0 )1 |
Cas1(B1 )1 |
... |
Cas1(Bn-1 )1 |
Cas1(Bn )1 |
) |
> |
A1 |
( |
Cas1(B0 )0 |
Cas1(B1 )0 |
... |
Cas1(Bn-1 )0 |
Cas1(Bn )0 |
) |
> |
A0 |
Performing xor on the differentes col we can transform M in :
|
B? xor ... xor B? |
B? xor ... xor B? |
... |
B? xor ... xor B? |
B? xor ... xor B? |
|
|
|
|
\/ |
\/ |
\/ |
\/ |
\/ |
|
|
|
( |
1 |
Cas1(B1 )n |
... |
Cas1(Bn-1 )n |
Cas1(Bn )n |
) |
> |
An |
( |
0 |
1 |
... |
Cas1(Bn-1 )n-1 |
Cas1(Bn )n-1 |
) |
> |
An-1 |
( |
... |
... |
... |
... |
... |
) |
> |
... |
( |
0 |
0 |
... |
1 |
Cas1(Bn )1 |
) |
> |
A1 |
( |
0 |
0 |
... |
0 |
1 |
) |
> |
A0 |
Now it is easy to dermine which col have to be xored to:
AnAn-1...A1A0 = ModifiedSerial...
|
|
|
... |
|
B? xor ... xor B? |
|
|
|
|
\/ |
\/ |
\/ |
\/ |
\/ |
|
|
|
( |
1 |
Cas1(B1 )n |
... |
Cas1(Bn-1 )n |
(ModifiedSerial)n |
) |
> |
An |
( |
0 |
1 |
... |
Cas1(Bn-1 )n-1 |
(ModifiedSerial)n-1 |
) |
> |
An-1 |
( |
... |
... |
... |
... |
... |
) |
> |
... |
( |
0 |
0 |
... |
1 |
(ModifiedSerial)1 |
) |
> |
A1 |
( |
0 |
0 |
... |
0 |
(ModifiedSerial)0 |
) |
> |
A0 |
So Cas1(B? xor ... xor B?) = ModifiedSerial... in others words B? xor
... xor B? = Serial
seg000:00403BCB Cas2
proc near
seg000:00403BCB
seg000:00403BCB var_2C
= dword ptr -2Ch
seg000:00403BCB var_28
= dword ptr -28h
seg000:00403BCB var_24
= dword ptr -24h
seg000:00403BCB MagicKey
= dword ptr 8
seg000:00403BCB Serial
= dword ptr 0Ch
seg000:00403BCB
seg000:00403BCB
push ebp
seg000:00403BCC
mov ebp, esp
seg000:00403BCE
pusha
seg000:00403BCF
xor ecx, ecx
seg000:00403BD1
push ecx
seg000:00403BD2
push ecx
seg000:00403BD3
push ecx
seg000:00403BD4
push ecx
seg000:00403BD5
mov esi, [ebp+MagicKey]
seg000:00403BD8
mov edi, [ebp+Serial]
seg000:00403BDB
cld
seg000:00403BDC
mov edx, 80h
seg000:00403BE1
seg000:00403BE1 LoopCase2:
seg000:00403BE1
xor ebx, ebx
seg000:00403BE3
seg000:00403BE3 Loop64_C2:
seg000:00403BE3
lodsd
seg000:00403BE4
and eax, [ecx+edi]
seg000:00403BE7
call NumEax
seg000:00403BEC
add ecx, 4
seg000:00403BEF
and ecx, 0Fh
seg000:00403BF2
jnz short Loop64_C2
seg000:00403BF4
shr ebx, 1
seg000:00403BF6
rcl [esp+30h+var_24], 1
seg000:00403BFA
rcl [esp+30h+var_28], 1
seg000:00403BFE
rcl [esp+30h+var_2C], 1
seg000:00403C02
rcl dword ptr [esp], 1
seg000:00403C05
dec edx
seg000:00403C06
jnz short LoopCase2
seg000:00403C08
pop eax
seg000:00403C09
stosd
seg000:00403C0A
pop eax
seg000:00403C0B
stosd
seg000:00403C0C
pop eax
seg000:00403C0D
stosd
seg000:00403C0E
pop eax
seg000:00403C0F
stosd
seg000:00403C10
popa
seg000:00403C11
leave
seg000:00403C12
retn 8
seg000:00403C12 Cas2
endp
We can use the matrix N:
|
B0 |
B1 |
... |
Bn-1 |
Bn |
|
|
|
|
\/ |
\/ |
\/ |
\/ |
\/ |
|
|
|
( |
Cas2(B0 )n |
Cas2(B1 )n |
... |
Cas2(Bn-1 )n |
Cas2(Bn )n |
) |
> |
An |
( |
Cas2(B0 )n-1 |
Cas2(B1 )n-1 |
... |
Cas2(Bn-1 )n-1 |
Cas2(Bn )n-1 |
) |
> |
An-1 |
( |
... |
... |
... |
... |
... |
) |
> |
... |
( |
Cas2(B0 )1 |
Cas2(B1 )1 |
... |
Cas2(Bn-1 )1 |
Cas2(Bn )1 |
) |
> |
A1 |
( |
Cas2(B0 )0 |
Cas2(B1 )0 |
... |
Cas2(Bn-1 )0 |
Cas2(Bn )0 |
) |
> |
A0 |
... to determine the Serial value thanks to ModifiedSerial value...
So to obtain the Serial thanks to the ModifiedSerial we have to make
rotate the name in the reverse order and use the differents matrices (
M if the current bit is 0, N if it is 1 )
3 - Check
Then the first part of the serial is checked:
seg000:004039FD
push esi
; ModifiedSerial
seg000:004039FE
push edi
; NameKey
seg000:004039FF
call vTEA
seg000:00403A04
jb short Fin
seg000:00403A81 vTEA
proc near
seg000:00403A81
seg000:00403A81 TEA1_A
= dword ptr -28h
seg000:00403A81 TEA1_B
= dword ptr -24h
seg000:00403A81 TEA2_A
= dword ptr -20h
seg000:00403A81 TEA2_B
= dword ptr -1Ch
seg000:00403A81 var_18
= byte ptr -18h
seg000:00403A81 arg_0
= dword ptr 8
seg000:00403A81 arg_4
= dword ptr 0Ch
seg000:00403A81
seg000:00403A81
push ebp
seg000:00403A82
mov ebp, esp
seg000:00403A84
pusha
seg000:00403A85
mov esi, [ebp+arg_4]
seg000:00403A88
mov edi, [ebp+arg_0]
seg000:00403A8B
mov ecx, 2
seg000:00403A90
seg000:00403A90 LoopTEA_Encode:
seg000:00403A90
sub esp, 8
seg000:00403A93
push esp
; Buffer
seg000:00403A94
push edi
; Key
seg000:00403A95
push esi
; Data
seg000:00403A96
call vTEA_Encode
seg000:00403A9B
xchg esi, edi
seg000:00403A9D
loop LoopTEA_Encode
seg000:00403A9F
mov eax, [esp+28h+TEA1_A]
seg000:00403AA2
xor eax, [esp+28h+TEA2_A]
seg000:00403AA6
jnz short Invalid
seg000:00403AA8
mov eax, [esp+28h+TEA1_B]
seg000:00403AAC
sub eax, [esp+28h+TEA2_B]
seg000:00403AB0
jnz short Invalid
seg000:00403AB2
mov ecx, 4
seg000:00403AB7
repe cmpsd
seg000:00403AB9
jecxz short Invalid
seg000:00403ABB
clc
seg000:00403ABC
seg000:00403ABC loc_0_403ABC:
seg000:00403ABC
lea esp, [esp+10h]
seg000:00403AC0
popa
seg000:00403AC1
leave
seg000:00403AC2
retn 8
So to be good vTEA_Encode(ModifiedSerial,NameKey) has to be
equal to vTEA_Encode(NameKey,ModifiedSerial) and ModifiedSerial !=
NameKey
seg000:00403B17 vTEA_Encode proc near
seg000:00403B17
seg000:00403B17 InternalKey4 = dword ptr -34h
seg000:00403B17 InternalKey2 = dword ptr -30h
seg000:00403B17 InternalKey1 = dword ptr -2Ch
seg000:00403B17 InternalKey0 = dword ptr -28h
seg000:00403B17 ptrData
= dword ptr 8
seg000:00403B17 ptrKey
= dword ptr 0Ch
seg000:00403B17 Buffer
= dword ptr 10h
seg000:00403B17
seg000:00403B17
push ebp
seg000:00403B18
mov ebp, esp
seg000:00403B1A
pusha
seg000:00403B1B
cld
seg000:00403B1C
mov esi, [ebp+ptrKey]
seg000:00403B1F
mov ebx, [ebp+ptrData]
seg000:00403B22
push [ebp+Buffer]
seg000:00403B25
lodsd
seg000:00403B26
push eax
seg000:00403B27
lodsd
seg000:00403B28
push eax
seg000:00403B29
lodsd
seg000:00403B2A
push eax
seg000:00403B2B
lodsd
seg000:00403B2C
push eax
seg000:00403B2D
xor edx, edx
seg000:00403B2F
mov esi, [ebx]
seg000:00403B31
mov edi, [ebx+4]
seg000:00403B34
mov ebp, 20h
seg000:00403B39
seg000:00403B39 LoopTEA:
seg000:00403B39
add edx, 9E3779B9h
seg000:00403B3F
mov eax, edi
seg000:00403B41
mov ecx, eax
seg000:00403B43
mov ebx, edi
seg000:00403B45
rol eax, 4
seg000:00403B48
ror ebx, 5
seg000:00403B4B
add eax, [esp+34h+InternalKey0]
seg000:00403B4F
add ebx, [esp+34h+InternalKey1]
seg000:00403B53
add ecx, edx
seg000:00403B55
xor ecx, eax
seg000:00403B57
xor ecx, ebx
seg000:00403B59
add esi, ecx
seg000:00403B5B
mov eax, esi
seg000:00403B5D
mov ebx, esi
seg000:00403B5F
mov ecx, esi
seg000:00403B61
shl eax, 4
seg000:00403B64
shr ebx, 5
seg000:00403B67
add eax, [esp+34h+InternalKey2]
seg000:00403B6B
add ebx, [esp+34h+InternalKey4]
seg000:00403B6E
add ecx, edx
seg000:00403B70
xor ecx, eax
seg000:00403B72
xor ecx, ebx
seg000:00403B74
add edi, ecx
seg000:00403B76
dec ebp
seg000:00403B77
jnz short LoopTEA
seg000:00403B79
add esp, 10h
seg000:00403B7C
pop ebx
seg000:00403B7D
mov [ebx], esi
seg000:00403B7F
mov [ebx+4], edi
seg000:00403B82
popa
seg000:00403B83
leave
seg000:00403B84
retn 0Ch
seg000:00403B84 vTEA_Encode endp
So vTEA_Encode performs:
void code(long* v, long* k) {
unsigned long y=v[0],z=v[1], sum=0, /* set up */
delta=0x9e3779b9, n=32 ;
/* a key schedule constant */
while (n-->0) {
/* basic cycle start */
sum += delta ;
y += (ROL4(z)+k[0]) xor ( y+sum)
xor ( ROL5(z)+k[1]) ;
z += (SHL4(y)+k[2]) xor ( y+sum)
xor ( SHR5(y)+k[3]) ;
}
v[0]=y ; v[1]=z ; } |
So if we remplace k[2] and k[3] by ( k[2] xor 2^31 ) and ( k[3] xor
2^31 ) the result will be the same but the keys will be different...
The fact that each TEA (this routine is very close to TEA ) key has
3 other equivalent keys is described in the paper "Key-Schedule Cryptanalysis
of IDEA, G-DES, GOST, SAFER, and Triple-DES"
So [ModifiedSerial ] = [ NameKey ],
[ModifiedSerial +4 ] = [ NameKey + 4
],
[ModifiedSerial +8 ] = [ NameKey
+ 8] xor 2^31,
[ModifiedSerial + 12 ] = [ NameKey +12]
xor 2^31
Then the second part of the serial is checked:
seg000:00403A0C
push esi
; Serial p2
seg000:00403A0D
push edi
; Name
seg000:00403A0E
push esi
; Serial p2
seg000:00403A0F
call CheckSerialP2
seg000:00403A14
dec dword ptr [esi+0Ch]
seg000:00403A17
xor ecx, ecx
seg000:00403A19
seg000:00403A19 CheckSerial:
seg000:00403A19
mov eax, [ecx+esi]
seg000:00403A1C
test eax, eax
seg000:00403A1E
jnz short Fin
seg000:00403A20
add ecx, 4
seg000:00403A23
and ecx, 0Fh
seg000:00403A26
jnz short CheckSerial
So the result of CheckSerialP2 has to be equal to 1
seg000:00403CAC CheckSerialP2 proc near
seg000:00403CAC
seg000:00403CAC Serial_
= dword ptr 8
seg000:00403CAC Name_P2
= dword ptr 0Ch
seg000:00403CAC Serial
= dword ptr 10h
seg000:00403CAC
seg000:00403CAC
push ebp
seg000:00403CAD
mov ebp, esp
seg000:00403CAF
pusha
seg000:00403CB0
sub esp, 20h
seg000:00403CB3
mov ecx, 4
seg000:00403CB8
mov edi, esp
seg000:00403CBA
mov esi, [ebp+Serial_]
seg000:00403CBD
cld
seg000:00403CBE
repe movsd
seg000:00403CC0
mov ecx, 4
seg000:00403CC5
mov ebx, edi
seg000:00403CC7
mov esi, [ebp+Name_P2]
seg000:00403CCA
repe movsd
seg000:00403CCC
xor eax, eax
seg000:00403CCE
mov ecx, 4
seg000:00403CD3
mov edi, [ebp+Serial]
seg000:00403CD6
repe stosd
seg000:00403CD8
sub edi, 10h
seg000:00403CDB
mov ecx, 80h
seg000:00403CE0
clc
seg000:00403CE1
seg000:00403CE1 Loop:
seg000:00403CE1
mov eax, 3
seg000:00403CE6
seg000:00403CE6 _Loop1:
seg000:00403CE6
rcl dword ptr [edi+eax*4], 1
seg000:00403CE9
dec eax
seg000:00403CEA
jns short _Loop1
seg000:00403CEC
mov eax, 3
seg000:00403CF1
jnb short _Loop2
seg000:00403CF3
xor dword ptr [edi+0Ch], 87h
seg000:00403CFA
seg000:00403CFA _Loop2:
seg000:00403CFA
seg000:00403CFA
rcl dword ptr [esp+eax*4], 1
seg000:00403CFD
dec eax
seg000:00403CFE
jns short _Loop2
seg000:00403D00
jnb short Next
seg000:00403D02
mov eax, 3
seg000:00403D07
seg000:00403D07 _Loop3:
seg000:00403D07
mov edx, [ebx+eax*4]
seg000:00403D0A
xor [edi+eax*4], edx
seg000:00403D0D
dec eax
seg000:00403D0E
jns short _Loop3
seg000:00403D10
seg000:00403D10 Next:
seg000:00403D10
loop Loop
seg000:00403D12
add esp, 20h
seg000:00403D15
popa
seg000:00403D16
leave
seg000:00403D17
retn 0Ch
seg000:00403D17 CheckSerialP2 endp
Here again we can use a matrix: L
|
B0 |
B1 |
... |
Bn-1 |
Bn |
|
|
|
|
\/ |
\/ |
\/ |
\/ |
\/ |
|
|
|
( |
CheckSerialP2(B0 )n |
CheckSerialP2(B1 )n |
... |
CheckSerialP2(Bn-1 )n |
CheckSerialP2(Bn )n |
) |
> |
An |
( |
CheckSerialP2(B0 )n-1 |
CheckSerialP2B1 )n-1 |
... |
CheckSerialP2(Bn-1 )n-1 |
CheckSerialP2(Bn )n-1 |
) |
> |
An-1 |
( |
... |
... |
... |
... |
... |
) |
> |
... |
( |
CheckSerialP2(B0 )1 |
CheckSerialP2(B1 )1 |
... |
CheckSerialP2(Bn-1 )1 |
CheckSerialP2(Bn )1 |
) |
> |
A1 |
( |
CheckSerialP2(B0 )0 |
CheckSerialP2(B1 )0 |
... |
CheckSerialP2(Bn-1 )0 |
CheckSerialP2(Bn )0 |
) |
> |
A0 |
... to determine whose ModifiedSerial_P2 verifies: CheckSerialP2(ModifiedSerial_P2)
== 1
So now we are able to compute ModifiedSerial_P1 and ModifiedSerial_P2
(that is to say ModifiedSerial) and we know how obtain Serial value using
ModifiedSerialValue... so we can compute a valid Serial ;)
Name |
Amenesia |
Serial |
93180C843E7004FA4F749AB6E82A9E883A4C2C6FBBDF8921927E9ACBC6C885E1 |
I know that my english is very bad so if you find an error or if you
think that this tut is un-understandable feel free to contact me : tchernozium@yahoo.fr
|
|