リバースエンジニアリングへの道
はじめまして、出田 守と申します。
最近、情報セキュリティに興味を持ち、『リバースエンジニアリング-Pythonによるバイナリ解析技法』という本(以降、「教科書」と呼びます)を読みました。
「こんな世界があるのか!かっこいい!」と感動し、私も触れてみたいということでド素人からリバースエンジニアリングができるまでを書いていきたいと思います。
ちなみに、教科書ではPython言語が使用されているので私もPython言語を使用しています。
ここを見ていただいた諸先輩方からの意見をお待ちしております。
リバースエンジニアリングとは
ざっくりでしか私は理解していないので、まずは、「リバースエンジニアリングとは何か?」から調べましたので、自分なりのまとめを書きます。
リバースエンジニアリングとはハードウェア/ソフトウェアの構造や動作を調査・確認する行為です。
もともと製品として販売されているハードウェアの構造や動作を調べることを指していたようです。それがソフトウェアにも適用されるようになっていったようです。
製品として販売されたハードウェア/ソフトウェアは仕様書やソースコードが公開されていない限り、詳細な構造や動作を知ることができません。そこでリバースエンジニアリングを行うことでハードウェア/ソフトウェアの構造や動作を知ることができます。
現在では、マルウェア解析にリバースエンジニアリングが使われていたりします。
環境
OS: Windows10 64bit Home (日本語)
CPU: Intel® Core™ i3-6006U CPU @ 2.00GHz × 1
メモリ: 2048MB
Python: 3.6.5
私の環境は、普段Ubuntu16.04を使っていますが、ここではWindows10 64bitを仮想マシン上で立ち上げております。
ちなみに教科書では、Windowsの32bitで紹介されています。
自作デバッガへの道 - その1 「CreateProcess」
リバースエンジニアリングではデバッガの理解が不可欠です。
そこで、ここからはデバッガの基本動作を理解するために、自作デバッガを構築していきたいと思います。
デバッガはプロセスを制御下に置くことでプロセスを詳細に調べることが出来ます。
プロセスをデバッガの制御下で実行させるための1つめの方法は、デバッガからプロセスを生成することです。そのために使用する関数は「CreateProcess」です。
WindowsのAPI関数の多くには「関数名A」や「関数名W」が付いておりそれぞれ「Ansi」と「Unicode」という意味だそうです。
なので日本語環境の場合は「CreateProcessW」を使用します。
CreateProcessの仕様は以下です。
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 実行可能モジュールの名前
LPTSTR lpCommandLine, // コマンドラインの文字列
LPSECURITY_ATTRIBUTES lpProcessAttributes, // セキュリティ記述子
LPSECURITY_ATTRIBUTES lpThreadAttributes, // セキュリティ記述子
BOOL bInheritHandles, // ハンドルの継承オプション
DWORD dwCreationFlags, // 作成のフラグ
LPVOID lpEnvironment, // 新しい環境ブロック
LPCTSTR lpCurrentDirectory, // カレントディレクトリの名前
LPSTARTUPINFO lpStartupInfo, // スタートアップ情報
LPPROCESS_INFORMATION lpProcessInformation // プロセス情報
);
デバッガからプロセスを生成するには、lpApplicationName/lpCommandLine, dwCreationFlags, lpStartupInfo, lpProcessInformationが重要になります。
lpApplicationName/lpCommandLineは生成するプロセス名またはコマンドで、 dwCreationFlagsはプロセスをどのように生成するか(デバッグプロセスとして生成など)、lpStartupInfo, lpProcessInformationは構造体です。(詳しくは各リンクを参照)
typedef struct _STARTUPINFO {
DWORD cb;
LPTSTR lpReserved;
LPTSTR lpDesktop;
LPTSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
構造体定義
pythonのctypesを使用して、まずSTARTUPINFO構造体とPROCESS_INFORMATION構造体を定義します。
ctypesで構造体を定義するには以下のように書きます。
from ctypes import *
class 構造体の名前(Structure):
_fields_ = [
("メンバ名", メンバの型),
...
]
なので、それぞれ以下のようになります。
class STARTUPINFO(Structure):
_fields_ = [
("cb", DWORD),
("lpReserved", LPTSTR),
("lpDesktop", LPTSTR),
("lpTitle", LPTSTR),
("dwX", DWORD),
("dwY", DWORD),
("dwXSize", DWORD),
("dwYSize", DWORD),
("dwXCountChars", DWORD),
("dwYCountChars", DWORD),
("dwFillAttribute", DWORD),
("dwFlags", DWORD),
("wShowWindow", WORD),
("cbReserved2", WORD),
("lpReserved2", LPBYTE),
("hStdInput", HANDLE),
("hStdOutput", HANDLE),
("hStdError", HANDLE),
]
class PROCESS_INFORMATION(Structure):
_fields_ = [
("hProcess", HANDLE),
("hhThread", HANDLE),
("dwProcessId", DWORD),
("dwThreadId", DWORD),
]
プロセス生成
自作デバッガからプロセスを生成します。
kernel32.dllにCreateProcessWがあるようです。
プロセス生成のソースの一部は以下です。
...
startupinfo = STARTUPINFO()
process_information = PROCESS_INFORMATION()
startupinfo.dwFlags = 0x1 # windowを操作するためのフラグ
startupinfo.wShowWindow = 0x0 # windowをHideにするためのフラグ
startupinfo.cb = sizeof(startupinfo) # STARTUPINFO構造体のサイズ(byte)
if kernel32.CreateProcessW(exe_path, # 実行ファイルパス
None,
None,
None,
None,
0x00000001, # デバッグプロセスとして生成
None,
None,
byref(startupinfo), # 構造体への参照
byref(process_information)):
...
テスト
テストとしてexe_pathとして電卓を指定します。
import my_debugger
debugger = my_debugger.Debugger()
debugger.load("C:\\Windows\\System32\\calc.exe")
ちなみにCreateProcessAではやはり正しく私の環境では生成出来ませんでした。
今回はリバースエンジニアリングのデバッガのほんの先っちょに触れてみました。
それでも、苦戦するところが多く学ぶことも多かったです。
まとめ
- リバースエンジニアリングとは、ハードウェア/ソフトウェアの構造や動作を調査・確認するための行為のこと。
- デバッガの制御下にプロセスを生成するにはWindows API関数の「CreateProcess」を使用する。