当我查看应用程序的机器代码时,是否可以从生成的机器代码中辨别出提示和模式,这些提示和模式会指示使用哪个编译器(可能还有版本)来生成它?

是否知道用于生成应用程序的编译器可以帮助我更有效地将生成的对象反向工程到源代码,如果确实有帮助,怎么回事?

评论

当您说“帮助我更有效地将生成的对象反向工程到源代码可能是什么”时,您的目标是反编译代码还是了解代码的功能?

甚至可以完全反编译代码吗?我会说如果可能的话要反编译,否则至少要了解其功能。

#1 楼

这方面有一些学术研究,您想要的关键字是“工具链来源”。 Nate Rosenblum撰写了一篇有关该主题的相当不错的论文,距离我阅读本文已有一段时间了,但是您可以使用许多技术来建立此信息。我认为有些人使用机器学习,而另一些人可以使用一大堆关于编译器行为的启发式方法或公理。

建立此方法的用途仅限于IMO。在对抗性情况下,当您试图获取有关恶意软件组或威胁行为者的情报时,它可能会很有用,但请记住,此类信息可能会被混淆或破坏。该信息的一种潜在用途是确定使用某些公司的SDK编译了一些二进制软件,其中包括具有该公司独有的签名信息的编译器。建立工具链出处可以帮助您确定购买SDK的人违反了许可或合同,例如制造恶意软件。

行为差异的一个示例是参数编写。有两种方法可以将值放置到堆栈上,一种方法是使用“ push”,另一种方法是使用mov,其地址基于esp作为目标操作数。因此,一个编译器可以执行以下操作:

push eax
push ebx


另一个编译器可以执行以下操作:

mov [esp+foo], eax
mov [esp+foo+4], ebx


。通常,至少在一些非常有限的测试/观察中,MSVC做第一个示例,GCC做第二个示例...

#2 楼

在查看机器代码时,通常会遵循“尾迹”,除非所产生的二进制代码经过某种程度的清理。例如,我现在在Linux机器上使用GCC使用标准选项gcc -Wall hello.c生成了一个小的“ hello world”应用程序,如果您使用hexedit之类的工具,则可以在机器代码中看到包含构建信息的部分:



很明显,您可以在其中看到,我使用GCC版本4.6.3构建了它。其他编译器将具有其他类型的签名,这些签名是Microsoft的“丰富”签名。

评论


有趣的是,剥离文件后的外观如何...

–烛光
13年3月20日在20:07

问题特别是关于机器代码。希望OP在询问之前已经尝试了一些基本方法,例如使用十六进制编辑器或objdump并查找琐碎的字符串。在这种情况下,这不是答案。但是可以肯定的是,如果他们不知何故,那将是有意义的。 ;-)

– underscore_d
16-4-3在13:04



@underscore_d-“一个会希望”,的确是一个。我只是在确保我们不必仅希望OP知道这一点。我喜欢不要做太多的假设!

–迈克
16年5月15日在6:48

#3 楼

在Recon上有一个名为“包装遗传学:自私密码”的演讲,描述了一种解决方法。他们使用一些统计信息从已编译的程序中提取最常见的代码序列,并用它来检测解包的结束,但是该方法可以轻松地用于识别特定的编译器。

从幻灯片15中查看: http://blog.zynamics.com/2010/07/16/recon-slides-packer-genetics-the-selfish-code-bochspython/

幻灯片似乎有些截断,我相信实际的演示文稿具有更多信息。

#4 楼


是否知道用于生成应用程序的编译器是否可以帮助我
更有效地将生成的对象反向工程化为
源代码可能是什么,如果确实有帮助,怎么做?由于以下原因,我认为了解二手编译器是非常重要的一步:


它可以帮助您选择合适的工具( s)分析目标。
知道运行时对于分析很重要,例如在Delphi中,TFileStream是读取/写入文件的常用对象。知道该对象的vtable可以帮助我了解是否可以读取/写入/查找偏移量等。 IDA Pro。或者至少我们可以用它生成一个MAP文件/ IDC脚本,以改善IDA中的符号。但是对于使用Visual Basic编写的目标,可以使用VB反编译器,等等。

#5 楼

我想确定编译器版本的第一件事应该是检查可执行文件的PE标头的“ MajorLinkerVersion”和“ MinorLinkerVersion”字段,除非是字面意思是编译器版本而不是链接器版本,否则它应该是EXE,DLL,或SYS。请参见下面的列表。

