我目前使用一些代码替换方案在32位移动到另一个位置的代码读取变量和类指针。由于x86_64不支持绝对寻址,因此无法在代码的新位置获取变量的正确地址。详细的问题是,由于rip相对寻址,指令指针地址与编译时不同。绝对寻址x86_64运行时代码替换
那么有没有办法在x86_64中使用绝对地址或其他方式来获取不是相对于指令指针的变量地址?
类似于:leaq variable(%%rax), %%rbx
也会有所帮助。我只想不依赖指令指针。
我目前使用一些代码替换方案在32位移动到另一个位置的代码读取变量和类指针。由于x86_64不支持绝对寻址,因此无法在代码的新位置获取变量的正确地址。详细的问题是,由于rip相对寻址,指令指针地址与编译时不同。绝对寻址x86_64运行时代码替换
那么有没有办法在x86_64中使用绝对地址或其他方式来获取不是相对于指令指针的变量地址?
类似于:leaq variable(%%rax), %%rbx
也会有所帮助。我只想不依赖指令指针。
尝试使用用于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”(不存在),与一些副作用,如标志变更等
你问的是可行的,但不是很容易。
这样做的一种方法是补偿代码在其指令中的移动。您需要查找所有使用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累加器指令。
感谢您的回答。据我所知,这种方法是在运行时重新计算movs的地址值。这看起来很重要的计划,所以如果这是我会考虑一个不同的实施 – nux
-mcmodel =大的唯一方法应该是解决方案。我必须调查为什么gcc osx编译器不支持它 – nux
也许是旧的。小型(标准)和中型代码模型被提前添加,大型模型出现较晚。 – hirschhornsalz
我非常感谢你,这看起来非常好,明确地解决绝对寻址问题 – nux