extract-elf について
ELF バイナリからデータを抽出するスキルです。ヘッダーと LOAD セグメントを解析し、ファイル内容を仮想アドレスに対応付けて構造化出力を作成します。ELF クラス、バイト順、データ型の扱いが重要な解析タスク向けです。
主な機能
- 01ELF マジック、クラス、バイト順の検証
- 02ELF ヘッダーとプログラムヘッダーの解析
- 03LOAD セグメントの特定と抽出
- 04セグメントデータの仮想アドレスへの対応付け
- 05出力範囲と値の検証
活用シーン
- 01ELF LOAD セグメントから値を抽出
- 02正しい幅とバイト順でバイナリフィールドを解釈
- 03ファイル内容と抽出したアドレスや値を照合
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ letta-ai/ skills --skill extract-elfインストール後、Claude Code で/extract-elfを入力して読み込まれたか確認します。
詳細
ELF バイナリデータの抽出
このスキルでは、ヘッダーの読み取り、セグメントの解析、バイナリコンテンツの構造化出力形式への変換など、ELF バイナリファイルからデータを抽出するタスクの手順を説明します。
アプローチの概要
ELF の抽出タスクでは、通常、次の手順が必要です。
- ELF ヘッダーを解析してファイル構造を把握する
- プログラムヘッダーを読み取って LOAD セグメントを特定する
- 正しい仮想アドレスからセグメントデータを抽出する
- バイナリデータを必要な出力形式に変換する
実装手順
ステップ 1:ELF ヘッダーの検証
処理を始める前に、ファイルが有効な ELF バイナリであることを確認します。
- オフセット 0 のマジックバイトを確認します:
0x7F 'E' 'L' 'F'(16 進数:7f 45 4c 46) - オフセット 4 で ELF クラス(32 ビットまたは 64 ビット)を特定します
- オフセット 5 でエンディアンを特定します(1 = リトルエンディアン、2 = ビッグエンディアン)
ステップ 2:ELF ヘッダーフィールドの解析
ELF クラスに基づいて、主要なヘッダーフィールドを抽出します。
32 ビット ELF の場合:
- プログラムヘッダーのオフセット:バイト 28-31
- プログラムヘッダーのエントリーサイズ:バイト 42-43
- プログラムヘッダーの数:バイト 44-45
64 ビット ELF の場合:
- プログラムヘッダーのオフセット:バイト 32-39
- プログラムヘッダーのエントリーサイズ:バイト 54-55
- プログラムヘッダーの数:バイト 56-57
ステップ 3:プログラムヘッダーの処理
プログラムヘッダーを順に確認し、LOAD セグメント(タイプ = 1)を特定します。
- 仮想アドレス(p_vaddr)を抽出します
- ファイルオフセット(p_offset)を抽出します
- ファイルサイズ(p_filesz)を抽出します
- メモリサイズ(p_memsz)を抽出します
ステップ 4:セグメントデータの抽出
各 LOAD セグメントについて、次の処理を行います。
- p_offset の位置からファイルデータを読み取ります
- p_vaddr から始まる仮想アドレスにデータを割り当てます
- 指定に従ってアラインメントとパディングを処理します
重要なデータ型に関する考慮事項
符号付き整数と符号なし整数
これは、バイナリデータ抽出タスクで最もよくあるエラーの原因です。
バイナリデータから複数バイトの整数値を読み取る場合:
- メモリアドレスは常に符号なしです
- サイズフィールドは常に符号なしです
- タスクで符号付きの解釈が明示的に求められていない限り、データ値は通常、符号なしとして読み取ります
API による一般的な違い:
- Node.js Buffer:
readUInt32LEとreadInt32LE - Python struct:
'I'(符号なし)と'i'(符号付き) - C/C++:
uint32_tとint32_t
検証:出力に負の数が含まれている一方、期待される出力が正の整数のみを示している場合は、符号の扱いを誤っています。
エンディアン
ELF ヘッダーで指定されたエンディアンに合わせます。
- リトルエンディアン(x86/x64 で最も一般的):
LE系のバリアントを使用します - ビッグエンディアン:
BE系のバリアントを使用します
整数のサイズ
ELF フィールドのサイズはクラスによって異なります。
- 32 ビット ELF:アドレスとオフセットは 4 バイトです
- 64 ビット ELF:アドレスとオフセットは 8 バイトです
検証方法
成功と判断する前に
- 出力形式を検証する:JSON の形式が正しく、キーの型が適切であることを確認します
- アドレス範囲を確認する:アドレスが想定されるセグメント境界内にあることを確認します
- サンプル値を検証する:16 進数の検査ツールを使い、いくつかのアドレスについて期待値を手計算します
手動検証コマンド
抽出した値の検証には、次のツールを使用します。
# View ELF header information
readelf -h <binary>
# View program headers (segments)
readelf -l <binary>
# Dump section contents in hex
objdump -s <binary>
# View raw hex bytes at specific offset
xxd -s <offset> -l <length> <binary>
# Calculate expected value from hex bytes (little-endian example)
# For bytes: 41 42 43 44 -> value = 0x44434241 = 1145258561
値の妥当性チェック
- 出力例に正の整数のみが示されている場合は、出力に負の値が含まれていないことを確認します
- 計算した値をいくつか選び、手動での 16 進数計算結果と比較します
- アドレス範囲が想定されるセグメント範囲と一致していることを確認します
よくある落とし穴
-
符号なしデータを符号付き整数として読み取る - 最上位ビットが設定された値が負の数になります(例:4196274163 ではなく -98693133)
-
エンディアンの処理を誤る - 値が完全に誤ったものになります。ELF ヘッダーのバイト 5 と照合してください
-
セグメント境界でのオフバイワンエラー - サイズが境界を含むか含まないかを注意深く確認してください
-
4 バイト境界へのアラインメントを前提とする - セグメントサイズが読み取りサイズの倍数かどうかを確認し、境界での部分的な読み取りを処理してください
-
32 ビットと 64 ビットのフィールドサイズを混在させる - 必ず ELF クラスを確認し、適切なフィールドサイズを使用してください
-
検証せずに過信する - 「値はバイナリから直接読み取るので、一致するはず」と決して決めつけず、必ずサンプル値を手動で検証してください
出力形式に関する考慮事項
構造化された出力(例:JSON)を生成する場合:
- アドレスを JSON オブジェクトのキーとして使用する必要がある場合は、文字列キーを使用してください(JSON のキーには文字列が必要です)
- 整数値が JavaScript/JSON の安全な整数範囲(完全な精度を保てる 2^53 - 1)内に収まるようにしてください
- タスクの要件に応じて、アドレスを 10 進数または 16 進数の文字列にするか検討してください