DUICUO

フル機能のバイナリファイル解析ツール「Radare2」のガイド

[[379816]]

Radare2 は、バイナリ解析向けにカスタマイズされたオープンソース ツールです。

「Linuxでバイナリを解析する10の方法」では、Linuxの豊富なネイティブツールを使ってバイナリファイルを解析する方法を説明しました。しかし、バイナリファイルをさらに詳しく調べたい場合は、バイナリ解析専用に設計されたツールが必要になります。バイナリ解析が初めてで、主にスクリプト言語を使用している場合は、この記事「GNU binutilsの9つの武器」が、コンパイルプロセスとバイナリコードとは何かを学ぶのに役立ちます。

なぜ別のツールが必要なのでしょうか?

既存のLinuxネイティブツールで同様の機能を実現できるのであれば、なぜ別のツールが必要なのかと疑問に思うのは当然でしょう。それは、スマートフォンを目覚まし時計、メモ取り、カメラ、音楽プレーヤー、インターネット、そして時折の通話などとして使うのと同じ理由です。以前は、これらの機能はそれぞれ別のデバイスやツールで処理されていました。例えば、写真を撮るための物理的なカメラ、メモを取るための小さなノート、ベッドサイドの目覚まし時計などです。ユーザーにとって、複数の(しかし関連性のある)機能を1つのデバイスで実行できるのは便利です。さらに、これらの独立した機能間の相互運用性こそが、このツールの最大の魅力です。

同様に、多くのLinuxツールは特定の用途に特化していますが、類似した(そしてより優れた)機能を1つのツールにまとめることは非常に便利です。だからこそ、バイナリを扱う必要がある場合はRadare2が最適なツールだと考えています。

GitHubの説明によると、Radare2(別名r2)は「Unix系システム向けのリバースエンジニアリングフレームワークおよびコマンドラインツールセット」です。名前の「2」は、このバージョンがゼロから書き直され、よりモジュール化されたことに由来しています。

Radare2を選ぶ理由

バイナリ解析用の Linux ツール(非ネイティブ)が数多くある中で、なぜ Radare2 を選ぶのでしょうか?その理由はシンプルです。

まず、これは活発で健全なコミュニティを持つオープンソースプロジェクトです。これは、バグ修正を提供する新しい機能やツールを探している人にとって重要です。

次に、Radare2はコマンドラインから使用でき、Cutterと呼ばれる機能豊富なグラフィカルユーザーインターフェース(GUI)環境を備えているため、GUIに慣れている人に適しています。長年のLinuxユーザーとして、私はシェルでの入力に慣れています。Radare2のコマンドに慣れるには多少の学習が必要ですが、Vimの学習に例えることができます。まずは基本を学び、それをマスターしたら、より高度な機能へと進むことができます。すぐに、筋肉の記憶のように身に付くでしょう。

第三に、Radare2はプラグインを通じて外部ツールへの優れたサポートを提供します。例えば、最近オープンソース化されたGhidraバイナリアナライザーや…リバースエンジニアリングツール逆転ツール逆コンパイル機能がリバースエンジニアリングの重要な要素であるため、非常に人気があります。GhidraデコンパイラはRadare2コンソールから直接インストールして使用できるため、両方の長所を兼ね備えた素晴らしいツールです。

Radare2を使い始める

Radare2をインストールするには、リポジトリをクローンし、 user.shスクリプトを実行するだけです。システムにプリインストールされていないパッケージがある場合は、インストールする必要があります。インストールが完了したら、 r2 -vコマンドを実行して、Radare2が正しくインストールされているかどうかを確認してください。

  1. $ git clone https : //github.com/radareorg/radare2.git
  2. $ cd radare2
  3. $ ./ sys / user . sh
  4. # version
  5. $ r2 - v
  6. radare2 4.6 . 0 - git 25266 @ linux - x86 - 64 git . 4.4 . 0 - 930 - g48047b317
  7. commit : 48047b3171e6ed0480a71a04c3693a0650d03543 build : 2020 - 11 - 17 __09 : 31 : 03
  8. $

バイナリテストサンプルを取得する

