160CM-015
这道题比较简单,我们运行看一下。程序启动后先会有一个弹窗,然后才进入主界面。
输入错误的注册码后,会弹出错误提示。破解要求去掉弹窗,输入正确的注册码。
1.注册码
这道题的注册码比较简单,固定明码,搜索一下错误字符串,就能看到注册码“2G83G35Hs2”。
输入,验证正确无误。
2.去弹窗
根据弹窗字符串,我们很容易找到弹窗处理事件的起始地址为402C17,返回分支有两处,返回指令都是retn,不带参数。
这里我们可以使用一个大招叫retn大法,只需要将起始地址的指令从“push ebp”改为“retn”,这样就可以将整个弹窗处理事件都屏蔽掉。当然,如果这个函数的返回指令带了参数,比如“retn 0x04”,那就需要改为“retn 0x04”,否则堆栈就不平衡了。
改完以后,弹窗就不会在出现了。这个适用于弹窗处理事件中只有弹窗相关的代码,没有影响后面注册的算法代码,比如在弹窗处理事件中根据机器码计算了真码,这时候如果把整个弹窗屏蔽掉就会导致后面的校验出现问题。如果有其他不能屏蔽的代码,那我们就需要在弹窗处理事件中再进一步定位精确的弹窗函数位置。
在这个程序中,我们可以看到具体的弹窗动作是在402CFE处调用了一个系统弹窗函数进行弹窗,对于这种情况,由于调用的函数是系统函数,我们不能到系统领空中去修改程序代码,那怎么办?
很简单,将call指令改成nop就可以了。但是同样需要注意堆栈平衡的问题,处理方法就是,进入到call调用的函数里面,看一下函数返回时的指令retn后面有没有带参数,如果没有参数,直接nop掉就可以,如果有参数,比如这个msgbox函数,返回代码为“retn 0x14”,就需要将call指令改成“add esp,0x14”,这样才能保持堆栈平衡。
这样改完以后,弹窗没有了,但是程序也自动退出了,这是因为弹窗时有个选择操作,选择取消的时候,程序就退出了,现在我们把弹窗函数屏蔽了,默认的参数就是取消,因此我们还需要将402D53处的跳转指令改一下,不让它去执行退出程序的代码。
3.总结
- retn:函数返回指令,将栈顶储存的地址传送给eip;
- retn 0x04: 相当于两条指令,“pop eip”和“add esp,0x04”;
- 编译器编译时,都是在函数返回时进行堆栈平衡的调整,我们在nop掉函数的时候只要关注retn指令后面带的参数即可。但是实际上函数代码中的push,pop指令都会影响堆栈指针,还有些会用“add esp”指令,如果作者手动去修改了add esp指令的参数(比如在add esp时减少2,在retn时增加2),这个时候如果nop掉call指令后直接按retn后面的参数来调整esp指针,也会导致堆栈不平衡。(个人理解,没碰到故意这么搞的程序)
- 补充了解了一下关于堆栈平衡方式的概念,常见调用约定的堆栈平衡方式有:
__stdcall 函数自己平衡
__cdecl 调用者负责平衡
__thiscall 调用者负责平衡
__fastcall 调用者负责平衡
__naked 编译器不负责平衡,由编写者自己负责
其中,函数自己平衡的方式就是函数返回时retn自带参数进行平衡,调用者负责平衡的方式就是函数调用完后,后面跟一个“add esp”指令进行平衡。
|