在两种情景下获得指定进程基地址的方法

情景一

最近在写一个简单调试器的时候,需要在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;
原文地址:https://www.cnblogs.com/revercc/p/13287072.html