動的解析
動的解析
動的解析(dynamic analysis)は、解析対象であるプログラムを実際に実行して 解析する手法です。 プログラムを実行することで生成されたプロセスが、画面に表示するメッセージ、 アクセスするファイル、ネットワーク通信で送受信されるパケットなどの 挙動や使用されるリソースを確認します。 動的解析ではプログラム内のコードを詳細には分析せず、プログラムを ブラックボックスと捉えて、プロセスの振る舞いを解析します。解析環境
解析対象のプログラムを実行することで、既存のファイルが破壊されたり、 情報が外部に送信されて漏洩したりする恐れがあります。そのため、動的解析は、ネットワークから隔離した環境を準備したり、仮想環境やサンドボックスを利用するなど、安全な環境で実行する必要があります。動的解析で使用するコマンド
動的解析で使用するコマンド
動的解析では解析対象のプログラムを実行して、作成されるファイルや、 ネットワーク通信のパケットを取得して、さらに解析を進めます。 動的解析では次のようなコマンドを使用します。⚫ ps
⚫ pstree
⚫ top
⚫ tcpdump
⚫ strace
⚫ ltrace
プログラムとプロセス
プログラム
プログラム(program)とは、コンピュータに目的の処理を実行させるための ソフトウェアです。プログラムは一連の処理手順を実行するための命令とデータから 構成されます。処理手順は、コンピュータのCPUが唯一理解できる機械語で書かれています。
プログラムは実行形式ファイルとして保存されます。
プロセス
プログラムはOSによってメモリに読み込まれて、プロセス(process)として 実行されます。つまり、プロセスは実行中のプログラムのことです。ある時点でCPUで実行されているプロセスは1つのみです。複数のプロセスを
並行して実行するマルチプロセスでは、OSはスケジューラ機能によって、
処理するプロセスを短い時間(ミリ秒単位)で切り替えます。
OSはプロセス生成時に、プロセスの識別子であるPID(Process ID)という
固有の正の整数値をプロセスに割り当てます。
プロセスは終了時に正の整数値で返り値(終了ステータス)を返します。
返り値は正常終了時には0、異常終了時には0以外の値であるのが一般的です。
[プロセスの終了ステータス]
カーネル
カーネル
カーネル(kernel)はOSの本体であるプログラムです。カーネルはシステムの 起動時点でメモリに読み込まれて、システムが停止するまで常駐して動き続けます。 カーネルは、プロセス管理、メモリ管理、スケジューラ、ハードウェアの制御、 ファイルの入出力、ネットワーク通信などの処理を実行します。ps / pstree コマンド
psコマンド
実行中のプロセスは、psコマンドで表示できます。使用している端末(制御端末) 以外のプロセスについても表示する場合にはaオプション、プロセスの所有者 (実行ユーザ)を表示する場合にはuオプション、端末を持たないシステムプロセス (デーモン)も表示する場合にはxオプションを指定します。pstreeコマンド
一番最初に起動されるプロセスは、PIDが1である初期化プロセスです。 初期化プロセスは、initやsystemdです(ディストリビューションによって異なる)。 プロセスは既存のプロセスから生成(フォーク)されます。既存のプロセスを親プロセス、生成されたプロセスを子プロセスといいます。 すべてのプロセスは初期化プロセスを頂点とするプロセスツリーのどこかに位置付けられます。 プロセスツリーは、psコマンドのfオプションやpstreeコマンドで表示できます。標準入出力
標準入出力
プロセスに、処理対象のファイルを指定しない場合には、入出力の対象には 標準入出力(standard input / output)デバイスという仮想的なデバイスが使用されます。 デフォルトでは、標準入力にはキーボード、標準出力と標準エラー出力には画面が割り当てられています。標準入出力ファイル
Linuxはデバイスファイルを通して、デバイスとの入出力をします。標準入出力を取り扱うデバイスファイルを標準入出力ファイルといいます。プロセスを起動すると 標準入出力ファイルは自動的にオープンされていて利用できるようになっています。 標準入出力に対するデバイスファイルとして、標準入力は/dev/stdin、標準出力は /dev/stdout、標準エラー出力は /dev/stderrが使われます。ファイルディスクリプタ
ファイルディスクリプタファイルをオープンすると、ファイルの識別子としてファイルディスクリプタ(filedescriptor、ファイル記述子)という0から始まる整数値が割り当てられます。 標準入力ファイルには0、標準出力ファイルには1、標準エラー出力ファイルは2が ファイルディスクリプタとして割り当てられます。システムコール
システムコール
システムコール(system calls)は、カーネルがプログラムからのリクエストを 受け付ける仕組みです。プロセスがシステムのリソースを利用するためには、 システムコールを発行するのが唯一の手段です。 システムコールには、システムコール番号とシステムコール名が割り当てられています。システムコールによって、プロセス生成、ファイル入出力、ネットワーク通信など
OSの管理するリソースを使用するオペレーションを実行します。
ausyscallコマンド
auditdパッケージがインストールされていると、ausyscallコマンドを使用する ことができます。ausyscallコマンドで、システムコール番号や名称を検索する ことができます。[ausyscallコマンドの実行例]
カーネルモードとユーザモード
CPUのモード
計算や文字列処理などの命令は、CPUのモードがユーザモード(user mode)で 実行されます。システムコールを発行すると、CPUに対する割り込みが発生します。 これによって、CPUのモードがカーネルモード(kernel mode)に移行します。 システムコールの処理が終わるとユーザモードに戻って処理が継続されます。 プロセスがユーザモードとカーネルモードでの処理に費やした時間の総計は、 timeコマンドで計測できます。[timeコマンドの実行例]
straceコマンド
straceコマンド
straceコマンドは、プロセスが発行するシステムコールをわかりやすく表示する コマンドです 。実行するプログラムや実行中のプロセスをトレースして、 システムコールの発行状況を表示します。 straceコマンドのオプションは下表の通りです。[straceコマンドの実行例①]
$ ./hello ※helloは標準出力に“hello,world”と表示するプログラムである
hello,world
$ strace ./hello ※straceでhelloのシステムコールをトレースする
execve("./hello", ["./hello"], [/* 19 vars */]) = 0
write(1, "hello,world¥n", 12hello,world
) = 12 ※ファイルディスクリプタは1(= 標準出力)
exit(0) = ?
+++ exited with 0 +++ ※終了ステータスは0(= 正常終了)
[straceコマンドの実行例②]
$ ./strace -P /var/log/wtmp last ※/var/log/wtmpファイルにアクセスするシステムコールのみ
openat(AT_FDCWD, "/var/log/wtmp", O_RDONLY) = 3 ※ファイルディスクリプタは3
fstat(3, {st_mode=S_IFREG|0664, st_size=67584, ...}) = 0
read(3,
"¥2¥0¥0¥0¥0¥0¥0¥0~¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0"..., 4096)
= 4096
fstat(3, {st_mode=S_IFREG|0664, st_size=67584, ...}) = 0
lseek(3, 65536, SEEK_SET) = 65536
read(3,
"¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0¥0"...,
4096) = 2048
~(略)~
wtmp begins Thu Dec 6 12:57:33 2018
close(3) = 0
+++ exited with 0 +++
ライブラリ関数
ライブラリとライブラリ関数
ライブラリは、よく使われる関数がまとめられたものです。ライブラリにまとめられている関数をライブラリ関数といいます。
ライブラリ関数はプログラムが利用します。ライブラリ関数は実行形式ファイルを作成する時にリンクされます。
ライブラリ関数には、内部でシステムコールを発行する関数と、システムコールを発行せずに実装されている関数があります。
標準ライブラリ関数
プログラミング言語の規格で定められているライブラリ関数を、標準ライブラリ関数 といいます。C言語の標準ライブラリはlibcです。 libcとして、GNU Projectの実装したglibcがよく使われます。 C言語で標準ライブラリ関数を使用するためには、ライブラリ関数が宣言された ヘッダファイルをインクルードする必要があります。ltraceコマンド
ltraceコマンド
ltraceコマンドは、プロセスが呼び出すダイナミックリンクライブラリ関数を 表示するコマンドです(スタティックリンクは表示されない)。 実行するプログラムや実行中のプロセスをトレースして、ライブラリ関数の 呼び出しをわかりやすく表示します。 ltraceコマンドのオプションは下表の通りです。#include <stdio.h> ※putchar関数、printf関数、fgets関数を使うためにインクルードが必要
#include <stdlib.h> ※atoi関数を使うためにインクルードが必要
void star(int n)
{ int i;
for (int i = 0; i < n; ++i) {
putchar('*');
}
putchar('¥n');
}
int main()
{ char buf[32];
int n;
printf("input number ===> ");
fgets(buf, 32, stdin);
n = atoi(buf);
star(n);
}
[ltraceコマンドの実行例]
$ ltrace ./star
__libc_start_main(0x80491f0, 1, 0xffb5ec04, 0x8049250 <unfinished ...>
printf("input number ===> ") = 18
fgets(input number ===> 5
"5¥n", 32, 0xf7ed25c0) = 0xffb5eb44
atoi(0xffb5eb44, 32, 0xf7ed25c0, 0xf7d26fcb) = 5
putchar(42, 0, 10, 5) = 42
putchar(42, 0, 42, 5) = 42
putchar(42, 0, 42, 5) = 42
putchar(42, 0, 42, 5) = 42
putchar(42, 0, 42, 5) = 42
putchar(10, 0, 42, 5*****
) = 10
+++ exited (status 0) +++
[sraceコマンドの実行でライブラリ関数とシステムコールの対応を確認]
$ strace ./star
~(略)~
write(1, "input number ===> ", 18input number ===> ) = 18
read(0, 5
"5¥n", 1024) = 2
write(1, "*****¥n", 6*****
) = 6
exit_group(0) = ?
+++ exited with 0 +++
[ltraceが使えない場合]
$ ltrace ./hello
Couldn’t find .dynsym or .dynstr in “/proc/18178/exe”
hello,world
$ file hello_static
hello_static: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux),
statically linked, for GNU/Linux 3.2.0, not stripped
$ ltrace ./hello_static
Couldn’t find .dynsym or .dynstr in “/proc/18183/exe”
hello,world
$ file hello_dynamic
hello_dynamic: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0,
not stripped
$ ltrace ./hello_dynamic
__libc_start_main(0x8049180, 1, 0xbfcc3184, 0x80491b0 <unfinished ...>
puts("hello,world"hello,world
) = 12
exit(0 <no return ...>
+++ exited (status 0) +++