64bitアプリケーションのPEBからImageBaseを取得してみる
64bitアプリケーションのPEBからImageBaseを取得してみる
Windowsにおいて各プロセスはエグゼクティブプロセス(Executive Process:EPROCESS)構造体によって表現される。 この構造体にはプロセスに関する多くの情報が保持されており、たとえば、複数のスレッドを持つプロセスであれば各スレッド情報を持つエグゼクティブスレッド(Executive Thread:ETHREAD)構造体へのポインタなどがある。 ただしEPROCESS構造体とその関連構造体の多くはシステムのアドレス空間上に存在する。 ではユーザーモードのコードからプロセス情報を得るにはどうすればよいだろうか。 じつはプロセス環境ブロック(Process Enciroment Block:PEB)という構造体がユーザーモードのアドレス空間に用意されており、ここからプロセスに関する種々のデータを取得することができる。
今回は64bitプロセスのPEBからImageBaseを取得してみる。 ImageBaseとはその名の通りイメージファイルがロードされるアドレスであり、例えばPEファイルならImageBaseからの2バイトはMZがくるといった具合だ。 このImageBase+RVAでロードされたPEファイルの各種データにアクセスすることができる。
WinDbgで確認してみる
まずはWinDbgでnotepad.exeのPEBからImageBaseを確認してみる。 次のコマンドでPEB構造体のフォーマットを出力できる。
0:007> dt _peb ntdll!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 BitField : UChar +0x003 ImageUsesLargePages : Pos 0, 1 Bit +0x003 IsProtectedProcess : Pos 1, 1 Bit +0x003 IsImageDynamicallyRelocated : Pos 2, 1 Bit +0x003 SkipPatchingUser32Forwarders : Pos 3, 1 Bit +0x003 IsPackagedProcess : Pos 4, 1 Bit +0x003 IsAppContainer : Pos 5, 1 Bit +0x003 IsProtectedProcessLight : Pos 6, 1 Bit +0x003 IsLongPathAwareProcess : Pos 7, 1 Bit +0x004 Padding0 : [4] UChar +0x008 Mutant : Ptr64 Void +0x010 ImageBaseAddress : Ptr64 Void // 省略
オフセット0x10のImageBaseAddressが求めたいImageBaseだ。
これはただPEB構造体の構造を出力しているだけでnotepad.exeのPEBではない。 notepad.exeのPEBを出力するにはPEBのアドレスを指定する必要がある。 次のようにすればよい。
0:007> r $peb $peb=000000b5755da000 0:007> dt _peb @$peb ntdll!_PEB +0x000 InheritedAddressSpace : 0 '' +0x001 ReadImageFileExecOptions : 0 '' +0x002 BeingDebugged : 0x1 '' +0x003 BitField : 0x84 '' +0x003 ImageUsesLargePages : 0y0 +0x003 IsProtectedProcess : 0y0 +0x003 IsImageDynamicallyRelocated : 0y1 +0x003 SkipPatchingUser32Forwarders : 0y0 +0x003 IsPackagedProcess : 0y0 +0x003 IsAppContainer : 0y0 +0x003 IsProtectedProcessLight : 0y0 +0x003 IsLongPathAwareProcess : 0y1 +0x004 Padding0 : [4] "" +0x008 Mutant : 0xffffffff`ffffffff Void +0x010 ImageBaseAddress : 0x00007ff7`8dec0000 Void // 省略
求めたいImageBaseはPEB構造体のImageBaseAddressから0x00007ff7`8dec0000であることがわかる。
dcコマンドでこのアドレスの中身を見てみる。
0:007> dc 0x00007ff7`8dec0000 00007ff7`8dec0000 00905a4d 00000003 00000004 0000ffff MZ.............. 00007ff7`8dec0010 000000b8 00000000 00000040 00000000 ........@....... 00007ff7`8dec0020 00000000 00000000 00000000 00000000 ................ 00007ff7`8dec0030 00000000 00000000 00000000 000000f8 ................ 00007ff7`8dec0040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th 00007ff7`8dec0050 70207369 72676f72 63206d61 6f6e6e61 is program canno 00007ff7`8dec0060 65622074 6e757220 206e6920 20534f44 t be run in DOS 00007ff7`8dec0070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......
確かにImageBaseが取得できているようだ。
ちなみに!pebコマンドを使えば一発で求まる。
0:007> !peb PEB at 000000b5755da000 InheritedAddressSpace: No ReadImageFileExecOptions: No BeingDebugged: Yes ImageBaseAddress: 00007ff78dec0000 // 省略
コードで確認してみる
次にコードでnotepad.exeのPEBからImageBaseを求めてみる。
#include <windows.h> #include <winternl.h> // PROCESS_BASIC_INFORMATION #include <iostream> #include <cstdlib> // pause() #include <sstream> // std::stringstream #include<stdio.h> std::stringstream ss; typedef NTSTATUS(WINAPI *fpNtQueryInformationProcess)( HANDLE ProcessHandle, DWORD ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength); PROCESS_BASIC_INFORMATION* FindRemotePEB(HANDLE hProcess){ HMODULE hNTDLL = LoadLibraryA("ntdll"); fpNtQueryInformationProcess NtQueryInformationProcess = (fpNtQueryInformationProcess)GetProcAddress( hNTDLL, "NtQueryInformationProcess"); // find remote PEB PROCESS_BASIC_INFORMATION *pBasicInfo = new PROCESS_BASIC_INFORMATION(); // get PROCESS_BASIC_INFORMATION NtQueryInformationProcess( hProcess, 0, pBasicInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL); // debug std::cout << "PEBase: " << pBasicInfo->PebBaseAddress << std::endl; return (PROCESS_BASIC_INFORMATION*)pBasicInfo; } DWORD64 ReadRemoteImageBase(HANDLE hProcess, PROCESS_BASIC_INFORMATION *pBasicInfo){ // get ImageBase offset address from the PEB DWORD64 pebImageBaseOffset = (DWORD64)pBasicInfo->PebBaseAddress + 0x10 ; // debug ss << std::hex << pebImageBaseOffset; std::cout << "pebImageBaseOffset: " << ss.str() << std::endl; // get ImageBase DWORD64 ImageBase = 0; SIZE_T ReadSize = 8; SIZE_T bytesRead = NULL; ReadProcessMemory(hProcess, (LPCVOID)pebImageBaseOffset, &ImageBase, ReadSize, &bytesRead); return ImageBase; } int main() { // open 64bit notepad.exe std::cout << "Creating process\r\n"; LPSTARTUPINFOA pStartupInfo = new STARTUPINFOA(); LPPROCESS_INFORMATION pProcessInfo = new PROCESS_INFORMATION(); CreateProcessA( 0, "notepad.exe", 0, 0, 0, 0, 0, 0, pStartupInfo, pProcessInfo); PROCESS_BASIC_INFORMATION *pBasicInfo = (PROCESS_BASIC_INFORMATION*)FindRemotePEB(pProcessInfo->hProcess); DWORD64 ImageBase = ReadRemoteImageBase(pProcessInfo->hProcess, pBasicInfo); // debug printf("ProcessId:%d\n", pProcessInfo->dwProcessId); printf("ImageBase:%p\n", ImageBase); system("PAUSE"); TerminateProcess(pProcessInfo->hProcess, NULL); }
プロセスハンドルからNtQueryInformationProcessでPROCESS_BASIC_INFORMATIONを取得する。 その中にPEBへのポインタがあるので、0x10加算したアドレスをReadProcessMemoryで読み取ればそれがImageBaseのアドレスというわけだ。
参考サイト
https://riosu.hateblo.jp/entry/2013/09/06/001208
https://www.microsoftpressstore.com/articles/article.aspx?p=2233328