【逆向分析】快速查找指定代码的几种方法
前言
每个人在调试中快速查找所需代码时都有不同的方法,但是最基本最常用的有下面 4 种。
学习这 4 种方法前我们需要思考一个问题。根据前两章内容,我们已经知道,运行 HelloWorld.exe 程序会弹出一个消息框,显示“Hello World!”信息。固然是因为我们编写了代码,可在这种情形下,只要运行一下程序,不论是谁都能轻松意识到这一点。
如果你熟悉 win32 api 的开发,看到弹出的消息框就会想到,这是调用 MessageBox() API 的结果。应用程序的功能非常明确,只要运行一下程序,就能大致推测出其内部结构。不过前提是你已经具备了开发和分析代码的经验。
一、代码执行法
我们需要查找的是 main()函数中调用 MessageBox()函数的代码。在调试器中调试 HelloWorld.exe(F8)时,main()函数的 MessageBox()函数在某个时刻就会被调用执行,弹出消息对话框,显示“Hello World!”这条信息。
上面就是代码执行法的基本原理,当程序功能非常明确时,我们可以逐条执行指令来查找需要查找的位置。但是代码执行法仅适用于被调试的代码量不大、且程序功能明确的情况。倘若被调试的代码量很大且比较复杂时,此种方法就不再适用了。下面使用代码执行法来查找代码中的 main()函数。从“大本营”(40104F)开始,按 F8 键逐行执行命令,在某个时刻弹出消息对话框,显示“Hello World!”信息。按 Ctrl+F2 键再次载入待调试的可执行文件并重新调试,不断按 F8 键,某个时刻一定会弹出消息对话框。弹出消息对话框时调用的函数即为 main()函数。如图 2-20 所示,地址 401144 处有一条函数调用指令“CALL00401000”,被调用的函数地址为 401000,按 F7 键(Step Into)进入被调用的函数,可以发现该函数就是我们要查找的 main()函数。
如上图,地址 40100E 处有一条调用 MessageBox() API 的语句。401002 与 401007 处分别有一条 PUSH 语句,他把消息对话框的标题与显示字符串(Title = "www.reversecore.com", Text = "Hello World!")保存到栈(Stack)中,并作为参数传递给 MessageBox W()函数。
这样我们就准确的查找到了 main()函数。
win32 应用程序中,API 函数的参数是通过栈传递的。VC++中默认字符串是使用 Unicode 码表示的,并且,处理字符串的 API 函数也全部变更为 Unicode 系列函数。
二、字符串检索法
右键菜单-search for-all referenced text strings
OllyDbg 初次载入待调试的程序时,都会先经历一个预分析过程。此过程中会查看进程内存,程序中引用的字符串和调用的 API 都会被摘录出来,整理到另外的一个列表中,这样的列表对调试时相当有用的。使用 all referenced text strings 命令会弹出一个窗口,其中列出了程序代码引用的字符串。
地址 401007 处有一条 PUSH 004092A0 命令,该命令中引用的 004092A0 处即是字符串“Hello World!”。双击字符串,光标定位到 main()函数中调用 MessageBox W()函数的代码处。
在 OllyDbg 的 value 窗口,然后按 Ctrl+G 命令,可以进一步查看位于内存 4092A0 地址处的字符串。首先使用鼠标单击 value 窗口,然后按 Ctrl+G 快捷键,打开 Enter expression to follow in Dump 窗口。(如果你的数据窗口不是这样的,右键单机选择 HEX)
ascii 前两行即使“Hello World!”字符串,它是以 unicode 码形式表示的,并且字符串的后面被填充上了 NULL 值(记住这块 null 值的区域,我们以后还会再讲的)。
VC++中,static 字符串会被默认保存为 Unicode 码形式,static 字符串是指在程序内部被硬编码的字符串。
上图我们还需要注意的是 4092A0 这个地址,它与我们之前看到的代码区域地址比如 401XXX 是不一样的。在 HelloWorld 进程中,409XXX 地址空间被用来保存程序使用的数据。大家要清楚一点:代码和数据所在的区域是彼此分开的。
我们后面会慢慢学习代码和数据在文件里是怎么保存,以及怎么加载到内存的,这些是 windows PE 文件格式的相关内容,我们暂时先不管他。
三、API 检索法
3.1 在调用代码中设置断点
右键单击-search for-all intermodular calls
windows 编程中,如果想让显示器显示内容,则需要使用 win32API 向 OS 请求显示输出。换句话说,应用程序向显示器输出内容时,需要在程序内部调用 win32 API。认真观察一个程序的功能后,我们能够大致推测出它在运行时调用的 WIN32 API,则会为程序调式带来极大便利。以 HelloWorld.exe 为例,它在运行时会弹出一个消息窗口,由此我们可以推断出该程序调用了 user_32.MessageBox W() API。(敏感词所以加了下划线)
在 OllyDbg 的预分析中,不仅可以分析出程序中使用的字符串,还可以摘录出程序运行时调用的 API 函数列表。如果只想查看程序代码中调用了哪些 API 函数,可以直接使用 all intermodular calls 命令。如下图窗口列出了程序中调用的所有 API。
可以看到调用 MessageBox W()的代码,该函数位于 40100E 地址处,他是 user_32.MessageBox W() API。双击它,光标就会定位到调用它的地址处(40100E)。观察一个程序的行为特征,若能事先推测出代码中使用的 API,则使用上述方法能够帮助我们快速查找到需要的部分。
对于程序中调用的 API,OllyDbg 如何准确摘录出他们的名称呢?首先,他不是通过查看源代码来摘取的,如果想要了解其中的原理,我们需要理解 PE 文件格式的 IAT(Import Address Table,导入地址表)结构。我们会在后面的文章中提到这些内容。
3.2 在 API 代码中设置断点
鼠标右键菜单-search for - name in all calls
OllyDbg 并不能为所有可执行文件都列出 API 函数调用列表。使用压缩器、保护器工具对可执行文件进行压缩或保护之后,文件结构就会改变,此时 OllyDbg 就无法列出 API 调用列表了(甚至连调试都会变得十分困难)。
压缩器(Run time Packer,运行时压缩器)
压缩器是一个实用压缩工具,能够压缩可执行文件的代码、数据、资源等,与普通压缩不同,它压缩后的文件本身就是一个可执行文件。
保护器
保护器不仅具有压缩功能,还添加了反调试、反模拟、反转储等功能,能够有效保护进程。如果想仔细分析保护器,我们还需要具有更高级的逆向知识。
这种情况下,DLL 代码库被加载到进程内存后,我们可以直接向 DLL 代码库添加断点。API 是操作系统对用户应用程序提供的一系列函数,他们实现于 C:\Windows\system32 文件夹中的 *.dll 文件(如 kernel32.dll、user_32.dll、gdi32.dll、advapi32.dll、ws2_32.dll 等)内部。简单的说,我们编写的应用程序执行某种操作时(如各种 I/O 操作),必须使用 OS 提供的 API 向 OS 提出请求,然后与被调用 API 对应的系统 DLL 文件就会被加载到应用程序的进程内存。
在 OllyDbg 菜单中选择 View-Memory 菜单(Alt+M),打开内存映射窗口。如下图,内存映射窗口中显示了一部分 HelloWorld.exe 进程内存。在图底部可以看到 user_32 库被加载到了内存。
使用 OllyDbg 中的 Name in all modules 命令可以列出被加载的 DLL 文件中提供的所有 API。使用 Name in all moudules 命令打开 All names 窗口,单机 Name 栏目按名称排序,通过键盘敲出 MessageBox W 后,光标会自动定位到 MessageBox W 上。
USER_32 模块中有一个 Export 类型的 MessageBoxW 函数(不同环境下函数地址不同)。双击 MessageBoxW 函数后就会显示其代码,它实现于 USER_32.dll 库中,如图
观察 MessageBoxW 函数的地址空间可以发现,它与 HelloWorld.exe 使用的地址空间完全不同。在函数起始地址上按 F2 键,设置好断点后按 F9 继续执行。
如果 HelloWorld.exe 应用程序中调用了 MessageBoxW() API,则调试时程序运行到该处就会暂停。
与预测的一样,程序执行到 MessageBoxW 代码的断点处就停了下来,此时寄存器窗口中的 ESP 值为 19FF18
它是进程栈的地址。在右下角的栈窗口中可以看到更详细的信息
我们会在后面的教程中详细说明函数调用以及站动作原理,现在可以暂时忽略
如上图,ESP 值的 12FF18 处对应一个返回地址 401014,HelloWorld.exe 的 main()函数调用完 MessageBoxW 函数后,程序执行流将返回到该地址处。按 Ctrl+F9 快捷键使程序运行到 MessageBoxW 函数的 RETN 命令处,然后按 F7 键也可以返回到 401014 地址处。地址 401014 的上方就是地址 40100E,它正是调用 MessageBoxW 函数的地方。
以上就是快速查找代码的 4 种方法。需要学习资料可以私信回复“资料”
评论