こんばんは!
皆さん、お久しぶりです!
私は無事、希望していた高校に合格し、やっと落ち着いてきたところです。
そして今日から夏休み。これから1ヶ月、勉強しつつこちらでも遊んでいけたらと思っています。
最近の興味:リバースエンジニアリング
最近、リバースエンジニアリングにハマっています。
具体的には、IDA ProやGhidraなどを使って、静的解析をちょくちょく試しています。
中学2年生の頃にもGhidraを少し触ってみたことがあったのですが、当時は「何をどこからどう勉強すればいいのか」が全く分からず、結局放置してしまいました。
でも今は、AIの発達もあり、無料でも相当優秀なAI教師が身近にいますよね。
そのおかげで、最近またGhidraなどに触れて解析を再開してみると、パズルのように問題が解けていく感覚があり、とても楽しく感じるようになりました。
将来やってみたいこと
この経験を通じて、将来やってみたい仕事が明確になってきました。
それは――リバースエンジニアリングの仕事です。
とはいえ、リバースエンジニアリングにも色々な役職がありますよね。
- ゼロデイ脆弱性の発見
- マルウェア・ランサムウェアの挙動解析
- バイナリ解析全般
- ファームウェア解析
- エクスプロイト解析
私は、そのすべてに興味があります。
ですから、高校3年間の間に2,500〜3,500時間の学習時間を確保し、しっかりとスキルを身につけ、リバースエンジニアリングの仕事に携わりたいと考えています。
本題:IDA Proでの解析メモ(今回の狙い)
今回は、exeファイルのデコンパイル/ディスアセンブルについての実作業メモを共有します。
目的は「知人が作成したexeファイルから、暗号化されたファイルを復号するためのキーを特定すること」です。
手順や気づきを中心に、私の学習メモとしてまとめています。
前準備
- IDA Proはインストール済みとして説明します。
- 私は毎回
ida.exe
を開くのが面倒なので、レジストリ登録して右クリックで簡単に開けるようにしています。 - 逆アセンブルが終わったら Produce File → Create C File で疑似Cを出力して大枠を掴みます。
- 不明な関数があれば map ファイルや関数一覧も保存して見比べます。
- 動的解析(x64dbg等)による確認手法もありますが、今回はまず静的解析でどこまで絞れるかを試しました。Process Monitor 等でファイル作成先を調べるのも有用です。
逆コンパイル結果の解析
まずは実行ファイルのメタデータを確認します。プロパティ → 詳細で、元のファイル名や言語設定が分かることがあります。今回もここに手掛かりがありました。言語欄が Neutral になっているなど、実行時の挙動のヒントになる場合があります。
次に、Windows API(CreateFileW, WriteFileW, CreateDirectory, temp 等)で検索すると、自己展開型(SFX)や一時展開の痕跡がヒットすることがあり、展開先(Temp等)周りを調べるとファイル実体にアクセスできる場合があります。
疑似Cの一部にこんな記述がありました(雰囲気のみ):
v5 = sub_xxx(&NewDirectory, L"{TEMP}\\onefile_{PID}_{TIME}", ...);
v6 = sub_xxx("NUITKA_ONEFILE_PARENT");
このような文字列から、Nuitka 由来のビルドである可能性が示唆されます。Nuitka は Python コードをネイティブ化するツールなので、Python/C API の利用や、展開・実行のパターンに特徴があります。展開がメモリ上で行われるケースもあるので、その点は注意が必要です。
逆アセンブル結果の解析
アセンブリ(mov 等)は低レベルでちんぷんかんぷんですが、AI に助けてもらうと動作ロジックの推測ができます。
個人的には「まず疑似Cで全体像を掴んでから、読みづらい部分だけアセンブリで深掘りする」のが効率的だと感じました。アセンブリは関数名や変数名がなく可読性が低いので、必要箇所に集中するのが吉です。
Temp に保存されたファイルの解析
Temp に展開されていたファイル群の中に、暗号化に関係しそうな DLL を見つけました。メタデータや文字列(例: Crypto.Cipher._raw_aes)から、暗号ライブラリ(pycryptodome 相当)が使われている可能性が見て取れました。ただし、クロスリファレンスが直接見つからない場合は、間接参照やテーブル参照で呼び出されていることがあります。
Imports / API まわりの掘り下げ
静的に Imports を見ても該当 API が見つからないときは、ランタイムで GetProcAddress といった仕組みで動的にアドレス解決されているケースが考えられます。そうなると静的だけで追うのは難しく、動的に実行して確認するのが現実的になります。
また、Nuitka 由来のバイナリでは PyBytes_FromStringAndSize や PyObject_GetAttrString のような Python/C API の痕跡を探すと、Python 側にデータが渡るポイントが見つかることがあります。そこを起点に、どんなデータ(鍵やIVなど)が渡されているかを推測する、という考え方が使えます。
即値を手掛かりにした絞り込み
設計的な事実(例: AES-256 の鍵長は 32 バイト、IV は 16 バイト)を利用して、定数周りを手掛かりにフローを絞り込む手法があります。
静的解析ツールの検索機能を使って「長さをセットしている箇所」を探し、そこから呼び出しフローを追っていくと、復号に関係する関数に到達できることがあります。
(※ ここでは具体的なバイト列検索の手順や特定のアドレス列は割愛します。)
アセンブリ
以下は概念を示すために一般化したアセンブリ風抜粋です。実際のバイナリでは命令やレジスタの使い方が異なるので、雰囲気を掴むための例です。
; (一般的な流れの例)
mov edx, <鍵長> ; 鍵長をセット
jmp <ラベル>
...
<ラベル>:
mov rax, [<構造体+offset>]
mov rbx, [<構造体+offset2>]
...
mov rcx, r12 ; 候補バッファのアドレスをセット
call <復号処理の関数>
このような「長さをセットして、候補バッファのアドレスを関数に渡す」パターンを見つけられれば、静的解析でかなり絞り込めます。ただし、アドレスが動的に決定されている(GetProcAddress 等)場合や、展開が実行時に行われる場合は静的だけでは断定できません。
静的解析の限界(結論)
静的解析で「ここまで絞れる」は明確にありますが、実行時にメモリで展開されるデータや、ランタイムで動的に解決される処理は静的解析だけでは特定しにくいことが多いです。そうした場合は、x64dbg 等で実行時にブレークポイントを置いて確認するのが合理的な次の手になります。
私は今回は静的解析を中心にスキルを高めたかったので、ここで一旦区切りました。もちろん今後は動的解析編もやる予定です(笑)
学びとアドバイス
- 仮説立てが超重要:AES.new のようなライブラリ固有のキーワードや鍵長など、仕様知識を元に仮説を立てると効率が劇的に上がる。
- まず疑似Cで全体→必要箇所をアセンブリで深掘りが効率的。
- 静的解析で怪しい箇所を見つけたら動的解析へ。両方の使い分けが解析速度を劇的に改善する。
今後の予定(動的解析編)
- x64dbg を使った実行時トレースで、静的で絞った箇所を実行時に確認する。
- Frida 等を使ったランタイムフックの習得も進めたいと考えています。
締め
長々と書いてしまいました。誤りや補足があれば指摘してくれるとありがたいです。コメント欄で議論できれば嬉しいです。ではまた、気が向いたら動的解析編を上げます!ご清聴ありがとうございました!!