新手学破解记录贴【160个CrackMe】之004
本人刚刚入门汇编逆向破解分析,本帖将记录我对160个CrackMe程序中的第004程序的分析过程。在学习的过程中,我会边学边记录。如果有不正确之处,还请各位指正。因为003程序我这里并不能正常运行 所以直接跳过003来分析一下004程序。
程序查壳是无壳的 Delphi语言编写的 。输入假码之后没有任何错误提示 只知道如果破解成功 下面的图片框会显示一幅图片。
首先还是查找字符串 看一下有没有什么有用的字符提示
字符串这里找到了一个注册成功 上面0x0045803B地址是关键跳转 会跳过注册成功的代码 我们把它nop掉,然后输入完用户名和密码点击图片框 就会直接爆破成功
已经爆破成功了 继续来研究一下他的注册码算法, 现在这个代码的头部下一个断点 然后来看一下汇编代码。
下图这串汇编代码 相当于 就把用户名的长度加0x5 得出来的十进制数值在和 用户名 和ebx寄存器值进行拼接,和下面的关键比较完全没有关系啊!!!
cmp dword ptr ds:,85 这行代码就是 esi+30的地址数值 如果等于0x85就注册成功,上面的汇编代码完全没有改变esi+30C里面的数值啊!!
关键点在于esi+30C的数值等于0x85 才可以,是哪里访问的esi+30C这个位置呢我们下一个硬件访问断点在重新注册试一下看。
下完这个断点之后我们发现 只要在注册码的文本框输入一个新的字符 就会断下来。
断下来之后我们发现了一个 字符串 “黑头Sun Bird15dseloffc-012-OKa123456789” 我测试了一下是正确的注册码 (经过测试发发现 输入完激活码需要双击才能注册成功 爆破的时候只需要单击就可以)
除了这个正确的激活码我们还发现了 有一个跳转 如果不成立就会给刚才注册的关键比较esi+0x30C的位置赋值,但是赋的值是0x3E 并不是0x85 (跟上面的双击有关 作者留的一个小坑 一会说)
现在已经知道了正确的注册码了 我们向上看 分析一下汇编代码。
第一部分 这部分代码实现了注册码的生成方式 首先 取用户名长度 然后加上0x5 得出来一个数值转成十进制 然后 进行拼接: 黑头Sun Bird&用户名长度&dseloffc-012-OK&用户名
拼接后的值为 黑头Sun Bird15dseloffc-012-OKa123456789这个也就是正确的注册码。
00457C43 | 51 | push ecx |
00457C44 | B9 05000000 | mov ecx,5 |
00457C49 | 6A 00 | push 0 |
00457C4B | 6A 00 | push 0 |
00457C4D | 49 | dec ecx |
00457C4E | 75 F9 | jne ckme.457C49 | 循环5次push 0
00457C50 | 51 | push ecx |
00457C51 | 874D FC | xchg dword ptr ss:,ecx | :"13a1234567890"
00457C54 | 53 | push ebx | ebx:&"鵀B"
00457C55 | 56 | push esi |
00457C56 | 8BD8 | mov ebx,eax | ebx:&"鵀B"
00457C58 | 33C0 | xor eax,eax |
00457C5A | 55 | push ebp |
00457C5B | 68 3D7E4500 | push ckme.457E3D |
00457C60 | 64:FF30 | push dword ptr fs: |
00457C63 | 64:8920 | mov dword ptr fs:,esp |
00457C66 | 8BB3 F8020000 | mov esi,dword ptr ds: | 用户名长度
00457C6C | 83C6 05 | add esi,5 | 用户名长度+0x5
00457C6F | FFB3 10030000 | push dword ptr ds: | :"黑头Sun Bird"
00457C75 | 8D55 F8 | lea edx,dword ptr ss: | :"15"
00457C78 | 8BC6 | mov eax,esi |
00457C7A | E8 85FEFAFF | call ckme.407B04 | 用户名长度加0x5之后转成10进制字符
00457C7F | FF75 F8 | push dword ptr ss: | :"15"
00457C82 | FFB3 14030000 | push dword ptr ds: | :"dseloffc-012-OK"
00457C88 | 8D55 F4 | lea edx,dword ptr ss: | :"a123456789"
00457C8B | 8B83 D4020000 | mov eax,dword ptr ds: | :&"鵀B"
00457C91 | E8 B2B6FCFF | call ckme.423348 |
00457C96 | FF75 F4 | push dword ptr ss: | :"a123456789"
00457C99 | 8D83 18030000 | lea eax,dword ptr ds: | :"黑头Sun Bird15dseloffc-012-OKa123456789"
00457C9F | BA 04000000 | mov edx,4 |
00457CA4 | E8 93BFFAFF | call ckme.403C3C | 拼接字符串 黑头Sun Bird&用户名长度&dseloffc-012-OK&用户名
第二部分:跟之前分析的基本一样 只不过是把用户名的长度加0x3 得出来的十进制数值在和 用户名 和esi寄存器值进行拼接循环19次,之前的和这部分汇编代码好像没什么作用。
00457CC9 | 8D55 EC | lea edx,dword ptr ss: | :"a123456789"
00457CCC | 8B83 D4020000 | mov eax,dword ptr ds: | :&"鵀B"
00457CD2 | E8 71B6FCFF | call ckme.423348 |
00457CD7 | 8B45 EC | mov eax,dword ptr ss: | :"a123456789"
00457CDA | E8 9DBEFAFF | call ckme.403B7C |
00457CDF | 83C0 03 | add eax,3 |
00457CE2 | 8D55 F0 | lea edx,dword ptr ss: | :"13"
00457CE5 | E8 1AFEFAFF | call ckme.407B04 |
00457CEA | FF75 F0 | push dword ptr ss: | :"13"
00457CED | 8D55 E8 | lea edx,dword ptr ss: | :"a123456789"
00457CF0 | 8B83 D4020000 | mov eax,dword ptr ds: | :&"鵀B"
00457CF6 | E8 4DB6FCFF | call ckme.423348 |
00457CFB | FF75 E8 | push dword ptr ss: | :"a123456789"
00457CFE | 8D55 E4 | lea edx,dword ptr ss: |
00457D01 | 8BC6 | mov eax,esi |
00457D03 | E8 FCFDFAFF | call ckme.407B04 |
00457D08 | FF75 E4 | push dword ptr ss: |
00457D0B | 8D45 FC | lea eax,dword ptr ss: | :"13a1234567890"
00457D0E | BA 03000000 | mov edx,3 |
00457D13 | E8 24BFFAFF | call ckme.403C3C |
00457D18 | 46 | inc esi |
00457D19 | 83FE 13 | cmp esi,13 |
00457D1C | 75 AB | jne ckme.457CC9 | 循环19次
第三部分: 这部分代码主要就是看输入的激活码和生成的一致不一致,如果一致就会把 注册成功的关键地址esi+30C赋值0x3E。
00457D1E | 8D55 E0 | lea edx,dword ptr ss: |
00457D21 | 8B83 D8020000 | mov eax,dword ptr ds: | :&"鵀B"
00457D27 | E8 1CB6FCFF | call ckme.423348 |
00457D2C | 8B45 E0 | mov eax,dword ptr ss: |
00457D2F | 8B93 18030000 | mov edx,dword ptr ds: | :"黑头Sun Bird15dseloffc-012-OKa123456789"
00457D35 | E8 52BFFAFF | call ckme.403C8C | 生成的激活码和输入的进行对比 然后决定下面的跳转是否赋值0x3E
00457D3A | 75 0A | jne ckme.457D46 |
00457D3C | C783 0C030000 3E000000 | mov dword ptr ds:,3E | 如果跳转不成立给注册成功关键对比的内存地址赋值3E
00457D46 | 8B83 0C030000 | mov eax,dword ptr ds: |
通过上面这些代码有存在一个问题 就是激活码都已经一致了不应该赋值0x85注册才能成功吗 为什么赋值0x3E呢 这样赋值即便激活码输入对了那注册不还是不成功吗?
确实是这样的,这个也就是作者留的一个小小的坑,之前有说过 如果直接爆破 在图片框单击一下就可以注册成功, 如果输入正确的激活码那要双击才能成功,那由此就可以推断出来
点击图片框只会进行一个 ebx+0x30C 是否等于0x85的判断 相等就注册成功否则就不成功 所以直接爆破 把这个判断nop掉就会直接成功,如果要输入活码要双击才能成功 那就是在双击的时候 会触发一个把ebx+0x30C的值改变成0x85的一个动作,
既然要改变成0x85 那肯定在双击的时候还会访问这个内存地址,我们接着下一个硬件访问断点 然后输入正确的激活码双击看一下。
双击后断在了这个位置 结果很明显了, 如果 这个 ebx+0x30C = 0x3E 那就赋值0x85。
总结:注册码算法就是 “黑头Sun Bird”+用户名长度+ 0x5+“dseloffc-012-OK”+用户名 ,只要在文本输入框按下字符 就会检测一次激活码 如果激活码正确就会直接给esi+0x30c地址赋值0x3E 双击图片框后会 判断esi+0x30c地址的值是否为0x3E 如果是 在赋值0x85 这个时候在点击图片框 会判断这个地址的值是否是0x85 如果是那就注册成功。
注册机编写:
#include <iostream>
#include <string>
using namespace std;
int main()
{
//获取输入字符串
string str;
cout << "请输入账号:" << endl;
cin >> str;
int len = str.length();
int num = len + 0x5 ;
string key = "黑头Sun Bird"+ to_string(num)+"dseloffc-012-OK"+ str;
cout<< "您的注册key:" << key << endl;
return 0;
}
感谢分享 感谢分享思路 思路是对的;www 一起开启学习之路。 谢谢分享
页:
[1]