C++ 获取 64 位应用程序的模块基地址
Posted
技术标签:
【中文标题】C++ 获取 64 位应用程序的模块基地址【英文标题】:C++ Get Module Base Address for 64bit Application 【发布时间】:2014-10-26 11:28:09 【问题描述】:我最近一直在玩内存读取/编辑,并遇到了一个我认为是由于 64 位应用程序的问题,我也尝试在 64 位下编译。 我对这个脚本使用 32 位应用程序没有任何问题,但是当我在纸牌上尝试这个时,它无法获得基地址,然后无法锻炼正确的偏移量等。 这是脚本:
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
using namespace std;
DWORD dwGetModuleBaseAddress(DWORD dwProcessID, TCHAR *lpszModuleName)
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessID);
DWORD dwModuleBaseAddress = 0;
if (hSnapshot != INVALID_HANDLE_VALUE)
MODULEENTRY32 ModuleEntry32 = 0 ;
ModuleEntry32.dwSize = sizeof(MODULEENTRY32);
if (Module32First(hSnapshot, &ModuleEntry32))
do
if (_tcscmp(ModuleEntry32.szModule, lpszModuleName) == 0)
dwModuleBaseAddress = (DWORD)ModuleEntry32.modBaseAddr;
break;
while (Module32Next(hSnapshot, &ModuleEntry32));
CloseHandle(hSnapshot);
return dwModuleBaseAddress;
int main()
DWORD address = 0xBAFA8;
HWND hwnd = FindWindow(0, L"Solitaire");
DWORD pid;
int data = 0;
int newData = 0;
if (hwnd)
GetWindowThreadProcessId(hwnd, &pid);
HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
if (phandle)
DWORD base = dwGetModuleBaseAddress(pid, L"Solitaire.exe");
cout << "Base: " << (void*)base << endl;
ReadProcessMemory(phandle, (LPCVOID)(base + address), &data, sizeof(data), 0);
else
cout << "Couldnt get handle" << endl;
else
cout << "Couldn't find window" << endl;
cin.get();
return 0;
问题可能是我使用的函数使用了 MODULE32,但是我尝试了其他函数(使用 EnumModules)仍然无法返回地址。
任何想法如何获得 64 位应用程序的基地址或让这个脚本工作?
谢谢
【问题讨论】:
这可能有用(注意讨论引用目标程序和寻找程序的位数)***.com/questions/17412545/…modBaseAddr
是一个指针。停止将其转换为 32 位整数。您知道在 64 位 Windows 上指针是 64 位宽的吗?您将无法从在 32 位 WOW64 模拟器下运行的 32 位进程查看 64 位进程。所以以 64 位运行是必不可少的。
【参考方案1】:
好吧,您的代码永远不会成功运行,因为您在谈论 64 位,但您使用 DWORD 作为基址! Solitare可能有一个 32 位地址,但你不能保证,你永远不应该假设它。
此功能有效。它只需要相关进程的进程 ID,并且假定您需要该进程的基地址。即不是它的 DLL 之一。如果您不想要拥有进程,则需要使用 for (int i=0; i<moduleCount; i++ ) // do something with moduleArray[i]
之类的内容遍历 moduleArray
,然后检查模块文件名。
如果您只想要启动进程(可执行文件),您可以假设它是数组中的第一个元素。
DWORD_PTR GetProcessBaseAddress( DWORD processID )
DWORD_PTR baseAddress = 0;
HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
HMODULE *moduleArray;
LPBYTE moduleArrayBytes;
DWORD bytesRequired;
if ( processHandle )
if ( EnumProcessModules( processHandle, NULL, 0, &bytesRequired ) )
if ( bytesRequired )
moduleArrayBytes = (LPBYTE)LocalAlloc( LPTR, bytesRequired );
if ( moduleArrayBytes )
unsigned int moduleCount;
moduleCount = bytesRequired / sizeof( HMODULE );
moduleArray = (HMODULE *)moduleArrayBytes;
if ( EnumProcessModules( processHandle, moduleArray, bytesRequired, &bytesRequired ) )
baseAddress = (DWORD_PTR)moduleArray[0];
LocalFree( moduleArrayBytes );
CloseHandle( processHandle );
return baseAddress;
【讨论】:
感谢那个工作脚本,我想这只能在 64 位上工作并且没有通用的解决方案? 我确实将代码编译为 64 位。我真的不知道你想对图像做什么。您可能可以将其编译为 32 位并对其进行处理,但您必须为任何指针类型(如基地址和任何偏移量)显式使用 64 位值。但是 64 位程序可以更轻松地访问 32 位或 64 位图像。【参考方案2】:正如大卫所暗示的那样,这行是错误的:
dwModuleBaseAddress = (DWORD)ModuleEntry32.modBaseAddr;
因为您将 64 位指针(在 64 位应用程序上)投射到 DWORD
截断值(来自调试器的屏幕截图,64 位应用程序打开 64 位进程)
您应该选择指针类型(DWORD_PTR
也适用于便携式解决方案)。
【讨论】:
为什么是ULONGLONG
?将指针存储在指针数据类型中。 unsigned char*
似乎很难被击败。
@DavidHeffernan 这是图像库的 64 类型。无论如何,DWORD_PTR 可能更便携。
所以我可以使用原来的功能,只需将其更改为强制转换 DWORD_PTR?
这将解决这个特定的截断问题,是的。 DWORD_PTR 被定义为 32 位代码上的 32 位指针和 64 位代码上的 64 位指针,因此它也应该是可移植的。我相信其他问题会阻止你从 32 位应用程序打开 64 位进程,但除此之外你应该很好。
我确实将其更改为强制转换 DWORD_PTR 并更改了 'DWORD dwModuleBaseAddress = 0;'到 DWORD_PTR 并且仍然无法让原始功能发挥作用?【参考方案3】:
基于@Nostromoo 给出的示例的示例控制台应用程序
应该在 x86/x64 上工作
#include "stdafx.h"
#include <windows.h>
#include <psapi.h>
#include <vector>
#define MODULE_NAME L"TestModule"
#define WINDOW_NAME L"TestConsole"
bool GetBaseModuleInfo(MODULEINFO* baseModuleInfo);
HMODULE GetProcessBaseAddressFromProcessHandle(HANDLE processHandle);
bool EnumProcessModulesPlattform(HANDLE processHandle, HMODULE* lphModule, DWORD cb, LPDWORD bytesRequired);
DWORD GetProcessIdentifier(const wchar_t* windowName);
int main()
SetConsoleTitle(WINDOW_NAME);
// Wait for the title to be set otherwise the window might not be found.
Sleep(200);
MODULEINFO baseModuleInfo;
bool success = GetBaseModuleInfo(&baseModuleInfo);
if (success == false)
wprintf(MODULE_NAME L"main() - GetBaseModuleInfo() failed.\n");
std::getchar();
return 1;
wchar_t buffer[2000];
#ifdef _WIN64
swprintf_s(buffer, sizeof(buffer) / sizeof(*buffer), MODULE_NAME L"main() - baseAddress: '%llX' size: '%lX'\n", baseModuleInfo.lpBaseOfDll, baseModuleInfo.SizeOfImage);
#elif _WIN32
swprintf_s(buffer, sizeof(buffer) / sizeof(*buffer), MODULE_NAME L"main() - baseAddress: '%lX' size: '%lX'\n", baseModuleInfo.lpBaseOfDll, baseModuleInfo.SizeOfImage);
#endif
wprintf(buffer);
std::getchar();
return 0;
bool GetBaseModuleInfo(MODULEINFO* baseModuleInfo)
DWORD processID = GetProcessIdentifier(WINDOW_NAME);
if (processID == NULL)
wprintf(MODULE_NAME L"GetBaseModuleInfo() - GetProcessIdentifier() returned NULL.\n");
return false;
HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
if (processHandle == NULL)
wprintf(MODULE_NAME L"GetBaseModuleInfo() - OpenProcess() returned NULL.\n");
return false;
HMODULE baseAddress = GetProcessBaseAddressFromProcessHandle(processHandle);
if (baseAddress == NULL)
wprintf(MODULE_NAME L"GetBaseModuleInfo() - GetProcessBaseAddressFromProcessHandle() returned NULL.\n");
return false;
bool resultGMI = GetModuleInformation(processHandle, baseAddress, baseModuleInfo, sizeof(*baseModuleInfo));
// NOTE: GetModuleInformation doesn't fail even if the baseAddress is wrong. Maybe it uses the nearest module?
if (resultGMI == false)
wprintf(MODULE_NAME L"GetBaseModuleInfo() - GetModuleInformation() failed.\n");
return false;
CloseHandle(processHandle);
return true;
HMODULE GetProcessBaseAddressFromProcessHandle(HANDLE processHandle)
DWORD bytesRequired;
if (EnumProcessModulesPlattform(processHandle, NULL, 0, &bytesRequired) == false)
wprintf(MODULE_NAME L"GetProcessBaseAddressFromProcessHandle() - EnumProcessModules() error.\n");
return NULL;
if (bytesRequired == NULL)
wprintf(MODULE_NAME L"GetProcessBaseAddressFromProcessHandle() - EnumProcessModules() returned 0 bytesRequired.\n");
return NULL;
unsigned int moduleCount = bytesRequired / sizeof(HMODULE);
std::vector<HMODULE> hModules(moduleCount, 0);
if (EnumProcessModulesPlattform(processHandle, &hModules[0], bytesRequired, &bytesRequired) == false)
wprintf(MODULE_NAME L"GetProcessBaseAddressFromProcessHandle() - EnumProcessModules(moduleArray) error.\n");
return NULL;
// The first item is always the baseModule.
HMODULE baseAddress = hModules[0];
return baseAddress;
bool EnumProcessModulesPlattform(HANDLE processHandle, HMODULE* lphModule, DWORD cb, LPDWORD bytesRequired)
#ifdef _WIN64
bool returnValue = EnumProcessModulesEx(processHandle, lphModule, cb, bytesRequired, LIST_MODULES_64BIT);
#elif _WIN32
bool returnValue = EnumProcessModulesEx(processHandle, lphModule, cb, bytesRequired, LIST_MODULES_32BIT);
#endif
DWORD lastError = GetLastError();
if (lastError != 0)
wprintf(MODULE_NAME L"EnumProcessModulesPlattform() - lastError != 0 EnumProcessModulesEx().\n");
return false;
return returnValue;
DWORD GetProcessIdentifier(const wchar_t* windowName)
HWND windowHandle = FindWindow(NULL, windowName);
if (windowHandle == NULL)
wprintf(MODULE_NAME L"GetProcessIdentifier() - Could not find hwnd of '%s'.\n", windowName);
return NULL;
DWORD processID;
GetWindowThreadProcessId(windowHandle, &processID);
return processID;
【讨论】:
怎么想 -sizeof(baseModuleInfo.lpBaseOfDll)
的价值是什么?基于什么 ?它不是在编译时已经定义了吗?
@RbMm 如果它是在 x86 上编译的,它是 4 字节,如果它是在 x64 上编译的,我是 8 字节。它是在编译时定义的......它可以与定义有关,但这部分只是为了展示该函数是如何使用的。
是的。所以这首先必须不是在运行时而是在编译时由#if
块选择。其次,如果您编译为 32 位代码 - 您不会获得 64 位模块基址。第三,您的解决方案效率不高,也不是最好的
我不明白你所说的“如果你编译为 32 位代码 - 你不会得到 64 位模块基址”是什么意思。高效:我不希望它在程序中经常运行,所以它足够高效。
我的意思是32位代码不能得到64位模块的基地址。 EnumProcessModules
在这种情况下失败以上是关于C++ 获取 64 位应用程序的模块基地址的主要内容,如果未能解决你的问题,请参考以下文章
将 32 位 C++ 代码移植到 64 位 - 值得吗?为啥?
在 MS VC 2013 Express 中将 C++ dll 从 32 位转换为 64 位
Linux x64 -- 内核程序(驱动程序)读取任意进程数据实现