4. 提取__TEXT段中的代码

任务说明

本案例目标是从一个 Mach-O 文件的 __TEXT 段中提取可执行代码,并在 Ghidra 中将其反汇编为汇编指令。你将学习如何定位 __TEXT 段在文件中的偏移与大小,使用命令行工具抽取原始字节,再通过 Ghidra 的导入与分析功能完成反汇编。

环境准备

  • macOS 或 Linux 系统(需能运行 otool 或 llvm-objdump)
  • Ghidra 11.x(已安装并配置好 JDK 17+)
  • 一个测试用 Mach-O 可执行文件(可用 /bin/ls 或任意编译出的二进制)

实现步骤

1. 定位 __TEXT 段偏移与大小

使用 otool -l 查看段信息,找到 __TEXT 段的 fileoff 与 filesize:

otool -l /bin/ls | grep -A 5 "sectname __text"

输出示例(实际值因系统而异):

sectname __text
segname __TEXT
addr 0x0000000100003e50
size 0x0000000000002e52
offset 15824

这里 offset 是 __text 节(section)在文件中的偏移,size 是节大小。若需提取整个 __TEXT 段,则使用段级 fileoff 与 filesize(通常段偏移为 0,大小为所有节之和)。

2. 提取原始字节

用 dd 命令按偏移和大小抽取字节到独立文件:

# 假设 __TEXT 段 fileoff=0, filesize=0x1000(示例值,请用实际值替换)
dd if=/bin/ls of=text_segment.bin bs=1 skip=0 count=4096

若只提取 __text 节,则使用节的 offset 和 size:

dd if=/bin/ls of=text_section.bin bs=1 skip=15824 count=11858

3. 在 Ghidra 中反汇编

  1. 打开 Ghidra,创建新项目,导入 text_segment.bin(或 text_section.bin)。
  2. 在导入对话框中选择语言为 AARCH64:LE:64:v8A(Apple Silicon)或 x86:LE:64:default(Intel Mac),具体取决于你的测试文件架构。
  3. 双击打开导入的文件,点击“分析”按钮,选择默认分析选项并运行。
  4. 分析完成后,Ghidra 会自动将原始字节反汇编为指令,在 Listing 窗口中显示汇编代码。

4. 验证反汇编结果

对比 Ghidra 反汇编结果与系统工具输出是否一致:

# 使用 llvm-objdump 反汇编原始 __text 节作为参照
llvm-objdump --macho --disassemble /bin/ls | head -20

观察 Ghidra Listing 窗口中的前几条指令是否与 llvm-objdump 输出匹配。若匹配,说明提取与反汇编流程正确。

关键代码(提取脚本)

以下 Python 脚本自动完成段定位与提取,适用于任意 Mach-O 文件:

import struct
import subprocess
import sys

def extract_text_segment(binary_path, output_path):
    # 使用 otool 获取 __TEXT 段信息
    result = subprocess.run(['otool', '-l', binary_path], capture_output=True, text=True)
    lines = result.stdout.split('\n')
    
    fileoff = None
    filesize = None
    for i, line in enumerate(lines):
        if 'segname __TEXT' in line:
            # 向后查找 fileoff 和 filesize
            for j in range(i, min(i+10, len(lines))):
                if 'fileoff ' in lines[j]:
                    fileoff = int(lines[j].split()[-1], 16)
                if 'filesize ' in lines[j]:
                    filesize = int(lines[j].split()[-1], 16)
                if fileoff is not None and filesize is not None:
                    break
            break
    
    if fileoff is None or filesize is None:
        print("未找到 __TEXT 段信息")
        sys.exit(1)
    
    # 读取并提取字节
    with open(binary_path, 'rb') as f:
        f.seek(fileoff)
        data = f.read(filesize)
    
    with open(output_path, 'wb') as f:
        f.write(data)
    
    print(f"已提取 {filesize} 字节到 {output_path}")

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("用法: python extract_text.py <输入二进制> <输出文件>")
        sys.exit(1)
    extract_text_segment(sys.argv[1], sys.argv[2])

运行与验证

  1. 保存上述脚本为 extract_text.py,运行:
python3 extract_text.py /bin/ls text_segment.bin
  1. 将 text_segment.bin 导入 Ghidra,选择正确架构后分析。
  2. 在 Listing 窗口中查看反汇编结果,确认指令序列合理(如 sub sp, sp, #0x30、stp x29, x30, [sp, #0x20] 等典型函数序言)。
  3. 用 llvm-objdump --macho --disassemble /bin/ls 对比同一区域的指令,若前 10 条指令一致,则验证通过。

通过条件:Ghidra 成功将提取的 __TEXT 字节反汇编为可读的汇编指令,且与系统反汇编工具输出一致,无“未知指令”或数据误判为代码的情况。