c++ - C++ 仅读取进程范围内的内存地址
问题描述
所以我一直在尝试用 C++ 进行内存读取,我认为一个很酷的项目是读取进程正在使用的所有地址(类似于 Cheat Engine 的工作方式)。
我从阅读开始
Link1: 读取进程 C++ 的内存
Link2: 读取64bit进程地址的内存
链接3: http ://www.cplusplus.com/forum/general/42132/
我还在 youtube 上观看了一个教程,他解释了一个过程(游戏)如何处理地址。链接到 youtube 视频: https ://www.youtube.com/watch?v=wiX5LmdD5yk
这导致我采用了三种不同的方法:
DWORD GetProcId(const wchar_t* procName) {
DWORD pid = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hSnap != INVALID_HANDLE_VALUE) {
PROCESSENTRY32 procEntry;
procEntry.dwSize = sizeof(procEntry);
if(Process32First(hSnap, &procEntry)) {
do {
if(!_wcsicmp(procEntry.szExeFile, procName)) {
pid = procEntry.th32ProcessID;
break;
}
} while (Process32Next(hSnap, &procEntry));
}
}
CloseHandle(hSnap);
return pid;
}
这种方法是获取进程ID,我也可以通过在任务管理器中找到相同的PID(稍后给我相同的基地址)来手动输入它。
uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName) {
uintptr_t modBaseAddr = 0;
//I use 0x10 instead of TH32CS_SNAPMODULE32 since it didnt work and according to documentation
// this is the value it should have.
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | 0x10, procId);
if(hSnap != INVALID_HANDLE_VALUE) {
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if(Module32First(hSnap, &modEntry)) {
do {
if(!_wcsicmp(modEntry.szModule, modName)) {
modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
break;
}
} while(Module32Next(hSnap, &modEntry));
}
}
CloseHandle(hSnap);
return modBaseAddr;
}
这个方法(我假设)将返回进程的基地址。例如,在我的代码中,我试图找到 discord.exe 进程的基地址。当 discord.exe 没有运行时,我得到了 0,而当它运行时,我得到了一个地址(我相信这是正确的基地址,如果我错了,请纠正我)。
还有我的main
方法:
int main() {
DWORD procId = GetProcId(L"Discord.exe");
uintptr_t moduleBase = GetModuleBaseAddress(procId, L"Discord.exe");
HANDLE hProcess = 0;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, procId);
uintptr_t dynamicPtrBaseAddr = moduleBase;
std::cout << "Dynamic: " << dynamicPtrBaseAddr << std::endl;
int value = 0;
int arr [10000] = {};
for (int i = 0; i < 100000; i++) {
ReadProcessMemory(hProcess, (BYTE*)dynamicPtrBaseAddr, &value, sizeof(value),0);
dynamicPtrBaseAddr += 1;
arr[i] = value;
}
}
我尝试将所有 100000 个地址的所有值放入一个数组中。
所以我的问题是:
- 我是否正确检索了进程的基地址?
- 为了读取其他地址,我只需将 dynamicPtrBaseAddr 增加 1,是否有更好的方法来实现偏移?或者这是正确的方法?
- 现在我将基地址增加100000。我可以找到进程的最后一个地址吗?
g++ main.cpp -o test -lpsapi -DUNICODE
我用(MinGW)编译。
解决方案
您必须以管理员身份运行,并且必须编译为与目标进程相同的位数。
您不应该一次读取 1 个字节,尤其是在外部 hack 中。
您应该使用 VirtualQueryEx() 正确循环仅正确的内存区域:
DWORD procid = GetProcId("ac_client.exe");
unsigned char* addr = 0;
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procid);
MEMORY_BASIC_INFORMATION mbi;
while (VirtualQueryEx(hProc, addr, &mbi, sizeof(mbi)))
{
if (mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS && mbi.Protect != PAGE_GUARD)
{
std::cout << "base : 0x" << std::hex << mbi.BaseAddress << " end : 0x" << std::hex << (uintptr_t)mbi.BaseAddress + mbi.RegionSize << "\n";
}
addr += mbi.RegionSize;
}
这可以确保您只读取正确的内存。接下来,您将一次性将每个区域到达本地缓冲区,这将显着提高速度,因为您不会有为每个字节调用 API 的开销。
您看起来正在尝试做的是模式扫描。您可以在找到您所遵循的原始教程的同一位置找到我们的模式扫描教程。
推荐阅读
- excel - Power BI DAX 公式从上一行获取结果
- java - 添加片段 onCreateView 时无法解析 Inflate 方法
- windows - 控制台应用程序中的 DOSCommand (TurboPack)
- java - 方法引发了“java.lang.IllegalArgumentException”异常。如何使用 java 反射设置私有最终值
- javascript - Multer 无法读取未定义的属性“文件名”
- php - 用 Twig 中的数组值替换字符串
- latex - 为什么我会收到“\
有一个额外的 }" 错误 - android - Android Kotlin Room Repository 无法从详细活动中检索行
- python - ValueError:分类指标无法处理未知目标和二进制目标的混合:0 和 1 预测
- javascript - 只记录到第一个数组