修复损坏的 ELF 标头字段“e_shnum”以在 GDB 中使用

逆向工程 数据库 小精灵 快手
2021-06-21 11:00:11

如果通常可以使用读取的 ELF 标头readelf已被手动操作,假设通过增加“节标头的大小”的值,二进制文件仍然可以执行并且运行良好。

但是,这种操作似乎会绊倒 GDC 和 GDB 等逆向工程工具给我的错误:not in executable format: File format not recognized.

有没有办法在不知道“节标题大小”的原始值的情况下修复 ELF 标题,以便能够再次使用标准工具分析文件

详细信息:

GDB 无法运行二进制文件,因为它说文件是,not in executable format : File format not recognized但它在 GDB 之外工作。libbfd解析器也会发生同样的事情,它无法解析,因为无法识别文件格式。事实是我只更改了部分标题的数量。

代码

#include <stdio.h>

int main()
{
    printf("Hello World!\n");
    return 0;
}

通过调用make hello或 在 64 位系统上构建make CFLAGS=-m32 hello

之前的 ELF 标头

$ readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048320
  Start of program headers:          52 (bytes into file)
  Start of section headers:          4472 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         30 <-- notice me!
  Section header string table index: 27

后的 ELF 标头

$ readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048320
  Start of program headers:          52 (bytes into file)
  Start of section headers:          4472 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         52 <-- already changed!
  Section header string table index: 27

GDB 将输出,

不是可执行格式:无法识别文件格式

但是如果我在 GDB 之外运行它,

$ ./hello output:
Hello World!

那么是否有一种方法可以在e_shnum 知道正确值的情况修复该值,或者有一种解决方法以便我可以在 GDB 中调试该文件?

2个回答

在这种特殊情况下,可以自动修复标题。由于存在节头字符串表,e_shnum可以通过对表中字符串的个数进行计数来找到的原始值

原来的:

$ readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x3e0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          6056 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         29  <-----------------
  Section header string table index: 28

损坏:

$ readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x3e0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          6056 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         52  <------------------
  Section header string table index: 28
readelf: Error: Reading 2080 bytes extends past end of file for section headers
readelf: Error: Reading 7216 bytes extends past end of file for dynamic string table

读取节头字符串表:

#!/usr/bin/python3

from elftools.elf.elffile import ELFFile

with open('hello', 'rb') as f:
    elffile = ELFFile(f)
    print("original e_shnum:\t" + str(len(elffile.get_section(28).data().decode('ascii').split('\x00')) + 1))

对标头损坏的二进制文件运行时,输出如下:

$ python3 recover_e_shnum.py 
original e_shnum:   29

此脚本将自动修复标题:

#!/usr/bin/python3

from elftools.elf.elffile import ELFFile
from struct import pack

with open('hello', 'rb+') as f:
    elffile = ELFFile(f)
    e_shnum = len(elffile.get_section(28).data().decode('ascii').split('\x00')) + 1 
    f.seek(48)
    f.write(pack('h', e_shnum))

我重新创建了您的二进制文件,然后使用 Radare完全按照您的方式破坏了标题,将节标题的数量更改为 52。

r2 -w a.out -1c's 0; pfo elf64; .pf.elf_header.shnum=52'

这只会在随后调用 时生成警告readelf

$ readelf -h a.out 
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:               0x1050
  Start of program headers:          64 (bytes into file)
  Start of section headers:          14624 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         11
  Size of section headers:           64 (bytes)
  Number of section headers:         52
  Section header string table index: 28
readelf: Error: Reading 3328 bytes extends past end of file for section headers

但是我从 GDB 得到了类似的错误,

不是可执行格式:文件被截断

而且,如果我尝试调试该文件,我会得到

未指定可执行文件。

我可以很容易地回到原来的

r2 -w a.out -c's 0; pfo elf64; .pf.elf_header.shnum=52'

然而,要回答你关于工具的问题,

  • 即使标头设置为 52,Radare 也可以使用它。您仍然可以调试它。
  • 如果标头小于原始标头,您可以让 gdb 工作shnum,例如将其设置为1只会警告您,

    BFD:警告:/tmp/a.out 有一个损坏的字符串表索引 - 忽略

    但您仍然可以调试程序。