情景一
最近在写一个简单调试器的时候,需要在CreateProcess()创建调试进程后获得程序的基地址。
一开始我是在CreateProcess()前,利用内存映射文件来加载指定可执行文件,然后从文件的NT文件头中读取程序的加载基地址。但是我忽略了有可能程序发生基址随机化,这样的话就获得的基地址就不准确了。
解决方法
可以在CreateProcess()创建进程前利用内存映射文件读NT文件头中的入口地址的RVA(入口地址随基地址变化,但是入口地址的RVA是不变的)。然后在CreateProcess()创建调试进程后,获取调试进程的进程环境块(CONTEXT),context . eax即为其程序的入口地址。然后减去入口地址的RVA即为真正的基地址。
HANDLE hFile; //文件句柄
HANDLE hMapFile; //内存映射文件对象句柄
LPVOID lpMapFile; //内存映射文件指针
hFile = CreateFile(ExeName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
lpMapFile = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
IMAGE_DOS_HEADER* lpDosMZ = (IMAGE_DOS_HEADER*)lpMapFile;
IMAGE_NT_HEADERS* lpNT = (IMAGE_NT_HEADERS*)lpMapFile;
lpNT = (IMAGE_NT_HEADERS*)((BYTE*)lpNT + lpDosMZ->e_lfanew);
dwEntryPointRVA = lpNT->OptionalHeader.AddressOfEntryPoint; //获得入口地址的RVA
UnmapViewOfFile(lpMapFile); //撤销映射
CloseHandle(hMapFile); //关闭内存映射对象句柄
CloseHandle(hFile); //关闭文件
//创建被调试程序进程
CreateProcess(
ExeName,
NULL,
NULL,
NULL,
FALSE, // 不可继承
DEBUG_ONLY_THIS_PROCESS | DEBUG_PROCESS, // 调试模式启动 (DEBUG_ONLY_THIS_PROCESS标志表示其不能调试进程如果被调试的话,此新进程不会成为其调试进程的调试对象)
NULL,
NULL,
&si,
&pi);
//获取入口地址
stContext.ContextFlags = CONTEXT_ALL;
GetThreadContext(pi.hThread, &stContext);
dwEntryPoint = stContext.Eax;
//获取基地址
hInstance = dwEntryPoint - dwEntryPointRVA;
情景二
如果要调试一个已运行的进程需要对其进行附加,附加之后需要获得其基地址。(进一步获得其入口地址)
解决方法
可以先创建一个进程快照,获得指定进程的PID之后。在建立一个基于这个进程的模块快照,然后获得此进程第一个模块的基地址,此基地址即为这个进程的基地址。然后在通过基地址调用ReadProcessMemory()读取NT头获得入口地址RVA,在与基地址相加即为此进程的入口地址。
//dwData是指定进程的PID,通过创建进程快照获得
//进程模块快照,(获得已运行进程基地址)
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwData);
Module32First(hModuleSnap, &me32);
dwInstance = (DWORD)me32.modBaseAddr; //进程基地址
CloseHandle(hModuleSnap);
//获取入口地址RVA
DWORD lpNT; //指向NT头
ReadProcessMemory(hIsDebuggedProcess, (LPVOID)(dwInstance + 0x3c), &lpNT, 4, NULL);
lpNT = lpNT + dwInstance;
ReadProcessMemory(hIsDebuggedProcess, (LPVOID)(lpNT + 0x28), &dwEntryPointRVA, 4, NULL);
//获取入口地址
dwEntryPoint = dwInstance + dwEntryPointRVA;