【发布时间】:2018-07-26 10:47:27
【问题描述】:
今天,只是为了测试的目的,我提出了以下想法,在CodeBlocks中创建和编译一个朴素的源代码,使用Release目标删除不必要的调试代码,一个main函数三个 nop 操作只是为了更快地找到 main 函数的入口点。
CodeBlocks 示例幼稚程序:
使用 IDA 反汇编程序,我看到了一些奇怪的东西,操作系统实际上可以在主函数中添加额外的机器代码调用(隐式添加),对驻留在 kernel32.dll 中的系统函数的调用,用于操作系统线程处理。
IDA 程序视图:
在机器代码中,仅出于测试原因,三个“nop”(90)被替换为“and esp,0FFFFFFF0h”,程序再次重新打包,这就是为什么“无操作”操作码在视图中不可用.
观察到的行为:
为每个打开的进程创建一个新线程是逻辑,我们可以在TaskManager中探索它,一个进程在它自己的线程中运行,这就是编译器添加此代码的原因(隐式默认线程)。
我的问题:
编译器如何知道在哪里自动“注入”这个调用代码?
为什么之前没有在将路由到主函数入口点的上层函数(sub_401B8C)中进行此调用?
【问题讨论】:
-
大概只添加到
main。调用函数是 C 运行时的一部分,在此编译期间不会生成。也不是操作系统添加它,它可能是在机器代码生成阶段之前添加的。实际上,您可以在gcc -S输出中看到它已经为call ___main(一个不幸的命名选择) PS:请不要将代码发布为图像。 -
投了反对票,因为您所有的代码示例都以图片的形式发布。请不要那样做!
-
你的陈述语无伦次。在您的问题中,您声称操作系统能够以某种方式注入代码,但在您解释的comment 中,调用是由编译器或链接器插入的。实际上,您甚至没有提供那么多细节。该结构无助于澄清这些。
-
您已经确定,“[i] 物理上不存在于磁盘上的文件中”。这几乎排除了操作系统以任何方式参与。如果代码在磁盘上的 PE 映像中结束,则生成该代码的要么是编译器,要么是链接器。我不明白,您对什么感到困惑,或者 “两种变体” 是什么。
-
代码是在编译时添加的,当生成的代码用于托管环境(而不是独立的)时,对于名为
main的函数是特殊的。and esp, 0FFFFFFF0h用于对齐堆栈在 GCC 生成的代码所需的 16 字节边界上,正如 Jester 在他的回答中指出的那样,调用是调用全局构造函数的 GCC 运行时代码(库)。你不会发现编译器为main以外的函数生成的额外代码
标签: winapi assembly mingw portable-executable ida