2

我正在尝试为 x86 和 amd64 编写蹦床,以便将给定的函数调用立即向量化到存储在已知内存位置的地址(目的是确保第一个目标地址位于给定的 DLL(窗口)中)。

以下代码试图_fn用作内存位置(或其中一组)来启动实际目标地址:

(*_fn[IDX])(); // rough equivalent in C

.globl _asmfn
_asmfn:
  jmp *_fn+8*IDX(%rip)

旨在使用一些 CPP 宏来构建,IDX以提供一系列嵌入式 DLL 向量,每个向量都唯一地映射到_fn函数指针数组中的一个槽。这适用于一个简单的测试程序,但是当我实际将它放入共享库时(目前在 OSX 上进行测试),在尝试引导到 _asmfn 代码时出现总线错误:

Invalid memory access of location 0x10aa1f320 rip=0x10aa1f320

这段代码的最终目标是 Windows,虽然我还没有在那里尝试过(我想我至少可以先在 OSX/intel 上的测试用例中证明程序集)。amd64 跳转至少在名义上是正确的,还是我错过了什么?

amd64 上关于蹦床的一个很好的参考。

编辑

跳转在 Windows 7 上确实可以正常工作(终于有机会测试了)。但是,我仍然很想知道为什么它在 OSX 上失败了。总线错误是由 KERN_PROTECTION_FAILURE 引起的,这似乎表明操作系统保护正在阻止执行该代码。目标地址分配的内存(它是由 libffi 生成的蹦床),但我相信它被正确标记为可执行内存。如果这是一个可执行内存问题,那就可以解释为什么我的独立测试代码可以工作(回调蹦床是编译的,而不是分配的)。

4

2 回答 2

0

使用 PC 相对寻址时,请记住偏移量必须在 +- 2GB 范围内。这意味着您的跳台和蹦床不能相距太远。关于蹦床本身,在 Windows x64 上可以在不需要破坏任何寄存器的情况下进行传输是:

  1. 一个序列:
    PUSH <high32>
    MOV DWORD PTR [ RSP - 4 ], <low32>
    RET
    这适用于 Win64 和 UN*X x86_64。尽管在 UN*X 上,如果该函数使用 redzone,那么您正在崩溃……

  2. 一个序列:
    JMP [ RIP ]
    .L: <tgtaddr64>
    同样适用于 Win64 和 UN*X x86_64。

  3. 一个序列:这是特定于 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).

于 2012-08-14T16:55:21.843 回答
0

@HarryJohnston had the right of it, the permissions issue was encountered on OS X only. The code runs fine on its target windows environment.

于 2013-01-01T22:36:09.537 回答