VB 调试之分析 N-Code(二)
之前分析过这个 CrackMe(参见:VB 调试之分析 N-Code(一)),需要借助修改版的 OD 才能去除 NAG 窗口,在阅读逆向工程核心原理一书后,学到了一种新的技巧,记录下来;
使用工具
- OllyDbg 1.10原版,简称
OD
; OD
汉化
和插件
均来自互联网;- CrackMe来自互联网,仅供学习使用;
- 文中特殊数字均是
HEX
,为了书写方便采用DEC
;
分析思路
将
CrackMe
倒入OD
,查找rtcMsgBox
并设置断点:然后
F9
运行程序,程序会中断在调用位置,与直接给 API 设置的断点不同,这样设置的断点会中断在调用位置,而给 API 设置的断点会中断在rtcMsgBox
函数的行首:向上滚动代码,找到函数栈帧,然后设置断点:
至于什么是栈帧?
ESP 始终指向栈的顶端,程序运行中,ESP 寄存器的值随时变化,访问栈中函数的局部变量、参数时,若以 ESP 的值为基准会十分困难,使 CPU 很难引用到准确的位置;
所以,调用函数时,先要把作为基准点(函数起始地址)的 ESP 值保存到 EBP,并维持在函数内部,这样,无论 ESP 如何变化,以 EBP 的值为基准总是能够安全的访问到相关函数的局部变量、参数、返回地址,这就是栈帧;
也就是说,ESP 寄存器承担着栈顶指针的作用,而 EBP 寄存器则负责行使栈帧指针的职能;
取消其它断点,重载并运行程序,程序会在断点处中断:
然后在
堆栈窗口
选择反汇编窗口中跟随
到调用位置;给调用位置和返回位置设置
F2
断点并取消其它断点:重载并运行程序,程序中断在调用位置设置的断点处:
记录调用位置栈顶指针的地址;
F9
运行程序,NAG 窗口弹出,点击确定,程序中断在函数的返回位置:记录返回位置栈顶指针的地址;
用返回位置的栈顶指针也就是 ESP 的值减去调用位置 ESP 的值;
以图为例:
0012FA10 - 0012FA08 = 8(16 进制)
,是不是可以这么理解:在调用函数中使用了 8 个字节的栈内存;Win32 API 使用的函数调用约定是 stdcall 方式,这种方式的栈内存由被调用者清理,在这里就是由被调用的函数清理,如果在函数的起始位置直接清理栈内存,函数就不会被执行,也就不会弹出 NAG 窗口了;
函数调用约定参见:寄存器小记
重载并运行程序,程序中断在调用位置,
F7
单步步入到函数内部:然后来到了 VB 程序类似于交通枢纽的位置,继续
F7
,跳转到创建 NAG 窗口的函数内部;创建 NAG 窗口的函数:
咦,好眼熟啊,是的,这里就是设置程序第 1 个断点的函数,调用
rtcMsgBox
的函数;之前的步骤终于派上用场了,根据计算,调用创建 NAG 窗口的函数会使用 8 个字节的栈内存,所以,修改函数的行首,清理栈内存:
然后运行程序,没有 NAG 窗口,直接弹出程序主窗体;
保存修改到可执行文件,然后运行程序:
将修改后的 CrackMe 倒入
OD
,开始处理序列号的问题,然后发现,给rtcMsgBox
设置断点后,程序并不会中断,怎么办?既然程序在输入错误的序列号后会弹出错误弹窗,那么程序内部肯定验证了序列号,那就查找关于字符串比较的函数:
找到
__vbaStrCmp
并设置断点,然后F9
运行程序;程序中断在调用
__vbaStrCmp
的位置,然后,在 EAX 中发现了输入的随机序列号:既然 EAX 是第 2 参数,那么 ECX 中存放的就是第 1 参数,可能就是真正的序列号;
数据窗口中跟随 ECX 后发现,西欧文字系统无法显示,就是一堆乱码,既然真实注册码无法使用,那有没有可能让任意字符成为注册码,继续向下分析:
首先,向下执行一行代码后,EAX 为非 0 值,这里使用的是
__vbaStrCmp
,所以非 0 说明不相等;接着,把返回值拷贝到 EDI 中,进行了一系列操作后,来到了一个跳转,这就很可疑了;
这里对 EDI 进行的一系列操作,如果 EDI 为 0,则没有任何影响,最后的结果是跳转实现,但随机注册码和指定字符串比较,结果肯定不为 0,最终的结果是跳转失败;
按照这个逻辑来推断,这个跳转应该是关键跳转,如何证明呢?修改
JE
为JMP
,然后运行程序:修改
JE
为JMP
并运行程序后,弹出提示序列号正确的弹窗,至于为什么这是正确的弹窗,很简单,因为它和错误弹窗不一样;保存修改到可执行程序,然后运行程序:
输入任意字符,点击 OK 后,弹出成功弹窗,结束!