• 保证模拟游戏中小老鼠不被打死

      • 血量不减
      • 判断die跳过
      • 血量不变
    • 知识点

      • 获得进程PID的方法
        • 方法一:CreateToolhelp32Snapshot、Process32First、Process32Next、CloseHandle
        • 方法二:FindWindow、GetWindowThreadProcessId
      • 获取进程句柄
        • OpenProcess
      • 搜索要patch的位置
        • SearchMemory(自定义):一块一块的查找
          • EnumProcessModules:获得指定进程中所有模块的句柄
          • VirtualQueryEx:查询地址空间中内存地址的信息(保护属性等)
          • ReadProcessMemory:读取内存
          • CloseHandle
      • 读取memory
        • ReadProcessMemory
      • 写入memory
        • WriteProcessMemory
    • 模块(exe或者dll)被加载后,其开始地址就是该模块的句柄值,通常应用程序都是通过模块句柄来访问它的每个进程中的模块(一个应用程序可能启动多个进程),事实上模块句柄的值就是该模块映射到进程中的地址。进程是系统中运行的应用程序,进程句柄中储存用于访问该进程的一些信息,句柄值是一个索引值,通过该索引可以访问句柄中的内容

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <windows.h>
    4. #include <tlhelp32.h>
    5. #include <tchar.h>
    6. #include <psapi.h>
    7. #include<stdio.h>
    8. #include<malloc.h>
    9. #include <array>
    10. BYTE* SearchMemory(HANDLE hProcess, DWORD pSearchBuffer1, DWORD pSearchBuffer2,DWORD dwSearchBufferSize)
    11. {
    12. // 根据PID, 打开进程获取进程句柄
    13. //HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    14. //if (NULL == hProcess)
    15. //{
    16. // ShowError("OpenProcess");
    17. // return FALSE;
    18. //}
    19. // 获取进程加载基址
    20. HMODULE hModule[1024];
    21. DWORD cbNeeded;
    22. EnumProcessModules(hProcess, (HMODULE*)hModule, sizeof(hModule), &cbNeeded);
    23. // 把加载基址作为遍历内存的起始地址, 开始遍历
    24. BYTE* pSearchAddress2 = (BYTE * )hModule[0];//从加载的第一个模块开始
    25. printf("%x\n", pSearchAddress2);
    26. BYTE* pSearchAddress = (BYTE*)0x00010000;
    27. MEMORY_BASIC_INFORMATION mbi = { 0 };
    28. DWORD dwRet = 0;
    29. BOOL bRet = FALSE;
    30. DWORD* pTemp = NULL;
    31. DWORD i = 0;
    32. DWORD* pBuf = NULL;
    33. while (pSearchAddress >= (BYTE*)0 && pSearchAddress <= (BYTE*)pSearchAddress2 && mbi.RegionSize >= 0)
    34. {
    35. // 查询地址空间中内存地址的信息
    36. ::RtlZeroMemory(&mbi, sizeof(mbi));
    37. dwRet = ::VirtualQueryEx(hProcess, (LPVOID)pSearchAddress, &mbi, sizeof(mbi));
    38. //printf("%d\n", dwRet);
    39. if (0 == dwRet)
    40. {
    41. //printf("hhhh");
    42. break;
    43. }
    44. // printf("0x%p\n", pSearchAddress);
    45. // 过滤内存空间, 根据内存的状态和保护属性进行过滤
    46. if ((MEM_COMMIT == mbi.State) &&
    47. (PAGE_READONLY == mbi.Protect || PAGE_READWRITE == mbi.Protect ||
    48. PAGE_EXECUTE_READ == mbi.Protect || PAGE_EXECUTE_READWRITE == mbi.Protect))
    49. {
    50. // printf("find pages\n");
    51. // 申请动态内存
    52. //pBuf = new DWORD[mbi.RegionSize / 4];
    53. byte* FindArray = new byte[mbi.RegionSize];
    54. DWORD dwData;
    55. DWORD dwData2;
    56. // printf("RegionSize: %d\n", mbi.RegionSize);
    57. ::RtlZeroMemory(FindArray, mbi.RegionSize);
    58. // 读取整块内存
    59. if (ReadProcessMemory(hProcess, (LPVOID)pSearchAddress, FindArray, mbi.RegionSize, &dwRet))
    60. {
    61. // printf("read success\n");
    62. int len = 8;
    63. byte* intBuff = new byte[len];
    64. for (int i = 0; i < mbi.RegionSize - dwSearchBufferSize; i++)
    65. {
    66. int tmp = i;
    67. for (int j = 0;j < len;j++)
    68. {
    69. intBuff[j] = FindArray[tmp];
    70. tmp += 1;
    71. }
    72. dwData = intBuff[0];
    73. for (int j = 1;j < len/2;j++)
    74. {
    75. dwData= dwData | intBuff[j] << len *j;
    76. }
    77. dwData2 = intBuff[4];
    78. for (int j = 5;j < len;j++)
    79. {
    80. dwData2 = dwData2 | intBuff[j] << len * (j-4);
    81. }
    82. if (dwData == (DWORD)pSearchBuffer1 && dwData2 == (DWORD)pSearchBuffer2) {
    83. return (BYTE*)mbi.BaseAddress + i;
    84. }
    85. }
    86. }
    87. // 释放内存
    88. delete[]pBuf;
    89. pBuf = NULL;
    90. }
    91. // 继续对下一块内存区域进行遍历
    92. pSearchAddress = pSearchAddress + mbi.RegionSize;
    93. }
    94. // 释放内存, 关闭句柄
    95. if (pBuf)
    96. {
    97. delete[]pBuf;
    98. pBuf = NULL;
    99. }
    100. ::CloseHandle(hProcess);
    101. return 0;
    102. }
    103. int main()
    104. {
    105. //获取进程PID_方法一
    106. // int pid=0;
    107. // PROCESSENTRY32 processentry={0}; //创建一个进程结构体
    108. // processentry.dwSize=sizeof(PROCESSENTRY32);
    109. // HANDLE hprocessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); //获取进程快照
    110. // if(hprocessSnap==INVALID_HANDLE_VALUE){
    111. // return -1;
    112. // }
    113. // int flag=Process32First(hprocessSnap,&processentry); //获取第一个进程
    114. // while(flag){
    115. // if(lstrcmpi(processentry.szExeFile,"Afkayas.exe")==0){ //lstrcmpi函数用于比较两个字符串,相同时返回0
    116. // pid=processentry.th32ProcessID;
    117. // }
    118. // // printf("%d----%s\n",processentry.th32ProcessID,processentry.szExeFile);
    119. // flag=Process32Next(hprocessSnap,&processentry);
    120. // }
    121. // CloseHandle(hprocessSnap);
    122. // if(pid==0){
    123. // printf("请先打开进程");
    124. // return 0;
    125. // }
    126. //获取进程PID_方法二
    127. HWND hWnd = FindWindow(NULL, _T("MemSearchTest"));
    128. if (hWnd == NULL) { //如果无法获取句柄则报错
    129. printf("无法获取进程句柄,请检查进程是否存在!");
    130. getchar();
    131. return -1;
    132. }
    133. //GetWindowThreadProcessId获取进程ID的变量类型是DWORD,但是GetWindowThreadProcessId在MSDN中并没有明确表示返回值得成功与失败,所以我们定义一个DWORD类型变量赋值为0,在调用GetWindowThreadProcessId之后看一下ID是否还为0如果为0则失败,一般来说不会失败!
    134. DWORD Process_ID = 0;
    135. GetWindowThreadProcessId(hWnd, &Process_ID); //PID这里传址
    136. printf("PID:%d\n", Process_ID);
    137. if (Process_ID == 0) { //判断是否与原值相同
    138. printf("无法获取进程ID");
    139. }
    140. //获取进程句柄
    141. HANDLE procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Process_ID);
    142. if (procHandle == NULL) {
    143. printf("打开进程失败!");
    144. return 0;
    145. }
    146. // 搜索要替换的位置
    147. BYTE* address=SearchMemory(procHandle, 0x0000005A, 0x0000425C,8);
    148. int blood;
    149. int ord;
    150. DWORD buffer;
    151. /* 永恒方法一
    152. // 运算失效: -10 -> -0
    153. if(ReadProcessMemory(procHandle,(LPCVOID)0x00400000,&tmp,1,&buffer)){
    154. printf("读取成功,读取内容为:%#X\n",tmp);
    155. }
    156. else{
    157. printf("读取进程内容失败!");
    158. exit(-1);
    159. }
    160. tmp=0x00;
    161. if(WriteProcessMemory(procHandle,(LPVOID)0x00401E2D,&tmp,1,&buffer)){
    162. printf("写入进程内容成功\n");
    163. }
    164. else{
    165. printf("写入进程内容失败!\n");
    166. exit(-1);
    167. }
    168. if(ReadProcessMemory(procHandle,(LPCVOID)0x00401E2D,&tmp,1,&buffer)){
    169. printf("读取成功,读取内容为:%#X\n",tmp);
    170. }
    171. else{
    172. printf("读取进程内容失败!");
    173. exit(-1);
    174. }
    175. */
    176. /* 永恒方法二*/
    177. // 血量不变
    178. while(TRUE)
    179. {
    180. blood=0x64;
    181. if (!ReadProcessMemory(procHandle, (LPCVOID)address, &ord, 1, &buffer)) {
    182. printf("读取进程内容失败!");
    183. exit(-1);
    184. }
    185. if (ord != blood)
    186. {
    187. if (!WriteProcessMemory(procHandle, (LPVOID)address, &blood, 1, &buffer)) {
    188. printf("写入进程内容失败!\n");
    189. exit(-1);
    190. }
    191. }
    192. //PostMessage(hWnd, WM_PAINT, 0,0);
    193. }
    194. /*永恒方法三
    195. //判断失效
    196. if(ReadProcessMemory(procHandle,(LPCVOID)0x00401E36,&tmp,1,&buffer)){
    197. printf("读取成功,读取内容为:%#X\n",tmp);
    198. }
    199. else{
    200. printf("读取进程内容失败!");
    201. exit(-1);
    202. }
    203. tmp=0xEB;//jg->jmp
    204. if(WriteProcessMemory(procHandle,(LPVOID)0x00401E36,&tmp,1,&buffer)){
    205. printf("写入进程内容成功\n");
    206. }
    207. else{
    208. printf("写入进程内容失败!\n");
    209. exit(-1);
    210. }
    211. if(ReadProcessMemory(procHandle,(LPCVOID)0x00401E36,&tmp,1,&buffer)){
    212. printf("读取成功,读取内容为:%#X\n",tmp);
    213. }
    214. else{
    215. printf("读取进程内容失败!");
    216. exit(-1);
    217. }
    218. */
    219. return 0;
    220. }