r2がインストールされたので、試してみるにはサンプルバイナリが必要です。任意のシステムバイナリ( lsbashなど)を使用できますが、このチュートリアルをシンプルにするために、次の C プログラムをコンパイルしてください。

  1. $ cat adder . c
  1. # include < stdio . h >
  2. int adder ( int num ) {
  3. return num + 1 ;
  4. }
  5. int main () {
  6. int res , num1 = 100 ;
  7. res = adder ( num1 );
  8. printf ( "Number now is : %d\n" , res );
  9. return 0 ;
  10. }
  1. $ gcc adder . c - o adder
  2. $ file adder
  3. adder : ELF 64 - bit LSB executable , x86 - 64 , version 1 ( SYSV ), dynamically linked , interpreter / lib64 / ld - linux - x86 - 64.so . 2 , for GNU / Linux 3.2 . 0 , BuildID [ sha1 ]= 9d4366f7160e1ffb46b14466e8e0d70f10de2240 , not stripped
  4. $ ./ adder
  5. Number now is : 101

バイナリファイルを読み込む

バイナリファイルを解析するには、Radare2 内で読み込む必要があります。`r2` コマンドのコマンドライン引数としてファイル名を指定して読み込みます。シェルとは別の Radare2 コンソールが表示されます。コンソールを終了するには、 QuitまたはExit r2入力するか、 Ctrl+Dを押します。

  1. $ r2 ./ adder
  2.   -- Learn pancake as if you were radare !
  3. [ 0x004004b0 ]> quit
  4. $

バイナリの分析

バイナリを調べる前に、 r2に解析させる必要があります。r2 r2aaaコマンドを実行することで解析できます。

  1. $ r2 ./ adder
  2. -- Sorry , radare2 has experienced an internal error .
  3. [ 0x004004b0 ]>
  4. [ 0x004004b0 ]>
  5. [ 0x004004b0 ]> aaa
  6. [ x ] Analyze all flags starting with sym . and entry0 ( aa )
  7. [ x ] Analyze function calls ( aac )
  8. [ x ] Analyze len bytes of instructions for references ( aar )
  9. [ x ] Check for vtables
  10. [ x ] Type matching analysis for all functions ( aaft )
  11. [ x ] Propagate noreturn information
  12. [ x ] Use - AA or aaaa to perform additional experimental analysis .
  13. [ 0x004004b0 ]>

つまり、解析するバイナリファイルを選択するたびに、バイナリをロードした後にaaaという追加のコマンドを入力する必要があります。これを回避するには、コマンドの後に-Aを追加してr2を起動します。これにより、 r2はバイナリを自動的に解析します。

  1. $ r2 - A ./ adder
  2. [ x ] Analyze all flags starting with sym . and entry0 ( aa )
  3. [ x ] Analyze function calls ( aac )
  4. [ x ] Analyze len bytes of instructions for references ( aar )
  5. [ x ] Check for vtables
  6. [ x ] Type matching analysis for all functions ( aaft )
  7. [ x ] Propagate noreturn information
  8. [ x ] Use - AA or aaaa to perform additional experimental analysis .
  9.   -- Already up - to - date .
  10. [ 0x004004b0 ]>

バイナリに関する基本情報を入手する

バイナリファイルの解析を始める前に、いくつかの背景情報が必要です。多くの場合、バイナリファイルのフォーマット(ELF、PEなど)、アーキテクチャ(x86、AMD、ARMなど)、そしてバイナリが32ビットか64ビットかといった情報が必要になります。便利なiI r2使えば、必要な情報を得ることができます。

  1. [ 0x004004b0 ]> iI
  2. arch x86
  3. baddr 0x400000
  4. binsz 14724
  5. bintype elf
  6. bits 64
  7. canary false
  8. class ELF64
  9. compiler GCC : ( GNU ) 8.3 . 1 20190507 ( Red Hat 8.3 . 1 - 4 )
  10. crypto false
  11. endian little
  12. havecode true
  13. intrp / lib64 / ld - linux - x86 - 64.so . 2
  14. laddr 0x0
  15. lang c
  16. linenum true
  17. lsyms true
  18. machine AMD x86 - 64 architecture
  19. maxopsz 16
  20. minopsz 1
  21. nx true
  22. os linux
  23. pcalign 0
  24. pic false
  25. relocs true
  26. relro partial
  27. rpath NONE
  28. sanitiz false
  29. static false
  30. stripped false
  31. subsys linux
  32. va true
  33. [ 0x004004b0 ]>
  34. [ 0x004004b0 ]>

輸入と輸出

