在两种情景下获得指定进程基地址的方法
发布日期:2021-05-19 04:41:12 浏览次数:16 分类:博客文章

本文共 2124 字,大约阅读时间需要 7 分钟。

情景一

最近在写一个简单调试器的时候,需要在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;
上一篇:父窗口与拥有者窗口的一点思考
下一篇:在编写简单调试器时遇到的一些问题的总结

发表评论

最新留言

很好
[***.229.124.182]2025年04月22日 20时32分14秒