这也是学习游戏安全的一部分(确信
主要是体会一下游戏里面的各种数据修改大概是个什么样子。先拿这个比较 ez 的练练手,后面写点更有趣的(指 fps 的挂
希望写出来不是屎山
游戏版本:Plants vs. Zombies GOTY Edition(steam 的这玩意也没更新过吧
获取进程 id 和基址
这一步和游戏本身暂时没关系
Windows提供了CreateToolhelp32Snapshot
函数来创建系统中进程快照,以及Process32First
与Process32Next
函数来遍历系统中的所有进程。
我们创建一个系统中所有进程的快照后,使用Process32First
获取第一个进程信息,并通过Process32Next
来遍历系统中的其他进程。每个进程的信息都保存在PROCESSENTRY32
结构体中。遍历过程中,将每个进程的名字与 PlantVSZombie.exe 进行比较。如果找到匹配的进程名,则返回该进程的ID。这里使用wcscmp
函数对进程名进行比较。
// 根据进程名获取进程 ID
DWORD GetProcessIDByName(const wchar_t* processName)
{
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 进程快照
if (snapshot == INVALID_HANDLE_VALUE)
{
return 0;
}
PROCESSENTRY32 pEntry = {0}; // PROCESSENTRY32 结构体,用于存储进程信息
pEntry.dwSize = sizeof(PROCESSENTRY32); // 不初始化的话下面的 Process32First 函数会失败
if (!Process32First(snapshot, &pEntry)) // 获取第一个进程
{
CloseHandle(snapshot);
return 0;
}
// 遍历进程列表
do
{
wchar_t currentProcessName[MAX_PATH] = {0}; // 存储当前进程名
wcscpy(currentProcessName, pEntry.szExeFile); // 将当前进程名拷贝到 currentProcessName
if (wcscmp(currentProcessName, processName) == 0) // 如果当前进程名和目标进程名相同,返回进程 ID
{
CloseHandle(snapshot);
return pEntry.th32ProcessID;
}
} while (Process32Next(snapshot, &pEntry)); // 获取下一个进程
CloseHandle(snapshot); // 没有找到目标进程,关闭句柄
return 0;
}
在多模块的进程中,获取目标模块的基址是访问模块内存的前提。我们可以使用EnumProcessModules
函数枚举出目标进程的所有模块,然后通过GetModuleFileNameEx
函数匹配出目标模块,最终使用GetModuleInformation
获取模块基址。
// 根据进程 ID 和模块名获取基址
LPVOID GetModuleBaseAddress(DWORD ProcessID, LPCWSTR moduleName)
{
LPVOID lpBaseAddress = NULL;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID); // 打开进程句柄
if (hProcess == NULL)
{
return lpBaseAddress;
}
HMODULE hMods[1024]; // 存储模块句柄
DWORD cbNeeded; // 存储模块句柄数量
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) //
{
DWORD dwModuleCount = cbNeeded / sizeof(HMODULE); // 模块数量
for (DWORD i = 0; i < dwModuleCount; i++)
{
wchar_t szModuleName[MAX_PATH] = {0}; // 存储模块名
if (GetModuleFileNameEx(hProcess, hMods[i], szModuleName, sizeof(szModuleName) / sizeof(wchar_t))) // 获取模块名
{
if (wcsstr(szModuleName, moduleName) != NULL) // 如果模块名包含目标模块名
{
MODULEINFO moduleInfo = {0};
if (GetModuleInformation(hProcess, hMods[i], &moduleInfo, sizeof(moduleInfo))) // 获取模块信息,保存到 moduleInfo
{
lpBaseAddress = moduleInfo.lpBaseOfDll; // 获取模块基址
break;
}
}
}
}
CloseHandle(hProcess); // 关闭进程句柄
}
return lpBaseAddress;
}
无限阳光
这当然是最基本的一个功能
首先得把阳光的偏移给找出来,用我们最爱的 Cheat Engine
这里可以看见已经可以改阳光了,现在我们得找出修改阳光数值的基址在哪了,右键 -> 找出是什么访问了这个地址,
这里可以看见,两个偏移都是 0x5578,那随便看哪个都行,比如我们看这个 add eax
这里可以看见,edx 是 0x29C39328,那我们再搜这个值
这下地址就很多了,一个简单的筛查方法是,看地址的前2 3位,如果这几位相同的比较多,那大概率没啥用。这样我们就看到 0x3BBC1C0 这个地址,用它试试咯,依然是重复上面的过程
这次我们看见了绿色的基址,而且这个东西不是啥系统进程的,它就是 PVZ 进程里面的。我们用第一个试试
两层偏移,分别是 5578 和 868,然后确定,指针的结果就是我们地址栏里面最下面那一行,可以看见数值和阳光的值是一样的,那么这里就是修改阳光的基址了。
其实这四个绿色的基址都是修改阳光的,用同样的偏移都能指到 233 这个值
现在我们知道阳光的偏移是 +5578 +868了。
为什么我们要这么费力去找这个基址呢,我们重开一下这把游戏看看
可以看到,我们最开始改阳光的那个地址还是 233,没随着游戏重开变成 50,但是我们用基址+偏移找到的这个值随着改变了,我们修改这个50,现在的阳光也会随着改变。
现在我们右键看是什么改写了该指针指向的地址,然后种一个植物,对找到的汇编查看反汇编窗口
![[自写一个植物大战僵尸修改器] 8](F:\Desktop\My Blog's Backup\img[自写一个植物大战僵尸修改器] 8.png)
标签:return,自写,基址,获取,修改器,snapshot,模块,进程,僵尸 From: https://www.cnblogs.com/Here-is-SG/p/18408806