ログイン
編集不可のページディスカッション情報添付ファイル
"CTF/Toolkit/METASM"の差分

MMA
8と9のリビジョン間の差分
2014-01-23 18:21:25時点のリビジョン8
サイズ: 7523
編集者: nomeaning
コメント:
2014-01-23 18:25:22時点のリビジョン9
サイズ: 7576
編集者: nomeaning
コメント:
削除された箇所はこのように表示されます。 追加された箇所はこのように表示されます。
行 194: 行 194:
{{{#!wiki note
現在実装されていない
}}}

METASM

METASMはPure Rubyで記述されている、アセンブラ、逆アセンブラ、コンパイラ、リンカー、デバッガーである。

対応アーキテクチャ
x86_64,ia32,mips,ppc (unstable: arc,arm,bpf,cy16,dalvik,python,sh4,z80)
対応フォーマット
a.out,elf,pe,raw,Mach-O,nds,xcoffなど

http://metasm.cr0.org/

ドキュメントはソースコードとなっているが、それなりに綺麗に書かれている。

逆アセンブラの主な特徴は以下の通り。

  • 関数を自動で識別し、呼び出し元などを列挙してくれる
  • Windowsの関数呼び出しもある程度関数名を表示してくれる
  • 逆コンパイルすることが出来る
  • 関数の呼び出しグラフや関数内でのジャンプグラフなどを表示することが出来る(GUI版)
  • 文字列参照をコメントで表示することが出来る
  • ローカル変数を列挙することが出来る

インストール

リポジトリをクローンする。

$ git clone https://github.com/jjyg/metasm/ ~/local/metasm

環境変数RUBYLIBにmetasmのディレクトリを追加する。.bashrcや.zshenvなどに以下を記述。

[[ -s "$HOME/local/metasm" ]] && export RUBYLIB="$HOME/local/metasm"

関連gemをインストールする(GUI逆アセンブラーを使う場合のみ)

$ gem install gtk2

Tools

samplesディレクトリに利用例としてツールが入っている。(大体はRubyから扱うためのSampleである)

  • disassemble-gui.rb
    • GUI版逆アセンブラ。ファイルの編集が出来たり、関数の分岐などをグラフにして表示する機能があったりする。
  • disassemble.rb
    • 逆アセンブラ
  • lindebug.rb
    • Win32/Linux用のラインデバッガー
  • dump_upx.rb
    • UPXをUnpackする

有用性が微妙なもの

  • bindiff.rb
    • ふたつの逆アセンブルを比較する
  • struct_offset.rb
    • 構造体の要素の位置を調べる

逆アセンブラ

基本的な利用方法

$ ruby disassmble.rb --cpu <CPU名> --exe <ファイルフォーマット> [その他オプション] 実行ファイル

--cpu--exeを省略しても自動的に適切なものを選んでくれる

逆アセンブル(CUI)

デフォルトでちゃんとbacktraceしてくれる。

関数の呼び出し元なども表示してくれるのが便利。

$ ruby disassemble.rb --cpu x86_64 --exe ELF a.out                              
// ELF segment at 400000h, flags = R, X
db 7fh, "ELF", 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; @400000h
db 2, 0, 3eh, 0, 1, 0, 0, 0, 40h, 4, 40h, 0, 0, 0, 0, 0 ; @400010h
db 40h, 0, 0, 0, 0, 0, 0, 0, 38h, 0bh, 0, 0, 0, 0, 0, 0 ; @400020h
(中略)
// Xrefs: __libc_start_main
main:
// function binding: r10,r11,r8,r9,rcx,rdi,rdx -> unknown, rax -> 0, rsp -> rsp+8
// function ends at 400539h
    push rbp                                     ; @40050ch  55  
    mov rbp, rsp                                 ; @40050dh  4889e5  
    sub rsp, 10h                                 ; @400510h  4883ec10  
    mov [rbp-4], edi                             ; @400514h  897dfc  
    cmp dword ptr [rbp-4], 1                     ; @400517h  837dfc01  
    jnz loc_400529h                              ; @40051bh  750c  x:loc_400529h

    mov edi, 4005ech                             ; @40051dh  bfec054000  
    call thunk_puts                              ; @400522h  e8b9feffff  x:thunk_puts
    jmp loc_400533h                              ; @400527h  eb0a  x:loc_400533h


