gpt4 book ai didi

linux - 在 Linux 上的 64 位进程中运行 32 位代码 - 内存访问

转载 作者:IT王子 更新时间:2023-10-29 00:54:32 26 4
gpt4 key购买 nike

我正在尝试在 64 位 Linux 进程中运行 32 位代码。 32 位代码是完全独立的,它自己直接进行 IA32 系统调用。如果我在 32 位进程中加载​​此代码,它会运行得很好。

最初,我以为我可以为 32 位代码分配一个堆栈,切换到它,一切都会正常工作,但事情并不顺利。主要是因为与堆栈相关的指令 (POP/PUSH/...) 正在执行 8 字节移位而不是 4 字节。

通过谷歌搜索,我了解到可以通过切换到段选择器 0x23 来转换到 32 位模式。不幸的是,我对分段知之甚少。

我可以用这样的东西(内联 AT&T 汇编)转换到 32 位模式:

movl $0x23, 4(%%rsp) // segment selector 0x23
movq %0, %%rax
movl %%eax, (%%rsp) // target 32-bit address to jump to
lret

其中 %0 包含代码映射位置的 32 位地址。代码开始运行,我可以看到 PUSH/POP 现在按其应有的方式工作,但它崩溃的时间甚至比我在 64 位模式下按看似无害的指令运行代码时更早:

0x8fe48201      mov    0xa483c(%rbx),%ecx

%rbx(或者更像 %ebx,因为这段代码已经是 32 位的,GDB 只是不知道)包含 0x8fe48200。它试图从中读取的地址 (0x8feeca3c) 是有效且可读的(根据 /proc/XXX/maps),当我从 GDB 中读取它时,它包含期望值。

然而,Linux 发送一个 SIGSEGV 到这个指令的进程,错误地址是 0(如 stracep $_siginfo._sifields._sigfault.si_addrgdb 中)。不知何故,0x8feeca3c 似乎不是 32 位领域中的有效地址。

有什么想法可以继续吗?

更新:我写了一个最小的例子,它读取地址 0 时出现段错误,尽管地址 0 并没有真正被引用。似乎读取内存中的任何地址都失败(甚至读取刚刚执行的指令的地址!),尽管堆栈操作工作正常。

#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>

// 32-bit code we're executing
const unsigned char instructions[] = {
0x6a, 0, // push 0
0x58, // popl %eax
0xe8, 0, 0, 0, 0, // call the next line to get our location in memory
0x5b, // pop %ebx
// THE FOLLOWING mov SEGFAULTS, but it is well within the mapped area (which has size 0x3000)
// A simpler "mov (%ebx), %eax" (0x8b, 0x03) would fail as well
0x8b, 0x83, 0, 0x20, 0, 0, // mov 0x2000(%ebx), %eax
0xf4 // hlt, not reached
};

int main()
{
void* area;
void* stack;

area = mmap(NULL, 3*4096, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
memcpy(area, instructions, sizeof(instructions));

stack = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
stack = (void*) (((uint64_t) stack) + 4096 - 4);

memset(((char*)area) + 2*4096, 0xab, 100); // Place 0xAB in the area we mov from in 32-bit instructions

// Switch to 32-bit mode and jump into the code
__asm__ volatile("movq %1, %%rsp;" \
"subq $8, %%rsp;" \
"movl $0x23, 4(%%rsp);" \
"movq %0, %%rax;" \
"movl %%eax, (%%rsp);" \
"lret" :: "m"(area), "r"(stack) :);
}

最佳答案

好问题:)

问题是 ds 仍然设置为零,在 64 位模式下它没有被使用。所以,你需要重新加载它,它会工作。将初始测试 push/pop 更改为 push $0x2b; pop %ds 就可以了:

const unsigned char instructions[] = {
0x6a, 0x2b, // push $0x2b
0x1f, // pop %ds

我从一个 32 位程序中提取了 0x2b 值。我一直想知道为什么 push 有效。仔细观察,ss 也设置为 64 位模式,因此将其复制到 dses 可能更安全。

关于linux - 在 Linux 上的 64 位进程中运行 32 位代码 - 内存访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41921711/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com