[[379816]] Radare2 は、バイナリ解析向けにカスタマイズされたオープンソース ツールです。
「Linuxでバイナリを解析する10の方法」では、Linuxの豊富なネイティブツールを使ってバイナリファイルを解析する方法を説明しました。しかし、バイナリファイルをさらに詳しく調べたい場合は、バイナリ解析専用に設計されたツールが必要になります。バイナリ解析が初めてで、主にスクリプト言語を使用している場合は、この記事「GNU binutilsの9つの武器」が、コンパイルプロセスとバイナリコードとは何かを学ぶのに役立ちます。 既存のLinuxネイティブツールで同様の機能を実現できるのであれば、なぜ別のツールが必要なのかと疑問に思うのは当然でしょう。それは、スマートフォンを目覚まし時計、メモ取り、カメラ、音楽プレーヤー、インターネット、そして時折の通話などとして使うのと同じ理由です。以前は、これらの機能はそれぞれ別のデバイスやツールで処理されていました。例えば、写真を撮るための物理的なカメラ、メモを取るための小さなノート、ベッドサイドの目覚まし時計などです。ユーザーにとって、複数の(しかし関連性のある)機能を1つのデバイスで実行できるのは便利です。さらに、これらの独立した機能間の相互運用性こそが、このツールの最大の魅力です。 同様に、多くのLinuxツールは特定の用途に特化していますが、類似した(そしてより優れた)機能を1つのツールにまとめることは非常に便利です。だからこそ、バイナリを扱う必要がある場合はRadare2が最適なツールだと考えています。 GitHubの説明によると、Radare2(別名r2)は「Unix系システム向けのリバースエンジニアリングフレームワークおよびコマンドラインツールセット」です。名前の「2」は、このバージョンがゼロから書き直され、よりモジュール化されたことに由来しています。 バイナリ解析用の Linux ツール(非ネイティブ)が数多くある中で、なぜ Radare2 を選ぶのでしょうか?その理由はシンプルです。 まず、これは活発で健全なコミュニティを持つオープンソースプロジェクトです。これは、バグ修正を提供する新しい機能やツールを探している人にとって重要です。 次に、Radare2はコマンドラインから使用でき、Cutterと呼ばれる機能豊富なグラフィカルユーザーインターフェース(GUI)環境を備えているため、GUIに慣れている人に適しています。長年のLinuxユーザーとして、私はシェルでの入力に慣れています。Radare2のコマンドに慣れるには多少の学習が必要ですが、Vimの学習に例えることができます。まずは基本を学び、それをマスターしたら、より高度な機能へと進むことができます。すぐに、筋肉の記憶のように身に付くでしょう。 第三に、Radare2はプラグインを通じて外部ツールへの優れたサポートを提供します。例えば、最近オープンソース化されたGhidraバイナリアナライザーや…リバースエンジニアリングツール逆コンパイル機能がリバースエンジニアリングの重要な要素であるため、非常に人気があります。GhidraデコンパイラはRadare2コンソールから直接インストールして使用できるため、両方の長所を兼ね備えた素晴らしいツールです。 Radare2をインストールするには、リポジトリをクローンし、 user.shスクリプトを実行するだけです。システムにプリインストールされていないパッケージがある場合は、インストールする必要があります。インストールが完了したら、 r2 -vコマンドを実行して、Radare2が正しくインストールされているかどうかを確認してください。 -
$ git clone https : //github.com/radareorg/radare2.git -
$ cd radare2 -
$ ./ sys / user . sh -
# version -
$ r2 - v -
radare2 4.6 . 0 - git 25266 @ linux - x86 - 64 git . 4.4 . 0 - 930 - g48047b317 -
commit : 48047b3171e6ed0480a71a04c3693a0650d03543 build : 2020 - 11 - 17 __09 : 31 : 03 -
$
r2がインストールされたので、試してみるにはサンプルバイナリが必要です。任意のシステムバイナリ( ls 、 bashなど)を使用できますが、このチュートリアルをシンプルにするために、次の C プログラムをコンパイルしてください。
-
$ cat adder . c
# include < stdio . h >-
int adder ( int num ) { return num + 1 ;-
} -
int main () { int res , num1 = 100 ; res = adder ( num1 ); printf ( "Number now is : %d\n" , res ); return 0 ;-
}
$ gcc adder . c - o adder-
$ file adder -
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 -
$ ./ adder -
Number now is : 101
バイナリファイルを解析するには、Radare2 内で読み込む必要があります。`r2` コマンドのコマンドライン引数としてファイル名を指定して読み込みます。シェルとは別の Radare2 コンソールが表示されます。コンソールを終了するには、 QuitまたはExit r2入力するか、 Ctrl+Dを押します。 -
$ r2 ./ adder -
-- Learn pancake as if you were radare ! -
[ 0x004004b0 ]> quit -
$
バイナリを調べる前に、 r2に解析させる必要があります。r2 r2でaaaコマンドを実行することで解析できます。 -
$ r2 ./ adder -- Sorry , radare2 has experienced an internal error .-
[ 0x004004b0 ]> -
[ 0x004004b0 ]> -
[ 0x004004b0 ]> aaa -
[ x ] Analyze all flags starting with sym . and entry0 ( aa ) -
[ x ] Analyze function calls ( aac ) -
[ x ] Analyze len bytes of instructions for references ( aar ) -
[ x ] Check for vtables -
[ x ] Type matching analysis for all functions ( aaft ) -
[ x ] Propagate noreturn information -
[ x ] Use - AA or aaaa to perform additional experimental analysis . -
[ 0x004004b0 ]>
つまり、解析するバイナリファイルを選択するたびに、バイナリをロードした後にaaaという追加のコマンドを入力する必要があります。これを回避するには、コマンドの後に-Aを追加してr2を起動します。これにより、 r2はバイナリを自動的に解析します。 -
$ r2 - A ./ adder -
[ x ] Analyze all flags starting with sym . and entry0 ( aa ) -
[ x ] Analyze function calls ( aac ) -
[ x ] Analyze len bytes of instructions for references ( aar ) -
[ x ] Check for vtables -
[ x ] Type matching analysis for all functions ( aaft ) -
[ x ] Propagate noreturn information -
[ x ] Use - AA or aaaa to perform additional experimental analysis . -
-- Already up - to - date . -
[ 0x004004b0 ]>
バイナリファイルの解析を始める前に、いくつかの背景情報が必要です。多くの場合、バイナリファイルのフォーマット(ELF、PEなど)、アーキテクチャ(x86、AMD、ARMなど)、そしてバイナリが32ビットか64ビットかといった情報が必要になります。便利なiI r2使えば、必要な情報を得ることができます。 -
[ 0x004004b0 ]> iI -
arch x86 -
baddr 0x400000 -
binsz 14724 -
bintype elf -
bits 64 -
canary false -
class ELF64 -
compiler GCC : ( GNU ) 8.3 . 1 20190507 ( Red Hat 8.3 . 1 - 4 ) -
crypto false -
endian little -
havecode true -
intrp / lib64 / ld - linux - x86 - 64.so . 2 -
laddr 0x0 -
lang c -
linenum true -
lsyms true -
machine AMD x86 - 64 architecture -
maxopsz 16 -
minopsz 1 -
nx true -
os linux -
pcalign 0 -
pic false -
relocs true -
relro partial -
rpath NONE -
sanitiz false -
static false -
stripped false -
subsys linux -
va true -
[ 0x004004b0 ]> -
[ 0x004004b0 ]>
通常、作業対象のファイルの種類がわかったら、バイナリプログラムがどの標準ライブラリ関数を使用しているか、あるいはプログラムの潜在的な機能を理解したいと考えるでしょう。このチュートリアルのCプログラムの例では、ライブラリ関数は情報を出力するprintfのみです。これは、バイナリによってインポートされているすべてのライブラリを表示するiiコマンドを実行することで確認できます。 -
[ 0x004004b0 ]> ii -
[ Imports ] -
nth vaddr bind type lib name -
――――――――――――――――――――――――――――――――――――― -
1 0x00000000 WEAK NOTYPE _ITM_deregisterTMCloneTable -
2 0x004004a0 GLOBAL FUNC printf -
3 0x00000000 GLOBAL FUNC __libc_start_main -
4 0x00000000 WEAK NOTYPE __gmon_start__ -
5 0x00000000 WEAK NOTYPE _ITM_registerTMCloneTable
バイナリには独自のシンボル、関数、またはデータが含まれる場合があります。これらの関数は通常、 Exports下に表示されます。このテストバイナリは、 mainとadder 2つの関数をエクスポートします。残りの関数は、バイナリがビルドされるコンパイルフェーズで追加されます。ローダーはバイナリをロードするためにこれらの関数を必要とします(今のところ、これらについてはあまり気にする必要はありません)。 -
[ 0x004004b0 ]> -
[ 0x004004b0 ]> iE -
[ Exports ] -
nth paddr vaddr bind type size lib name -
―――――――――――――――――――――――――――――――――――――――――――――――――――――― -
82 0x00000650 0x00400650 GLOBAL FUNC 5 __libc_csu_fini -
85 ---------- 0x00601024 GLOBAL NOTYPE 0 _edata -
86 0x00000658 0x00400658 GLOBAL FUNC 0 _fini -
89 0x00001020 0x00601020 GLOBAL NOTYPE 0 __data_start -
90 0x00000596 0x00400596 GLOBAL FUNC 15 adder -
92 0x00000670 0x00400670 GLOBAL OBJ 0 __dso_handle -
93 0x00000668 0x00400668 GLOBAL OBJ 4 _IO_stdin_used -
94 0x000005e0 0x004005e0 GLOBAL FUNC 101 __libc_csu_init -
95 ---------- 0x00601028 GLOBAL NOTYPE 0 _end -
96 0x000004e0 0x004004e0 GLOBAL FUNC 5 _dl_relocate_static_pie -
97 0x000004b0 0x004004b0 GLOBAL FUNC 47 _start -
98 ---------- 0x00601024 GLOBAL NOTYPE 0 __bss_start -
99 0x000005a5 0x004005a5 GLOBAL FUNC 55 main -
100 ---------- 0x00601028 GLOBAL OBJ 0 __TMC_END__ -
102 0x00000468 0x00400468 GLOBAL FUNC 0 _init -
[ 0x004004b0 ]>
2つのバイナリファイルが類似しているかどうかは、どうすればわかるでしょうか?バイナリファイルを開いてソースコードを見るだけでは判断できません。多くの場合、バイナリファイルのハッシュ値(md5sum、sha1、sha256)はitそのファイルを一意に識別するために使用されます。`it` コマンドを使えば、バイナリのハッシュ値を調べることができます。 -
[ 0x004004b0 ]> it -
md5 7e6732f2b11dec4a0c7612852cede670 -
sha1 d5fa848c4b53021f6570dd9b18d115595a2290ae -
sha256 13dd5a492219dac1443a816ef5f91db8d149e8edbf26f24539c220861769e1c2 -
[ 0x004004b0 ]>
コードは関数ごとにグループ化されています。バイナリに含まれる関数の一覧を表示するには、 aflコマンドを実行してください。以下のリストはmain関数とadder関数を示しています。通常sym.impで始まる関数は標準ライブラリ(ここではglibc)からインポートされます。 -
[ 0x004004b0 ]> afl -
0x004004b0 1 46 entry0 -
0x004004f0 4 41 -> 34 sym . deregister_tm_clones -
0x00400520 4 57 -> 51 sym . register_tm_clones -
0x00400560 3 33 -> 32 sym . __do_global_dtors_aux -
0x00400590 1 6 entry . init0 -
0x00400650 1 5 sym . __libc_csu_fini -
0x00400658 1 13 sym . _fini -
0x00400596 1 15 sym . adder -
0x004005e0 4 101 loc .. annobin_elf_init . c -
0x004004e0 1 5 loc .. annobin_static_reloc . c -
0x004005a5 1 55 main -
0x004004a0 1 6 sym . imp . printf -
0x00400468 3 27 sym . _init -
[ 0x004004b0 ]>
C言語では、 main関数はプログラムの実行開始位置です。理想的には、他のすべての関数はmain関数から呼び出され、プログラムが終了すると、 main関数はオペレーティングシステムに終了ステータスを返します。これはソースコードからも明らかです。しかし、バイナリプログラムの場合はどうでしょうか? adder関数の呼び出し位置をどのように特定するのでしょうか? axtコマンドに関数名を続けて実行すると、 adder関数がどこで呼び出されているかを確認できます。下の画像に示すように、 main関数から呼び出されています。これが意味するところです…相互参照しかし、 main関数自体は何によって呼び出されるのでしょうか? 次のaxt mainからわかるように、これはentry0によって呼び出されます ( entry0の詳細については説明しません。読者の皆さんの練習にお任せします)。 -
[ 0x004004b0 ]> axt sym . adder -
main 0x4005b9 [ CALL ] call sym . adder -
[ 0x004004b0 ]> -
[ 0x004004b0 ]> axt main -
entry0 0x4004d1 [ DATA ] mov rdi , main -
[ 0x004004b0 ]>
テキストファイルを扱う場合、ファイル内の移動には行番号や列番号、行番号などを使うことが多いですが、バイナリファイルではアドレスを使う必要があります。アドレスは0xで始まり、その後にアドレスが続く16進数です。バイナリファイル内の現在位置を確認するには、 sコマンドを実行します。別の位置に移動するには、 sコマンドに続けてアドレスを指定します。 関数名はラベルのように機能し、内部的にはアドレスで表現されます。関数名がバイナリ形式(ストリップされていない形式)の場合、関数名の後にsコマンドを使用すると、特定の関数アドレスにジャンプできます。同様に、バイナリの先頭にジャンプしたい場合は、 s 0 」と入力してください。 -
[ 0x004004b0 ]> s -
0x4004b0 -
[ 0x004004b0 ]> -
[ 0x004004b0 ]> s main -
[ 0x004005a5 ]> -
[ 0x004005a5 ]> s -
0x4005a5 -
[ 0x004005a5 ]> -
[ 0x004005a5 ]> s sym . adder -
[ 0x00400596 ]> -
[ 0x00400596 ]> s -
0x400596 -
[ 0x00400596 ]> -
[ 0x00400596 ]> s 0 -
[ 0x00000000 ]> -
[ 0x00000000 ]> s -
0x0 -
[ 0x00000000 ]>
通常、生のバイナリデータには意味がありません。バイナリ表現とそれに相当するASCII表現を16進モードで表示すると便利です。 -
[ 0x004004b0 ]> s main -
[ 0x004005a5 ]> -
[ 0x004005a5 ]> px -
- offset - 0 1 2 3 4 5 6 7 8 9 ABCDEFnbsp; 0123456789ABCDEF -
0x004005a5 5548 89e5 4883 ec10 c745 fc64 0000 008b UH .. H .... E . d .... -
0x004005b5 45fc 89c7 e8d8 ffff ff89 45f8 8b45 f889 E ......... E .. E .. -
0x004005c5 c6bf 7806 4000 b800 0000 00e8 cbfe ffff .. x .@........... -
0x004005d5 b800 0000 00c9 c30f 1f40 00f3 0f1e fa41 .........@..... A -
0x004005e5 5749 89d7 4156 4989 f641 5541 89fd 4154 WI .. AVI .. AUA .. AT -
0x004005f5 4c8d 2504 0820 0055 488d 2d04 0820 0053 L .%.. . UH .-.. . S -
0x00400605 4c29 e548 83ec 08e8 57fe ffff 48c1 fd03 L ). H .... W ... H ... -
0x00400615 741f 31db 0f1f 8000 0000 004c 89fa 4c89 t . 1. ....... L .. L . -
0x00400625 f644 89ef 41ff 14dc 4883 c301 4839 dd75 . D .. A ... H ... H9 . u -
0x00400635 ea48 83c4 085b 5d41 5c41 5d41 5e41 5fc3 . H ...[] A\A ] A ^ A_ . -
0x00400645 9066 2e0f 1f84 0000 0000 00f3 0f1e fac3 . f .............. -
0x00400655 0000 00f3 0f1e fa48 83ec 0848 83c4 08c3 ....... H ... H .... -
0x00400665 0000 0001 0002 0000 0000 0000 0000 0000 ................ -
0x00400675 0000 004e 756d 6265 7220 6e6f 7720 6973 ... Number now is -
0x00400685 2020 3a20 2564 0a00 0000 0001 1b03 3b44 : % d ........; D -
0x00400695 0000 0007 0000 0000 feff ff88 0000 0020 ............... -
[ 0x004005a5 ]>
コンパイルされたバイナリファイルを使用している場合、ソースコードを表示することはできません。コンパイラはソースコードをCPUが理解して実行できる機械語命令に変換し、結果としてバイナリファイルまたは実行可能ファイルが生成されます。ただし、アセンブリ命令(ニーモニックワード)を参照することで、プログラムの動作を理解することはできます。例えば、 main関数の動作を確認したい場合は、 s mainコマンドを使用してmain関数のアドレスを取得し、 pdfコマンドを実行して逆アセンブルされた命令を表示できます。 アセンブリ命令を理解するには、アーキテクチャ マニュアル (ここでは x86) とそのアプリケーション バイナリ インターフェイス (ABI、または呼び出し規約) を参照し、スタックの動作の基本を理解する必要があります。 -
[ 0x004004b0 ]> s main -
[ 0x004005a5 ]> -
[ 0x004005a5 ]> s -
0x4005a5 -
[ 0x004005a5 ]> -
[ 0x004005a5 ]> pdf -
; DATA XREF from entry0 @ 0x4004d1 -
┌ 55 : int main ( int argc , char ** argv , char ** envp ); -
│ ; var int64_t var_8h @ rbp - 0x8 -
│ ; var int64_t var_4h @ rbp - 0x4 -
│ 0x004005a5 55 push rbp -
│ 0x004005a6 4889e5 mov rbp , rsp -
│ 0x004005a9 4883ec10 sub rsp , 0x10 -
│ 0x004005ad c745fc640000 . mov dword [ var_4h ], 0x64 ; 'd' ; 100 -
│ 0x004005b4 8b45fc mov eax , dword [ var_4h ] -
│ 0x004005b7 89c7 mov edi , eax -
│ 0x004005b9 e8d8ffffff call sym . adder -
│ 0x004005be 8945f8 mov dword [ var_8h ], eax -
│ 0x004005c1 8b45f8 mov eax , dword [ var_8h ] -
│ 0x004005c4 89c6 mov esi , eax -
│ 0x004005c6 bf78064000 mov edi , str . Number_now_is__ : __d ; 0x400678 ; "Number now is : %d\n" ; const char * format -
│ 0x004005cb b800000000 mov eax , 0 -
│ 0x004005d0 e8cbfeffff call sym . imp . printf ; int printf ( const char * format ) -
│ 0x004005d5 b800000000 mov eax , 0 -
│ 0x004005da c9 leave -
└ 0x004005db c3 ret -
[ 0x004005a5 ]>
これは加算adderの逆アセンブリ結果です。 -
[ 0x004005a5 ]> s sym . adder -
[ 0x00400596 ]> -
[ 0x00400596 ]> s -
0x400596 -
[ 0x00400596 ]> -
[ 0x00400596 ]> pdf -
; CALL XREF from main @ 0x4005b9 -
┌ 15 : sym . adder ( int64_t arg1 ); -
│ ; var int64_t var_4h @ rbp - 0x4 -
│ ; arg int64_t arg1 @ rdi -
│ 0x00400596 55 push rbp -
│ 0x00400597 4889e5 mov rbp , rsp -
│ 0x0040059a 897dfc mov dword [ var_4h ], edi ; arg1 -
│ 0x0040059d 8b45fc mov eax , dword [ var_4h ] -
│ 0x004005a0 83c001 add eax , 1 -
│ 0x004005a3 5d pop rbp -
└ 0x004005a4 c3 ret -
[ 0x00400596 ]>
バイナリ内のどの文字列がバイナリ解析の出発点となり得るかを特定するには、バイナリを調べます。バイナリにハードコードされた文字列は重要な手がかりとなることが多く、解析を特定の領域に絞り込むのに役立ちます。バイナリ内でizコマンドを実行すると、すべての文字列が表示されます。このテストバイナリには、ハードコードされた文字列が1つだけ含まれています。 -
[ 0x004004b0 ]> iz -
[ Strings ] -
nth paddr vaddr len size section type string -
――――――――――――――――――――――――――――――――――――――――――――――――――――――― -
0 0x00000678 0x00400678 20 21 . rodata ascii Number now is : % d\n -
[ 0x004004b0 ]>
関数と同様に、文字列を相互参照して、文字列がどこから印刷されているかを確認し、その周囲のコードを理解することができます。 -
[ 0x004004b0 ]> ps @ 0x400678 -
Number now is : % d -
[ 0x004004b0 ]> -
[ 0x004004b0 ]> axt 0x400678 -
main 0x4005c6 [ DATA ] mov edi , str . Number_now_is__ : __d -
[ 0x004004b0 ]>
コードが複雑で複数の関数が呼び出されると、混乱しがちです。特定の条件に基づいて、どの関数が呼び出され、どのパスが取られるかをグラフィカルまたは視覚的に確認できると便利です。興味のある関数に移動した後、 VVコマンドを使用してr2の視覚化モードを調べることができます。例えば、 adder関数の場合は次のようになります。 -
[ 0x004004b0 ]> s sym . adder -
[ 0x00400596 ]> -
[ 0x00400596 ]> VV
(ガウラフ・カマテ、CC BY-SA 4.0) これまで行ってきたのは静的解析、つまりバイナリを実行せずに内容を確認しただけです。場合によっては、バイナリを実行し、実行時にメモリ内の様々な情報を解析する必要がありますr2の内部デバッガーを使用すると、バイナリの実行、ブレークポイントの設定、変数値の解析、レジスタ内容のダンプなどを行うことができます。 -dフラグを付けてデバッガーを起動し、分析用バイナリをロードする際に-Aフラグを追加します。db db <function-name>コマンドを使用して、関数やメモリアドレスなどのさまざまな場所にブレークポイントを設定できます。既存のブレークポイントを表示するには、 dbiコマンドを使用します。ブレークポイントを設定したら、 dcコマンドを使用してバイナリの実行を開始します。dbt コマンドdbt使用してスタックを表示し、関数呼び出しを表示できます。最後に、 drrコマンドを使用してレジスタの内容をダンプできます。 -
$ r2 - d - A ./ adder -
Process with PID 17453 started ... -
= attach 17453 17453 -
bin . baddr 0x00400000 -
Using 0x400000 -
asm . bits 64 -
[ x ] Analyze all flags starting with sym . and entry0 ( aa ) -
[ x ] Analyze function calls ( aac ) -
[ x ] Analyze len bytes of instructions for references ( aar ) -
[ x ] Check for vtables -
[ x ] Type matching analysis for all functions ( aaft ) -
[ x ] Propagate noreturn information -
[ x ] Use - AA or aaaa to perform additional experimental analysis . -
-- git checkout hamster -
[ 0x7f77b0a28030 ]> -
[ 0x7f77b0a28030 ]> db main -
[ 0x7f77b0a28030 ]> -
[ 0x7f77b0a28030 ]> db sym . adder -
[ 0x7f77b0a28030 ]> -
[ 0x7f77b0a28030 ]> dbi -
0 0x004005a5 E : 1 T : 0 -
1 0x00400596 E : 1 T : 0 -
[ 0x7f77b0a28030 ]> -
[ 0x7f77b0a28030 ]> afl | grep main -
0x004005a5 1 55 main -
[ 0x7f77b0a28030 ]> -
[ 0x7f77b0a28030 ]> afl | grep sym . adder -
0x00400596 1 15 sym . adder -
[ 0x7f77b0a28030 ]> -
[ 0x7f77b0a28030 ]> dc -
hit breakpoint at : 0x4005a5 -
[ 0x004005a5 ]> -
[ 0x004005a5 ]> dbt -
0 0x4005a5 sp : 0x0 0 [ main ] main sym . adder + 15 -
1 0x7f77b0687873 sp : 0x7ffe35ff6858 0 [??] section .. gnu . build . attributes - 1345820597 -
2 0x7f77b0a36e0a sp : 0x7ffe35ff68e8 144 [??] map . usr_lib64_ld_2 . 28.so . r_x + 65034 -
[ 0x004005a5 ]> dc -
hit breakpoint at : 0x400596 -
[ 0x00400596 ]> dbt -
0 0x400596 sp : 0x0 0 [ sym . adder ] rip entry . init0 + 6 -
1 0x4005be sp : 0x7ffe35ff6838 0 [ main ] main + 25 -
2 0x7f77b0687873 sp : 0x7ffe35ff6858 32 [??] section .. gnu . build . attributes - 1345820597 -
3 0x7f77b0a36e0a sp : 0x7ffe35ff68e8 144 [??] map . usr_lib64_ld_2 . 28.so . r_x + 65034 -
[ 0x00400596 ]> -
[ 0x00400596 ]> -
[ 0x00400596 ]> dr -
rax = 0x00000064 -
rbx = 0x00000000 -
rcx = 0x7f77b0a21738 -
rdx = 0x7ffe35ff6948 -
r8 = 0x7f77b0a22da0 -
r9 = 0x7f77b0a22da0 -
r10 = 0x0000000f -
r11 = 0x00000002 -
r12 = 0x004004b0 -
r13 = 0x7ffe35ff6930 -
r14 = 0x00000000 -
r15 = 0x00000000 -
rsi = 0x7ffe35ff6938 -
rdi = 0x00000064 -
rsp = 0x7ffe35ff6838 -
rbp = 0x7ffe35ff6850 -
rip = 0x00400596 -
rflags = 0x00000202 -
orax = 0xffffffffffffffff -
[ 0x00400596 ]>
アセンブリ言語を理解することは、バイナリ解析の前提条件です。アセンブリ言語は、バイナリが構築され、実行が想定されるアーキテクチャと常に関連付けられています。ソースコード1行とアセンブリコードの間には、1対1の対応関係はありません。通常、Cソースコード1行は複数行のアセンブリコードを生成します。したがって、アセンブリコードを1行ずつ読み取るのは最適なアプローチではありません。 これが逆コンパイラの役割です。逆コンパイラは、アセンブリ命令から可能なソースコードを再構築しようとします。これは、バイナリの作成に使用されたソースコードと全く同じではありません。アセンブリに基づいてソースコードを近似的に表現したものです。さらに、コンパイラが実行する最適化(処理速度の向上やバイナリサイズの削減などのために異なるアセンブリコードを生成するなど)を考慮すると、逆コンパイラの作業はさらに困難になります。さらに、マルウェア作成者は、マルウェア分析を阻止するために、意図的にコードを難読化することがよくあります。 Radare2はプラグインを通じてデコンパイラを提供します。Radare2がサポートするデコンパイラはどれでもインストールできます。現在のプラグインを確認するには、 r2pm -lコマンドを使用してください。サンプルのデコンパイラr2decをインストールするには、 r2pm installコマンドを使用してください。 -
$ r2pm - l -
$ -
$ r2pm install r2dec -
Cloning into 'r2dec' ... -
remote : Enumerating objects : 100 , done . -
remote : Counting objects : 100 % ( 100 / 100 ), done . -
remote : Compressing objects : 100 % ( 97 / 97 ), done . -
remote : Total 100 ( delta 18 ), reused 27 ( delta 1 ), pack - reused 0 -
Receiving objects : 100 % ( 100 / 100 ), 1.01 MiB | 1.31 MiB / s , done . -
Resolving deltas : 100 % ( 18 / 18 ), done . -
Install Done For r2dec -
gmake : Entering directory '/root/.local/share/radare2/r2pm/git/r2dec/p' -
[ CC ] duktape / duktape . o -
[ CC ] duktape / duk_console . o -
[ CC ] core_pdd . o -
[ CC ] core_pdd . so -
gmake : Leaving directory '/root/.local/share/radare2/r2pm/git/r2dec/p' -
$ -
$ r2pm - l -
r2dec -
$
バイナリファイルを逆コンパイルするには、バイナリをr2にロードし、自動的に解析します。この例では、 s sym.adderコマンドを使用して対象のadder関数に移動し、 pddaコマンドを使用してアセンブリコードと逆コンパイルされたソースコードを並べて表示します。この逆コンパイルされたソースコードを読む方が、アセンブリコードを1行ずつ読むよりも簡単な場合が多いです。 -
$ r2 - A ./ adder -
[ x ] Analyze all flags starting with sym . and entry0 ( aa ) -
[ x ] Analyze function calls ( aac ) -
[ x ] Analyze len bytes of instructions for references ( aar ) -
[ x ] Check for vtables -
[ x ] Type matching analysis for all functions ( aaft ) -
[ x ] Propagate noreturn information -
[ x ] Use - AA or aaaa to perform additional experimental analysis . -
-- What do you want to debug today ? -
[ 0x004004b0 ]> -
[ 0x004004b0 ]> s sym . adder -
[ 0x00400596 ]> -
[ 0x00400596 ]> s -
0x400596 -
[ 0x00400596 ]> -
[ 0x00400596 ]> pdda -
; assembly | /* r2dec pseudo code output */ -
| /* ./adder @ 0x400596 */ -
| # include & lt ; stdint . h > -
| -
; ( fcn ) sym . adder () | int32_t adder ( int64_t arg1 ) { -
| int64_t var_4h ; -
| rdi = arg1 ; -
0x00400596 push rbp | -
0x00400597 mov rbp , rsp | -
0x0040059a mov dword [ rbp - 4 ], edi | *(( rbp - 4 )) = edi ; -
0x0040059d mov eax , dword [ rbp - 4 ] | eax = *(( rbp - 4 )); -
0x004005a0 add eax , 1 | eax ++; -
0x004005a3 pop rbp | -
0x004005a4 ret | return eax ; -
| } -
[ 0x00400596 ]>
Radare2に慣れてきたら、ワークフローに合わせて設定を変更したくなるでしょう。Radare2のデフォルト設定を確認するにはr2 eコマンドを使用します。特定の設定を行うには、 eコマンドの後にconfig = valueを追加します。 -
[ 0x004005a5 ]> e | wc - l -
593 -
[ 0x004005a5 ]> e | grep syntax -
asm . syntax = intel -
[ 0x004005a5 ]> -
[ 0x004005a5 ]> e asm . syntax = att -
[ 0x004005a5 ]> -
[ 0x004005a5 ]> e | grep syntax -
asm . syntax = att -
[ 0x004005a5 ]>
設定変更を永続化するには、 r2起動時に読み込まれる.radare2rcという名前の起動ファイルに設定を保存します。このファイルは通常ホームディレクトリにありますが、存在しない場合は作成できます。設定オプションの例としては、以下が挙げられます。 -
$ cat ~/. radare2rc -
e asm . syntax = att -
e scr . utf8 = true -
eco solarized -
e cmd . stack = true -
e stack . size = 256 -
$
Radare2の機能については十分に理解し、ツールの基本を理解していただいたと思います。Radare2はUnix哲学に基づいているため、メインコンソールから様々な操作を実行できますが、タスクを実行するには別のバイナリセットを使用します。 以下にリストされているスタンドアロンバイナリの動作を確認してください。例えば、コンソールでiIコマンドを使用して表示されるバイナリ情報はrabin2 <binary> ` コマンドでも確認できます。 -
$ cd bin / -
$ -
$ ls -
prefix r2agent r2pm rabin2 radiff2 ragg2 rarun2 rasm2 -
r2 r2 - indent r2r radare2 rafind2 rahash2 rasign2 rax2 -
$
|