MASM32 / TASM32编写,无壳,是一个Keyfile验证方式,界面打开窗口空白,file菜单只有一个exit,help菜单也只有一个About说明。
crackme标注的验证方式是Keyfile,所以直接搜索字符串,可以搜索到“CRACKME3.KEY”,明显的Key文件,同时还有"Good work cracker!“及"Crackedby: Now try the next crackme!“字符串,估计是成功后标志。直接双击“CRACKME3.KEY”来到代码处:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
00401016 |. 6A 00 push 0x0 ; /hTemplateFile = NULL
00401018 |. 68 80000000 push 0x80 ; |Attributes = NORMAL
0040101D |. 6A 03 push 0x3 ; |Mode = OPEN_EXISTING
0040101F |. 6A 00 push 0x0 ; |pSecurity = NULL
00401021 |. 6A 03 push 0x3 ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
00401023 |. 68 000000C0 push 0xC0000000 ; |Access = GENERIC_READ|GENERIC_WRITE
00401028 |. 68 D7204000 push Cruehead.004020D7 ; |CRACKME3.KEY
0040102D |. E8 76040000 call <jmp.&KERNEL32.CreateFileA> ; \CreateFileA
00401032 |. 83F8 FF cmp eax,-0x1
00401035 |. 75 0C jnz short Cruehead.00401043
00401037 |> 68 0E214000 push Cruehead.0040210E ; CrackMe v3.0
0040103C |. E8 B4020000 call Cruehead.004012F5
00401041 |. EB 6B jmp short Cruehead.004010AE ; 打开文件失败的jmp
00401043 |> A3 F5204000 mov dword ptr ds:[0x4020F5],eax
00401048 |. B8 12000000 mov eax,0x12 ; 读取文件的最大字节数
0040104D |. BB 08204000 mov ebx,Cruehead.00402008
00401052 |. 6A 00 push 0x0 ; /pOverlapped = NULL
00401054 |. 68 A0214000 push Cruehead.004021A0 ; |pBytesRead = Cruehead.004021A0
00401059 |. 50 push eax ; |BytesToRead = 0x0
0040105A |. 53 push ebx ; |Buffer = 7FFDC000
0040105B |. FF35 F5204000 push dword ptr ds:[0x4020F5] ; |hFile = NULL
00401061 |. E8 30040000 call <jmp.&KERNEL32.ReadFile> ; \ReadFile
00401066 |. 833D A0214000>cmp dword ptr ds:[0x4021A0],0x12 ; 读到的文件字节数
0040106D |.^ 75 C8 jnz short Cruehead.00401037 ; 要求大小为0x12字节
0040106F |. 68 08204000 push Cruehead.00402008
00401074 |. E8 98020000 call Cruehead.00401311
00401079 |. 8135 F9204000>xor dword ptr ds:[0x4020F9],0x12345678
00401083 |. 83C4 04 add esp,0x4
00401086 |. 68 08204000 push Cruehead.00402008
|
分析此块汇编代码:
- CreateFileA参数
Mode = OPEN_EXISTING == 0x0
,也就是打开已存在的文件,若文件不存在,函数返回-1,是用来验证Key文件是否存在的。
- ReadFile函数读取文件内容,参数
pBytesRead = Cruehead.004021A0是
读取文件返回的字节数,接下来一句代码cmp dword ptr ds:[0x4021A0],0x12
,如果读取的字节数不等于0x12(18)个,则跳至失败处。
于是我们在CrackMe目录下新建一个文件CRACKME3.KEY
,并写入不少于18个字节的数据,内容如下。再次载入程序,在ReadFile后代码进行跟踪。
1
2
|
00000000h: 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0D 0E ; ................
00000010h: 0F 10 11 ; ... ; 4..
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
00401000 >/$ 6A 00 push 0x0 ; /pModule = NULL
00401002 |. E8 7D040000 call <jmp.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
00401007 |. A3 E9204000 mov dword ptr ds:[0x4020E9],eax
0040100C |. C705 F9204000>mov dword ptr ds:[0x4020F9],0x0 ; 设置为0
00401016 |. 6A 00 push 0x0 ; /hTemplateFile = NULL
00401018 |. 68 80000000 push 0x80 ; |Attributes = NORMAL
0040101D |. 6A 03 push 0x3 ; |Mode = OPEN_EXISTING
0040101F |. 6A 00 push 0x0 ; |pSecurity = NULL
00401021 |. 6A 03 push 0x3 ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
00401023 |. 68 000000C0 push 0xC0000000 ; |Access = GENERIC_READ|GENERIC_WRITE
00401028 |. 68 D7204000 push Cruehead.004020D7 ; |CRACKME3.KEY
0040102D |. E8 76040000 call <jmp.&KERNEL32.CreateFileA> ; \CreateFileA
00401032 |. 83F8 FF cmp eax,-0x1
00401035 |. 75 0C jnz short Cruehead.00401043
00401037 |> 68 0E214000 push Cruehead.0040210E ; CrackMe v3.0
0040103C |. E8 B4020000 call Cruehead.004012F5
00401041 |. EB 6B jmp short Cruehead.004010AE ; 打开文件失败的jmp
00401043 |> A3 F5204000 mov dword ptr ds:[0x4020F5],eax
00401048 |. B8 12000000 mov eax,0x12 ; 读取文件的最大字节数
0040104D |. BB 08204000 mov ebx,Cruehead.00402008
00401052 |. 6A 00 push 0x0 ; /pOverlapped = NULL
00401054 |. 68 A0214000 push Cruehead.004021A0 ; |pBytesRead = Cruehead.004021A0
00401059 |. 50 push eax ; |BytesToRead = 0x0
0040105A |. 53 push ebx ; |Buffer = NULL
0040105B |. FF35 F5204000 push dword ptr ds:[0x4020F5] ; |hFile = 00000044 (window)
00401061 |. E8 30040000 call <jmp.&KERNEL32.ReadFile> ; \ReadFile
00401066 |. 833D A0214000>cmp dword ptr ds:[0x4021A0],0x12 ; 读到的文件字节数
0040106D |.^ 75 C8 jnz short Cruehead.00401037 ; 要求大小为0x12字节
0040106F |. 68 08204000 push Cruehead.00402008 ; 读到的数据
00401074 |. E8 98020000 call Cruehead.00401311 ; 数据计算
00401079 |. 8135 F9204000>xor dword ptr ds:[0x4020F9],0x12345678
00401083 |. 83C4 04 add esp,0x4
00401086 |. 68 08204000 push Cruehead.00402008 ; 计算的数据
0040108B |. E8 AC020000 call Cruehead.0040133C ; 再计算:0xE ~ 0x12的4字节 =》 0x100F0E0D
00401090 |. 83C4 04 add esp,0x4
00401093 |. 3B05 F9204000 cmp eax,dword ptr ds:[0x4020F9] ; 进行数值比较
00401099 |. 0f94c0 sete al
0040109C |. 50 push eax
0040109D |. 84C0 test al,al
0040109F |.^ 74 96 je short Cruehead.00401037
004010A1 |. 68 0E214000 push Cruehead.0040210E ; CrackMe v3.0
004010A6 |. E8 9B020000 call Cruehead.00401346
004010AB |. 83C4 04 add esp,0x4
004010AE |> 6A 00 push 0x0 ; /Title = NULL
004010B0 |. 68 28214000 push Cruehead.00402128 ; |No need to disasm the code!
004010B5 |. E8 9A030000 call <jmp.&USER32.FindWindowA> ; \FindWindowA
004010BA |. 0BC0 or eax,eax
004010BC |. 74 01 je short Cruehead.004010BF
004010BE |. C3 retn
00401311 /$ 33C9 xor ecx,ecx ; ecx = 0
00401313 |. 33C0 xor eax,eax ; eax = 0
00401315 |. 8B7424 04 mov esi,dword ptr ss:[esp+0x4] ; 数据
00401319 |. B3 41 mov bl,0x41
0040131B |> 8A06 /mov al,byte ptr ds:[esi]
0040131D |. 32C3 |xor al,bl
0040131F |. 8806 |mov byte ptr ds:[esi],al
00401321 |. 46 |inc esi
00401322 |. FEC3 |inc bl
00401324 |. 0105 F9204000 |add dword ptr ds:[0x4020F9],eax
0040132A |. 3C 00 |cmp al,0x0
0040132C |. 74 07 |je short Cruehead.00401335
0040132E |. FEC1 |inc cl
00401330 |. 80FB 4F |cmp bl,0x4F
00401333 |.^ 75 E6 \jnz short Cruehead.0040131B
00401335 |> 890D 49214000 mov dword ptr ds:[0x402149],ecx
0040133B \. C3 retn
0040133C /$ 8B7424 04 mov esi,dword ptr ss:[esp+0x4] ; 计算的数据
00401340 |. 83C6 0E add esi,0xE
00401343 |. 8B06 mov eax,dword ptr ds:[esi]
00401345 \. C3 retn
|
通过分析得出将文件的0~11字节进行计算,结果与12~17的数值进行比较,相等及成功!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
#include <iostream>
int main() {
unsigned int ecx = 0;
unsigned int nuber = 0;
unsigned char data[18] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0D, 0x0E, 0x0F, 0x10};
unsigned char* ptr_data = data;
unsigned char bl = 0x41;
while (true) {
unsigned char al = *ptr_data;
al ^= bl;
*ptr_data = al;
ptr_data++;
bl++;
nuber += al;
if (al == 0) {
break;
}
ecx++;
if (bl == 0x4f) {
break;
}
}
nuber ^= 0x12345678;
ptr_data = data + 0xE;
unsigned int key;
memcpy((void*)&key, (void*)ptr_data, 4);
if (nuber == key) {
printf("Success\n");
}
else {
printf("Error\n");
}
return 0;
}
|
更改文件内容如下:
1
2
|
00000000h: 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E F8 55 ; ..............鳸
00000010h: 34 12 11 ; 4..
|