【问题标题】:x86 Linux ELF Loader Troublesx86 Linux ELF 加载程序问题
【发布时间】:2019-06-10 18:51:18
【问题描述】:

我正在尝试为 x86-64 Linux 编写一个 ELF 可执行加载程序,类似于在 ARM 上实现的 thisChris Rossbach's advanced OS class 包括一个基本上可以做我想做的事情的实验室。我的目标是将一个简单的(静态链接的)“hello world”类型的二进制文件加载到我的进程的内存中,并在没有execveing 的情况下运行它。我已经成功mmap'd ELF 文件,设置堆栈,并跳转到 ELF 的入口点(_start)。

// put ELF file into memory. This is just one line of a complex
// for() loop that loads the binary from a file.
mmap((void*)program_header.p_vaddr, program_header.p_memsz, map, MAP_PRIVATE|MAP_FIXED, elffd, program_header.p_offset);


newstack = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); // Map a page for the stack

if((long)newstack < 0) {
  fprintf(stderr, "ERROR: mmap returned error when allocating stack, %s\n", strerror(errno));
  exit(1);
}

topstack = (unsigned long*)((unsigned char*)newstack+4096); // Top of new stack

*((unsigned long*)topstack-1) = 0; // Set up the stack
*((unsigned long*)topstack-2) = 0; // with argc, argv[], etc.
*((unsigned long*)topstack-3) = 0;
*((unsigned long*)topstack-4) = argv[1];
*((unsigned long*)topstack-5) = 1;

asm("mov %0,%%rsp\n"     // Install new stack pointer
    "xor %%rax, %%rax\n" // Zero registers
    "xor %%rbx, %%rbx\n"
    "xor %%rcx, %%rcx\n"
    "xor %%rdx, %%rdx\n"
    "xor %%rsi, %%rsi\n"
    "xor %%rdi, %%rdi\n"
    "xor %%r8, %%r8\n"
    "xor %%r9, %%r9\n"
    "xor %%r10, %%r10\n"
    "xor %%r11, %%r11\n"
    "xor %%r12, %%r12\n"
    "xor %%r13, %%r13\n"
    "xor %%r14, %%r14\n"
    :   
    : "r"(topstack-5)
    :"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14");
asm("push %%rax\n"
    "pop %%rax\n"
    :   
    :   
    : "rax");

asm("mov %0,%%rax\n" // Jump to the entry point of the loaded ELF file
    "jmp *%%rax\n"
    :   
    : "r"(jump_target)
    : );

然后我在gdb 中逐步执行此代码。我在下面粘贴了启动代码的前几条指令。在第一个 push 指令(已加星标)之前,一切都很好。 push 会导致段错误。

0x60026000      xor    %ebp,%ebp
0x60026002      mov    %rdx,%r9
0x60026005      pop    %rsi
0x60026006      mov    %rsp,%rdx
0x60026009      and    $0xfffffffffffffff0,%rsp
0x6002600d *    push   %rax
0x6002600e      push   %rsp
0x6002600f      mov    $0x605f4990,%r8

我试过了:

  1. 使用原始进程中的堆栈。
  2. mmap一个新堆栈(如上面的代码):(1)和(2)都导致段错误。
  3. pushing 和poping 在jmping 到加载的 ELF 文件之前/从堆栈。这不会导致段错误。
  4. 将第二个mmap 中的堆栈保护标志更改为PROT_READ | PROT_WRITE | PROT_EXEC。这没什么区别。

我怀疑这可能与段描述符有关(也许?)。似乎我正在加载的 ELF 文件中的代码对堆栈段没有写访问权限,无论它位于何处。我没有尝试修改新加载的二进制文件的段描述符或更改架构段寄存器。这是必要的吗?有人知道如何解决这个问题吗?

【问题讨论】:

  • 也许是asm volatile 以防止 GCC 通过优化搞砸事情。另外,您是否确认您使用的是LP64 数据模型?
  • 感谢您的建议。我已经通过在 gdb 汇编模式下单步执行验证了我的内联汇编在编译后的二进制文件中被忠实地转换了。我假设程序处于 LP64 模式,但不确定。它使用 rax、rbx、rcx 等,其中包含 64 位整数。进入 LP64 模式就足够了吗?
  • topstack的类型是什么? topstack-5$RSP 在崩溃点的值是多少?
  • topstack 是一个unsigned long *。在崩溃之前,topstack-5 是 1,$rsp0x7ffff7ff6fe0

标签: linux binary elf loader


【解决方案1】:

事实证明,当我单步执行 gdb 中加载的代码时,当我输入 nexti 时,调试器会一直被第一条 push 指令破坏,而是继续执行。实际上,导致段错误的不是 push 指令,而是 C 库启动代码中的一条更晚的指令。问题是由于在初始二进制加载中调用 mmap 失败导致的,我没有进行错误检查。

关于gdb随机决定继续执行而不是步进:这可以通过在跳转到新加载的可执行文件后从目标可执行文件加载符号来解决。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 2011-02-19
    相关资源
    最近更新 更多