PR 本記事には広告(Amazonアソシエイト・もしもアフィリエイト・A8.net等)が含まれます。掲載情報の正確性には努めていますが、商品の詳細は必ずリンク先で最新情報をご確認ください。
CTF・セキュリティ学習

【Pwn入門編】x86/x64アセンブリと関数呼び出し規約をスッキリ整理|CTF思考フレームワーク #58

かも次郎とアンペンが「Pwn」を解説するマスコットイラスト
安全に生きたい編集部

広告・PRを含みます。この記事にはアフィリエイトリンクが含まれます。掲載内容は編集方針に基づいて作成していますが、価格・キャンペーン内容はリンク先で最新情報を確認してください。

こんにちは、アンペンです!ここから新章Pwn編。バイナリ脆弱性を突いて制御を奪う領域です。最初はアセンブリと関数呼び出し規約から。

『Pwn』はCTFの花形ジャンル。プログラムのバグを突いて、開発者も想定していない動き——たとえば本来呼ばれないはずの“フラグ表示関数”を強制的に実行させたりする、いわば“ハッキングらしいハッキング”の領域です。その第一歩が、コンピュータが実際に何をしているかを映す『アセンブリ』を読めるようになること。身構えず、ゆっくり地図の読み方を覚える感覚でいきましょう。

アセンブリって聞くと拒否反応…必要?

Pwnでは必須。といっても暗記は不要。レジスタ・スタック・呼び出し規約の3点を押さえれば、CTF問題の8割は読める。

最初に安心材料を。アセンブリと聞くと『何百個もの命令を暗記するの…?』と尻込みしがちですが、Pwnで使うのはごく一部です。覚えるべきは、ざっくり3つ——『データを置く小箱(レジスタ)』『作業メモの束(スタック)』『関数を呼ぶときの決まりごと(呼び出し規約)』。この3点さえ押さえれば、CTFのバイナリの8割は“なんとなく読める”ようになります。

まず結論

Pwn入門は(1)主要レジスタ、(2)スタックの伸び方とフレーム、(3)呼び出し規約(System V AMD64=引数はRDI/RSI/RDX/RCX/R8/R9順)の3点を押さえれば一気に読めるようになります。ツールはgdb+pwndbg / Ghidra / pwntoolsが三種の神器。次回からこの基礎で実際に脆弱性を突きます。

この記事で分かること

  • x86/x64の主要レジスタの役割
  • スタックフレーム(push/pop/RSP/RBP)
  • System V AMD64呼び出し規約
  • pwndbg/pwntoolsの初期設定
難易度:初〜中級 所要時間:12分 体験:簡単なバイナリを読む おすすめ:#57の後

📖 はじめてのWebセキュリティ #58|Pwn入門編
x86/x64アセンブリと関数呼び出し規約を扱います。 シリーズ一覧を見る →

⚠️ 大事なお約束
他者のバイナリ・サービスへのexploitは違法。CTF・自分の検証バイナリのみで確認してください。

レジスタ:CPUの『手元の小箱』

レジスタはCPU内部の超高速メモリ。x64ではRAX/RBX/RCX/RDX/RSI/RDI/RBP/RSP/R8〜R15の16本が汎用。それぞれ用途の慣習があります。

レジスタを身近にたとえると、CPUが作業するときに手元へ置いておく“小箱”のようなもの。引き出し(メインメモリ)から物を出すより、机の上の小箱に入っているほうが圧倒的に速く取り出せますよね。だからCPUは、いま使う値をこの小箱に置いて高速に処理します。16個ある小箱には『戻り値はここ』『1番目の引数はここ』といった“担当”がだいたい決まっていて、それを知っていると読むのがぐっと楽になります。

図解:主要レジスタの役割マップ

『戻り値はRAX、引数はRDI/RSI/…、スタック頂はRSP、フレーム底はRBP、命令ポインタはRIP』というキャラ設定を覚えるだけで一気に読めるようになります。

