机器代码中的提示可以将我指向用于生成它的编译器?


31

当我在查看应用程序的机器代码时,是否存在提示和模式,我可以从生成的机器代码中辨别哪些编译器(以及可能的版本)用于生成它?

是否知道用于生成应用程序的编译器能够帮助我更有效地从生成的对象逆向工程回到源代码可能的位置,并且如果确实有帮助,那又如何?

+1

当您说“帮助我更有效地从生成的对象逆向工程回到源代码可能已经”时,您的目标是反编译代码或理解代码的功能? 19 3月. 132013-03-19 20:25:31

  0

它甚至有可能完全反编译代码?如果可能的话,我会说要反编译,否则至少要了解功能。 19 3月. 132013-03-19 20:43:31

30

在这个领域有一些学术研究,你想要的关键词是'工具链出处'。 Nate Rosenblum on this topic有一篇相当不错的论文,我读了这篇论文已经有一段时间了,但是你可以用很多技术来建立这些信息。我认为有些人使用机器学习,其他人可以使用大量的启发式或编译器行为公理。

建立这个是有限的公用事业国际海事组织。在您试图获得有关恶意软件组或威胁演员的情报的对抗情况下,它可能会有用,但是请记住,这类信息可能会被混淆或破坏。这种信息的一个潜在用途是确定某些二进制软件是使用某些公司的SDK编译的,其中包括一个具有该公司特有签名信息的编译器。建立工具链出处可以帮助您确定购买SDK的用户违反许可证或合同(例如制造恶意软件)。

行为差异的一个例子是参数写入。有两种方法可以将值放入堆栈,一种使用“推送”,另一种使用mov,其地址基于esp作为目标操作数。因此,一个编译器可以做到这一点:

 
push eax 
push ebx 

而另一个可以这样做:

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

而且他们这样做。一般来说,MSVC做的第一例和GCC做第二个例子,在一些非常有限的测试/观察至少刚才...


4

如果您只是谈论机器代码(或汇编代码),则没有太多信息。大多数现代编译器会产生类似的输出,或者输出不足以看到差异。有一点可能会引起注意,那就是编译器优化,这是我没有经历过的,而其他人也应该这样做。如果您确实拥有整个ELF文件,并且符号可用,那么您可以根据哪些类型的库链接(例如,libgcc将是一个赠品)或编译器特定功能的名称。如果ELF包含调试信息,您甚至可以看到诸如“GCC:(Ubuntu/Linaro 4.6.3-1ubuntu5)4.6.3”之类的东西。如果您正在处理C++代码,那么符号名称可能会将其丢弃。

然而,当你问自己,我很好奇你为什么需要这些信息。我不知道通过了解编译器会做的事情会获得多少帮助。我在ARM上做了更多的工作,并且我知道该平台有一个编译器/汇编代码必须遵守的应用程序二进制接口。这个ABI提供了关于如何调用函数的信息,应该用什么寄存器来做什么等。我知道,对于没有严格ABI的平台,操作系统通常会向开发人员提供有关这些主题的信息。无论如何,编译器都应该创建兼容的代码,所以我不知道用于识别创建代码的编译器的任何用法。

+7

这个答案缺乏理由或参考为什么不会有输出的差异。我对x86的个人经验与此相矛盾,但我的样本数量太小,不足以说这通常是真的。同时询问为什么需要这些信息并不是真正的答案,而是更多的澄清请求,并更好地适应对这个问题的评论。 19 3月. 132013-03-19 20:31:30

+1

感谢您的建设性批评。我是回答问题的新手,所以我不明白所有的细节。我会尽力找到更多的参考。 19 3月. 132013-03-19 20:34:57

+4

编译器之间有许多惊人的差异,尤其是在x86代码中有太多不同的指令可供选择。开关语句实现,堆栈布局决策和寄存器选择都可以提供关于使用哪种编译器的提示。 20 3月. 132013-03-20 02:23:00


10

在查看机器码时,通常会有一个“踪迹”,除非产生的二进制文件被清理了一些,否则可以遵循这个“踪迹”。例如,我产生一个小的“Hello World”使用我的Linux机器的标准选项GCC应用gcc -Wall hello.c现在,如果你把一个工具,像hexedit您可以在本机代码中看到有含建设信息的部分:

enter image description here

