Upgrade to Pro — share decks privately, control downloads, hide ads and more …

eBPFを使った動的解析手法

Avatar for Arata Arata
September 23, 2025
480

 eBPFを使った動的解析手法

Avatar for Arata

Arata

September 23, 2025
Tweet

Transcript

  1. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata 導入: rev問題における動的解析 • 動的解析: バイナリを実際に動かして解析する手法

    • 問題によって異なるアプローチが求められる ◦ 例: gdb, strace, バイナリパッチ, VM改変 など • 様々な動的解析の手法を知り、使い分けることが重要 → 本発表ではeBPFを使った動的解析手法について紹介 2
  2. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata 用意された「フックポイント」通過時にコードを実行できる フックポイントの例: • syscalls ◦

    システムコール • kprobe / kretprobe ◦ カーネル空間のコード • uprobe / uretprobe ◦ ユーザー空間のコード ◦ 今回特に重要 eBPFがトレースできるもの 6 https://github.com/bpftrace/bpftrace
  3. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata Delve: https://github.com/go-delve/delve • Golang向けデバッガー •

    関数呼び出し時の引数と戻り値をトレースする機能で試験的にeBPF を利用 • ptraceと比較して高速な点がeBPF利用の主な動機 ◦ ptrace: ブレーク毎にユーザー空間にスイッチする ◦ eBPF: カーネル空間でブレークを処理、一定単位でデータを取得 デバッガーにおけるeBPFの利用 #1 7 https://developers.redhat.com/articles/2023/02/13/how-debugging-go-programs-delve-and-ebpf-faster
  4. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata eDBG: https://github.com/ShinoLeah/eDBG • Android向けデバッガー •

    ptraceを使わずeBPFでデバッガーのコア機能を実装 • 本来eBPFはトレース対象のプログラムを停止しない ◦ トレース対象のプログラムにSIGSTOP/SIGCONTを送信 ◦ これによってインタラクティブな操作を実現 • anti-debugの回避がeBPF利用の主な動機 デバッガーにおけるeBPFの利用 #2 8
  5. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata 問題概要 • よくあるcrackme問題 • 実行するとフラグの入力を求め、正しいか判定する

    実例 #1: gomen (SatokiCTF 2024) 12 https://tan.hatenadiary.jp/entry/2024/08/28/021429#Rev-easy-gomen-2-teams-solves-200-points
  6. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata デバッガーの検知方法 • /proc/self/statusのTracerPidを監視する ◦ TracerPidはptraceでアタッチしているプロセスのPID

    ◦ TracerPid = 0: プロセスはアタッチされていない ◦ TracerPid ≠ 0: プロセスはアタッチされている • gdb, strace, ltrace等でアタッチすると検知されてしまう 実例 #1: gomen (SatokiCTF 2024) 14
  7. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata 方針 • 天啓: 復号した正解フラグを引数にstrlen,memcpy関数が呼ばれる •

    ltraceで関数呼び出しをトレースしたいが、今回は難しい • eBPFでltraceっぽいことをしてみる 実例 #1: gomen (SatokiCTF 2024) 15
  8. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata eBPFを用いた解法 • bpftraceを使用 ◦ DSLを用いてeBPFによるトレースを行えるツール

    • libcの関数が呼ばれたら、その第一引数を表示したい • DSLでの記法 ◦ libcの関数が呼ばれたら: uprobe:libc:* ◦ その第一引数を表示する: print(str(arg0)) • 実行するとフラグが得られた 実例 #1: gomen (SatokiCTF 2024) 16
  9. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata 問題概要 • よくあるcrackme問題 • 実行するとフラグの入力を求め、正しいか判定する

    • 0 solves 実例 #2: AVX-512 (IERAE CTF 2025) 17 https://gmo-cybersecurity.com/blog/ierae-ctf-2025-writeup-reversing/
  10. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata 問題の特徴 • 命令の実行フローを動的制御して難読化 ◦ EFLAGSのTrap

    flagを立てて、1命令実行毎にSIGTRAPを送信 ◦ シグナルハンドラでRIPを操作して実行フローを動的制御 ◦ 様々な命令が乱雑に置かれていて、それらを適切な順番でピック して実行していくイメージ • フラグの判定ロジックがAVX-512を使用 実例 #2: AVX-512 (IERAE CTF 2025) 18
  11. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata 難読化手法とGDBの相性の悪さ • 実行された命令をGDBでトレースしたい • しかしGDBは以下の機能のためにSIGTRAPを使う

    ◦ ブレークポイント ◦ ステップ実行 • 1命令実行毎にSIGTRAPが送信されるプログラムでは GDBがまともに使えない 実例 #2: AVX-512 (IERAE CTF 2025) 19
  12. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata 公式Writeupの方針 • 実行フローの制御ロジックを静的解析 • 実行される命令をシミュレートして難読化解除

    シミュレート実装における様々な落とし穴 • syscall命令でのTrap flagのクリア • sub命令によるループ • add命令による条件分岐 実例 #2: AVX-512 (IERAE CTF 2025) 20
  13. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata eBPFを用いた解法の問題点 • フラグの判定ロジックは複数パートに分かれており、不正解だと分 かった時点で早期returnする •

    つまり実行された判定ロジックしかわからない • 正解した扱いにするにはレジスタの書き換えが必要だが、eBPFでは 不可能 ◦ 一方でメモリの書き換えは可能 実例 #2: AVX-512 (IERAE CTF 2025) 23 https://devblog.lac.co.jp/entry/20220620
  14. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata • 既存のツールでトレースを取れない場合に、代替ツールとしてeBPF が使える可能性がある ◦ 特にstraceやltraceの代替として

    • プログラムの状態を書き換えつつトレースを取りたい場合には不向き ◦ 使えるなら素直にgdb scriptを使った方が早い ここまでのまとめ 24
  15. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata eBPF(特にuprobe)によるトレースを防ぐことは可能か? トレースの検知 • int3命令の存在確認 •

    リターンアドレスの検証 • [uprobes]領域の存在確認 トレースの回避 • コード領域をwrite可能にする anti-eBPF 26
  16. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata eBPF(特にuprobe)によるトレースを防ぐことは可能か? トレースの検知 • int3命令の存在確認 •

    リターンアドレスの検証 • [uprobes]領域の存在確認 トレースの回避 • コード領域をwrite可能にする anti-eBPF 27
  17. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata • uretprobeはトレース対象のリターンアドレスを変更する • リターンアドレスが変化していればトレースされている可能性がある •

    この挙動が原因でGo言語のバイナリでは使えない anti-eBPF: リターンアドレスの検証 29 https://mechpen.github.io/posts/2022-10-30-golang-bpf/ リターンアドレスが[uprobes]領域に向いている様子:
  18. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata • 置換した命令を配置・実行するために[uprobes]領域が確保される a. int3命令でトラップ b.

    [uprobes]領域にコードを生成、RIPを向ける c. コードをシングルステップ実行 d. 元のコードの次の命令にRIPを戻す • /proc/self/mapsに[uprobes]領域があればトレースされている可能 性がある • (推測)この方式はマルチスレッド対応のため? anti-eBPF: [uprobes]領域の存在確認 30 https://blog.quarkslab.com/defeating-ebpf-uprobe-monitoring.html
  19. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata • 命令をエミュレートできる時[uprobes]領域を使用しない • そのような命令はarch_uprobe_analyze_insn関数を読むとわかる ◦

    https://elixir.bootlin.com/linux/v6.16.8/source/arch/x86/ker nel/uprobes.c#L980 ◦ コメントによればpush, nop, jmp, cond jmp命令など • トレース対象の命令が上記になるよう調整すれば回避できる • オフセットでトレース対象を指定するにはbpftraceをbfd有効でビル ドするか、バイナリをstripする anti-eBPF: [uprobes]領域の存在確認(回避) 31 https://www.sh1no.icu/posts/28348c4/
  20. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata eBPF(特にuprobe)によるトレースを防ぐことは可能か? トレースの検知 • int3命令の存在確認 •

    リターンアドレスの検証 • [uprobes]領域の存在確認 トレースの回避 • コード領域をwrite可能にする anti-eBPF 32
  21. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata • uprobeの対象アドレスはvalid_vma関数でチェックされる ◦ https://elixir.bootlin.com/linux/v6.16.8/source/kernel/event s/uprobes.c#L1295

    • アドレスの指す領域がwrite可能だとこのチェックで弾かれる • つまりwrite可能なコード領域にはuprobeを設定できない • チェックされるタイミング ◦ uprobe設定時: eBPFでアタッチしたとき ◦ mmap時: ライブラリがロードされたとき anti-eBPF: コード領域をwrite可能にする 33 https://blog.quarkslab.com/defeating-ebpf-uprobe-monitoring.html
  22. 2025/09/23 魔女のお茶会 #8(2025 夏) @Arata eBPFについて • 『Binary Hacks Rebooted』

    #50 • 『入門 eBPF』 • https://zenn.dev/hidenori3/articles/e1352e8cfeb2af bpftraceについて • https://github.com/bpftrace/bpftrace?tab=readme-ov-file#exa mple-one-liners • https://www.brendangregg.com/BPF/bpftrace-cheat-sheet.html eBPF, bpftraceの資料 35