通常、作業対象のファイルの種類がわかったら、バイナリプログラムがどの標準ライブラリ関数を使用しているか、あるいはプログラムの潜在的な機能を理解したいと考えるでしょう。このチュートリアルのCプログラムの例では、ライブラリ関数は情報を出力するprintfのみです。これは、バイナリによってインポートされているすべてのライブラリを表示するiiコマンドを実行することで確認できます。

  1. [ 0x004004b0 ]> ii
  2. [ Imports ]
  3. nth vaddr bind type lib name
  4. ―――――――――――――――――――――――――――――――――――――
  5. 1 0x00000000 WEAK NOTYPE _ITM_deregisterTMCloneTable
  6. 2 0x004004a0 GLOBAL FUNC printf
  7. 3 0x00000000 GLOBAL FUNC __libc_start_main
  8. 4 0x00000000 WEAK NOTYPE __gmon_start__
  9. 5 0x00000000 WEAK NOTYPE _ITM_registerTMCloneTable

バイナリには独自のシンボル、関数、またはデータが含まれる場合があります。これらの関数は通常、 Exports下に表示されます。このテストバイナリは、 mainadder 2つの関数をエクスポートします。残りの関数は、バイナリがビルドされるコンパイルフェーズで追加されます。ローダーはバイナリをロードするためにこれらの関数を必要とします(今のところ、これらについてはあまり気にする必要はありません)。

  1. [ 0x004004b0 ]>
  2. [ 0x004004b0 ]> iE
  3. [ Exports ]
  4. nth paddr vaddr bind type size lib name
  5. ――――――――――――――――――――――――――――――――――――――――――――――――――――――
  6. 82 0x00000650 0x00400650 GLOBAL FUNC 5 __libc_csu_fini
  7. 85 ---------- 0x00601024 GLOBAL NOTYPE 0 _edata
  8. 86 0x00000658 0x00400658 GLOBAL FUNC 0 _fini
  9. 89 0x00001020 0x00601020 GLOBAL NOTYPE 0 __data_start
  10. 90 0x00000596 0x00400596 GLOBAL FUNC 15 adder
  11. 92 0x00000670 0x00400670 GLOBAL OBJ 0 __dso_handle
  12. 93 0x00000668 0x00400668 GLOBAL OBJ 4 _IO_stdin_used
  13. 94 0x000005e0 0x004005e0 GLOBAL FUNC 101 __libc_csu_init
  14. 95 ---------- 0x00601028 GLOBAL NOTYPE 0 _end
  15. 96 0x000004e0 0x004004e0 GLOBAL FUNC 5 _dl_relocate_static_pie
  16. 97 0x000004b0 0x004004b0 GLOBAL FUNC 47 _start
  17. 98 ---------- 0x00601024 GLOBAL NOTYPE 0 __bss_start
  18. 99 0x000005a5 0x004005a5 GLOBAL FUNC 55 main
  19. 100 ---------- 0x00601028 GLOBAL OBJ 0 __TMC_END__
  20. 102 0x00000468 0x00400468 GLOBAL FUNC 0 _init
  21. [ 0x004004b0 ]>

ハッシュ情報

2つのバイナリファイルが類似しているかどうかは、どうすればわかるでしょうか?バイナリファイルを開いてソースコードを見るだけでは判断できません。多くの場合、バイナリファイルのハッシュ値(md5sum、sha1、sha256)はitそのファイルを一意に識別するために使用されます。`it` コマンドを使えば、バイナリのハッシュ値を調べることができます。

  1. [ 0x004004b0 ]> it
  2. md5 7e6732f2b11dec4a0c7612852cede670
  3. sha1 d5fa848c4b53021f6570dd9b18d115595a2290ae
  4. sha256 13dd5a492219dac1443a816ef5f91db8d149e8edbf26f24539c220861769e1c2
  5. [ 0x004004b0 ]>

関数

コードは関数ごとにグループ化されています。バイナリに含まれる関数の一覧を表示するには、 aflコマンドを実行してください。以下のリストはmain関数とadder関数を示しています。通常sym.impで始まる関数は標準ライブラリ(ここではglibc)からインポートされます。

  1. [ 0x004004b0 ]> afl
  2. 0x004004b0     1 46 entry0
  3. 0x004004f0     4 41   -> 34 sym . deregister_tm_clones
  4. 0x00400520     4 57   -> 51 sym . register_tm_clones
  5. 0x00400560     3 33   -> 32 sym . __do_global_dtors_aux
  6. 0x00400590     1 6 entry . init0
  7. 0x00400650     1 5 sym . __libc_csu_fini
  8. 0x00400658     1 13 sym . _fini
  9. 0x00400596     1 15 sym . adder
  10. 0x004005e0     4 101 loc .. annobin_elf_init . c
  11. 0x004004e0     1 5 loc .. annobin_static_reloc . c
  12. 0x004005a5     1 55 main
  13. 0x004004a0     1 6 sym . imp . printf
  14. 0x00400468     3 27 sym . _init
  15. [ 0x004004b0 ]>

