他プロセスのメモリを読む(C++編)
メモリの読み込み自体はWin32APIのReadProcessMemoryを使えば良い。
ただし、WindowsVista以降ではASLR(Address space layout randomization)機能により、実行ファイルなどの配置アドレスがランダムになっている。そのため、まずはその実行ファイルのメインモジュールの先頭アドレスを取得し、そこからの相対位置指定でメモリを読むことにする。
メインモジュールの先頭アドレスはPEB(Process Enviroment Block)内のImageBaseAddressの値を見れば良い。
(その他にGetModuleHandler(NULL)でも取得できるらしい)
ターゲットプロセスのハンドルを取得
/* ターゲットプロセスのハンドルを取得 */ | |
HANDLE get_process_hundle(TCHAR* target_exe_file) { | |
// 起動している全プロセスの情報を取得 | |
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | |
if(hSnapshot == INVALID_HANDLE_VALUE) { | |
printf("Error: hSnapshot is INVALID_HANDLE_VALUE\n"); | |
return INVALID_HANDLE_VALUE; | |
} | |
PROCESSENTRY32 pe; | |
pe.dwSize = sizeof(PROCESSENTRY32); | |
// プロセスの取得 (初回のみProcess32First) | |
if(!Process32First(hSnapshot, &pe)) { | |
CloseHandle(hSnapshot); | |
printf("Error: couldn't get entry by Process32First\n"); | |
return INVALID_HANDLE_VALUE; | |
} | |
do { | |
// 実行ファイル名で比較 | |
if(_tcscmp(pe.szExeFile, target_exe_file) == 0) { | |
// 対象のプロセスを発見 | |
CloseHandle(hSnapshot); | |
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe.th32ProcessID); | |
if(hProcess) { | |
return hProcess; | |
} else { | |
return INVALID_HANDLE_VALUE; | |
} | |
} | |
} while(Process32Next(hSnapshot, &pe)); | |
// ハンドルを閉じる | |
CloseHandle(hSnapshot); | |
return INVALID_HANDLE_VALUE; | |
} |
プロセスのベースアドレスを取得
プロセスハンドルからNtQueryInformationProcessを使ってPROCESS_BASIC_INFORMATIONを取得する。
その中にPBEのベースアドレスがあるので、それを利用してReadProcessMemoryを行う
PBE構造体
既存の定義ではImageBaseAddressが載っていないので、以下の構造体定義を行う。
struct PEB_INTERNAL { | |
BYTE InheritedAddressSpace; | |
BYTE ReadImageFileExecOptions; | |
BYTE BeingDebugged; | |
BYTE Spare; | |
PVOID Mutant; | |
PVOID ImageBaseAddress; | |
PPEB_LDR_DATA Ldr; | |
PRTL_USER_PROCESS_PARAMETERS ProcessParameters; | |
BYTE Reserved4[104]; | |
PVOID Reserved5[52]; | |
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; | |
BYTE Reserved6[128]; | |
PVOID Reserved7[1]; | |
ULONG SessionId; | |
}; |
ベースアドレス取得
/* プロセスのベースアドレスを取得 */ | |
PVOID get_process_base_address(HANDLE hProcess) { | |
// ntdllのインスタンスを取得 | |
HINSTANCE hNtDll = GetModuleHandleW(L"ntdll.dll"); | |
// ntdllのモジュールを配置 | |
NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hNtDll, "NtQueryInformationProcess"); | |
RtlNtStatusToDosErrorPtr RtlNtStatusToDosError = (RtlNtStatusToDosErrorPtr)GetProcAddress(hNtDll, "RtlNtStatusToDosError"); | |
if(!NtQueryInformationProcess || !RtlNtStatusToDosError) { | |
printf("Functions cannot be located.\n"); | |
return NULL; | |
} | |
// PROCESS_BASIC_INFORMATIONを取得 | |
PROCESS_BASIC_INFORMATION pbi; | |
ULONG len; | |
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &len); | |
SetLastError(RtlNtStatusToDosError(status)); | |
if(NT_ERROR(status) || !pbi.PebBaseAddress) { | |
printf("NtQueryInformationProcess(ProcessBasicInformation) failed.\n"); | |
return NULL; | |
} | |
// PBE(Process Environment Block)の読み込み | |
SIZE_T bytesRead = 0; | |
PEB_INTERNAL peb; | |
if(!ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), &bytesRead)) | |
{ | |
printf("Reading PEB failed.\n"); | |
return NULL; | |
} | |
return peb.ImageBaseAddress; | |
} |
実際にメモリ内容を取得してみる
int main(void) { | |
HANDLE hProcess = get_process_hundle(L"test.exe"); | |
if(hProcess == INVALID_HANDLE_VALUE) { | |
exit(1); | |
} | |
PVOID pBaseAddress = get_process_base_address(hProcess); | |
if(pBaseAddress == NULL) { | |
exit(1); | |
} | |
// 整数値に変換 | |
uint64_t iBaseAddress = reinterpret_cast<uintptr_t>(pBaseAddress); | |
// テスト読み込み | |
int data = 0; | |
SIZE_T bytesRead = 0; | |
ReadProcessMemory(hProcess, (PVOID)(iBaseAddress + 0x109A6E0), &data, 4, &bytesRead); | |
printf("data is %d\n", data); | |
} |