这是旗帜:
旗帜{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)