1625-5 王子昂 总结《2017年6月19日》 【连续第260天总结】
A.CrackMe(33)
B.
眼花不小心解压到后面好几个之后的CM了_(:з」∠)_做完才发现
拖入PEiD,显示MASM,汇编写的程序。前天那个异或自己指令的程序真是给我留下心理阴影了……
F9运行,窗口空白,菜单栏的FILE点不开,估计是后一个KeyFile类型的时候用的
HELP里有Register和About,点开Register里面就是Name/Serial的注册了
输入123/321,确认后有两个报错窗口
再次输入,在第一个报错窗口弹出的时候到OD里F12暂停,然后ALT+K查看调用堆栈,找到用户领空的调用行
004013AA |. /EB 15 jmp short Cruehead.004013C1
004013AC |> |5E pop esi ; user32.761FCB33
004013AD |. |6A 30 push 0x30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004013AF |. |68 60214000 push Cruehead.00402160 ; |Title = "No luck!"
004013B4 |. |68 69214000 push Cruehead.00402169 ; |Text = "No luck there, mate!"
004013B9 |. |FF75 08 push [arg.1] ; |hOwner = 00402048
004013BC |. |E8 79000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
004013C1 \> \C3 retn
发现之前有一个判断,但是之后就之前返回了。说明这一个错误窗口不触发的话就直接跳过,下断后重新运行,然后单步跟出去,就到了主领空里。
00401228 . 68 8E214000 push Cruehead.0040218E ; ASCII "ABC"
0040122D . E8 4C010000 call Cruehead.0040137E ; 第一个错误弹窗
00401232 . 50 push eax ; 判断字符是否都为字母,并以大写形式将ASCII累加再与0x5678异或
00401233 . 68 7E214000 push Cruehead.0040217E ; ASCII "123"
00401238 . E8 9B010000 call Cruehead.004013D8 ; 累加和先*0xA再+serial的每个字节的ASCII-0x30,最后与0x1234异或
0040123D . 83C4 04 add esp,0x4 ; 结果保存在bx中
00401240 . 58 pop eax ; user32.761FCB33
00401241 . 3BC3 cmp eax,ebx ; 结果必须等于Name的结果
00401243 . 74 07 je short Cruehead.0040124C ; 第二个关键跳
00401245 . E8 18010000 call Cruehead.00401362 ; 第二个错误弹窗
0040124A ^ EB 9A jmp short Cruehead.004011E6
0040124C > E8 FC000000 call Cruehead.0040134D ; 成功弹窗
00401251 .^ EB 93 jmp short Cruehead.004011E6
在一堆跳转之中,很容易判断出004011E6是退出的地址,那么跳过两个错误弹窗以后,最后一个退出之前很明显就是成功弹窗了
把00101243的je改成jmp就可以爆破成功
注册机:
跟进第一个错误弹窗的call里是一个循环处理字符的结构:
0040137E /$ 8B7424 04 mov esi,dword ptr ss:[esp+0x4] ; Cruehead.00402048
00401382 |. 56 push esi
00401383 |> 8A06 /mov al,byte ptr ds:[esi]
00401385 |. 84C0 |test al,al
00401387 |. 74 13 |je short Cruehead.0040139C
00401389 |. 3C 41 |cmp al,0x41 ; 每个字符都要大于等于0x41,即为字母
0040138B |. 72 1F |jb short Cruehead.004013AC ;失败
0040138D |. 3C 5A |cmp al,0x5A ; 小于5A时不跳转
0040138F |. 73 03 |jnb short Cruehead.00401394;大于5A(即小写字母)则跳转
00401391 |. 46 |inc esi
00401392 |.^ EB EF |jmp short Cruehead.00401383
00401394 |> E8 39000000 |call Cruehead.004013D2 ; 小写转大写
00401399 |. 46 |inc esi
0040139A |.^ EB E7 \jmp short Cruehead.00401383
0040139C |> 5E pop esi ; user32.761FCB33
0040139D |. E8 20000000 call Cruehead.004013C2 ; ASCII累加
004013A2 |. 81F7 78560000 xor edi,0x5678 ;与0x5678异或
004013A8 |. 8BC7 mov eax,edi ; Cruehead.00402048
004013AA |. EB 15 jmp short Cruehead.004013C1
004013AC |> 5E pop esi ; user32.761FCB33
004013AD |. 6A 30 push 0x30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004013AF |. 68 60214000 push Cruehead.00402160 ; |Title = "No luck!"
004013B4 |. 68 69214000 push Cruehead.00402169 ; |Text = "No luck there, mate!"
004013B9 |. FF75 08 push [arg.1] ; |hOwner = 00402048
004013BC |. E8 79000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
004013C1 \> C3 retn
当Name存在数字字符的时候会触发第一个错误弹窗,并且此时的eax值为数字的ASCII
第二个循环就简单多了:
004013D8 /$ 33C0 xor eax,eax
004013DA |. 33FF xor edi,edi ; Cruehead.00402048
004013DC |. 33DB xor ebx,ebx
004013DE |. 8B7424 04 mov esi,dword ptr ss:[esp+0x4] ; Cruehead.00402048
004013E2 |> B0 0A /mov al,0xA
004013E4 |. 8A1E |mov bl,byte ptr ds:[esi]
004013E6 |. 84DB |test bl,bl
004013E8 |. 74 0B |je short Cruehead.004013F5
004013EA |. 80EB 30 |sub bl,0x30 ; ASCII-0x30
004013ED |. 0FAFF8 |imul edi,eax ; di*0xA
004013F0 |. 03FB |add edi,ebx ; 整型值加到di上
004013F2 |. 46 |inc esi
004013F3 |.^ EB ED \jmp short Cruehead.004013E2
004013F5 |> 81F7 34120000 xor edi,0x1234 ; 与0x1234异或
004013FB |. 8BDF mov ebx,edi ; Cruehead.00402048
004013FD \. C3 retn
循环:累加和先乘0xA(即10),然后加上第n个字符减0x30,最后与0x1234异或
然后前后两个进行比较,失败则显示第二个错误弹窗,否则显示成功弹窗
实际上就是将数字字符串转换为整型值,最后与0x1234异或,因为乘10相当于十进制下左移一位嘛
这样看来纯汇编的StrToInt还要自己写也挺麻烦的……识别起来也是OTZ给个API的话OD一下就识别出来了
因此注册机很好做:只需要将Name的值算出来后,与0x1234异或即可得到Serial
我本来以为可能在第一个错误的情况下第二个通过然后弹正确呢,没想到最后的messagebox的call会使得eax变为1。不过仍然可以计算出这种情况,令1与0x1234异或得到0x1235,则Name输入数字,Serial输入4661就都会触发先报错再成功的提示了。
虽然程序本身挺简单的,不过找到BUG也是挺有意思的。下一个是同作者的KeyFile验证,第一次碰到这样的,有点新奇。
C. 明日计划
CrackMe(28)