1

我一直在binfmt为 Ubuntu Linux 12.04、Intel x86_64 架构开发一个自定义 PE 处理程序(如果这听起来很熟悉,我已经发布了一些与该项目相关的问题)。如果我提供的信息量过大,我会提前道歉。

binfmt处理程序非常标准;我读入 PE 头和节,然后将这些节写入用户空间内存中节表中指定的地址。然后,当一切准备就绪时,我打电话

start_thread(regs, entry_addr, current->mm->start_stack);

就像内置的 Linux 处理程序一样;在我的情况下,regs = 0xcf7dffb4entry_addr = 0x401000start_stack = 0xbffff59b

我在 Intel x86 程序集中有以下代码:

push ebp
mov ebp, esp

mov eax, 4
add eax, 5

pop ebp
ret

我使用fasm将此程序编译为 PE 格式的可执行文件(math1.exe),并binfmt使用insmod. 如果我在 中调试这个程序gdb,我会看到:

(gdb) set disassembly-flavor intel
(gdb) x/6i 0x401000
    0x401000:   push   ebp
    0x401001:   mov    ebp,esp
    0x401003:   mov    eax,0x4
    0x401008:   add    eax,0x5
    0x40100b:   pop    ebp
    0x40100c:   ret

所以我知道代码已加载到正确的地址。然后:

(gdb) run
Starting program: /media/sf_Sandbox/math1.exe 

Program received signal SIGSEGV, Segmentation fault.
0x0040100c in ?? ()

当我进行寄存器转储时:

(gdb) info registers
eax            0x9  9
ecx            0x81394e8    135501032
edx            0x8137808    135493640
ebx            0x8139548    135501128
esp            0xbfffe59b   0xbfffe59b
ebp            0x0  0x0
esi            0x81394e8    135501032
edi            0x2f7ff4 3112948
eip            0x40100c 0x40100c
...other registers...

您可以看到代码确实执行了eax = 0x9,因为它应该执行。不过,从表面上看,我找不到任何理由在ret声明中出现段错误。检查dmesg,我发现

math1.exe[1864] general protection ip:40100c sp:bffff5bd error:0

但我发现很少有关于可能导致这种情况的文档。我知道问题不在于代码本身,因为使用相同的汇编程序编译为 ELF 格式的相同代码运行起来没有任何问题。

我目前关于这个问题的理论是:

  1. 我并没有真正弄乱处理程序中的堆栈指针。内置的 Linux 处理程序(用于 ELF、a.out 和平面格式,仅举三例)具有create_*_tables()处理argcargvenvp参数的函数。我一开始没有包含这个函数,因为测试程序不接受任何输入,但是实现这个create_flat_tables()函数(从平面处理程序)到目前为止并没有解决问题。(我知道从其他模块中盲目粘贴和调用函数是个主意,但是该函数的 a.out 和 flat 版本本质上是相同的,所以它似乎不太依赖于可执行格式;我以为我会试一试。)
  2. 我发现这篇文章是关于在main(). objdumpmath1.exe 的 仅包含上面给出的汇编代码,但同一objdump程序在汇编为 ELF 格式(生成*.o文件)和链接gcc(以获取 ELF 二进制文件)后包含文章中提到的其他函数 ( _start(), __libc_start_main(), ETC。)。也许这些功能在 Linux 平台上比我之前想象的更加强制性。

我正在寻找可以采取的任何解释/建议/进一步的故障排除步骤。提前致谢!

4

1 回答 1

3

_start您确实需要实现and序列的至少一个方面__libc_start_main:调用 _exit 系统调用。您不能只从 execve 创建的框架中执行“ret”,并期望它会导致进程干净地终止。从 main 返回退出进程是 C 功能,而您的程序不是 C。

我对系统调用界面的记忆有点模糊,但我相信它是这样的:

  1. 将 %eax 设置为系统调用号 ( _NR_exit)
  2. 将 %ebx 设置为第一个参数(退出代码)
  3. 诠释 $0x80

%ecx、%edx、...的附加参数我不确定顺序。但是 _exit() 只需要一个参数,我很确定它在 %ebx 中。

于 2012-08-20T22:25:38.500 回答