160个CrackMe之004
环境和工具
Win10+x32dbg+PEID
程序分析
程序说明:没有按钮,错误没有提示,如果成功会出现一张照片.
1.先查壳:无壳.Delphi程序
2.尝试注册:
- 用户名:123456789012 发现用户名最多12位
- 注册码:abcdefghijk 注册码好像没有长度限制
目前就这些信息了,开始分析
分析过程
没什么头绪,先搜索字符串看看,发现注册成功字符串
定位过去,有一个跳转,但是这个跳转是往上跳的,继续向上找一找
很快发现又有两个跳转,0x0045803B处的跳转刚好跳过注册成功函数,这里应该就是关键跳转了,Nop掉验证一下
此处果然是关键跳转,出来一张漂亮小姐姐的照片,那么跳转之前的代码应该就是注册码的计算了.
首先我们先看看此处跳转的判断条件,比较的是[esi+0x30C]与0x85,大致翻了翻,并没有发现有[esi+0x30C]的赋值,那我们就先不管他哪来的,先看看是什么吧.发现了两处需要关注的地方:
1.[esi+0x30C]+0:此处是0,等于0x85时注册成功
[esi+0x30C]+4:“黑头Sun Bird”
[esi+0x30C]+8:“dseloffc-012-OK”
[esi+0x30C]+C:“黑头Sun Bird”+“9”+“dseloffc-012-OK”+“15pb”,可能是注册码
2.堆栈窗口可以看到"15pb",这是用户名,"3415pb18"这里做了计算,得到新的字符串
验证一下我们的猜测:黑头Sun Bird9dseloffc-012-OK15pb是不是注册码,标志位不再是0了,变为了0x3E,但并不是0x85,没有猜对,那就老老实实从靠头分析吧
00457FCE | 55 | push ebp |
00457FCF | 68 FD804500 | push <sub_4580FD> |
00457FD4 | 64:FF30 | push dword ptr fs:[eax] |
00457FD7 | 64:8920 | mov dword ptr fs:[eax],esp |
00457FDA | 33DB | xor ebx,ebx |
00457FDC | 8D55 F4 | lea edx,dword ptr ss:[ebp-0xC] | [ebp-C]:"15pb"
00457FDF | 8B86 D4020000 | mov eax,dword ptr ds:[esi+0x2D4] | eax:sub_457FB6+75, [esi+2D4]:"聪A"
00457FE5 | E8 5EB3FCFF | call <sub_423348> | 获取用户名并计算用户名长度
00457FEA | 8B45 F4 | mov eax,dword ptr ss:[ebp-0xC] | eax:sub_457FB6+75, [ebp-C]:"15pb"
00457FED | E8 8ABBFAFF | call <sub_403B7C> | 用户名长度
00457FF2 | 83C0 1E | add eax,0x1E | eax:sub_457FB6+75
00457FF5 | 8D55 F8 | lea edx,dword ptr ss:[ebp-0x8] | [ebp-8]:"34"
00457FF8 | E8 07FBFAFF | call <sub_407B04> |
00457FFD | FF75 F8 | push dword ptr ss:[ebp-0x8] | local2
00458000 | 8D55 F0 | lea edx,dword ptr ss:[ebp-0x10] | Loacl3
00458003 | 8B86 D4020000 | mov eax,dword ptr ds:[esi+0x2D4] | eax:sub_457FB6+75, [esi+2D4]:"聪A"
00458009 | E8 3AB3FCFF | call <sub_423348> |
0045800E | FF75 F0 | push dword ptr ss:[ebp-0x10] | [ebp-10]:"15pb"
00458011 | 8D55 EC | lea edx,dword ptr ss:[ebp-0x14] | [ebp-14]:"18"
00458014 | 8BC3 | mov eax,ebx | eax:sub_457FB6+75
00458016 | E8 E9FAFAFF | call <sub_407B04> | 返回值有变化
0045801B | FF75 EC | push dword ptr ss:[ebp-0x14] | [ebp-14]:"18"
0045801E | 8D45 FC | lea eax,dword ptr ss:[ebp-0x4] | eax:sub_457FB6+75, [ebp-4]:"3415pb18"
00458021 | BA 03000000 | mov edx,0x3 |
00458026 | E8 11BCFAFF | call <sub_403C3C> |
0045802B | 43 | inc ebx |
0045802C | 83FB 13 | cmp ebx,0x13 |
0045802F | 75 AB | jne 0x457FDC | 循环对用户名做计算
00458031 | 81BE 0C030000 8500000 | cmp dword ptr ds:[esi+0x30C],0x85 |
0045803B | 75 76 | jne 0x4580B3 | 跳过注册成功函数
这段代码分析完,发现虽然对用户名做了一定的计算,得到一个字符串"3115pb18"(上面提到的),但是跟比较的地方[esi+0x30C]一点关系都没有,而且esi+0x30C明显是一个申请内存的地址,尝试下APIVirtualAlloc
断点,发现esi总是等于申请空间地址+0x18E4,也就是说[esi+0x30C]=[申请空间地址+0x18E4+0x30C]
申请空间地址=0x02260000,在0x02265934处下硬件写入断点
尝试了几次后发现,随便输入的注册码,标志位一值是0,输入字符串黑头Sun Bird9dseloffc-012-OK15pb标志位会变为0x3E,在标志这里下硬件写入断点
试了几次发现一直是0x3E,用Delphi的DarkDe工具,这个程序单击和双击响应事件,分别分析下
单击响应事件代码
00457FB8 | 55 | push ebp |
00457FB9 | 8BEC | mov ebp,esp |
00457FBB | B9 04000000 | mov ecx,0x4 | ecx:EntryPoint
00457FC0 | 6A 00 | push 0x0 |
00457FC2 | 6A 00 | push 0x0 |
00457FC4 | 49 | dec ecx | ecx:EntryPoint
00457FC5 | 75 F9 | jne 0x457FC0 |
00457FC7 | 51 | push ecx | ecx:EntryPoint
00457FC8 | 53 | push ebx |
00457FC9 | 56 | push esi | esi:EntryPoint
00457FCA | 8BF0 | mov esi,eax | esi:EntryPoint
00457FCC | 33C0 | xor eax,eax |
00457FCE | 55 | push ebp |
00457FCF | 68 FD804500 | push <sub_4580FD> |
00457FD4 | 64:FF30 | push dword ptr fs:[eax] |
00457FD7 | 64:8920 | mov dword ptr fs:[eax],esp |
00457FDA | 33DB | xor ebx,ebx |
00457FDC | 8D55 F4 | lea edx,dword ptr ss:[ebp-0xC] | edx:EntryPoint
00457FDF | 8B86 D4020000 | mov eax,dword ptr ds:[esi+0x2D4] |
00457FE5 | E8 5EB3FCFF | call <sub_423348> | 获取用户名并计算用户名长度
00457FEA | 8B45 F4 | mov eax,dword ptr ss:[ebp-0xC] |
00457FED | E8 8ABBFAFF | call <sub_403B7C> | 用户名长度
00457FF2 | 83C0 1E | add eax,0x1E |
00457FF5 | 8D55 F8 | lea edx,dword ptr ss:[ebp-0x8] | edx:EntryPoint, [ebp-8]:@BaseThreadInitThunk@12
00457FF8 | E8 07FBFAFF | call <sub_407B04> |
00457FFD | FF75 F8 | push dword ptr ss:[ebp-0x8] | local2
00458000 | 8D55 F0 | lea edx,dword ptr ss:[ebp-0x10] | Loacl3
00458003 | 8B86 D4020000 | mov eax,dword ptr ds:[esi+0x2D4] |
00458009 | E8 3AB3FCFF | call <sub_423348> |
0045800E | FF75 F0 | push dword ptr ss:[ebp-0x10] | [ebp-10]:@BaseThreadInitThunk@12+24
00458011 | 8D55 EC | lea edx,dword ptr ss:[ebp-0x14] | edx:EntryPoint
00458014 | 8BC3 | mov eax,ebx |
00458016 | E8 E9FAFAFF | call <sub_407B04> | 返回值有变化
0045801B | FF75 EC | push dword ptr ss:[ebp-0x14] |
0045801E | 8D45 FC | lea eax,dword ptr ss:[ebp-0x4] |
00458021 | BA 03000000 | mov edx,0x3 | edx:EntryPoint
00458026 | E8 11BCFAFF | call <sub_403C3C> |
0045802B | 43 | inc ebx |
0045802C | 83FB 13 | cmp ebx,0x13 |
0045802F | 75 AB | jne 0x457FDC | 循环对用户名做计算
00458031 | 81BE 0C030000 8500000 | cmp dword ptr ds:[esi+0x30C],0x85 |
0045803B | 75 76 | jne 0x4580B3 | 跳过注册成功函数
0045803D | 33DB | xor ebx,ebx |
双击响应事件代码
;关键就三行代码
00457EF5 | 83BE 0C030000 3E | cmp dword ptr ds:[esi+0x30C],0x3E | 如果是3E则不跳转,改为0x85
00457EFC | 75 0A | jne 0x457F08 |
00457EFE | C786 0C030000 8500000 | mov dword ptr ds:[esi+0x30C],0x85 |
00457F08 | 33DB | xor ebx,ebx |
分析完代码,双击事件会先判断[esi+0x30C]标志位是否为0x3E,是的话将标志位改为0x85,此时再单击图片就显示出来了,这里要注意,如果单击事件里有断点,则无法触发双击事件.
算法分析
多试几次会发现:注册码格式: 黑头Sun Bird+数字+dseloffc-012-OK+用户名
- 用户名:15pb
- 注册码:黑头Sun Bird9dseloffc-012-OK15pb
分析一下注册码有何特点:
- 黑头Sun Bird :常量字符串
- dseloffc-012-OK: 常量字符串
- 用户名
- 数字(关键就是这个数字是怎么来的?)
分析chkcode响应事件代码
00457C66 | 8BB3 F8020000 | mov esi,dword ptr ds:[ebx+0x2F8] | 用户名长度
00457C6C | 83C6 05 | add esi,0x5 | 用户名长度+5
00457C6F | FFB3 10030000 | push dword ptr ds:[ebx+0x310] | [ebx+310]:"黑头Sun Bird"
[ebx+0x2F8]这个一开始也不确定,多换几个用户名观察下就可以确定是用户名长度了.注册码中的数字其实就是(用户名长度+5)