相互参照

C言語では、 main関数はプログラムの実行開始位置です。理想的には、他のすべての関数はmain関数から呼び出され、プログラムが終了すると、 main関数はオペレーティングシステムに終了ステータスを返します。これはソースコードからも明らかです。しかし、バイナリプログラムの場合はどうでしょうか? adder関数の呼び出し位置をどのように特定するのでしょうか?

axtコマンドに関数名を続けて実行すると、 adder関数がどこで呼び出されているかを確認できます。下の画像に示すように、 main関数から呼び出されています。これが意味するところです…相互参照相互参照しかし、 main関数自体は何によって呼び出されるのでしょうか? 次のaxt mainからわかるように、これはentry0によって呼び出されます ( entry0の詳細については説明しません。読者の皆さんの練習にお任せします)。

  1. [ 0x004004b0 ]> axt sym . adder
  2. main 0x4005b9 [ CALL ] call sym . adder
  3. [ 0x004004b0 ]>
  4. [ 0x004004b0 ]> axt main
  5. entry0 0x4004d1 [ DATA ] mov rdi , main
  6. [ 0x004004b0 ]>

位置を見つける

テキストファイルを扱う場合、ファイル内の移動には行番号や列番号、行番号などを使うことが多いですが、バイナリファイルではアドレスを使う必要があります。アドレスは0xで始まり、その後にアドレスが続く16進数です。バイナリファイル内の現在位置を確認するには、 sコマンドを実行します。別の位置に移動するには、 sコマンドに続けてアドレスを指定します。

関数名はラベルのように機能し、内部的にはアドレスで表現されます。関数名がバイナリ形式(ストリップされていない形式)の場合、関数名の後にsコマンドを使用すると、特定の関数アドレスにジャンプできます。同様に、バイナリの先頭にジャンプしたい場合は、 s 0 」と入力してください。

  1. [ 0x004004b0 ]> s
  2. 0x4004b0
  3. [ 0x004004b0 ]>
  4. [ 0x004004b0 ]> s main
  5. [ 0x004005a5 ]>
  6. [ 0x004005a5 ]> s
  7. 0x4005a5
  8. [ 0x004005a5 ]>
  9. [ 0x004005a5 ]> s sym . adder
  10. [ 0x00400596 ]>
  11. [ 0x00400596 ]> s
  12. 0x400596
  13. [ 0x00400596 ]>
  14. [ 0x00400596 ]> s 0
  15. [ 0x00000000 ]>
  16. [ 0x00000000 ]> s
  17. 0x0
  18. [ 0x00000000 ]>

16進表示

通常、生のバイナリデータには意味がありません。バイナリ表現とそれに相当するASCII表現を16進モードで表示すると便利です。

  1. [ 0x004004b0 ]> s main
  2. [ 0x004005a5 ]>
  3. [ 0x004005a5 ]> px
  4. - offset -   0 1   2 3   4 5   6 7   8 9 ABCDEFnbsp; 0123456789ABCDEF
  5. 0x004005a5   5548 89e5 4883 ec10 c745 fc64 0000 008b UH .. H .... E . d ....
  6. 0x004005b5   45fc 89c7 e8d8 ffff ff89 45f8 8b45 f889 E ......... E .. E ..
  7. 0x004005c5 c6bf 7806 4000 b800 0000 00e8 cbfe ffff .. x .@...........
  8. 0x004005d5 b800 0000 00c9 c30f 1f40 00f3 0f1e fa41 .........@..... A
  9. 0x004005e5   5749 89d7 4156 4989 f641 5541 89fd 4154 WI .. AVI .. AUA .. AT
  10. 0x004005f5   4c8d 2504 0820 0055 488d 2d04 0820 0053 L .%.. . UH .-.. . S
  11. 0x00400605   4c29 e548 83ec 08e8 57fe ffff 48c1 fd03 L ). H .... W ... H ...
  12. 0x00400615   741f 31db 0f1f 8000 0000 004c 89fa 4c89 t . 1. ....... L .. L .
  13. 0x00400625 f644 89ef 41ff 14dc 4883 c301 4839 dd75 . D .. A ... H ... H9 . u
  14. 0x00400635 ea48 83c4 085b 5d41 5c41 5d41 5e41 5fc3   . H ...[] A\A ] A ^ A_ .
  15. 0x00400645   9066 2e0f 1f84 0000 0000 00f3 0f1e fac3 . f ..............
  16. 0x00400655   0000 00f3 0f1e fa48 83ec 0848 83c4 08c3   ....... H ... H ....
  17. 0x00400665   0000 0001 0002 0000 0000 0000 0000 0000   ................
  18. 0x00400675   0000 004e 756d 6265 7220 6e6f 7720 6973   ... Number now is
  19. 0x00400685   2020 3a20 2564 0a00 0000 0001 1b03 3b44     : % d ........; D
  20. 0x00400695   0000 0007 0000 0000 feff ff88 0000 0020   ...............
  21. [ 0x004005a5 ]>