丸暗記はせず、“キャラ設定”として覚えるのがコツです。RAX=結果を持って帰る係、RDI/RSI…=引数を順番に渡す係、RSP=今いる場所を指す係、RIP=次に読む命令を指す係。人物の役割を覚える要領で頭に入れると、ズラッと並んだアセンブリも『あ、これは結果をRAXに入れて持ち帰ってるな』と、物語のように追えるようになりますよ。

x64 16レジスタの役割をグループ別に色分けして示した図解
図1:x64の主要レジスタはこの役割を覚えるだけでOK
📚 たとえるなら、組み立て式の机

スタックは『下に向かって伸びる積み重ねメモ帳』。関数を呼ぶたびに新しいページ(フレーム)を下に積み、終わったらそのページを破って戻ります。RBPは『今のページの上端』、RSPは『今のページの下端』を指す指。RIPは『次に読むべき命令の位置』。この3つで現在地が完全に決まります。

スタックを下に伸びる積み重ねメモ帳にたとえてRBP・RSP・RIPの役割を示したイラスト
図2:スタックは下に伸びるメモ帳。RBP/RSP/RIPの3指がポイント

ここで覚える用語:System V AMD64 ABI
Linux/macOSのx64で標準の関数呼び出し規約。引数は左から順に RDI, RSI, RDX, RCX, R8, R9、それを超えたらスタック。戻り値はRAXRBP/RBX/R12〜R15は呼ばれた側が保存(callee-saved)。Windows x64では引数が RCX/RDX/R8/R9 と異なるので要注意。

レジスタとスタックが分かったら、次は『関数が呼ばれて、終わって戻る』までの一連の流れを追ってみましょう。ここがPwnでいちばん“おいしい”ポイント。なぜなら、攻撃の多くは、この“呼んで戻る”仕組みのスキマに入り込むからです。

関数の入口と出口(プロローグ/エピローグ)

  • プロローグ:push rbp; mov rbp, rsp; sub rsp, N(古いRBPを保存、新しいフレーム作成)
  • 本体:変数アクセスは [rbp - X][rsp + X] で参照
  • エピローグ:mov rsp, rbp; pop rbp; ret(フレーム解除、戻る)
  • call/ret:callで戻りアドレスをpush、retでpopしてジャンプ

プロローグとエピローグは、関数の“入室・退室の儀式”だと思ってください。部屋に入るときは『自分用の作業スペースを確保する』(プロローグ)、出るときは『片付けて元の部屋に戻る』(エピローグ)。毎回ほぼ同じ定型動作なので、アセンブリを読むときも『あ、ここが関数の入口/出口だな』と、一目で見分けられるようになります。

『retが何を見て戻り先を決めるか』がスタックBoF(次回)の核心。戻りアドレスをスタック上で書き換えれば、任意のコードに飛ばせます。

ここがPwnの肝なので、もう一度。関数は出ていくとき、『どこに戻ればいいか』を、スタックに置いてある“戻りアドレス”を見て決めます。ということは——もしこの戻りアドレスを書き換えられたら? 関数は何の疑いもなく、攻撃者の指定した場所へ飛んでいってしまうんです。次回のスタックBoFは、まさにこの“戻り先のメモ”を上書きする攻撃。今日の知識が、そのまま次回の武器になります。

三種の神器:gdb+pwndbg・Ghidra・pwntools

  • gdb + pwndbg:動的デバッガ。checksec / vmmap / telescope 等が便利
  • Ghidra(またはIDA Free):静的逆コンパイル。Cっぽい疑似コードが見える
  • pwntools(Python):exploit記述ライブラリ。p64()/u64()/process()/remote()
  • 他:radare2 / Cutter / one_gadget / ROPgadgetもよく使う

