内存进程读写可以让我们访问其他进程的内存空间并读取或修改其中的数据。这种技术通常用于各种调试工具、进程监控工具和反作弊系统等场景。在Windows系统中,内存进程读写可以通过一些API函数来实现,如OpenProcess、ReadProcessMemory和WriteProcessMemory等。这些函数提供了一种通用的方式来访问其他进程的内存,并且可以用来读取或写入不同类型的数据,例如整数、字节集、浮点数等。
在开始编写内存读者功能之前我们先来实现一个获取特定进程内特定模块基址的功能,该功能的实现分为两部分首先我们封装一个GetProcessModuleHandle函数,该函数用户可传入一个进程PID以及需要获取的进程内的模块名,此时会通过循环的方式找到所需返回的模块并返回该模块的moduleEntry.hModule基址,由于使用了进程快照函数所以在使用时需要引入TlHelp32.h库。- // 根据PID模块名(需要写后缀.dll)获取模块入口地址
- HMODULE GetProcessModuleHandle(DWORD pid, CONST TCHAR* moduleName)
- {
- MODULEENTRY32 moduleEntry;
- HANDLE handle = NULL;
- // 获取进程快照中包含在th32ProcessID中指定的进程的所有的模块
- handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
- if (!handle)
- {
- CloseHandle(handle);
- return NULL;
- }
- ZeroMemory(&moduleEntry, sizeof(MODULEENTRY32));
- moduleEntry.dwSize = sizeof(MODULEENTRY32);
- if (!Module32First(handle, &moduleEntry))
- {
- CloseHandle(handle);
- return NULL;
- }
- do
- {
- if (_tcscmp(moduleEntry.szModule, moduleName) == 0)
- {
- return moduleEntry.hModule;
- }
- } while (Module32Next(handle, &moduleEntry));
- CloseHandle(handle);
- return 0;
- }
复制代码 有了上述读取模块基址的函数,接着就是要封装实现GetProcessModuleBase函数,该函数接收两个参数,分别是进程名以及模块名,并返回该模块在指定进程中的句柄。如果指定的模块名称不存在于所给进程的模块列表中,函数会返回NULL。- // 返回ExeName进程中指定Dll的基址
- DWORD GetProcessModuleBase(string ExeName, string DllName)
- {
- HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 进程快照句柄
- PROCESSENTRY32 process = { sizeof(PROCESSENTRY32) }; // 存放进程快照的结构体
- // 遍历进程
- while (Process32Next(hProcessSnap, &process))
- {
- // 寻找ExeName指定进程 char* 转 string
- string s_szExeFile = process.szExeFile;
- if (s_szExeFile == ExeName)
- {
- HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
- if (hProcess != NULL)
- {
- // 寻找DllName并返回DWORD类型基址
- return (DWORD)GetProcessModuleHandle(process.th32ProcessID, DllName.c_str());
- }
- }
- }
- return 0;
- }
复制代码 参数说明:
- hProcess:指定进程的句柄,通常可以通过OpenProcess函数获取。
- lpModuleName:要获取的模块名称,可以是一个字符串形式的模块名称或者指向模块名称字符串的指针。
当有了上述两个模块的支持那么实现进程模块基址的读取将变得非常容易实现,如下是一段读取模块句柄的代码示例,在代码中我们分别读取了Tutorial-i386.exe自身模块基地址,以及该进程内user32.dll模块基址,需要注意的是运行该程序需要使用管理员身份。
[code]int main(int argc, char *argv[]){ // "Tutorial-i386.exe"+256650 DWORD DllBase = GetProcessModuleBase("Tutorial-i386.exe", "Tutorial-i386.exe"); if (DllBase != 0) { std::cout |