我使用Android二进制文件作为参考来帮助反转Windows二进制文件。到目前为止,我已经注意到两个二进制文件之间的vtable条目似乎具有相同的顺序,即可以在两个二进制文件的相同vtable索引中找到相同的函数。但是,我想知道的是,可以安全地假设它们将始终处于相同的顺序,或者该顺序是否随编译器而变化。
#1 楼
通常,在现代操作系统中,顺序不取决于编译器,但可能取决于操作系统。任何现代操作系统都不仅定义操作系统API,而且还定义了操作系统API。 ,许多标准库使程序更易于访问该API。另外,有很多库提供的服务不能直接转换为OS API,而是为其他内容提供通用的API。像Linux的QT或Windows的MSVC一样。以便能够与这些标准库交互。如果您试图让人们使用“您的”编译器,但告诉他们他们不能使用OS或标准库提供的接口,则很难说服他们。这意味着,任何生成Windows代码的编译器都将确保其符合MSVC标准。任何生成Linux代码的编译器都将遵守gcc的Linux / BSD标准。这不仅涉及vtable的布局,还包括名称修改,函数参数/返回值的寄存器和堆栈用法,浮点处理以及许多其他内容。其名称为ABI(应用程序二进制接口)。
请注意,并非总是如此。在DOS的后期/ Windows的早期,当Microsoft C(++)和Watcom C(++)成为(最重要的)竞争者时,它们具有截然不同的ABI。您无法(无需经历很多额外的麻烦)将使用Watcom编译的程序链接到使用MS编译的库,但这没什么大不了的,因为编译器无论如何都包含所有库,并且开源库被包含在您的程序中(大部分)是闻所未闻的。 (当时,Watcom甚至没有使用vtables;他们在每个对象中使用函数指针,每次创建新对象时都会实例化该对象)。因此,您回到时间越远,同一操作系统的两个编译器共享相同的ABI的可能性就越小。 ,因此在处理不同内容方面还有更多的余地。但是,通常在定义ABI时会做出相同的决定。您的类构造函数通常是第一个vtable条目,因为每个类都需要一个构造函数。析构函数通常是第二个条目,因为每个类都需要析构函数。其他vtable条目通常按其在源代码中出现的顺序位于vtable中的下一个位置。不是因为您试图与其他任何东西兼容;而是因为您尝试与其他任何东西兼容。只是因为这是实现所需实现的最简单方法。
这也意味着您不能依靠它。如果有一家新公司,我们称它们为Orange,使用其称为O-IS(橙色集成系统)的操作系统来设计新设备,他们可能想推出自己的编译器。也许他们决定在ABI中,方法名称应在其vtable中按字母顺序排序。也许他们决定如何处理多重继承和虚拟功能,这与MSVC和gcc处理这些事情的方式不同。当O-IS出现时,gcc人决定他们希望gcc能够针对O-IS进行编译,他们会将代码生成调整为Orange为O-IS设置的新标准。
有时,公司会尝试就新处理器的ABI达成一致,例如运气不好的Itanium。当然,这些只是建议而不是法律,因此操作系统/设备供应商可能会选择遵守,也可能不会选择遵守。检查这些操作系统的编译器的ABI定义。
Wikipedia的条目显示了gcc如何处理Linux ABI(的一部分),尽管我找不到MS进行MSVC的任何官方文档,但openrce.org上有一篇有关那