重新组装 ELF 二进制文件

逆向工程 小精灵
2021-07-06 11:16:45

当我遇到一些我不熟悉的事情时,我一直在做一些 CTF 活动。由于我对开发和逆向工程有点陌生,这对我来说很陌生。

我有 7 个 ELF 二进制文件。我一直在使用 readelf 来更好地了解每件作品的放置位置。但我仍然无法收集订单,除了第 4 部分包含标题,第 6 部分包含部分名称。

我一直在使用“cat”将这些部分粘合在一起,然后通过 readelf 和 gdb 运行它们。有没有更简单的方法让我失踪?

作为参考,可以在0x0539.net -> Fangorn Forest -> Fragmented上找到特定示例我不要答案。我正在寻找的是能够为我指明正确方向的人。

2个回答

这是旗帜:

旗帜{1ts_l1k3_4_m0r3_fun_puzz1e_m0m}

笔记:

  • readelf -SW用于查看 2 个小的基线二进制文件:一个简单的“hello world”风格的 ELF 二进制文件和cat实用程序。这为 GCC 如何组织二进制信息提供了参考框架。
  • 然后将片段的十六进制转储与这两个程序的十六进制转储进行比较。
  • 特别重要的领域是某些部分和其边界之间,例如.plt.text.rodata包含字符串的部分提供了有用的参考点,某些部分包含具有重复模式的类似表格的结构,并且还可以仅通过查看转储来识别目标代码。十六进制转储中的偏移量可以与输出的信息交叉引用,readelf -SW以将转储中的位置与不同的部分相匹配。
  • 在识别出第一块和最后一块之后,对它们进行进一步分析以确定它们包含哪些部分。正如您所指出的,frag_4包含 ELF 标头并frag_6包含节标头字符串表。然而,这两部分实际上包含的不仅仅是这些信息。
  • frag_3包含过程链接表和.text的开头
  • 第 2、3、5 和 7 部分由目标代码组成
  • frag_5看起来它包含了该.text部分的结尾
  • 此时,顺序如下所示: 4, 1, {2, 3, 7 - 无法从十六进制转储中推断出顺序}, 5, 6
  • 第 2、3 和 7 部分是使用 Capstone 使用以下脚本分解的:
#!/usr/bin/python3

from capstone import *
import sys


md = Cs(CS_ARCH_X86, CS_MODE_64)

def disassemble(buf):
    for i in md.disasm(buf, 0x4004e0):
        print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))


def main():
    file_buf = []

    for arg in sys.argv:
        file_buf.append(arg)

    buf = []
    for file in file_buf[1:]:
        with open(file, 'rb') as f:
            buf = f.read()
            print(f.name + '\n')
            disassemble(buf)
            print('\n\n')

if __name__ == '__main__':
    main()
  • 看完拆解后,做了一个有根据的猜测:3, 2, 7
  • 然后重建文件:
#!/usr/bin/python3

file_names = ['frag_4', 'frag_1', 'frag_3', 'frag_2', 'frag_7', 'frag_5', 'frag_6']

bytes = b''
for file in file_names:
    with open(file, 'rb') as f:
        buf = f.read()
        bytes += buf

with open('reassembled.elf', 'wb') as f:
    f.write(bytes)

结果文件:

$ readelf -SW reassembled.elf 
There are 31 section headers, starting at offset 0x1ac8:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        0000000000400238 000238 00001c 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            0000000000400254 000254 000020 00   A  0   0  4
  [ 3] .note.gnu.build-id NOTE            0000000000400274 000274 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        0000000000400298 000298 00001c 00   A  5   0  8
  [ 5] .dynsym           DYNSYM          00000000004002b8 0002b8 000090 18   A  6   1  8
  [ 6] .dynstr           STRTAB          0000000000400348 000348 00005f 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          00000000004003a8 0003a8 00000c 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         00000000004003b8 0003b8 000030 00   A  6   1  8
  [ 9] .rela.dyn         RELA            00000000004003e8 0003e8 000018 18   A  5   0  8
  [10] .rela.plt         RELA            0000000000400400 000400 000060 18  AI  5  24  8
  [11] .init             PROGBITS        0000000000400460 000460 00001a 00  AX  0   0  4
  [12] .plt              PROGBITS        0000000000400480 000480 000050 10  AX  0   0 16
  [13] .plt.got          PROGBITS        00000000004004d0 0004d0 000008 00  AX  0   0  8
  [14] .text             PROGBITS        00000000004004e0 0004e0 0003c2 00  AX  0   0 16
  [15] .fini             PROGBITS        00000000004008a4 0008a4 000009 00  AX  0   0  4
  [16] .rodata           PROGBITS        00000000004008b0 0008b0 00000f 00   A  0   0  4
  [17] .eh_frame_hdr     PROGBITS        00000000004008c0 0008c0 00004c 00   A  0   0  4
  [18] .eh_frame         PROGBITS        0000000000400910 000910 000154 00   A  0   0  8
  [19] .init_array       INIT_ARRAY      0000000000600e10 000e10 000008 00  WA  0   0  8
  [20] .fini_array       FINI_ARRAY      0000000000600e18 000e18 000008 00  WA  0   0  8
  [21] .jcr              PROGBITS        0000000000600e20 000e20 000008 00  WA  0   0  8
  [22] .dynamic          DYNAMIC         0000000000600e28 000e28 0001d0 10  WA  6   0  8
  [23] .got              PROGBITS        0000000000600ff8 000ff8 000008 08  WA  0   0  8
  [24] .got.plt          PROGBITS        0000000000601000 001000 000038 08  WA  0   0  8
  [25] .data             PROGBITS        0000000000601038 001038 000010 00  WA  0   0  8
  [26] .bss              NOBITS          0000000000601048 001048 000008 00  WA  0   0  1
  [27] .comment          PROGBITS        0000000000000000 001048 000035 01  MS  0   0  1
  [28] .shstrtab         STRTAB          0000000000000000 0019b8 00010c 00      0   0  1
  [29] .symtab           SYMTAB          0000000000000000 001080 0006c0 18     30  47  8
  [30] .strtab           STRTAB          0000000000000000 001740 000278 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

由于符号表和节头表是完整的,objdump -dj .text reassembled.elf用于比较文件中代码的反汇编与 的反汇编cat以检查一致性。有一些差异,但结果并不重要,因为即使程序出现段错误,标志也是可以恢复的:

$ ./reassembled.elf 
��Ϛ^FLAG{1ts_l1k3_4_m0r3_fun_puzz1e_m0m}
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)

研究标题中的标志的实际含义可能会有所帮助。这将告诉您有用的信息,例如“事物”的位置和所述“事物”的大小。此外,您将更好地了解 ELF 文件格式!

之后,您可能可以使用 python 脚本自动执行该过程,查找关键标志并使用它们将其拼凑在一起。