0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

備忘録:バイナリ解析3 静的解析

Posted at

静的解析

静的解析(static analysis)では、解析対象であるプログラムを実際に実行せずに ツールなどを用いて解析対象のプログラムコードを解析する手法です。

動的解析ではプログラムをブラックボックスと捉えて、実行した挙動から
解析しますが、静的解析ではプログラム内部の潜在的な機能やプログラム内に
含まれるバイト列などを解析します。

動的解析はブラックボックス解析的手法であり、静的解析はホワイトボックス解析的
手法です。
image.png

静的解析で使用するコマンド

静的解析で使用するコマンド
静的解析では解析対象のプログラムの内部がどのようであるかを、機械語レベルで 解析します。動的解析では解析できない隠されているコードや、プログラムの処理 の細部までも確認することができます。

静的解析では次のようなコマンドを使用します。
⚫ objdump
⚫ nm
⚫ readelf
⚫ gdb

CPU

CPU
CPU (Central Processing Unit)は、コンピュータの演算や制御の中心となる デバイスです。現在パソコンやサーバで使われているIntel社のCPUには、 32bit版(x86)と64bit版(x64)の2種類があります。

CPUは主記憶装置装置(メインメモリ)から機械語の命令(インストラクション)を読み込み、解釈して実行します。必要に応じて実行結果をメモリに書き戻します。
image.png

