execve ENOMEM(无法分配内存)执行某些x64二进制文件时出错

时间:2017-12-01 11:55:48

标签: ubuntu binary execve

当我在ubuntu 17.10中执行一些静态链接的二进制文件时, 其他二进制文件是可以的,但具体的一些二进制文件是问题。

gdb-peda$ x/15i 0x4000f0
   0x4000f0:    push   0x400105
   0x4000f5:    push   0x0
   0x4000f7:    xor    rax,rax
   0x4000fa:    mov    edx,0x10000
   0x4000ff:    mov    rsi,rsp
   0x400102:    xor    rdi,rdi
   0x400105:    syscall 
   0x400107:    mov    eax,0x1
   0x40010c:    mov    edx,0x1000
   0x400111:    pop    rsi
   0x400112:    mov    edi,0x1
   0x400117:    syscall 
   0x400119:    mov    eax,0x3c
   0x40011e:    ret

所有代码都像上面那样。 如果我运行这个二进制文件,我得到以下错误声明:

root@ubuntu:/home/hitcon/Desktop# ./biglittle 
Segmentation fault

我用strace跟踪它,因为这个二进制文件是静态链接的, 我明白了。

root@ubuntu:/home/hitcon/Desktop# strace -fFi ./biglittle 
[00007f9eaed12fa7] execve("./biglittle", ["./biglittle"], [/* 53 vars */]) = -1 ENOMEM (Cannot allocate memory)
[00007f9eaed12fa7] --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=NULL} ---
[????????????????] +++ killed by SIGSEGV +++
Segmentation fault (core dumped)

有人可以给我建议解决它吗?

1 个答案:

答案 0 :(得分:0)

我在一个特殊二进制文件上有完全相同的strace。我们的名字不是二进制foo.elf

在docker 中运行时,在内核空间中运行foo.elf segfaults *,这是在流控制到达ELF入口点之前的方式。与您显示的完全相同:execve返回ENOMEM,然后strace进程本身会收到带有si_code=SI_KERNEL的奇怪SIGSEGV。

*更准确地说,不是foo.elf进程的段错误(一个甚至从未开始运行) - 它是post-fork()父进程获取SIGSEGV。功能

但是,foo.elf在主机系统上运行良好!

令人费解。

所以这就是我在我的案例中找到的。我的foo.elf可执行文件是静态链接的:

⚓  file foo.elf
foo.elf: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), statically linked, stripped

- 这意味着它不使用共享库(.so)。反过来,这意味着ELF文件不会通过ld.so程序头(也称为段)请求动态链接器INTERP作为其解释器

⚓  readelf --segments foo.elf

Elf file type is DYN (Shared object file)
Entry point 0x14b5b0
There are 3 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000100000 0x0000000000100000
                 0x000000000004bddb 0x000000000004bddb  R E    10000
  LOAD           0x000000000000bc18 0x00000000000fbc18 0x00000000000fbc18
                 0x0000000000000000 0x0000000000000000  RW     1000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    0

- 请参阅,没有INTERP部分。与基线动态可执行文件比较:

⚓  readelf --segments /bin/ls

Elf file type is EXEC (Executable file)
Entry point 0x404b48
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000193fc 0x00000000000193fc  R E    200000
  LOAD           0x000000000001a328 0x000000000061a328 0x000000000061a328
                 0x00000000000012d8 0x0000000000001ff8  RW     200000
  DYNAMIC        0x000000000001ada8 0x000000000061ada8 0x000000000061ada8
                 0x0000000000000200 0x0000000000000200  RW     8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x0000000000016650 0x0000000000416650 0x0000000000416650
                 0x0000000000000744 0x0000000000000744  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
  GNU_RELRO      0x000000000001a328 0x000000000061a328 0x000000000061a328
                 0x0000000000000cd8 0x0000000000000cd8  R      1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
 [...]

这是正确的。

......等等!这是什么?

⚓  file foo.elf
foo.elf: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), statically linked, stripped

这个??

⚓ readelf --file-header foo.elf
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x14b5b0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         3
  Size of section headers:           64 (bytes)
  Number of section headers:         0
  Section header string table index: 0

...... Aah-huh!我们的可执行文件声称是一个共享库!

即。其ELF文件标头的e_type字段设置为ET_DYN(3),而不是ET_EXEC(2)。

这有点奇怪,但通常是可以接受的:ELF格式允许双重任务二进制文件,您可以dlopen()作为库,exec()作为程序。动态链接器(ld.so),我认为就是这样。

回到我的foo.elf:它是使用-fPIE CFLAG和-pie链接标记生成的,稍后使用几个ELF转换工具进行后处理。 PIE代表与位置无关的可执行文件;对我而言,删除-fPIE -pie以获取e_type == ET_EXEC二进制文件就足够了:

foo.elf.nopie: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

这个很棒。在码头工人或其他方面没有段错误。哇哇,谜题解决了!希望这有助于那里的人:)