首页 > 代码库 > 实例游戏内存修改器----CUI版本模拟
实例游戏内存修改器----CUI版本模拟
实现说明:
目标进程内存中很可能存在多个你要搜索的值, 所以在进行第一次搜索的时候, 要把搜索到的地址记录下来,然后让用户改变要搜索的值,再在记录的地址中搜索,直到搜索到的地址惟一为止。为此写两个辅助函数和 3 个全局变量。
BOOL FindFirst(DWORD dwValue); // 在目标进程空间进行第一次查找
BOOL FindNext(DWORD dwValue); // 在目标进程地址空间进行第2、3、4……次查找
DWORD g_arList[1024]; // 地址列表
int g_nListCnt; // 有效地址的个数
HANDLE g_hProcess; // 目标进程句柄
上面这 5 行代码就组成了一个比较实用的搜索系统。比如游戏中显示的金钱值是 12345,首先将 12345 传给 FindFirst 函数进行第一次搜索,FindFirst 函数会将游戏进程内存中所有内容为 12345 的地址保存在 g_arList 全局数组中,将这样地址的个数记录在 g_nListCnt 变量中。FindFirst 函数返回以后,检查 g_nListCnt 的值,如果大于 1 就说明搜索到的地址多于 1 个。这时应该做一些事情改变游戏显示的金钱值。比如改变后金钱值变成了 13345,你要以 13345 为参数调用 FindNext 函数。这个函数会在 g_arList 数组记录的地址中进行查找,并更新g_arList 数组的记录, 将所有内容为 13345 的地址写到里面, 将这样地址的个数写到 g_nListCnt 变量中。FindNext 函数返回后,检查 g_nListCnt 的值,如果不等于 1 还继续改变金钱值,调用函数 FindNext,直到最终 g_nListCnt 的值为 1 为止。这时,g_arList[0]的值就是目标进程中保存金钱值的地址。
程序运行说明:
现在基本功能都有了,启动程序。
(1)输入 199,发现找出的地址不惟一。
(2)在 TestExe.exe 窗口敲下回车,改变后再进行一次查找,这样循环直到找到的地址惟
一为止。
(3)输入期待的值,修改成功!
TestExe.cpp
1 // 辅助测试程序------类比于游戏程序 2 #include <stdio.h> 3 4 int g_nNum; // 全局变量测试 5 6 int main() 7 { 8 int i = 198; // 局部变量测试 9 g_nNum = 1003;10 11 while (1)12 {13 printf("i = %d, address = %08lX; g_nNum = %d, address = %08lX\n", ++i, &i, ++g_nNum, &g_nNum);14 getchar(); // 按回车可以使两个变量值递增变化一,但是地址是不变的,为的是在MemRepair中正确找到该变量的唯一地址。15 }16 return 0;17 }
MemRepair.cpp
1 #include <windows.h> 2 #include <stdio.h> 3 4 HANDLE g_hProcess; 5 int g_nListCnt; 6 int g_arList[1024]; 7 8 /* 9 Windows 采用了分页机制来管理内存,每页的大小是 4KB(在 x86 处理器上) 。也就是说 10 Windows 是以 4KB 为单位来为应用程序分配内存的。所以可以按页来搜索目标内存,以提高 11 搜索效率。 12 */ 13 //读取一页内存 14 BOOL CompareAPage(DWORD dwBaseAddress, DWORD dwValue) 15 { 16 BYTE arBytes[4096]; 17 if (!::ReadProcessMemory(g_hProcess, (LPVOID)dwBaseAddress, arBytes, 4096, NULL)) 18 { 19 return FALSE; // 此页不可读 20 } 21 // 在这一页内存中查找 22 DWORD* pdw; 23 for (int i = 0; i < 4*1024-3; ++i) 24 { 25 pdw = (DWORD*)&arBytes[i]; 26 if (pdw[0] == dwValue) // 等于要查找的值 27 { 28 if (g_nListCnt >= 1024) 29 { 30 return FALSE; 31 } 32 // 添加到全局变量中 33 g_arList[g_nListCnt++] = dwBaseAddress + i; 34 } 35 } 36 return TRUE; 37 } 38 39 /* 40 应该在目标进程的整个用户地址空间进行搜索。在进程的整个 4GB(即2^32) 地址中,Windows 98 41 系列的操作系统为应用程序预留的是 4MB 到 2GB 部分,Windows 2000 系列的操作系统预留 42 的是 64KB 到 2GB 部分, 43 */ 44 45 // FindFirst 函数将所有符合条件的内存地址都记录到了全局数组 g_arList 中。 46 BOOL FindFirst(DWORD dwValue) 47 { 48 const DWORD dwOneGB = 1024 * 1024 * 1024; // 1GB 49 const DWORD dwOnePage = 4 * 1024; // 4KB 50 if (g_hProcess == NULL) 51 { 52 return FALSE; 53 } 54 55 // 查看操作系统类型,以决定开始地址 56 DWORD dwBase; 57 OSVERSIONINFO vi = {sizeof(vi)}; 58 ::GetVersionEx(&vi); 59 if (vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) 60 { 61 dwBase = 4 * 1024 * 1024; // Windows 98 系列,4MB 62 } 63 else 64 { 65 dwBase = 64 * 1024; // Windows NT 系列,64KB 66 } 67 68 // 在开始地址到2GB的地址空间进行查找 69 for (; dwBase < 2 * dwOneGB; dwBase += dwOnePage) 70 { 71 // 比较一页的内存 72 CompareAPage(dwBase, dwValue); 73 } 74 return TRUE; 75 } 76 77 BOOL FindNext(DWORD dwValue) 78 { 79 // 保存m_arList数组中有效地址的个数,初始化新的m_nListCnt值 80 int nOrgCnt = g_nListCnt; 81 g_nListCnt = 0; 82 83 // 在m_arList数组记录的地址处查找 84 BOOL bRet = FALSE; 85 DWORD dwReadValue; 86 for (int i = 0; i < nOrgCnt; ++i) 87 { 88 if (::ReadProcessMemory(g_hProcess, (LPVOID)g_arList[i], &dwReadValue, sizeof(DWORD), NULL)) 89 { 90 if (dwReadValue =http://www.mamicode.com/= dwValue) 91 { 92 g_arList[g_nListCnt++] = g_arList[i]; 93 bRet = TRUE; 94 } 95 } 96 } 97 return bRet; 98 } 99 100 // 打印出搜索到的地址101 void ShowList()102 {103 for (int i = 0; i < g_nListCnt; ++i)104 {105 printf("%08lX\n", g_arList[i]);106 }107 }108 109 // 重写指定地址的值110 BOOL WriteMemory(DWORD dwAddr, DWORD dwValue)111 {112 return::WriteProcessMemory(g_hProcess, (LPVOID)dwAddr, &dwValue, sizeof(DWORD), NULL);113 }114 115 int main()116 {117 // 启动TestExe.exe进程118 char szFileName[] = "..\\TestExe\\Debug\\TestExe.exe";119 STARTUPINFO si = {sizeof(si)};120 PROCESS_INFORMATION pi;121 ::CreateProcess(NULL, szFileName, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);122 123 // 关闭线程句柄,既然我们不使用124 ::CloseHandle(pi.hThread);125 g_hProcess = pi.hProcess;126 127 // 输入要修改的值128 int iVal;129 printf("Input value = http://www.mamicode.com/");130 scanf("%d", &iVal);131 132 // 进行第一次查找133 FindFirst(iVal);134 135 // 打印出搜索的结果136 ShowList();137 138 while(g_nListCnt > 1)139 {140 printf("Input val = ");141 scanf("%d", &iVal);142 143 // 进行下次搜索144 FindNext(iVal);145 // 显示搜索结果146 ShowList();147 }148 149 if (g_nListCnt == 1) // 已经锁定了要修改的地址150 {151 // 设置新值152 printf("New value = http://www.mamicode.com/");153 scanf("%d", &iVal);154 // 写入新值155 if (WriteMemory(g_arList[0], iVal))156 {157 printf("Write data successfully\n");158 }159 }160 ::CloseHandle(g_hProcess);161 return 0;162 }
运行结果:
(1)在MemRepair.exe中输入值199查找,发现地址不唯一,有一百多个;
(2)在TestExe中打回车,使变量变为200,在MemRepair.exe中输入200查找,发现唯一一个地址,并且与TestExe中的地址相同;
(3)在MEMRepair.exe中输入希望修改的新值300,回车之后,在TestExe.exe中再打回车,发现变量已经变为301而不是201了。
截图如下:
实例游戏内存修改器----CUI版本模拟