图文记录
一.观察程序特点和运行逻辑
-
带弹窗
-
是VB开发的
-
需要用户名和注册码
-
有弹框
具备了很简单的特点……
错误弹框,如图
二.定位
弹窗内容入手,搜索关键字定位到关键跳,nop掉或者je改jne就可以实现,即怎么输入都是正确,但不是我们的重点,直接略过
三、研究算法
1.找块入口
从关键跳【0040258B】开始往上浏览,找到push ebp ,上面又是retn的地方(这个找多了下下断试试就有经验了),即【00402310】下断点,运行程序,输入用户名''123456",伪码"654321",程序如期断在断点处
2.F8步过分析反汇编代码
F8步过,关注寄存器,堆栈,注释等的变化,找到敏感函数和信息,这里找到了取字串输入内容的相关函数
004023B6 call dword ptr ds:[eax+0xA0] ; msvbvm50.7407A5B6
F8步过这个call后堆栈变化为输入的用户名"123456"
做个标签,这里需要注意的是:对于VB的反汇编,ollydbg就会出现丢失标签的情况,所以比较操蛋,敏感的入口地址自己还是记录一下,否则重载一次标签就没了,但是注释不丢,可以注释,但是毕竟没标签类似于重命名函数来的方便、直观!
继续F8继续走
3.发现算法
再继续F8发现算法部分,算法是:输入用户名长度*0x17cfb+用户名第一个字符ascii码,再转换10进制,再转字符串
如下图
这里深挖一下jo溢出跳转,增强一下基本功
-
imul是有符号乘法(即要考虑正负号)
-
edi是32位寄存器,即最大取址范围是FFFFFFFFF,考虑到符号,则正数是0-7FFFFFFF,负数是80000000-FFFFFFFF
-
换成十进制就是正数0到20亿多一些,负数是从-1到负20亿左右
-
我们输入的字符串长度取值乘以常量0x17CFB,所以只用考虑正数即可
-
自己算一下字符串长度上限应该是多少呢?
-
答案是0x5602也就是十进制的22018
-
也就是说你的用户名最长是22018位,再多程序就崩了个球了
-
有兴趣的可以自己写个Fuzzer来测试一下是不是,当然下断后修改寄存器数值也可以达到效果
-
题外话,这也是为什么早期的xp系统下的网络游戏端游游戏玩家经验值总是被限制到20亿,再多对于早期的开发就很烦了,当然我妄测一下,这也是早期出现游戏角色转生的技术诱因之一
回到正题,最后拼接”AKA-“给计算好的字符串组成完整激活码,来给输入的激活码比较,最后根据比较结果弹框。
三、写getkey
既然知道了算法开始写注册机,由于XP也想兼容的原因,就用“千年老妖” vc6来写吧,因为笔者vs2017出的程序xp死活是不行的(2015通过xp支持还是可以的,2017似乎全世界已经没人想管他到底xp是否可以搞了),废话不多说核心代码如下图
1 void CCM002Dlg::OnOK() 2 { 3 // TODO: Add extra validation here 4 5 //CDialog::OnOK(); 6 7 char szName[100]={0}; 8 int nKey = 0; 9 char szkey [100] = {0}; 10 GetDlgItemText(IDC_EDIT1,szName,100); 11 if (strlen(szName)>0) 12 { 13 nKey = strlen(szName) * 0x17CFB + szName[0]; 14 sprintf(szkey,"AKA-%d",nKey); 15 SetDlgItemText(IDC_EDIT2,szkey); 16 } 17 else 18 { 19 const char * szTitle="逆向驿站提示您"; 20 const char * szText="用户名不能为空!"; 21 MessageBoxA(szText,szTitle); 22 } 23 24 25 26 }