$ LANG=C lscpu ※lscpuでCPUの情報を表示する(表示が乱れるのでLANG=C で英語で表示にする)
Architecture: i686 ※CPUのアーキテクチャ(64bitならx86_64と表示される
CPU op-mode(s): 32-bit, 64-bit ※CPUがサポートするアーキテクチャ
Byte Order: Little Endian
CPU(s): 4 ※CPUの論理コア数
On-line CPU(s) list: 0-3
Thread(s) per core: 1
Core(s) per socket: 4
Socket(s): 1
Vendor ID: GenuineIntel ※Intel社のCPUはGenuineIntel
CPU family: 6
Model: 79
Model name: Intel(R) Xeon(R) CPU E5-2686 v4 @ 2.30GHz ※CPUの名称
Stepping: 1
CPU MHz: 2300.010 ※CPUの動作クロック数
BogoMIPS: 4600.02
Hypervisor vendor: Xen
Virtualization type: full
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 46080K

メモリ

メモリ
主記憶装置は、CPUから直接アクセスすることができる記憶装置です。一般的には メモリ(memory)やメインメモリといいます。

メモリにはストレージやデバイスから読み込んだ命令やデータが保持されます。また、
処理の結果によって得られたデータが書き込まれます。メモリは多数のバイトの
セルから構成されます。4Gbyteのメモリには、4,294,967,296個のセルが
あります(1,024 × 1,024 × 1,024 × 4)。

メモリへは、バイト単位(8bit)、ワード単位(16bit)、ダブルワード単位
(32bit)でアクセスされます。64bitCPUでは、クワッドワード単位(64bit)で
アクセスすることもあります。
image.png

アドレス

アドレス(番地)
メモリにはアドレス(address)または番地と呼ばれる一連の通し番号が、バイト 単位に振り当てられています。 メモリに格納されている命令やデータにアクセスする時にはアドレスが使われます。

image.png

[ddによるメモリのダンプでの確認(管理者権限が必要)]

$ dd if=/dev/mem | od -Ax -tx1 ※ddでメモリの内容を一部ダンプする
dd: '/dev/mem' を開けませんでした: 許可がありません
000000
$ sudo dd if=/dev/mem | od –Ax –tx1 | head -10 ※ddをsudoで管理者権限によって実行する
※アドレス アドレス位置に格納されているメモリの内容(バイト単位)
000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
* ※00が連続しているので * と表示される
099000 00 a0 09 00 86 a2 09 00 00 a0 09 00 00 d0 09 00
099010 00 c0 09 00 30 a2 09 00 08 00 00 00 00 00 00 00
099020 1f 00 20 90 09 00 00 00 ff ff 00 00 00 9b cf 00
099030 ff ff 00 00 00 9b af 00 ff ff 00 00 00 93 cf 00
099040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
099050 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00
099060 ff ff 60 90 09 00 00 00 ff ff 00 90 09 9b 00 00
099070 ff ff 00 01 00 93 00 00 00 00 00 00 00 00 00 00

レジスタ

レジスタ
レジスタ(register)は、演算や実行状態が保持されるCPU内部のメモリです。 x86アーキテクチャのCPUは、8個の汎用レジスタ(汎用ダブルワードレジスタ)、 6個のセグメントレジスタ、1個のフラグレジスタ、1個の命令ポインタを持ちます。

image.png

レジスタの種類(1)

汎用レジスタ(汎用ダブルワードレジスタ)
汎用レジスタ(general purpose register)には名称があり、それぞれに ある程度の用途が規定されています。 汎用レジスタのサイズ(ビット長)は32bitですが、必要に応じて16bitや8bitと して利用することもできます。

image.png

レジスタの種類(2)

セグメントレジスタ
セグメントレジスタ(segment register)は、用途毎に設定したセグメント (メモリをいくつかに区切った領域)の先頭アドレスを示すために使われます。 最近のOSではFSとGS以外の全ての値を同一に設定することが多いです。

レジスタの種類(3)

命令ポインタ
EIPは命令ポインタ(instruction pointer)です。次に実行される命令の アドレスを保持します。命令を実行する都度、CPUによって自動的にセットされます。
フラグレジスタ
フラグレジスタ(flag register)は32個のフラグ(1フラグは1bit)から 構成されます。命令の実行結果に応じて、CPUがセットしたりリセットしたりします。 フラグは命令を分岐する場合などに利用されます。下表は主なフラグです。

ELF

ELF
過去のLinuxでは、実行形式ファイルのフォーマットにa.out形式を使用していました。 現在のLinuxではELF(Executable and Linkable Format)が使用されています。 静的解析における対象のLinuxプログラムは、主にELFファイルになります。

image.png

image.png

セクション

セクション
セクション(section)は、ELF ファイル内を役割で分割する最小単位です。 セクションは、プログラムをメモリにロードされるときに使われるファイル内の 情報(命令、データ、シンボルテーブル、再配置情報など)を保持します。 下表は主なセクションです。

image.png
[C言語のプログラム: esperanto.c]

#include <stdio.h>
char message1[] = "Vivi kiel kuko"; ※書き換え可能なデータ(.dataセクションに配置
const char message2[] = "en butero"; ※読み取り専用データ(.rodataセクションに配置

void show_message(void) {          ※コード(.textセクションに配置)
printf("%s %s.¥n", message1, message2);  
}                       
int main()                                 
{ show_message();                         
}                                         

[objdumpによるセクションヘッダ情報の表示]

$ LANG=C objdump –h esperanto  ※objdumpでセクション情報を表示する
                               ※–h はセクションヘッダ情報の要約を表示する
esperanto: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
~(略)~
13 .text 000001b2 08048310 08048310 00000310 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 00000014 080484c4 080484c4 000004c4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 0000001a 080484d8 080484d8 000004d8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA 13
~(略)~
24 .data 00000017 0804a014 0804a014 00001014 2**2
CONTENTS, ALLOC, LOAD, DATA
25 .bss 00000001 0804a02b 0804a02b 0000102b 2**0
ALLOC
26 .comment 00000035 00000000 00000000 0000102b 2**0
CONTENTS, READONLY

[objdumpによるセクション内容のダンプ]

 objdump –sj .data esperanto ※objdumpで.dataセクションの内容をダンプする
esperanto: ファイル形式 elf32-i386 ※–s はセクション、-j は指定したセクションのみ表示
セクション .data の内容:
804a014 00000000 00000000 56697669 206b6965 ........Vivi kie
804a024 6c206b75 6b6f00 l kuko.
$ objdump –sj .rodata esperanto ※objdumpで.rodataセクションの内容をダンプする
esperanto: ファイル形式 elf32-i386
セクション .rodata の内容:
80484d8 03000000 01000200 656e2062 75746572 ........en buter
80484e8 6f002573 2025732e 0a00 o.%s %s...

シンボル

シンボル
シンボル(symbol)は、ファイル内のシンボルテーブルに保持されている 変数名、関数名、ファイル名などの名前情報です。バイナリ解析には役立ちますが、 リリースされた製品やマルウェアなどでは除去されている場合がほとんどです。

シンボルはnmコマンドで表示できます。nmコマンドは、シンボル値、タイプ、
シンボル名の順で表示します。下表は主なシンボルのタイプです。
image.png
[シンボルテーブルの表示]
image.png
[シンボルの削除]
image.png

コンパイル

コンパイル
実行形式ファイル(=プログラム)には、CPUに実行させるための命令やデータが 機械語で書かれています。

プログラムを機械語で直接記述するのは難易度が高いので、C言語やC++言語などの
プログラミング言語(programming language)で記述したソースコード
(source code)を、ソースファイル(source file)として保存します。
ソースファイルはコンパイラ(compiler)というツールでコンパイル(compile)
することで、機械語のオブジェクトコード(object code)を生成してオブジェク
トファイル(object file)に格納します。

C言語コンパイラとして、gccやclangなどが使われます。

リンク

リンク

実行形式ファイルは複数のオブジェクトファイルやライブラリファイルをリンク
(結合)することで作成されます。リンクに使われるツールがリンカ(linker)です。
リンカとして、ldコマンドが使われます。

リンクには、ライブラリを実行形式に含むスタティックリンクと、
実行時にライブラリファイルを利用するダイナミックリンクの2種類があります。
ダイナミックリンクで使用されるライブラリはlddコマンドで表示できます。

[コンパイルとリンク①]
image.png
[コンパイルとリンク②]
image.png
[コンパイルとリンク③]
image.png

逆アセンブル(機械語とアセンブリ言語)

機械語
CPUに命令をするときに使われるのが機械語(machine language)です。

デジタル回路では、1を高い電圧、0を低い電圧(またはその逆)で、電気信号をやり取りします。そのため、機械語の命令(=インストラクション)は1と0を組み合わせた2進数として符号化されています。ただし2進数では桁数が多く分かりにくいので、16進数に変換して表記するのが一般的です。

アセンブリ言語
機械語でプログラムを書いたり読んだりするのは困難なので、機械語に対応した 符号であるニーモニック(mnemonic)を使用することで理解を容易にします。

ニーモニックを使用したプログラミング言語をアセンブリ言語(assembly
language)といいます。アセンブリ言語をアセンブラということもあります。

ニーモニックは、オペレーション(operation)とオペランド(operand)から
構成されます。オペレーションは命令で、オペランドは命令の対象です。
オペランドは、レジスタ、メモリアドレス、即値などです。

(参考)ニーモニックの例

image.png

アセンブラと逆アセンブラ

アセンブラ
アセンブリ言語で書かれたソースファイルを機械語に変換(=アセンブル)する ツールをアセンブラ(assembler)といいます。 アセンブラとして、asコマンドやnasmコマンドが使われます。
逆アセンブラ
機械語で書かれたオブジェクトファイルをアセンブリ言語(ニーモニック)に 変換(=逆アセンブル)するツールを、逆アセンブラ(disassembler)といいます。 逆アセンブラとして、objdumpコマンドやgdbコマンドが使われます。 [objdumpによる逆アセンブル]
$ objdump –d goodbye  ※objdumpで逆アセンブル –d は逆アセンブルオプション
             ※デフォルトで .textセクションのバイナリを逆アセンブルする
goodbye: ファイル形式 elf32-i386
セクション .text の逆アセンブル:
08048054 <_start>:
8048054: b8 04 00 00 00 mov $0x4,%eax
8048059: bb 01 00 00 00 mov $0x1,%ebx
804805e: b9 76 80 04 08 mov $0x8048076,%ecx
8048063: ba 0f 00 00 00 mov $0xf,%edx
8048068: cd 80 int $0x80
804806a: b8 01 00 00 00 mov $0x1,%eax
804806f: bb 00 00 00 00 mov $0x0,%ebx
8048074: cd 80 int $0x80

実行形式ファイルの解(プログラムの骨格をつかむ)

main関数

C言語で書かれたプログラムは、main関数から処理が始まります。
実際にはスタートアップルーチン(_start)から始まり、main関数が呼び出されるため、マルウェアなどでスタートアップルーチンが書き換えられている場合にはそこから解析する必要があります。

プログラムはmain関数から他の関数が呼び出され、さらにその関数から別の関数が呼び出されるようになっています。

最初から関数の内部で何が行われているのかを詳細に解析するのではなく、
どのような関数があり、どのような呼び出し関係にあるのかを把握することが
大切です。

解析対象のバイナリファイルのソースファイルは通常入手できないので、動的解析と組み合わせてltraceコマンドでライブラリ関数をトレースしたり、objdumpコマンドでバイナリを逆アセンブルすることで解析を進めます。
[解析対象ws2ファイルを実行(動的解析)]

$ ./w2s   ※w2sを実行する
wareki? r5
2023
$ ./w2s
wareki? h17
2005
$ ./w2s
wareki? s41
1966
$ ./w2s
wareki? M44
1911
$ ./w2s
wareki? b20
error.

[readelfによるw2sファイルのELFヘッダの確認]

$ objdump -d w2s  ※objdumpで逆アセンブルする
w2s: ファイル形式 elf32-i386
セクション .text の逆アセンブル:
080483f0 <_start>:
80483f0: 31 ed xor %ebp,%ebp
80483f2: 5e pop %esi
~(略)~
080484eb <conv_wareki_to_seireki>:
80484eb: 55 push %ebp
80484ec: 89 e5 mov %esp,%ebp
~(略)~
0804856d <main>:  ※main関数から解析を始める
804856d: 8d 4c 24 04 lea 0x4(%esp),%ecx
8048571: 83 e4 f0 and $0xfffffff0,%esp
~(略)~

[nmによるw2sファイルのシンボルの確認]

$ ltrace ./w2s  ※ltraceでw2sで使用しているライブラリ関数をトレース
__libc_start_main(0x804856d, 1, 0xbf9f5f54, 0x8048610 <unfinished ...>
printf("wareki? ")
= 8
fgets(wareki? r5
"r5¥n", 32, 0xb76c95a0)
= 0xbf9f5e7c
atoi(0xbf9f5e7d, 0, 0xb7515700, 0)
= 5
printf("%d¥n", 20232023
) =
5
+++ exited (status 0) +++

関数呼び出しと戻り値
関数呼び出し
ライブラリ関数やプログラム内で定義した関数を利用することを、関数を呼び出す
といいます。ニーモニックではcall命令が使われます。
call命令の実行によって呼び出した関数に制御が移ります(ジャンプします)。
呼び出された関数側でret命令を実行すると、call命令の次に制御が戻ります
(リターンします)。
関数を呼び出すときには引き渡す値(=パラメータ)がスタックにセットされます。
プログラム内で定義した関数は、ltraceでトレースしても表示されませんが、
逆アセンブルするとcall命令で呼び出していることが分かります。
戻り値
関数の処理結果の値を戻り値といいます。戻り値は返り値やリターン値ともいいます。
戻り値は、計算した結果の値(数値)、文字列の格納されているアドレス、
成功か失敗かを示す値(成功なら0、失敗なら0以外)などです。
戻り値はeaxレジスタにセットされます。

[objdumpによるw2sのトレース]
image.png

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?