はじめに
本記事は、リバースエンジニアリングの学習・研究を目的とした、自作の暗号化サンプルバイナリおよび教育用検証プログラムを対象とした解析記録です。
第三者の知的財産権を侵害する解析や、不正アクセス・不正利用を推奨するものではありません。あくまでバイナリの構造把握、および静的解析と動的解析の使い分けといった「思考プロセス」の言語化に重点を置いています。
1. 解析の背景と目的
近年、セキュリティ学習において「中身がブラックボックスなバイナリから、いかに効率よくロジックを特定するか」というリバースエンジニアリング能力の重要性が高まっています。
今回は、自作した「特定のファイルを暗号化する検証用プログラム」を対象に、**「動的解析に頼る前に、静的解析だけでどこまでロジックを絞り込めるか」**という技術的限界の探究をテーマとしました。
2. 解析対象の特定と初期仮説の立案
まずは対象バイナリのメタデータを確認し、プログラムの「出自」を推測することから始めます。
2-1. コンパイラ・パッカーの推測
プロパティやバイナリエディタでの確認の結果、Nuitka 由来のセクション名や文字列が確認されました。
Nuitka は Python コードを C/C++ へ変換してコンパイルするツールです。ここから以下の仮説を立てました。
- 仮説A: 内部で Python のランタイムや DLL を展開して実行する「Onefile」形式である可能性が高い。
-
仮説B: 暗号化ロジックは Python の標準的なライブラリ(
pycryptodome等)を呼び出している可能性が高い。
2-2. 疑似Cコードによる構造把握
Ghidra の Decompiler を用い、エントリポイント付近を調査したところ、以下のような処理フローが見受けられました。
// 処理の雰囲気(簡略化)
v5 = sub_xxx(&NewDirectory, L"{TEMP}\\onefile_{PID}_{TIME}", ...);
v6 = sub_xxx("NUITKA_ONEFILE_PARENT");
この記述から、実行時に一時ディレクトリ(Temp)へ必要なモジュールを展開していることが可視化されました。静的解析の次のステップとして、この展開されるリソース群に注目します。
3. 暗号処理フローの追跡(静的解析)
3-1. インポート関数の調査と限界
Imports テーブルを確認しましたが、直接的な暗号化 API(Windows CryptoAPI 等)は見当たりませんでした。これは GetProcAddress による動的解決、あるいは Python ランタイム内での処理に隠蔽されていることを示唆します。
3-2. 即値(Constants)をヒントにした絞り込み
暗号アルゴリズムには、その特性上、特定の「長さ」や「定数」が現れることが多いです。今回の検証対象には AES を採用しているため、以下の観点で検索を行いました。
- 鍵長(Key Length): 32バイト(AES-256)や16バイトの定数が代入されている箇所。
- IV(Initialization Vector): 16バイトのバッファ確保。
Ghidra の検索機能でこれらの定数を追跡したところ、暗号化ライブラリのラッパーと思われる関数を特定できました。
3-3. アセンブリレベルでのデータフロー解析
特定した関数周辺のアセンブリを詳細に読み解きます。
; 解析対象のデータロード処理(概念図)
mov edx, 20h ; 32(0x20)をセット。鍵長の指定と推測。
jmp loc_xxxx
...
loc_xxxx:
mov rax, [rbp+var_Structure] ; 構造体へのポインタ
lea rcx, [rbp+var_Buffer] ; 鍵データが格納されるバッファ
call sub_CryptInit ; 暗号化初期化処理(仮称)への受け渡し
ここで重要なのは、「どのレジスタに、どのような意図でデータが格納されているか」というロジックの推測です。
edx に 20h(32バイト)がセットされ、その直後の呼び出しでバッファのアドレスが渡されていることから、**「この時点で暗号処理における鍵やIVがどのように扱われるか」**を静的解析で追跡することができました。
4. 解析結果の考察と「次の一手」の選定
4-1. 静的解析で得られた成果
- プログラムが
Nuitkaベースであることを特定し、Python レイヤの介在を予見できた。 - AES 固有の定数(32バイト)を手掛かりに、暗号化処理の中核となる関数箇所を絞り込めた。
4-2. 静的解析の限界と合理的判断
今回の解析では、静的な追跡のみでは「鍵がいつ、どこで生成(あるいは固定)されているか」の完全な特定には至りませんでした。これは、ランタイムによる動的なメモリ展開が行われているためです。
リバースエンジニアリングの実務においては、ここで静的解析に固執しすぎるのは非効率です。
**「静的解析で目星をつけたアドレスに対して、動的解析(x64dbg 等)でハードウェアブレークポイントを設置し、実行時のレジスタの値を観測する」**ことが、最も合理的で確実な次のステップであると判断しました。
おわりに
今回の演習を通じて、単に「コードを読む」だけでなく、コンパイラの癖や暗号アルゴリズムの仕様に基づいた「仮説立て」の重要性を再認識しました。
今後は、静的解析で得た知見を武器に、Frida によるランタイムフックや、動的解析ツールを用いたより高度な挙動解析スキルの習得に励みたいと考えています。バイナリの裏側にあるロジックを解き明かすプロセスを、常に倫理観を持って楽しんでいきたいと思います。
次に取り組みたい課題:
- アンチデバッグ手法のバイパス原理の学習
- Python バイトコードのデコンパイル手法とネイティブコードとの対応関係の調査
もし、解析手法の切り分けや効率的なアプローチについてアドバイスがあれば、ぜひコメントでご教示いただけますと幸いです。