逆向分析Hello World!程序
编辑源程序
注意编译时如果出现字符串无法转换的错误,解决方法:工程-设置-C/C++,在预处理程序定义处添加UNICODE,_UNICODE。
此外创建工程时要创建Win32 Console Application控制台项目,否则build会报错。
另外补充一点:在虚拟机中打开VC++和OllyDbg要选择以管理员方式运行,否则VC++会导致程序link失败,OllyDbg中无法另存文件。
#include "windows.h"
#include "tchar.h"
int _tmain(int argc,TCHAR *argv[])
{
MessageBox(NULL,
L"Hello World!",
L"www.reversecore.com",
MB_OK);
return 0;
}
ctrl+F2 重新调试:EP(入口点)处
F8单步执行,前面的复杂代码是启动函数。
F7跳入函数,此处跳入401005处。
jmp执行执行跳转。
查看函数内部,可以看到API函数MessageBox,由此可知该处为主函数,且MessageBox函数调用处为402038。
此外:
F4 执行到光标处
F2 下断点
Ctrl+G 跳转到指定地址
F9 运行
Ctrl+F9 在函数内部运行知道retn处
快速查找指定代码的方法
字符串检索法
右键-search for-all referenced text strings
双击即可跳转。
检索可知字符串"hello world!"通过push入栈,地址为42101C。在hex dump处使用Ctrl+G跳转到该地址
可以看到对应字符串,是以UNICODE编码的,后面用NULL填充。
API检索法一
右键-search for-all intermodular calls
可以看到窗口列出了所有程序调用的API,观察程序行为特征:弹出窗口,即可推测出调用API并进行定位。
API检索法二
View-Memory菜单(Alt+M)打开内存映射窗口
使用压缩器或保护器对可执行文件进行压缩或保护后,文件结构会改变,此时ollydbg就无法列出API调用表了。这种情况下,可以直接向DLL代码库查找。
API是操作系统对用户程序提供的一系列函数,它实现与C:\Windows\systems32文件夹中的*.dll文件(如kernel32.dll、user32.dll、gdi32.dll等)内部。简而言之,我们编写的应用程序执行某种操作时(如各种O/I操作),必须使用OS提供的API向OS提出请求,然后与被调用API对应的系统DLL文件就会被加载到应用程序的进程内存。
可以看到,内存映射窗口显示了部分HelloWorld.exe进程内存,底部可以看到USER32被加载到了内存。
Name in all modules命令列出被加载DLL文件中提供的所有API,单击Name按名称排序,用键盘敲出MessageBoxs后,光标会自动定位到对应处。
双击MessageBoxW函数,查看代码,在起始处F2下断点。
F9执行程序到该处,可以看到此时ESP值为12FEE8,它是进城栈的地址。查看右下角栈空间。
可以看到栈中12FEE8出存储着返回地址40103E,单步执行至retn处,
F7跳出函数,可以看到上条指令地址为401038,正是调用MessageBox函数的地方。
使用“打补丁”方式修改“Hello World!”字符串
在40102a处下断点,F9执行到断点处。
方法一:直接修改字符串缓冲区
上图可以看到,hello world!字符串地址为42101C。hex dump中Ctrl+G输入地址跳转,选中要修改的部分,Ctrl+E打开编辑窗口,用新字符覆写该区域。
注意:Unicode字符串必须以NULL结尾,它占两个字节(添加NULL不能直接在编辑窗口进行,而要在hex项目中添加),并且要注意新字符串长度,不能覆盖后面数据,否则可能导致程序内存引用错误。
此时F9运行程序将弹出下图窗口,可以看到新字符串。
接下来需要保存更改到可执行文件,选中更改后的字符串,右键–copy to executable file,打开如下窗口。
右键–save file–命名为helloreserving
运行helloreserving.exe文件,可以看到修改成功。
方法二:在其他内存区域新建字符串并传递给消息函数
如果替换字符串太长,方法一则不适用。
重新回到断点处,push 0042101c指令把地址42101c处的字符串以参数的形式传递给了MessageBox()函数。
向函数传递参数时,传递的是字符串所在区域的首地址。如果改变字符串地址,消息框就会显示变更后的字符串,因此可以在内存的某个区域新建一个长字符串,并将新字符串的首地址传递给MessageBoxW()函数。
在hex dump中向下查找空白内存,Ctrl+E编辑新的字符串。
可以看到新字符串被写入421FCC。接下来把新的缓冲区地址作为参数传递给MessageBox函数,在代码窗口修改代码即可,使用空格键打开assemble窗口。
F9运行程序,弹出窗口如下,可以看到修改成功。
小总结
整个函数的结构:
启动函数+主函数–主函数中的call MessageBox函数–进入–MessageBox函数内部,执行到retn返回–继续执行主函数中的call 指令后的指令
查找主函数:
可执行文件起始处是VC++启动函数(Ctrl+F2重新调试即可返回EP入口点),F7单步执行,依此跳转进入函数查看API调用,判断是否为主函数,若不是,Ctrl+F9执行到retn处跳出,直到跳入函数中看到MessageBox函数,即找到主函数位置。
查看内存映射窗口:
通过View-Memory可以查看内存映射窗口,单击Name排序,键入MessageBox找到函数,双击键入即可查看其代码。在该函数出下断点(F2),F9执行到断点处,继续执行至rtn跳出,可以看到上一条即为MessageBox函数的调用指令。
设置basecamp的方法:
- goto(Ctrl+F)命令
- F2下断点(view-breakpoint查看全部断点)
;
注释(右键-search for-user defined comment查看全部注释):
标签(右键-search for-user defined labels查看全部标签)
快速查找指定代码的方法:
- search for-all referenced text string查看所有引用字符串与调用API
- search for-all intermodular calls查看所有调用的API函数
- view-memory打开映射窗口, search for-name in all modules查看所有被加载DLL文件中提供的所有API函数
两种修改字符串的方法:
- 直接修改字符串缓冲区:Ctrl+E编辑数据(hex dump),右键-copy to executable file创建文本副本(修改的项目被保留)
- 在空白内存处编辑,并将函数参数入口地址修改为编辑处地址(空格键打开assemble修改汇编指令)