使用 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。
一个序列:这是特定于 Win64 的,因为它 (ab) 使用由 Win64 ABI
MOV DWORD PTR [ RSP + c ], <low32>
MOV DWORD PTR [ RSP + 8 ], <high32>
JMP [ RSP + 8 ]
保留的 32 字节“参数空间”的一部分(就在堆栈上的返回地址上方);与此等效的 UN*X x86_64 将(ab)使用保留的 128 字节“红色区域”的一部分(就在堆栈上的返回地址下方
MOV DWORD PTR [ RSP - c ], <low32>
MOV DWORD PTR [ RSP - 8 ], <high32>
JMP [ RSP - 8 ]
):
两者只有在可以接受破坏时才可用(覆盖)在调用蹦床时那里有什么。
如果可以直接在内存中构造这样一个与位置无关的寄存器中性蹦床 - 像这样(对于方法 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;
}
Note that this doesn't take into account whether any nonvolatile registers are being clobbered by the function "invoked"; I've also left out how to make the trampoline buffer executable (the stack ordinarily isn't on Win64/x86_64).