主要版本

0x5 0x0(5.0)Borland C ++ / MS Linker 5.0

0x6 0x0(6.0)Microsoft VIsual Studio 6

0x7 0xA(7.10)Microsoft VIsual Studio 2003

0x8 0x0(8.0)Microsoft VIsual Studio 2005

0x9 0x0(9.0)Microsoft VIsual Studio 2008

0xA 0x0(10.0)Microsoft VIsual Studio 2010

0x2 0x15(2.21)MinGw

0x2 0x19(2.0.0.25)Borland Delphi(链接程序2.0.0.25)

不幸的是,打包程序和保护程序倾向于覆盖这些值以编写自己的值和/或加深猜测原始编译器的过程。

此外,可执行文件的资源目录也是搜索特定链接器信息的好地方。例如具有名为“ DVCLAL”的资源的RT_RCDATA是Borland C ++或Delphi的标志,在MSVC生成的可执行文件的情况下,“ RT_MANIFEST”可以告诉我们它链接到的运行时DLL的特定版本,因此也可以告诉我们编译器版本。 br />
此外,“ TimeDateStamp”字段设置为0x2A425E19的可执行文件也表明已使用Delphi进行构建。

现在,如果要从汇编代码确定编译器,则最新的MSVC编译器版本的标志是看到仅在入口点生成堆栈cookie的函数。 “挂钩”是Borland C ++的标志,等等。

#6 楼


了解用于生成应用程序的编译器是否有助于我更有效地将生成的对象反向工程到源代码,如果确实有帮助,那么如何做?


,应该有帮助。

甚至更好:


确切的编译器版本;
确切的命令行参数;
构建环境(操作系统,补丁程序级别,...)。

这个想法是为了:


构建很多不同的测试用例案例(小型小程序)展示不同的结构并进行编译;
查看生成的机器代码(注意模式)。

其中许多案例可以通过主版本编译器(if和其他控制结构,基本语言函数等)。

对于同一程序,可能存在一些特定于编译器的优化,但差异很大。

(我想知道是否存在用于通用/有用cas的测试用例库es,以帮助对特定编译器生成的机器代码进行逆向工程。)

评论


抱歉,直言不讳,但您需要进行格式化,并摆脱掉随机大写字母。现在,答案很难读。

–伊戈尔·斯科钦斯基♦
13年3月19日在22:39

编辑是一项改进吗?

–迈克尔·安德森(Michael Anderson)
13年3月19日在22:56

#7 楼

如果仅谈论机器代码(或汇编代码),则没有太多信息。大多数现代编译器会产生类似的输出,否则输出将不足以看到差异。可能表明问题的一件事是编译器优化,这是我没有经验的,应该由其他人参与。如果您确实拥有整个ELF文件,并且有可用的符号,则可以根据哪种类型得出结论。库是链接的(例如,libgcc将是赠品)或特定于编译器的函数的名称。如果ELF包含调试信息,您甚至可能会看到类似“ GCC:(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3”的信息。如果您正在使用C ++代码,则符号名称修饰可以将其放弃。

但是,正如您问自己的那样,我很好奇为什么需要此信息。我不知道知道使它起作用的编译器将为您带来多少帮助。我在ARM上进行了更多工作,并且在该平台上知道,编译器/汇编代码必须遵守一个应用程序二进制接口。该ABI提供有关如何调用函数,应将哪些寄存器用作什么的信息,等等。我知道,对于没有严格ABI的平台,操作系统通常会向开发人员提供有关此类主题的信息。无论如何,编译器都应该创建兼容的代码,因此我不知道用于标识创建代码的编译器有什么用。

评论


对于为什么输出没有差异,这个答案缺乏理论依据或参考。我在x86上的个人经历与此矛盾,但是我的样本量太小,不能说总体上是正确的。也要问为什么需要此信息并不是答案的真正内容,而是更多的澄清要求,更适合于对该问题的评论。

– Jix
13年3月19日在20:31

感谢您的建设性批评。我是新来的回答问题的人,所以我不了解所有细节。我将尝试查找更多参考。

–易凡
13年3月19日在20:34

编译器之间有许多惊人的差异,尤其是在x86代码中,有很多不同的指令可供选择。 Switch语句的实现,堆栈布局决策和寄存器选择都可以提供有关使用哪个编译器的提示。

–道格拉
13年3月20日在2:23