// Xrefs: 40051bh
loc_400529h:
    mov edi, 4005f2h                             ; @400529h  bff2054000  
    call thunk_puts                              ; @40052eh  e8adfeffff  x:thunk_puts


// Xrefs: 400527h
loc_400533h:
    mov eax, 0                                   ; @400533h  b800000000  
    leave                                        ; @400538h  c9  
    ret                                          ; @400539h  c3  endsub main
db 90h, 90h, 90h, 90h, 90h, 90h                  ; @40053ah

// Xrefs: __libc_start_main
(以下省略)

文字列を参照している場合の表示を行うことが出来る。

$ ruby disassemble.rb --post-plugin ~/local/metasm/samples/dasm-plugins/stringsxrefs.rb (FILE)
(中略)
    push xref_406018h                            ; @401057h  6818604000  "password OK\n"
(中略)

backtraceのせいで逆コンパイルが終わらない場合は、--fastオプションを付与すると良い。

逆コンパイル

Boomerangと違って64bit ELFファイルの逆コンパイルもしてくれるが実用性はほとんどない。いろいろと間違ってるし。

   1 $ ruby disassemble.rb --decompile --cpu x86_64 --exe ELF a.out
   2 (中略)
   3 
   4 int main __attribute__((stackoff:-8))(int rbp __attribute__((register(rbp))), int rdi __attribute__((register(rdi))))
   5 {
   6         register int eax __attribute__((register(eax)));
   7         register int rbp_a0 __attribute__((register(rbp)));
   8         register int rsp __attribute__((register(rsp)));
   9         register int rsp_a0 __attribute__((register(rsp)));
  10         rsp_a0 = (rsp - 8);
  11         *(int*)(rsp_a0 - 8) = rbp;
  12         rbp_a0 = rsp_a0;
  13         *(int*)(rbp_a0 - 4) = rdi;
  14         *(__int32*)(rbp_a0 - 4) != 1;
  15         puts();
  16         return eax;
  17 }

シェルコード

RubyのExploit上で直接Shellcodeを書くことが可能になる。

   1 require 'metasm'
   2 
   3 print Metasm::Shellcode.assemble(Metasm::Ia32.new, <<SOURCE).encode_string
   4 mov eax, 1
   5 mov ebx, 42
   6 int 80h         // exit
   7 SOURCE
   8 

METASMとRubyを用いたデバッグ

デバッグの開始

プロセスを実行する

Metasm::OS.currentで現在のOSを取得することが出来る。(Metasm::LinOSかMetasm::WinOSのどちらか)

OS.create_processにより新規プロセスをコマンドを指定して実行出来る。

   1 require 'metasm'
   2 
   3 process = Metasm::OS.current.create_process('./a.out')
   4 # 引数を渡す場合は process = Metasm::OS.current.create_process('./a.out ARGV1 ARGV2')
   5 debugger = process.debugger
   6 debugger.run_forever

プロセスにアタッチする

   1 # 特定の名前またはpidを持つ最初のプロセスを返す
   2 pid = Metasm::OS.current.find_process('./a.out').pid
   3 
   4 process = Metasm::OS.current.open_process(pid)
   5 debugger = process.debugger

標準入出力を確保してプロセスを実行する

   1 io = IO.popen(['./a.out', 'aaa'], 'r+')
   2 debugger = Metasm::OS.current.open_process(io.pid).debugger

ブレークポイント

ソフトウェアブレークポイント

Debugger.bpm(アドレス, 一度のみか, &callback)

   1 debugger.bpx(0x40051d, true) do 
   2   puts "eax=#{debuger[:eax]}"
   3 end

ハードウェアブレークポイント

Debugger.hwbp(アドレス, タイプ(:r, :w, :x)のどれか, メモリサイズ, 一度のみか, &callback)

   1 debugger.hwbp(0x40051d, :x, 1, true) do 
   2   puts "eax=#{debuger[:eax]}"
   3 end

メモリアクセスブレークポイント

現在実装されていない

Debugger.mbp(アドレス, タイプ(:r, :w)のどれか, メモリサイズ, 一度のみか, &callback)

   1 debugger.mbp(0x40051d, :r, 1, true) do 
   2   puts "Access"
   3 end

CTF/Toolkit/METASM (最終更新日時 2016-02-12 13:56:08 更新者 nomeaning)