我只是在学习 Olly/IDA 并用 C 编写简单的程序,并查看 Ollydbg 和 IDA 中的 .EXE 代码。下面我有一个简单的 printf Hello World 程序,我在 Visual Studio 6 中编译了它。我可以看到事件链以 kernel32.dll 调用开始,以获取 Windows 版本号、设置堆、获取命令行等. 我认出了 main() 函数并看到字符串变量被放在堆栈上,可以推断出 00401010 处的函数是 printf(),如果我查看该地址的“名称窗口”,IDA 会确认,但 Olly 没有明确告诉我。
#include <stdio.h>
void main(void)
{
printf("blah blah\n");
return;
}
在 Olly 中,这是我的 main() 函数的程序集:
CPU Disasm
Address Hex dump Command Comments
00401000 /$ 68 30604000 PUSH OFFSET 00406030 ; ASCII "blah blah"
00401005 |. E8 06000000 CALL 00401010
0040100A |. 59 POP ECX
0040100B \. C3 RETN
所以上面的那些汇编指令都是对应我写的C代码的。.EXE 的其余部分充满了许多其他代码,我希望它们能够识别应用程序并将其与 printf() 等 LIBC 函数区分开来。我知道编译器会生成依赖于操作系统的设置和拆卸内容(如 windows 版本号、堆等),但我认为在几个程序和学习 PE 程序标准之后我应该能够认识到这一点(加上 Olly 需要无论如何,您将其传递给应用程序代码)。
例如,这里是我认为是 LIBC printf() 函数的顶级函数,由 CALL 00401010 调用:
CPU Disasm
Address Hex dump Command Comments
00401010 /$ 53 PUSH EBX
00401011 |. 56 PUSH ESI
00401012 |. BE 70604000 MOV ESI,OFFSET 00406070
00401017 |. 57 PUSH EDI
00401018 |. 56 PUSH ESI
00401019 |. E8 4B010000 CALL 00401169
0040101E |. 8BF8 MOV EDI,EAX
00401020 |. 8D4424 18 LEA EAX,[ARG.2]
00401024 |. 50 PUSH EAX
00401025 |. FF7424 18 PUSH DWORD PTR SS:[ARG.1]
00401029 |. 56 PUSH ESI
0040102A |. E8 04020000 CALL 00401233
0040102F |. 56 PUSH ESI
00401030 |. 57 PUSH EDI
00401031 |. 8BD8 MOV EBX,EAX
00401033 |. E8 BE010000 CALL 004011F6
00401038 |. 83C4 18 ADD ESP,18
0040103B |. 8BC3 MOV EAX,EBX
0040103D |. 5F POP EDI
0040103E |. 5E POP ESI
0040103F |. 5B POP EBX
00401040 \. C3 RETN
如果我在 IDA 中打开我的 EXE 并查看“名称窗口”,我会看到此代码从 00401010 开始为“_printf”,如果我双击它,我会看到以下内容:
这只是顶层,如果我向下跟踪几个级别(遵循 CALL 指令到更深层次的函数),我会发现像 KERNEL32.WriteFile() 这样的操作系统函数实际上使字符串出现在 CMD 窗口中。
我的问题是:如何识别 printf() 等库函数并将它们与应用程序代码区分开来?如果它们能出现标记就好了。我知道 Olly 有脚本和插件,但到目前为止(在我的早期学习中)我还没有找到如何做到这一点。
目前我只知道把同一个EXE带入IDA,在“Names Window”中查看地址,看是不是CLIB函数的一级函数。但是,将这个函数调用的函数标记为标准 C 库的一部分,而不是用户代码的一部分需要一些工作。
在 IDA 中查找地址并不是什么大问题,但是仍然有大量代码属于较低级别的函数,如果我能快速知道它们是 CLIB 的一部分而不是应用程序代码,那就太好了。
我猜这类似于构建 Ollydbg .UDD 文件或下载插件或脚本。对不起,如果它在某些文档中很明显,但到目前为止我还没有找到它。
综上所述,我可以想象链接器可能会改变一些事情,以至于如果进行了一些优化,则每次内存中都可能不存在确切的模式。