分解

コンパイルされたバイナリファイルを使用している場合、ソースコードを表示することはできません。コンパイラはソースコードをCPUが理解して実行できる機械語命令に変換し、結果としてバイナリファイルまたは実行可能ファイルが生成されます。ただし、アセンブリ命令(ニーモニックワード)を参照することで、プログラムの動作を理解することはできます。例えば、 main関数の動作を確認したい場合は、 s mainコマンドを使用してmain関数のアドレスを取得し、 pdfコマンドを実行して逆アセンブルされた命令を表示できます。

アセンブリ命令を理解するには、アーキテクチャ マニュアル (ここでは x86) とそのアプリケーション バイナリ インターフェイス (ABI、または呼び出し規約) を参照し、スタックの動作の基本を理解する必要があります。

  1. [ 0x004004b0 ]> s main
  2. [ 0x004005a5 ]>
  3. [ 0x004005a5 ]> s
  4. 0x4005a5
  5. [ 0x004005a5 ]>
  6. [ 0x004005a5 ]> pdf
  7.             ; DATA XREF from entry0 @ 0x4004d1
  8. 55 : int main ( int argc , char ** argv , char ** envp );
  9.           ; var int64_t var_8h @ rbp - 0x8
  10.           ; var int64_t var_4h @ rbp - 0x4
  11.           0x004005a5       55 push rbp
  12.           0x004005a6       4889e5 mov rbp , rsp
  13.           0x004005a9       4883ec10       sub rsp , 0x10
  14.           0x004005ad c745fc640000 . mov dword [ var_4h ], 0x64     ; 'd' ; 100
  15.           0x004005b4       8b45fc mov eax , dword [ var_4h ]
  16.           0x004005b7       89c7 mov edi , eax
  17.           0x004005b9 e8d8ffffff call sym . adder
  18.           0x004005be       8945f8 mov dword [ var_8h ], eax
  19.           0x004005c1       8b45f8 mov eax , dword [ var_8h ]
  20.           0x004005c4       89c6 mov esi , eax
  21.           0x004005c6 bf78064000 mov edi , str . Number_now_is__ : __d ; 0x400678 ; "Number now is : %d\n" ; const char * format
  22.           0x004005cb b800000000 mov eax , 0
  23.           0x004005d0 e8cbfeffff call sym . imp . printf         ; int printf ( const char * format )
  24.           0x004005d5 b800000000 mov eax , 0
  25.           0x004005da c9 leave
  26.           0x004005db c3 ret
  27. [ 0x004005a5 ]>

これは加算adderの逆アセンブリ結果です。

  1. [ 0x004005a5 ]> s sym . adder
  2. [ 0x00400596 ]>
  3. [ 0x00400596 ]> s
  4. 0x400596
  5. [ 0x00400596 ]>
  6. [ 0x00400596 ]> pdf
  7.             ; CALL XREF from main @ 0x4005b9
  8. 15 : sym . adder ( int64_t arg1 );
  9.           ; var int64_t var_4h @ rbp - 0x4
  10.           ; arg int64_t arg1 @ rdi
  11.           0x00400596       55 push rbp
  12.           0x00400597       4889e5 mov rbp , rsp
  13.           0x0040059a       897dfc mov dword [ var_4h ], edi ; arg1
  14.           0x0040059d       8b45fc mov eax , dword [ var_4h ]
  15.           0x004005a0       83c001 add eax , 1
  16.           0x004005a3       5d pop rbp
  17.           0x004005a4 c3 ret
  18. [ 0x00400596 ]>