3つの道具は、役割で覚えると迷いません。Ghidraは『設計図を眺める』静的な相棒——動かさずにプログラムの全体構造を読む。gdb+pwndbgは『実際に動かして観察する』動的な相棒——1命令ずつ進めて、レジスタやスタックの中身を覗く。そしてpwntoolsは『攻撃の手順を自動化する』相棒——組み立てた手順をPythonで送り込む。読む・観る・撃つ、の三役だと考えると、役割分担がクリアになります。

gdb+pwndbg・Ghidra・pwntoolsというPwnの3大ツールを横並びカードで示した図解
図3:Pwn三種の神器(動的+静的+exploit記述)

CTFでやってみよう:簡単なバイナリを読む

やってみよう / 自分の環境・CTFのみ

helloワールド+引数2つの関数を逆アセンブル

  1. Cで int add(int a, int b) { return a + b; } を含む小バイナリを作成
  2. objdump -d でadd関数のアセンブリを表示
  3. RDI=a, RSI=b, RAXに合計が入って ret する流れを確認
  4. gdb+pwndbgでbreak mainnextiで1命令ずつ追う
  5. checksec でNX/PIE/Canary/RELROの有無を確認
本番バイナリや他者ソフトのexploitは禁止。検証はCTF/自作バイナリのみで。

ところで、この知識は攻める人だけのものではありません。むしろ守る人にとってこそ価値があります。『なぜスタックを書き換えると乗っ取れるのか』を仕組みから理解していると、Canaryやアドレスのランダム化(ASLR)といった防御が“なぜ効くのか”まで腹落ちするからです。

守る側:『なぜ脆弱性が成立するか』を知る価値

守る側にとってのPwn知識
  • 『なぜスタックを書き換えると関数が乗っ取れるか』を理解すると、保護機構(Canary/NX/ASLR)の意味が腹落ちする
  • 使う言語の境界チェック有無(C/C++はナシ、Rust/Goはアリ)を意識
  • C/C++を書くならmemcpy/strcpy/sprintfを禁止、安全な代替を使う
  • ビルド時は-fstack-protector-strong -D_FORTIFY_SOURCE=2 -fPIE -pie -z relro -z now
  • 静的解析・Fuzzing(AFL/libFuzzer)を CI に組み込む

レジスタ・スタック・規約の3点だけ覚える、ってだいぶハードル下がるね。

うん。次回はスタックBoFで実際に戻りアドレスを書き換えるよ。ret2winで初Pwn成功体験を。

ここまでをひと言でまとめると、Pwn入門は『暗記ではなく、3つの仕組みの“地図”を持つこと』。小箱(レジスタ)、メモの束(スタック)、受け渡しの決まり(呼び出し規約)。この地図さえあれば、次回からの実際の攻撃も“どこをいじっているか”が見えるようになります。

まとめ:『レジスタ・スタック・規約』

今回のポイント
  • x64の主要レジスタ16本の役割を把握
  • スタックは下に伸びる。RBP=フレーム頂、RSP=現在地
  • System V AMD64: RDI/RSI/RDX/RCX/R8/R9
  • 三種の神器:gdb+pwndbg / Ghidra / pwntools

今日の持ち帰りは『アセンブリは“暗記科目”ではなく“地図の読み方”』。全命令を覚える必要はなく、レジスタ・スタック・呼び出し規約という3つの目印さえ押さえれば、迷子になりません。次回はいよいよ、この地図を手に“戻りアドレスの書き換え”という初Pwnに挑戦します。

次回はスタックBoF基礎編。ret書き換え+ret2winで初Pwnを決めましょう。

次に読みたい記事

参考資料

PR / 広告 CTFの練習は「壊しても作り直せる自分専用の環境」があると一気に捗ります。VPSなら数百円/月からLinux環境を持てて、ローカルPCを汚さずに検証できます(攻撃は必ず自分の環境・許可された環境だけで)。

▶ ConoHa VPS(時間課金あり・すぐ作ってすぐ消せる)を見る

記事URLをコピーしました