2011-11-02 17 views
8

我目前使用一些代码替换方案在32位移动到另一个位置的代码读取变量和类指针。由于x86_64不支持绝对寻址,因此无法在代码的新位置获取变量的正确地址。详细的问题是,由于rip相对寻址,指令指针地址与编译时不同。绝对寻址x86_64运行时代码替换

那么有没有办法在x86_64中使用绝对地址或其他方式来获取不是相对于指令指针的变量地址?

类似于:leaq variable(%%rax), %%rbx也会有所帮助。我只想不依赖指令指针。

回答

6

尝试使用用于x86_64的代码模型。在gcc中,这可以通过-mcmodel = large来选择。编译器将为代码和数据使用64位绝对地址。

您还可以添加-fno-pic禁止生成与位置无关的代码。

编辑:我建立一个小测试应用程序与-mcmodel =大产生的二进制包含像

400b81:  48 b9 f0 30 60 00 00 movabs $0x6030f0,%rcx 
400b88:  00 00 00 
400b8b:  49 b9 d0 09 40 00 00 movabs $0x4009d0,%r9 
400b92:  00 00 00 
400b95:  48 8b 39    mov (%rcx),%rdi 
400b98:  41 ff d1    callq *%r9 

序列这是一个绝对64位的立即的负载(在这种情况下的地址)接着是间接呼叫或间接加载。所述指令序列

moveabs variable, %rbx 
addq %rax, %rbx 

是相当于“leaq offset64bit(%RAX),%RBX”(不存在),与一些副作用,如标志变更等

+0

-mcmodel =大的唯一方法应该是解决方案。我必须调查为什么gcc osx编译器不支持它 – nux

+0

也许是旧的。小型(标准)和中型代码模型被提前添加,大型模型出现较晚。 – hirschhornsalz

+0

我非常感谢你,这看起来非常好,明确地解决绝对寻址问题 – nux

2

你问的是可行的,但不是很容易。

这样做的一种方法是补偿代码在其指令中的移动。您需要查找所有使用RIP相对寻址的指令(它们的ModRM字节为05h,0dh,15h,1dh,25h,2dh,35h或3dh),并根据移动量调整它们的disp32字段因此在虚拟地址空间中限制为+/- 2GB,如果64位地址空间大于4GB,则可能无法保证)。

你也可以用它们的等同替换这些说明,最有可能与一个以上的替换每个原始指令,例如:

; These replace the original instruction and occupy exactly as many bytes as the original instruction: 
    JMP Equivalent1 
    NOP 
    NOP 
Equivalent1End: 

; This is the code equivalent to the original instruction: 
Equivalent1: 
    Equivalent subinstruction 1 
    Equivalent subinstruction 2 
    ... 
    JMP Equivalent1End 

这两种方法都需要至少一些基本的x86拆卸程序。

前者可能要求在Windows(或Linux上的某些等效项)上使用VirtualAlloc(),以确保包含原始代码的修补副本的内存在原始代码的+/- 2GB范围内。并且特定地址的分配仍可能失败。

后者需要的不仅仅是原始的拆解,还包括全指令解码和生成。

可能还有其他的怪癖可以解决。

指令边界也可以通过设置RFLAGS寄存器中的TF标志来找到,以使CPU在每条指令执行结束时生成single-step调试中断。一个调试异常处理程序将需要捕获它们并记录下一条指令的RIP值。我相信这可以通过在Windows中使用Structured Exception Handling (SEH)来完成(从来没有尝试过使用调试中断),不确定有关Linux。为了这个工作,你必须让所有的代码都执行,每一条指令。

顺便说一句,在64位模式下有绝对寻址,例如参见从0A0h到0A3h操作码的MOV累加器指令。

+0

感谢您的回答。据我所知,这种方法是在运行时重新计算movs的地址值。这看起来很重要的计划,所以如果这是我会考虑一个不同的实施 – nux