バイナリ内のどの文字列がバイナリ解析の出発点となり得るかを特定するには、バイナリを調べます。バイナリにハードコードされた文字列は重要な手がかりとなることが多く、解析を特定の領域に絞り込むのに役立ちます。バイナリ内でizコマンドを実行すると、すべての文字列が表示されます。このテストバイナリには、ハードコードされた文字列が1つだけ含まれています。

  1. [ 0x004004b0 ]> iz
  2. [ Strings ]
  3. nth paddr vaddr len size section type string
  4. ―――――――――――――――――――――――――――――――――――――――――――――――――――――――
  5. 0   0x00000678 0x00400678 20   21   . rodata ascii Number now is   : % d\n
  6. [ 0x004004b0 ]>

相互参照文字列

関数と同様に、文字列を相互参照して、文字列がどこから印刷されているかを確認し、その周囲のコードを理解することができます。

  1. [ 0x004004b0 ]> ps @ 0x400678
  2. Number now is   : % d
  3. [ 0x004004b0 ]>
  4. [ 0x004004b0 ]> axt 0x400678
  5. main 0x4005c6 [ DATA ] mov edi , str . Number_now_is__ : __d
  6. [ 0x004004b0 ]>

ビジュアルモード

コードが複雑で複数の関数が呼び出されると、混乱しがちです。特定の条件に基づいて、どの関数が呼び出され、どのパスが取られるかをグラフィカルまたは視覚的に確認できると便利です。興味のある関数に移動した後、 VVコマンドを使用してr2の視覚化モードを調べることができます。例えば、 adder関数の場合は次のようになります。

  1. [ 0x004004b0 ]> s sym . adder
  2. [ 0x00400596 ]>
  3. [ 0x00400596 ]> VV

(ガウラフ・カマテ、CC BY-SA 4.0)

デバッガ

これまで行ってきたのは静的解析、つまりバイナリを実行せずに内容を確認しただけです。場合によっては、バイナリを実行し、実行時にメモリ内の様々な情報を解析する必要がありますr2の内部デバッガーを使用すると、バイナリの実行、ブレークポイントの設定、変数値の解析、レジスタ内容のダンプなどを行うことができます。

