最近我意识到你可以在 64 位代码中做到这一点:
const size_t kLowStackSize = 1024UL * 1024UL * 4UL;
void *low_stack = mmap(NULL, kLowStackSize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
struct __attribute__((packed, aligned(16))) {
int32_t address;
int16_t segment;
} target = {(uint32_t) (uint64_t) code, 0x23};
asm volatile(
"mov %%rsp, %%r8\n"
"mov %[stack], %%rsp\n"
"push %%r8\n"
"lcall *(%[target])\n"
"pop %%rsp"
:
: [stack] "r" (low_stack + kLowStackSize), [target] "r" (&target)
: "r8");
wherecode
指向位于地址空间较低 4GiB 的可执行页面上的一段 32 位代码,并且是Linux x86 标头中段选择器0x23
的值。__USER32_CS
我不知道跳跃目标是否需要属性,但我添加了for good measure。当然,为了使远程返回成为可能,这个调用代码本身必须位于虚拟地址空间的低 4 GiB 中的某个位置。我发现把它放进main
去就足够了。
我知道这几乎是无用的(没有加载 32 位库,调用约定不同等)并且容易损坏( 的值__USER32_CS
不是 Linux 面向用户空间的 API 的一部分)。
我的问题:有没有一种简单的方法来证明调用的目标确实是在 32 位模式下执行的?这种调用是否有任何实际用途(现有的库软件利用它,或者至少不那么不切实际的可能性)?