函数调用约定、Tut.ReverseMel.exe
Process Explorer
下载地址:https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer
下载后直接运行即可。
介绍:Process Explorer时Windows操作系统给下最优秀的进程管理工具。其运行界面如下:
画面左上侧以树结构显示当前运行的进程,右侧显示各进程的PID,CPU占有率,注册信息等。画面下方显示的是加载到所选进程中的DLL信息,或者当前选中的进程的所有对象的句柄。
函数调用约定
函数调用约定(calling convention)是对函数调用时如何传递参数的一种约定。
调用函数前先把参数压入栈,然后再传递给函数。栈就是定义在进程中的一段内存空间,向下扩展,且其大小被记录在PE头中。也就是说,进程运行时确定栈内存的大小,这与malloc/new动态分配内存不同。
函数执行完成后,栈中参数不用管,后续写入值会覆盖。ESP值要恢复到函数调用之前,这样可引用栈大小才不会缩减。函数调用后如何处理ESP,这是函数调用约定要处理的问题。
😃 主要函数调用约定如下:
- cdecl
- stdcall
- fastcall
不管哪种方式,通过栈传递参数的基本概念都是一样的。
cdecl
主要在C语言中使用,调用者负责处理栈。
cdecl示例代码:
#include "stdio.h"
int add(int a,int b)
{
return(a+b);
}
int main(int argc,char *argv[])
{
return add(1,2);
}
使用OD调试生成可执行文件。
可以看到,再main()函数中,add()函数的参数1、2逆序压入栈中,调用add()函数后,使用add eap,8
指令整理栈。调用者main()函数直接清理其压入战中的函数参数,这样的方式就叫做cdecl。
其优点在于,可以向被调用函数传递长度可变的参数。
stdcall
常用于Win32 API,该方式由被调用着清理栈。C语言默认的函数调用方式为cdecl。使用stdcall方式编译源码只需使用_stdcall关键词即可。
stdcall示例代码:
#include "stdio.h"
int _stdcall add(int a,int b)
{
return(a+b);
}
int main(int argc,char *argv[])
{
return add(1,2);
}
使用OD调试可执行文件。
可以看到main()函数中,在执行完add()函数后省略了add eap,8
指令。
栈的清理工作由add()函数中最后的retn 8
来执行。
retn 8
的含义是retn+pop 8字节
,即返回后使ESP增加到指定大小。
上面所说的被调用者add()函数内部清理栈的方式即为stdcall方式。该方式的好处在于:被调用者函数内部存在着栈清理代码,与每次调用函数时都要用add esp,xxx命令的cedcl方式相比,代码尺寸要小。
虽然Win 32 API使使用C语言编写的库,但他使用的使stdcall方式,而不是C语言默认的cdecl方式,这是为了获得更好的兼容性,使C语言之外的其他语言也能直接调用API。
fastcall
fastcall方式与stdcall方式类似,但该方式通常会使用寄存器而非栈内存去传递那些需要传递给函数的部分参数(前两个)。若某个函数有四个参数,则前两个参数分别使用ECX,EDX寄存器传递。
可以看到这里将函数参数传递给了edx与ecx。这里add()函数中没有出现retn 8,因为函数参数并未传入栈中,所以不需要调整栈。
fastcall方式的优势在于可以实现对函数的快速调用。但有时需要额外的开销来管理ECS与EDX寄存器。如果调用函数前它们中存有重要数据,那么使用前就要先备份。此外如果函数本身很复杂,需要把这两个寄存器用作其他用途时,也需要将它们中的参数值存储到另外某个地方。
视频讲座——Tut.ReverseMel.exe
Lena的40个crackme讲座下载地址:https://forum.tuts4you.com/files/file/1307-lenas-reversing-for-newbies/
下面破解文件Tut.ReverseMe1.exe
😦 找了好久一直到不到测试文件,最后在吾爱破解找到了本书的全部测试文件!
网址:https://www.52pojie.cn/forum.php?mod=viewthread&tid=666320&page=1
下载连接:https://pan.baidu.com/s/1JWkzSGN9u3tY2u51cFNDGA
密码: hkx2
解压密码:reversecore
简直太感动了!!!
运行
运行文件,弹出窗口:
点击确定:
输入Recode:
点击nags?
同样会弹出nags窗口。
两个目标:
- 去除nags消息框
- 查找注册码
😃 分析——去除nags消息框
使用OD打开可执行文件。
可以看到正是前面abec’ crackme #2中的VB启动代码。
要想消除nags消息框,只需要操作调用消息框的函数部分即可。
右键——search for——all intermodular calls查找程序调用的所有API。
Destination排序,可以看到共有4处rtcMsgBox函数。分别为错误弹窗、正确弹窗、nags弹窗、点击From弹窗。
右键——set breakpoint on every call to rtcMsgBox对这四处设置断点。
调试器中F9运行程序。到达第一个断点处,地址为402CFE。
向上查看:发现是nags弹窗。
继续F9运行程序,弹出nags窗口,点击确定。显示主画面,点击nags?
,发现仍然是地址004CFE处。
尤其可知,这两处的nags窗口具有相同的运行代码。因此只需要对一处打补丁即可。
首先的想法是,去除402CFE出的call rtcMsgBox()语句,这样就没有弹窗了。
函数执行前ESP:0012F3A8
函数执行后ESP:0012F3BC
函数执行前后,ESP增大了14h,函数执行后将传递的参数出栈,说明参数的大小为14h。
进行的修改如下所示,删除该条指令,并按照传递给该条函数的参数大小清理栈。同时用NOP填充使指令长度不变。
重新点击nags?
出现错误。原因在于没有处理好函数的返回值。如下图,可以看到函数执行完毕后,将返回值(EAX=00000001)存储到[ebp-9C]中了。
这里可以查看修改后执行完该函数的栈,可以看到EAX并未修改为1。
这是考虑进行如下修改:
但是这两条指令长度为8字节(3+5),而原本指令的长度为5字节,如果这样修改,可以能回侵占后面代码。
所以只能换一个思路:
从402CFE处函数调用向上查找,可以找到地址402C17处函数开始建立栈帧。
地址402CFE处的函数调用代码属于其他函数的内部代码,那么如果上层函数无法调用,或者直接返回,最终就不会调用rtcMsgBox函数。因此在形成栈帧处直接RETN返回即可。使用RETN XX
语句调整栈帧。
执行到push ebp,mov ebp,esp语句,查看栈中ESP处:
可以看到存储在栈中的函数返回地址7401E5A9。跳转到该处:
可以看到调用处为7401E5A7处的CALL EAX
指令。在该处以及返回地址处设置断点,运行。可以看到运行到此处时EAX值为402649。
继续运行跳转到EAX=402649处。
继续执行跳转到402C17处。此时ESP值为0012F478。之后开始建立栈帧。
继续执行至返回地址处7401E5A9处,此时ESP值为0012F480。
可以看到在建立栈帧后函对ESP进行的变化是从0012F478变为0012F480。即将8字节的参数出栈。
因此进行如下修改:retn 4
。
执行可以看到,跳出函数后ESP变为0012F480,以上修改无错误。这里也可以知道,栈由被调用者清理,因此采用的时stdcall函数调用方式。
将修改后的可执行文件另存:
此时发现直接运行无弹窗。
一个小问题:前面说到栈帧的变化是从0012F478变为0012F480,即将8字节参数出栈,那么指令为何不使用retn 8呢?可以试着修改为retn 8。可以看到这是函数返回后变为了0012F484,实际上增加了12字节(4+8字节)。
这是因为RETN XX
指令等价于POP IP,POP XX
。其中IP长度4字节。因此执行RETN XX
后,ESP实际增加的大小为4+XX字节。
😃 分析——查找注册码
查找字符串,跳转到对应地址处:
向上查找可以看到如下代码:
即将字符串“I’mleana151”入栈,之后执行比较函数。很容易由此推测该处执行的操作是将输入的注册码与该字符串比较。由此推测正确的注册码就是I'mlena151
。输入查看:
可以看到,注册码正确。
至此程序破解完成!
小总结
Process Explorer是优秀的进程管理工具。
主要的函数调用约定有:cdecl、stdcall、fastcall。其中cdecl是C语言默认的函数调用方式,Win32 API有C语言编写,但其函数调用方式为stdcall,是为了获得更好的兼容性。三者的差别在于清理栈(调整ESP)的方式,cdecl由调用者清理,其余两个由被调用者清理。
Tut.ReverseMe1.exe的破解思路:
查找所有使用的API,对弹出消息框的MsgBox()下断点。定位到MsgBox()函数嗲用地址,尝试删除调用语句,使用调整栈的语句替代:add esp,14
,执行报错,发现EAX存储的函数返回值有问题,添加修改返回值的语句,发现指令过长,可能会覆盖后面的代码。换个思路,查找到函数建立栈帧处,寻找上一级函数调用语句(看存储在栈中的返回地址),返回地址下断点,设法修改建立栈帧语句,直接返回到上一级调用的返回地址处。查看建立栈帧到返回地址之间ESP的变化,修改为RETN XX
语句即可。
如何学习代码逆向分析
- 检索便知九成。
- 马上行动。
- 查找简单的crackme、patchme、unpackme等程序分析学习,起初尝试修改记事本、计算器等,然后扩大对象范围。
- 汇编、Windows内部结构、PE文件格式、API钩取
- 不懂指令就查