-dフラグを付けてデバッガーを起動し、分析用バイナリをロードする際に-Aフラグを追加します。db db <function-name>コマンドを使用して、関数やメモリアドレスなどのさまざまな場所にブレークポイントを設定できます。既存のブレークポイントを表示するには、 dbiコマンドを使用します。ブレークポイントを設定したら、 dcコマンドを使用してバイナリの実行を開始します。dbt コマンドdbt使用してスタックを表示し、関数呼び出しを表示できます。最後に、 drrコマンドを使用してレジスタの内容をダンプできます。

  1. $ r2 - d - A ./ adder
  2. Process with PID 17453 started ...
  3. = attach 17453 17453
  4. bin . baddr 0x00400000
  5. Using 0x400000
  6. asm . bits 64
  7. [ x ] Analyze all flags starting with sym . and entry0 ( aa )
  8. [ x ] Analyze function calls ( aac )
  9. [ x ] Analyze len bytes of instructions for references ( aar )
  10. [ x ] Check for vtables
  11. [ x ] Type matching analysis for all functions ( aaft )
  12. [ x ] Propagate noreturn information
  13. [ x ] Use - AA or aaaa to perform additional experimental analysis .
  14.   -- git checkout hamster
  15. [ 0x7f77b0a28030 ]>
  16. [ 0x7f77b0a28030 ]> db main
  17. [ 0x7f77b0a28030 ]>
  18. [ 0x7f77b0a28030 ]> db sym . adder
  19. [ 0x7f77b0a28030 ]>
  20. [ 0x7f77b0a28030 ]> dbi
  21. 0 0x004005a5 E : 1 T : 0
  22. 1 0x00400596 E : 1 T : 0
  23. [ 0x7f77b0a28030 ]>
  24. [ 0x7f77b0a28030 ]> afl | grep main
  25. 0x004005a5     1 55 main
  26. [ 0x7f77b0a28030 ]>
  27. [ 0x7f77b0a28030 ]> afl | grep sym . adder
  28. 0x00400596     1 15 sym . adder
  29. [ 0x7f77b0a28030 ]>
  30. [ 0x7f77b0a28030 ]> dc
  31. hit breakpoint at : 0x4005a5
  32. [ 0x004005a5 ]>
  33. [ 0x004005a5 ]> dbt
  34. 0   0x4005a5 sp : 0x0                 0     [ main ] main sym . adder + 15
  35. 1   0x7f77b0687873 sp : 0x7ffe35ff6858       0     [??] section .. gnu . build . attributes - 1345820597
  36. 2   0x7f77b0a36e0a sp : 0x7ffe35ff68e8       144   [??] map . usr_lib64_ld_2 . 28.so . r_x + 65034
  37. [ 0x004005a5 ]> dc
  38. hit breakpoint at : 0x400596
  39. [ 0x00400596 ]> dbt
  40. 0   0x400596 sp : 0x0                 0     [ sym . adder ] rip entry . init0 + 6
  41. 1   0x4005be sp : 0x7ffe35ff6838       0     [ main ] main + 25
  42. 2   0x7f77b0687873 sp : 0x7ffe35ff6858       32   [??] section .. gnu . build . attributes - 1345820597
  43. 3   0x7f77b0a36e0a sp : 0x7ffe35ff68e8       144   [??] map . usr_lib64_ld_2 . 28.so . r_x + 65034
  44. [ 0x00400596 ]>
  45. [ 0x00400596 ]>
  46. [ 0x00400596 ]> dr
  47. rax = 0x00000064
  48. rbx = 0x00000000
  49. rcx = 0x7f77b0a21738
  50. rdx = 0x7ffe35ff6948
  51. r8 = 0x7f77b0a22da0
  52. r9 = 0x7f77b0a22da0
  53. r10 = 0x0000000f
  54. r11 = 0x00000002
  55. r12 = 0x004004b0
  56. r13 = 0x7ffe35ff6930
  57. r14 = 0x00000000
  58. r15 = 0x00000000
  59. rsi = 0x7ffe35ff6938
  60. rdi = 0x00000064
  61. rsp = 0x7ffe35ff6838
  62. rbp = 0x7ffe35ff6850
  63. rip = 0x00400596
  64. rflags = 0x00000202
  65. orax = 0xffffffffffffffff
  66. [ 0x00400596 ]>

デコンパイラ

アセンブリ言語を理解することは、バイナリ解析の前提条件です。アセンブリ言語は、バイナリが構築され、実行が想定されるアーキテクチャと常に関連付けられています。ソースコード1行とアセンブリコードの間には、1対1の対応関係はありません。通常、Cソースコード1行は複数行のアセンブリコードを生成します。したがって、アセンブリコードを1行ずつ読み取るのは最適なアプローチではありません。

これが逆コンパイラの役割です。逆コンパイラは、アセンブリ命令から可能なソースコードを再構築しようとします。これは、バイナリの作成に使用されたソースコードと全く同じではありません。アセンブリに基づいてソースコードを近似的に表現したものです。さらに、コンパイラが実行する最適化(処理速度の向上やバイナリサイズの削減などのために異なるアセンブリコードを生成するなど)を考慮すると、逆コンパイラの作業はさらに困難になります。さらに、マルウェア作成者は、マルウェア分析を阻止するために、意図的にコードを難読化することがよくあります。

Radare2はプラグインを通じてデコンパイラを提供します。Radare2がサポートするデコンパイラはどれでもインストールできます。現在のプラグインを確認するには、 r2pm -lコマンドを使用してください。サンプルのデコンパイラr2decをインストールするには、 r2pm installコマンドを使用してください。

  1. $ r2pm - l
  2. $
  3. $ r2pm install r2dec
  4. Cloning into 'r2dec' ...
  5. remote : Enumerating objects : 100 , done .
  6. remote : Counting objects : 100 % ( 100 / 100 ), done .
  7. remote : Compressing objects : 100 % ( 97 / 97 ), done .
  8. remote : Total 100 ( delta 18 ), reused 27 ( delta 1 ), pack - reused 0
  9. Receiving objects : 100 % ( 100 / 100 ), 1.01 MiB | 1.31 MiB / s , done .
  10. Resolving deltas : 100 % ( 18 / 18 ), done .
  11. Install Done For r2dec
  12. gmake : Entering directory '/root/.local/share/radare2/r2pm/git/r2dec/p'
  13. [ CC ] duktape / duktape . o
  14. [ CC ] duktape / duk_console . o
  15. [ CC ] core_pdd . o
  16. [ CC ] core_pdd . so
  17. gmake : Leaving directory '/root/.local/share/radare2/r2pm/git/r2dec/p'
  18. $
  19. $ r2pm - l
  20. r2dec
  21. $