显然你可以看到那里是的,我用GCC版本4.6.3构建了这个。其他编译器将有其他类型的签名Microsoft's "rich" signature

+2

有趣的是,在剥离文件之后它会如何看待...... 20 3月. 132013-03-20 20:07:27

  0

这个问题特别是关于机器代码。人们会希望OP在尝试之前已经尝试过使用十六进制编辑器或“objdump”这样的基本方法并寻找不重要的字符串。在这种情况下,这不是一个答案。但是可以肯定的是,如果他们不知道,那就相关了。 ;-) 03 4月. 162016-04-03 13:04:34

  0

@underscore_d - ''''''''''''''''''''我只是确保我们不必只希望OP知道这一点。我喜欢不要做太多的假设! 15 5月. 162016-05-15 06:48:07


9

有在侦察名为“派克遗传学:自私的代码”的演示所描述一个为此的方法。他们使用一些统计数据从编译的程序中提取最常用的代码序列,并用它来检测拆包的结束,但这种方法可以很容易地用于识别特定的编译器。

从幻灯片15,请参阅:http://blog.zynamics.com/2010/07/16/recon-slides-packer-genetics-the-selfish-code-bochspython/

幻灯片似乎有点截断,我相信实际提供了更多的信息。


4

是否知道用于生成应用程序的编译器可以帮助我更有效地从生成的对象逆向工程回到源代码可能的位置,如果它确实有帮助,那又如何?

是的,它应该帮助。

更妙的是:

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

的想法是:

  • 构建测试用例很多不同的情况下(小的小程序),展示不同的结构和编译它们;

  • 看所得的机器代码(注意到图案)。

很多这些情况都在编译器(if和其它控制结构,基本的语言功能,...)的主版本一概而论。

这可能是有一些具体的编译器优化,差别很大的相同的程序。

(我不知道是否有共同/有用的情况,以帮助的一个特定的编译器生成的机器代码的逆向工程测试用例库)。

  0

对不起,生硬,但你需要处理你的格式和摆脱随机资本。现在,答案很难阅读。 19 3月. 132013-03-19 22:39:29

  0

编辑是一种改进吗? 19 3月. 132013-03-19 22:56:26

+1

@Gilles:非常感谢。 19 3月. 132013-03-19 23:04:40


7

是否知道用来生成一个应用程序帮我编译到 更有效地进行反向工程从生成的对象回 什么的源代码可能是,如果它确实帮助,怎么回事?

我认为知道使用的编译器为由于以下原因,很重要的一步:

  1. 它可以帮助你选择合适的工具(S)来分析目标。
  2. 了解运行时对分析很重要,例如在Delphi中TFileStream是一个常用的读/写文件对象。知道该对象的虚函数表让我了解如果偏移读/写/搜索等

为了用一个例子阐明1:一个工具,如IDR可能是一个更适合一个Delphi目标比IDA Pro的。或者至少我们可以用它来生成一个MAP文件/ IDC脚本,它改进了IDA中的符号。但是对于使用Visual Basic编写的目标,可以使用VB Decompiler等。


6

我猜你应该做的,以确定编译器的版本,除非你从字面上的意思是编译器版本,而不是链接器版本的第一件事,就是检查“MajorLinkerVersion”和可执行文件的PE头的“MinorLinkerVersion”领域,无论是EXE,DLL或SYS。见下面的清单。

主要次要

0x5的为0x0(5.0)的Borland C++/MS接头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×15(2.21)MinGW的

0X2 0x19(2.0.0.25)Borland的Delphi(连接器2.0.0.25)

不幸的是,包装和保护者往往会重写这些价值来编写他们自己的和/或加强猜测原始编译器的过程。

此外,可执行文件的资源目录是搜索特定链接程序信息的好地方。例如RT_RCDATA有一个名为“DVCLAL”资源是Borland C++或Delphi和“RT_MANIFEST”的在MSVC内置的可执行文件的情况下,一个标志能告诉我们的运行时DLL的它与,因此编译器版本specfic版本。

此外,“TimeDateStamp”字段设置为0x2A425E19的可执行文件是使用Delphi构建的标志。现在

,如果你想确定由汇编代码的编译器,那么最近的MSVC编译器版本的标志是看到刚刚在入口点生成栈的cookie功能。

看起来,入口点后跟字符串“fb:C++ Hook”的JMP指令是Borland C++的标志,依此类推。