如何在通过CreateProcess创建的挂起进程中查找LoadLibrary地址?
在挂起进程中查找LoadLibrary函数地址的实现方法
核心思路
Windows系统中,LoadLibrary(实际对应LoadLibraryA或LoadLibraryW宏)是kernel32.dll导出的函数,所有Windows进程都会加载kernel32.dll。要获取目标进程中该函数的地址,需按以下步骤操作:
- 打开目标进程并获取足够访问权限
- 枚举目标进程已加载模块,定位
kernel32.dll的基地址 - 解析
kernel32.dll的PE结构,遍历导出表找到LoadLibrary的地址
实现代码
以下是接收PROCESS_INFORMATION参数的函数实现:
#include <windows.h> #include <psapi.h> #include <tchar.h> // 获取目标进程中LoadLibraryA的地址 FARPROC GetRemoteLoadLibraryAddress(const PROCESS_INFORMATION& pi) { if (pi.hProcess == NULL) return NULL; // 提升进程权限以访问目标进程 HANDLE hToken; if (!OpenProcessToken(pi.hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return NULL; TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid); tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); CloseHandle(hToken); // 枚举进程模块,找到kernel32.dll的基地址 HMODULE hMods[1024]; DWORD cbNeeded; if (!EnumProcessModules(pi.hProcess, hMods, sizeof(hMods), &cbNeeded)) return NULL; HMODULE hKernel32 = NULL; TCHAR szModName[MAX_PATH]; for (int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) { if (GetModuleFileNameEx(pi.hProcess, hMods[i], szModName, MAX_PATH)) { TCHAR* pFileName = _tcsrchr(szModName, _T('\\')); if (pFileName && _tcsicmp(pFileName + 1, _T("kernel32.dll")) == 0) { hKernel32 = hMods[i]; break; } } } if (!hKernel32) return NULL; // 读取kernel32.dll的DOS头 IMAGE_DOS_HEADER dosHeader; if (!ReadProcessMemory(pi.hProcess, hKernel32, &dosHeader, sizeof(dosHeader), NULL)) return NULL; // 读取NT头 IMAGE_NT_HEADERS ntHeaders; LPVOID pNtHeaders = (LPVOID)((DWORD_PTR)hKernel32 + dosHeader.e_lfanew); if (!ReadProcessMemory(pi.hProcess, pNtHeaders, &ntHeaders, sizeof(ntHeaders), NULL)) return NULL; // 读取导出表 IMAGE_EXPORT_DIRECTORY exportDir; LPVOID pExportDir = (LPVOID)((DWORD_PTR)hKernel32 + ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); if (!ReadProcessMemory(pi.hProcess, pExportDir, &exportDir, sizeof(exportDir), NULL)) return NULL; // 读取导出函数的名称表、地址表、序号表 DWORD* pNames = (DWORD*)((DWORD_PTR)hKernel32 + exportDir.AddressOfNames); DWORD* pAddresses = (DWORD*)((DWORD_PTR)hKernel32 + exportDir.AddressOfFunctions); WORD* pOrdinals = (WORD*)((DWORD_PTR)hKernel32 + exportDir.AddressOfNameOrdinals); // 遍历导出表查找LoadLibraryA FARPROC pLoadLibrary = NULL; for (DWORD i = 0; i < exportDir.NumberOfNames; i++) { CHAR szFuncName[256]; LPVOID pFuncName = (LPVOID)((DWORD_PTR)hKernel32 + pNames[i]); if (!ReadProcessMemory(pi.hProcess, pFuncName, szFuncName, sizeof(szFuncName), NULL)) continue; if (_stricmp(szFuncName, "LoadLibraryA") == 0) { DWORD dwFuncAddr = pAddresses[pOrdinals[i]]; pLoadLibrary = (FARPROC)((DWORD_PTR)hKernel32 + dwFuncAddr); break; } } return pLoadLibrary; }
关键注意事项
- 编译时需链接
psapi.lib(添加编译参数/link psapi.lib) - 代码中查找的是
LoadLibraryA,若需宽字符版本LoadLibraryW,修改字符串比较部分即可 - 所有跨进程内存操作必须使用
ReadProcessMemory,因为目标进程内存空间与当前进程隔离 - 若目标进程为64位,需确保代码使用
DWORD_PTR等适配64位的指针类型
内容的提问来源于stack exchange,提问作者loaded_dypper
相关产品推荐
相关产品推荐