デコンパイラビュー

バイナリファイルを逆コンパイルするには、バイナリをr2にロードし、自動的に解析します。この例では、 s sym.adderコマンドを使用して対象のadder関数に移動し、 pddaコマンドを使用してアセンブリコードと逆コンパイルされたソースコードを並べて表示します。この逆コンパイルされたソースコードを読む方が、アセンブリコードを1行ずつ読むよりも簡単な場合が多いです。

  1. $ r2 - A ./ adder
  2. [ x ] Analyze all flags starting with sym . and entry0 ( aa )
  3. [ x ] Analyze function calls ( aac )
  4. [ x ] Analyze len bytes of instructions for references ( aar )
  5. [ x ] Check for vtables
  6. [ x ] Type matching analysis for all functions ( aaft )
  7. [ x ] Propagate noreturn information
  8. [ x ] Use - AA or aaaa to perform additional experimental analysis .
  9.   -- What do you want to debug today ?
  10. [ 0x004004b0 ]>
  11. [ 0x004004b0 ]> s sym . adder
  12. [ 0x00400596 ]>
  13. [ 0x00400596 ]> s
  14. 0x400596
  15. [ 0x00400596 ]>
  16. [ 0x00400596 ]> pdda
  17.     ; assembly | /* r2dec pseudo code output */
  18.                                               | /* ./adder @ 0x400596 */
  19.                                               | # include & lt ; stdint . h >
  20.                                               |  
  21.     ; ( fcn ) sym . adder ()                     | int32_t adder ( int64_t arg1 ) {
  22.                                               |     int64_t var_4h ;
  23.                                               | rdi = arg1 ;
  24.     0x00400596 push rbp |    
  25.     0x00400597 mov rbp , rsp |    
  26.     0x0040059a mov dword [ rbp - 4 ], edi |     *(( rbp - 4 )) = edi ;
  27.     0x0040059d mov eax , dword [ rbp - 4 ]       | eax = *(( rbp - 4 ));
  28.     0x004005a0 add eax , 1                     | eax ++;
  29.     0x004005a3 pop rbp |    
  30.     0x004005a4 ret |     return eax ;
  31.                                               | }
  32. [ 0x00400596 ]>

構成設定

Radare2に慣れてきたら、ワークフローに合わせて設定を変更したくなるでしょう。Radare2のデフォルト設定を確認するにはr2 eコマンドを使用します。特定の設定を行うには、 eコマンドの後にconfig = valueを追加します。

  1. [ 0x004005a5 ]> e | wc - l
  2. 593
  3. [ 0x004005a5 ]> e | grep syntax
  4. asm . syntax = intel
  5. [ 0x004005a5 ]>
  6. [ 0x004005a5 ]> e asm . syntax = att
  7. [ 0x004005a5 ]>
  8. [ 0x004005a5 ]> e | grep syntax
  9. asm . syntax = att
  10. [ 0x004005a5 ]>

設定変更を永続化するには、 r2起動時に読み込まれる.radare2rcという名前の起動ファイルに設定を保存します。このファイルは通常ホームディレクトリにありますが、存在しない場合は作成できます。設定オプションの例としては、以下が挙げられます。

  1. $ cat ~/. radare2rc
  2. e asm . syntax = att
  3. e scr . utf8 = true
  4. eco solarized
  5. e cmd . stack = true
  6. e stack . size = 256
  7. $

さらに詳しく

Radare2の機能については十分に理解し、ツールの基本を理解していただいたと思います。Radare2はUnix哲学に基づいているため、メインコンソールから様々な操作を実行できますが、タスクを実行するには別のバイナリセットを使用します。

以下にリストされているスタンドアロンバイナリの動作を確認してください。例えば、コンソールでiIコマンドを使用して表示されるバイナリ情報はrabin2 <binary> ` コマンドでも確認できます。

  1. $ cd bin /
  2. $
  3. $ ls
  4. prefix r2agent r2pm rabin2 radiff2 ragg2 rarun2 rasm2
  5. r2 r2 - indent r2r radare2 rafind2 rahash2 rasign2 rax2
  6. $