我正在尝试为 x86 和 amd64 编写蹦床,以便将给定的函数调用立即引导到存储在已知内存位置的地址(目的是确保第一个目标地址存在于给定的 DLL (windows) 中).
以下代码试图使用 _fn
作为内存位置(或它们的一组)来启动实际目标地址:
(*_fn[IDX])(); // rough equivalent in C
.globl _asmfn
_asmfn:
jmp *_fn+8*IDX(%rip)
IDX
旨在使用一些 CPP 宏来构建,以提供一系列嵌入式 DLL 向量,每个向量都唯一映射到 _fn
函数指针数组中的一个插槽。
这在一个简单的测试程序中有效,但是当我实际将它放入共享库时(目前在 OSX 上进行测试),我在尝试指向 _asmfn 代码时遇到总线错误:
Invalid memory access of location 0x10aa1f320 rip=0x10aa1f320
此代码的最终目标是 Windows,尽管我还没有在那里尝试过(我认为我至少可以先在 OSX/intel 上的测试用例中证明该程序集)。 amd64 跳转至少在名义上是正确的,还是我错过了什么?
关于 trampolines on amd64 的一个很好的引用.
编辑
跳转 确实 在 Windows 7 上正常工作(终于有机会测试了)。但是,我仍然很想知道为什么它在 OSX 上失败。总线错误是由 KERN_PROTECTION_FAILURE 引起的,这似乎表明操作系统保护正在阻止该代码的执行。目标地址是分配的内存(它是由 libffi 生成的蹦床),但我相信它被正确标记为可执行内存。如果这是一个可执行内存问题,那就可以解释为什么我的独立测试代码可以工作(回调蹦床是编译的,而不是分配的)。
最佳答案
使用 PC 相对寻址时,请记住偏移量必须在 +- 2GB 范围内。这意味着您的跳台和蹦床不能相距太远。关于蹦床,在 Windows x64 上无需破坏任何寄存器就可以完成的传输是:
一个序列:
PUSH <high32>
MOV DWORD PTR [ RSP - 4 ], <low32>
RET
这适用于 Win64 和 UN*X x86_64。尽管在 UN*X 上,如果该函数使用 redzone,那么您就是在破坏...一个序列:
JMP [ RIP ]
.L: <tgtaddr64>
同样,适用于 Win64 和 UN*X x86_64。一个序列:
MOV DWORD PTR [ RSP + c ], <low32>
MOV DWORD PTR [ RSP + 8 ], <high32>
JMP [ RSP + 8 ]
这是特定于 Win64 的,因为它 (ab) 使用了 Win64 ABI 保留的 32 字节“参数空间”的一部分(恰好 高于 堆栈上的返回地址);与此等价的 UN*X x86_64 将(ab)使用保留的 128 字节“红色区域”的一部分(就在堆栈上的返回地址下方):
MOV DWORD PTR [ RSP - c ], <low32>
MOV DWORD PTR [ RSP - 8 ], <high32>
JMP [ RSP - 8 ]
只有在调用 trampoline 时可以接受破坏(覆盖)其中的内容时,两者才可用。
如果可以在内存中直接构造这样一个与位置无关的寄存器中性蹦床 - 就像这样(对于方法 1.):
#include <stdint.h>
#include <stdio.h>
char *mystr = "Hello, World!\n";
int main(int argc, char **argv)
{
struct __attribute__((packed)) {
char PUSH;
uint32_t CONST_TO_PUSH;
uint32_t MOV_TO_4PLUS_RSP;
uint32_t CONST_TO_MOV;
char RET;
} mycode = {
0x68, ((uint32_t)printf),
0x042444c7, (uint32_t)((uintptr_t)printf >> 32),
0xc3
};
void *buf = /* fill in an OS-specific way to get an executable buffer */;
memcpy(buf, &mycode, sizeof(mycode));
__asm__ __volatile__(
"push $0f\n\t" // this is to make the "jmp" return
"jmp *%0\n\t"
"0:\n\t" : : "r"(buf), "D"(mystr), "a"(0));
return 0;
}
请注意,这没有考虑是否有任何非 volatile 寄存器被“调用”函数破坏;我还遗漏了如何使蹦床缓冲区可执行(堆栈通常不在 Win64/x86_64 上)。
关于windows - 为什么 OSX 在 amd64 间接跳转时出现总线错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11932609/