x86的子集没有%gs寄存器:二进制补丁代码使用%gs而不是陷阱模拟?

由于这里的原因太复杂,我不得不在这个x86子集的平台上运行x86 GCC编译的Linux程序。 这个平台没有%gs寄存器,这意味着它必须被仿真,因为GCC依赖于%gs寄存器的存在。

目前我有一个包装程序,当程序试图访问%gs寄存器并且模拟它时捕获exception。 但这是狗慢。 有没有办法让我可以提前在ELF中修改操作码,并使用相同的指令,以避免陷阱和模拟?

(这是假设亚当·罗森菲尔德(Adam Rosenfields)的解决方案是不适用的,它或者类似的方法可能是更好的解决方法。

你没有说明你是如何模仿%gs寄存器的,但是除非你对程序有一些特别的了解,否则一般都会修补所有的用法,否则你将只有2个字节(最糟糕的是,常见的情况下),你可以修改你的补丁。 当然,如果你使用的是%es =%gs,它应该是相对简单的。

假设这可以以某种方式在您的情况下工作,策略是扫描ELF文件的可执行部分,并修补任何使用或修改GS寄存器的指令。 这至少是以下说明:

  • 任何带GS段的指令都会覆盖前缀( 65表示分支指令,在这种情况下前缀表示其他内容)
  • push gs0F A8
  • pop gs0F A9
  • mov r/m16, gs8C /r
  • mov gs, r/m168E /r
  • mov gs, r/m64REX.W 8E /r )(如果您支持64位模式)

和其他任何允许段寄存器的指令(我不认为这是更多,但我不是100%肯定)。

这一切都来自英特尔 ®64 和IA-32架构软件开发人员手册组合卷2A和2B:指令集参考,AZ 。 请注意,这些指令有时候以其他前缀为前缀,有时候不会,所以您应该使用一个库来执行指令解码,而不是盲目地搜索字节序列。

上面的一些指令应该是比较直接的,可以call my_patch或者类似的call my_patch ,但是你可能很难找到适合两个字节的东西,而且一般工作。 int XXCD XX )可能是一个很好的候选者,如果你可以设置一个中断向量,但是我不确定它会比你当前使用的方法更快。 您当然需要记录哪个指令被修补出来,并且根据返回地址(您的处理程序接收到),使中断处理程序(或其他)作出不同的反应。

如果你可以在-128..127字节内找到空间,并使用JMP rel8EB cb )跳转到蹦床(通常是另一个JMP ,但是这次有更多空间用于目标地址),你可以设置一个蹦床。然后处理指令仿真,并跳回修补%gs使用后的指令。

最后,我建议保持陷阱和模拟代码运行,以捕捉你可能没有想到的任何情况(例如自修改或注入代码)。 这样您也可以记录任何未处理的案例并将其添加到您的解决方案。

你有没有尝试用-mno-tls-direct-seg-refs选项编译你的代码? 从我的GCC手册页(i686-apple-darwin10-gcc-4.2.1):

  -mtls-direct-seg-refs -mno-tls-direct-seg-refs Controls whether TLS variables may be accessed with offsets from the TLS segment register (%gs for 32-bit, %fs for 64-bit), or whether the thread base pointer must be added. Whether or not this is legal depends on the operating system, and whether it maps the segment to cover the entire TLS area. For systems that use GNU libc, the default is on.