Windows 为什么OSX在amd64间接跳转上出现总线错误?
我正在尝试为x86和amd64编写蹦床,以便将给定的函数调用立即矢量化到存储在已知内存位置的地址(目的是确保第一个目标地址位于给定的DLL(windows)中) 以下代码试图使用Windows 为什么OSX在amd64间接跳转上出现总线错误?,windows,macos,assembly,x86-64,Windows,Macos,Assembly,X86 64,我正在尝试为x86和amd64编写蹦床,以便将给定的函数调用立即矢量化到存储在已知内存位置的地址(目的是确保第一个目标地址位于给定的DLL(windows)中) 以下代码试图使用\u fn作为内存位置(或其中一组)来启动实际目标地址: (*_fn[IDX])(); // rough equivalent in C .globl _asmfn _asmfn: jmp *_fn+8*IDX(%rip) IDX旨在使用一些CPP宏来构造,以提供一系列嵌入式DLL向量,每个向量都唯一地映射到函数
\u fn
作为内存位置(或其中一组)来启动实际目标地址:
(*_fn[IDX])(); // rough equivalent in C
.globl _asmfn
_asmfn:
jmp *_fn+8*IDX(%rip)
IDX
旨在使用一些CPP宏来构造,以提供一系列嵌入式DLL向量,每个向量都唯一地映射到函数指针数组中的插槽。
这在一个简单的测试程序中起作用,但当我实际将其放入一个共享库(目前在OSX上进行测试)时,我在尝试向_asmfn代码向量时遇到了一个总线错误:
Invalid memory access of location 0x10aa1f320 rip=0x10aa1f320
这段代码的最终目标是Windows,尽管我还没有在那里尝试过(我想我至少可以先在OSX/intel上的测试用例中证明这个程序集)。amd64跳转至少在名义上是正确的,还是我错过了什么
一个很好的参考
编辑
跳转在Windows7上运行正常(终于有机会进行测试)。然而,我仍然很想知道为什么它在OSX上失败了。总线错误是由KERN_PROTECTION_故障引起的,这似乎表明操作系统保护正在阻止该代码的执行。目标地址是分配的内存(它是libffi生成的蹦床),但我相信它被正确地标记为可执行内存。如果是可执行内存问题,这可以解释为什么我的独立测试代码可以工作(回调蹦床是编译的,而不是分配的)。使用PC相对寻址时,请记住偏移量必须在+-2GB范围内。这意味着你的跳台和蹦床之间不能太远。就蹦床而言,在Windows x64上可以做什么来传输而不需要删除任何寄存器:
推送
MOV-DWORD PTR[RSP-4],
RET
这适用于Win64和UN*X x86_64。虽然在UN*X上,如果函数使用了redzone,那么您正在使用
JMP[RIP]
.L:
同样,适用于Win64和UN*X x86_64
MOV-DWORD-PTR[RSP+c],
MOV DWORD PTR[RSP+8],
JMP[RSP+8]
这是特定于Win64的,因为它(ab)使用Win64 ABI保留的部分32字节“参数空间”(就在堆栈上的返回地址上方);UN*X x86_64相当于(ab)使用保留的128字节“红色区域”(就在堆栈上的返回地址下方)的一部分:
MOV-DWORD-PTR[RSP-c],
MOV-DWORD PTR[RSP-8],
JMP[RSP-8]
只有在调用蹦床时可以重击(覆盖)其中的内容时,这两个选项才可用
#包括
#包括
char*mystr=“你好,世界!\n”;
int main(int argc,字符**argv)
{
结构属性((压缩)){
炭推;
uint32持续推送;
uint32移动到4个PLC;
uint32施工至移动;
charret;
}mycode={
0x68,((uint32_t)printf),
0x042444c7,(uint32_t)((uintpttr_t)printf>>32),
0xc3
};
void*buf=/*以特定于操作系统的方式填充以获取可执行缓冲区*/;
memcpy(buf和mycode,sizeof(mycode));
__asm\uuuuuuuuuuuuuuuuuu挥发性__(
“push$0f\n\t”//这是为了使“jmp”返回
“jmp*%0\n\t”
“0:\n\t”:“r”(buf),“D”(mystr),“a”(0));
返回0;
}
注意,这并没有考虑函数“调用”是否正在对任何非易失性寄存器进行阻塞;我还遗漏了如何使trampoline缓冲区可执行(堆栈通常不在Win64/x86_64上)。使用PC相对寻址时,请记住偏移量必须在+-2GB范围内。这意味着你的跳台和蹦床之间不能太远。就蹦床而言,在Windows x64上可以做什么来传输而不需要删除任何寄存器:
推送
MOV-DWORD PTR[RSP-4],
RET
这适用于Win64和UN*X x86_64。虽然在UN*X上,如果函数使用了redzone,那么您正在使用
JMP[RIP]
.L:
同样,适用于Win64和UN*X x86_64
MOV-DWORD-PTR[RSP+c],
MOV DWORD PTR[RSP+8],
JMP[RSP+8]
这是特定于Win64的,因为它(ab)使用Win64 ABI保留的部分32字节“参数空间”(就在堆栈上的返回地址上方);UN*X x86_64相当于(ab)使用保留的128字节“红色区域”(就在堆栈上的返回地址下方)的一部分:
MOV-DWORD-PTR[RSP-c],
MOV-DWORD PTR[RSP-8],
JMP[RSP-8]
只有在调用蹦床时可以重击(覆盖)其中的内容时,这两个选项才可用
#包括
#包括
char*mystr=“你好,世界!\n”;
int main(int argc,字符**argv)
{
结构属性((压缩)){
炭推;
uint32持续推送;
uint32移动到4个PLC;
uint32施工图
#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;
}