re-format-macho について
re-format-machoは、macOSやiOSのMach-Oバイナリを調べるリバースエンジニアリングスキルです。構造、依存関係、エントリーポイント、署名、ライブラリ注入の確認手順を示します。
主な機能
- 01ファイル形式とアーキテクチャの特定
- 02ヘッダーとロードコマンドの確認
- 03動的ライブラリ依存関係の追跡
- 04シンボルとエントリーポイントの確認
- 05コード署名と注入の検査
活用シーン
- 01macOS実行ファイルやdylibの分析
- 02iOSバイナリ構造の確認
- 03不審なライブラリ読み込みの調査
- 04署名と依存情報の比較
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dslsdzc/ rev-skills --skill re-format-machoインストール後、Claude Code で/re-format-machoを入力して読み込まれたか確認します。
同梱ファイルreferences/examples.mdreferences/layout.md
詳細
Mach-O フォーマット解析
使用する場合/しない場合
- 使用する:対象が macOS/iOS の Mach-O バイナリ(実行ファイル/.dylib/.bundle/Framework)で、構造、load commands、エントリーポイント、コード署名、動的ライブラリの依存関係を理解する必要がある場合
- 使用する:dylib のインジェクション(異常な LC_LOAD_DYLIB エントリ)、署名/公証の状態、ユニバーサルバイナリのマルチアーキテクチャスライスを調べる場合
- 使用しない:PE([[re-format-pe]] を使用)、ELF([[re-format-elf]] を使用)
- 使用しない:関数のロジックだけが必要な場合(直接、逆コンパイルのスキルを使用);iOS の脱獄環境に関する問題は [[re-mobile]] の領域です
ツールの準備
[[re-analyze/platform-tips]] の macOS ブランチを参照してください。attach/デバッグは SIP と TCC による制限を受けますが、静的解析には影響しません。
otool(Apple 標準搭載)
- macOS:Xcode Command Line Tools:
xcode-select --install - 検証:
otool --version - Linux での代替:
llvm-otool(apt install llvm/brew install llvm/pacman -S llvm)。otool の一般的なサブコマンドをカバーしています
llvm-objdump(クロスプラットフォーム)
- macOS:
brew install llvm;Linux:apt install llvm/dnf install llvm/pacman -S llvm - 検証:
llvm-objdump --version - 使用方法:
llvm-objdump --macho --private-headers sample(Linux でも Mach-O を直接解析でき、出力は macOS と同じです)
jtool2(Mach-O 構造の詳細解析、macOS)
- macOS:GitHub(intezer/jtool2)から clone して
makeを実行するか、release バイナリをダウンロードします - 検証:
jtool2 -h sampleで load commands が出力されます
codesign / lipo(macOS 標準搭載)
- 検証:
codesign --version;lipo -info sample - Linux での代替:
llvm-lipo -info sample(llvm に同梱)
dyld_shared_cache_util(macOS)
- システム標準搭載(
/usr/bin/dyld_shared_cache_util);用途:dyld 共有キャッシュ内のシステム dylib を解析する際に、個別のライブラリを取り出します
操作手順
-
初期確認(まず種類とアーキテクチャを確認):
file sample # 可执行文件 / dylib / 通用二进制 / 字节序 lipo -info sample # 通用二进制列出各架构片;单架构提示 "non-fat file" xxd -l 16 sample # 魔数: cffaedfe=64位小端, feedfacf=64位大端, cebafaed=32位小端, cafebabe=fatマジックナンバー(ファイル内で確認できるバイト列):
cffa edfe(MH_MAGIC_64)、cefa edfe(MH_MAGIC、32 ビット)、cafe babe(FAT。その後に 4 バイトのアーキテクチャ数が続きます)。ユニバーサルバイナリは、まずlipo -thin <arch> -output piece sampleでスライスを抽出してから解析します。 -
mach_header と load commands(中核):
otool -h sample # magic/cputype/filetype/ncmds/flag 一行全出 otool -l sample | head -80 # 全部 load commands64 ビットの mach_header は固定長 32 バイト(32 ビットでは 28 バイト)です:magic(4) + cputype(4) + cpusubtype(4) + filetype(4) + ncmds(4) + sizeofcmds(4) + flags(4) + reserved(4、64 ビットのみ)。load commands は Mach-O の「セクションテーブル+インポートテーブル+エントリーポイント+署名」を統合したものです。先頭から末尾まで
cmdsizeの順に走査します。ncmdsは個数、sizeofcmdsは合計バイト数で、相互に検証できます。filetype:2=MH_EXECUTE、6=MH_DYLIB、8=MH_BUNDLE。一般的な LC 値とフィールドの表は [[layout]] を参照してください。 -
セグメントとセクション(__TEXT/__DATA/__LINKEDIT):
otool -l sample | grep -A8 'segname __TEXT' otool -l sample | grep -A8 'segname __DATA' llvm-objdump --macho --section-headers sample各セグメントの vmaddr/vmsize/fileoff を記録します。
__TEXT,__textはコード、__DATA,__dataは書き込み可能なデータ、GOT は__DATA_CONST,__got(読み取り専用の定数セグメント。古いバイナリでは__DATA,__got)にあります。__LINKEDITはvmaddr/vmsize を持つ通常のセグメントで、通常は読み取り専用領域としてマッピングされ(vmmap で確認できます)、シンボルテーブル/文字列テーブル/再配置/署名などのリンクメタデータを格納します。具体的な格納形式は dyld と shared cache の影響を受けます。 -
エントリーポイント(LC_MAIN / LC_UNIXTHREAD):
otool -l sample | grep -A6 LC_MAINentryoffはファイルヘッダーからのエントリーポイントの相対オフセットです。エントリー VA = __TEXT セグメントの vmaddr + entryoff(64 ビットでは __TEXT のベースアドレスは通常 0x100000000)。古いバイナリでは LC_UNIXTHREAD を使用します(レジスター state にエントリーポイントが含まれます)。実行ファイルに LC_MAIN も LC_UNIXTHREAD もない場合は不正な形式であり、重点的に調査してください。 -
dyld 情報(LC_DYLD_INFO_ONLY / エクスポート trie):
otool -l sample | grep -A9 LC_DYLD_INFO_ONLY llvm-objdump --macho --exports-trie sample # 导出符号(trie 解码)offset+size の組が 5 つ、合計 48 バイトあり、すべて LC_DYLD_INFO_ONLY に含まれます:rebase(ベースアドレス修正ポイント)、bind(外部シンボルのバインド、非遅延)、weak_bind(弱シンボルのバインド。弱シンボルの統合セマンティクスは独立しており、bind に含めてはいけません)、lazy_bind(遅延バインド)、export(エクスポート trie)。新しいバイナリでは LC_DYLD_CHAINED_FIXUPS(チェーン形式の修正、iOS 13 以降のデフォルト)を使用します。dylib のエクスポートシンボルは trie 内にあり、
nm -gUと同じ結果が得られます。 -
動的ライブラリの依存関係(インジェクション検出):
otool -L sample # LC_LOAD_DYLIB 列表(依赖库 + install name) otool -l sample | grep -A4 LC_LOAD_DYLIB各 LC_LOAD_DYLIB:name(絶対パス、または @rpath/@executable_path からの相対パス)+ timestamp/current_version。悪意のある dylib の永続化 = LC_LOAD_DYLIB エントリの追加です。署名時の一覧との不一致は重要な警告です(詳しくは「注意点」を参照)。two-level namespace ではシンボル検索が(ライブラリ、シンボル)の二つ組に基づき、インポートが厳密に解決されます。
-
シンボルテーブル(nlist_64):
llvm-nm -m sample # 含段归属与 N_EXT 标记;undefined = 导入 llvm-nm -gU sample # 仅外部定义符号(导出) otool -l sample | grep -A3 LC_SYMTAB # symoff/nsyms/stroff/strsize 定位表nlist_64 は各エントリが 16 バイトです:n_strx(文字列テーブル内のオフセット)+ n_type(N_EXT/N_SECT など)+ n_sect + n_desc + n_value。strip 後は
.symtabがなくなり、LC_DYLD_INFO のエクスポート trie と動的バインド情報だけが残ります。復元方法は ELF と同様です([[re-imports]] のシグネチャ照合 + 文字列の相互参照)。 -
コード署名の確認:
codesign -dv sample 2>&1 # 签名类型(adhoc / Apple Development / not signed) codesign --verify --deep sample # 完整性验证 jtool2 --sig sample | head -20 # LC_CODE_SIGNATURE 细节(CodeDirectory/哈希)adhoc:アドホック署名。変更後に再署名できます(codesign -f -s - sample)- Apple 署名:1 バイトでも変更すると無効になります。Library Validation が有効な場合、dylib のインジェクションは失敗します
- LC_CODE_SIGNATURE がない = 未署名(iOS では直接実行できません)
-
手動解析と検証:load command の出力に異常がある場合やヘッダーフィールドが偽装されている場合は、
xxd+ Pythonstructを使い、オフセットを指定して mach_header と各 LC を直接解析します(実行可能な最小サンプルとバイト列の例は [[examples]] を参照)。解析に失敗したからといって「破損ファイル」と判断して破棄しないでください。
分野横断の連携
- [[re-binary-core]]:ワークフローの第 3 ステップ、Mach-O の対象フォーマット解析
- [[re-mobile]]:iOS App/脱獄環境の解析に先立つ準備(arm64 Mach-O のメインバイナリと dylib)
- [[re-ios]] / [[re-macos]]:エコシステムとの連携(App 層、署名と公証、TCC/SIP による制限)
- [[re-cracking]]:macOS のクラック関連タスク(パッチ適用後の再署名手順は本スキルの手順 8 を参照)
- [[re-imports]]:dylib 依存関係とエクスポートシンボルのハイジャック対象の分析
- 動的デバッグでは [[re-lldb]] を併用(SIP/TCC による制限は [[re-analyze/platform-tips]] を参照)
常见坑与陷阱
- 签名验证会拦调试/注入:修改过的二进制 codesign 校验失败无法运行——先
codesign -f -s -重签(仅限自签/adhoc 场景);Library Validation 开启时DYLD_INSERT_LIBRARIES失效 - iOS 上需越狱环境:真机调试要开发者证书或越狱,模拟器进程受沙盒限制——静态解析可先行,动态部分交给 [[re-mobile]]
- LC_LOAD_DYLIB 注入检测:恶意 dylib 通过新增 LC_LOAD_DYLIB 实现持久化——
otool -L列表与签名时列表不一致是重要告警 - 通用二进制含多架构片:先
lipo -info确认架构再lipo -thin提取,别对全片解析;fat 头里各片 offset 是绝对文件偏移,按 (offset, size) 切出单片 - GOT 位置随版本迁移:新产物 GOT 在
__DATA_CONST,__got(只读,装载后重定位一次);旧产物在__DATA,__got(可写)——找 GOT 先otool -l看段名,别假设 - dyld 共享缓存里没有独立 dylib 文件:系统库(libSystem.dylib 等)实际在
/System/Library/dyld/缓存内,文件系统里只有 stub——分析系统库用dyld_shared_cache_util -extract抽出 - load command 遍历错位:现象——
otool -l输出中途乱码/报错;原因——ncmds 与 sizeofcmds 被伪造或某条 LC 的 cmdsize 异常;对策——所有 LC 至少 8 字节(load_command头本身cmd+cmdsize),64 位下 cmdsize 须为 8 的倍数、32 位为 4 的倍数,再按具体 cmd 校验其结构最小尺寸与尾随 section/字符串,累计不超 sizeofcmds - __LINKEDIT 是映射段,但内容可能被 dyld 加工:它有自己的 vmaddr/vmsize,通常以只读映射存在(
vmmap可见);不过 shared cache 共享库的链接元数据驻留在共享缓存里,直接从进程内存按文件偏移找符号表常常对不上——定位符号表优先用nm/dyldinfo/MachOView 等按 LC_SYMTAB 解析,而不是照抄文件偏移