笙若 发表于 2020-8-27 19:03

自制一个极简的内存修改器

本帖最后由 笙若 于 2020-8-27 19:13 编辑

这是前几天从论坛里发的教程里学到的,我觉得这个教程挺好的,对win32api编程有兴趣的可以学一学,链接是这个:
https://www.52pojie.cn/forum.php?mod=viewthread&tid=1247555&highlight=windows%B3%CC%D0%F2


#undef UNICODE
//按老师说的改了项目属性还是没用,搜到的避免字符串转换问题的方法就是加这一句
#include<stdio.h>
#include<Windows.h>
#include<iostream>
BOOL FindFirst(DWORD dwValue); //开始查找所有符合值的地址
BOOL FindNext(DWORD dwValue); //在已查找到的地址中重新查找

DWORD g_arrList; //存储找到的地址,只存1024个
int g_nListCnt; //有效的地址个数
HANDLE g_hProcess; //目标进程的句柄

void ShowList(); //显示有效的地址列表
BOOL WriteMemory(DWORD dwAddr, DWORD dwValue); //修改地址处的值

int main(int argc, char* argv[]) {
      //首先通过CreateProcess将目标进程加载,然后才能访问目标进程的内存空间
    char szFileName;
    //从命令行读取参数或者请求输入
    if(argc > 1)
      strcpy(szFileName, argv);
    else{
      printf("请输入要修改的程序:\n");
      std::cin >> szFileName;
    }
      STARTUPINFO si;
      memset(&si, 0, sizeof(si));
      PROCESS_INFORMATION pi;
      BOOL bRet = ::CreateProcess(
                NULL,
                szFileName,
                NULL,
                NULL,
                FALSE,
                CREATE_NEW_CONSOLE,
                NULL,
                NULL,
                &si,
                &pi
      );
      g_hProcess = pi.hProcess; //将目标进程的句柄保存在全局变量中
      BOOL flag = TRUE;
      int iVal;
      while (flag) {
                // 接收输入一个目标值,并进行搜索比对
                printf("第一次搜索的数值:\n");
                std::cin >> iVal;
                //第一次查找
                FindFirst(iVal);
                //显示地址列表
                ShowList();
                int nListCnt{ 0 }, count{ 0 };
                while (g_nListCnt > 1) {
                        nListCnt = g_nListCnt;
                        printf("输入要修改的数值:\n");
                        std::cin >> iVal;
                        //下一次搜索
                        FindNext(iVal);
                        ShowList();
                        if (nListCnt == g_nListCnt) {
                              count++; //如果重复3次都是同样的地址,则不再查找,选择其中的第一个修改
                              if (count > 1)
                                        break;
                        }
                }
                if (g_nListCnt == 1 || nListCnt == g_nListCnt) {
                        printf("目标地址为:%p\n", g_arrList);
                        //获取输入的新值,修改替换
                        printf("输入要修改为的值:\n");
                        std::cin >> iVal;
                        if (WriteMemory(g_arrList, iVal)) {
                              printf("修改成功\n");
                        }
                }
                else {
                        printf("未找到地址\n");
                }
                printf("是否重新查找(y/n):\n");
                char c;
                std::cin >> c;
                if (c == 'y')
                        flag = TRUE;
                else
                        flag = FALSE;
      }
      system("pause");
      return 0;
}

BOOL ComparePage(DWORD dwBaseAddr, DWORD dwValue) {
      BYTE arBytes;
      if (!::ReadProcessMemory(g_hProcess, (LPCVOID)dwBaseAddr, arBytes, 4096, NULL)) {
                return FALSE;
      }
      DWORD* pdw;
      //一个DWORD4个字节,所以从头开始一次取4个字节长度的值进行对比
      for (int i = 0; i < (int)4 * 1024 - 3; i++) {
                pdw = (DWORD*)&arBytes;
                if (pdw == dwValue) {
                        if (g_nListCnt >= 1024)
                              return FALSE;
                        g_arrList = dwBaseAddr + i;
                }
      }
      return TRUE;
}

BOOL FindFirst(DWORD dwValue)
{
      BOOL bRet = FALSE;
      const DWORD dwOneGb = 1024 * 1024 * 1024; //定义一个GB的大小
      const DWORD dwOnePage = 4 * 1024; //定义4KB一页
      g_nListCnt = 0;
      if (g_hProcess == NULL)
                return FALSE;
      DWORD dwBase = 64 * 1024; //刚开始的64kb是不能访问的,直接跳过
      for (; dwBase < 2 * dwOneGb; dwBase += dwOnePage) {
                //在2gb的空间里每次在4kb的页中寻找
                ComparePage(dwBase, dwValue);
      }
      return TRUE;
}

BOOL FindNext(DWORD dwValue)
{
      BOOL bRet = FALSE;
      int nOrgCnt = g_nListCnt;
      g_nListCnt = 0;
      DWORD dwReadValue; //存储读取值的地址
      for (int i = 0; i < nOrgCnt; i++) {
                if (::ReadProcessMemory(g_hProcess, (LPVOID)g_arrList, &dwReadValue, sizeof(DWORD), NULL)) {
                        if (dwReadValue == dwValue) {
                              g_arrList = g_arrList;
                              bRet = TRUE;
                        }
                }
      }
      return bRet;
}

void ShowList()
{
      for (int i = 0; i < g_nListCnt; i++) {
                std::cout << std::hex << g_arrList << std::endl;
      }
}

BOOL WriteMemory(DWORD dwAddr, DWORD dwValue)
{
      return ::WriteProcessMemory(g_hProcess, (LPVOID)dwAddr, &dwValue, sizeof(DWORD), NULL);
}


在老师的代码基础上做了一点点修改,老师是用纯C写的,但是vs2019里scanf老是会提示不安全,所以全部换成了c++的cin。
偶尔会用CE改游戏,通过这段代码也算稍微理解了一点原理,还是很有成就感的。
感兴趣的朋友希望可以一起坚持学下去。

ps122 发表于 2020-8-27 19:33

谢谢分享,有编译好的可执行文件吗?

笙若 发表于 2020-8-27 20:36

ps122 发表于 2020-8-27 19:33
谢谢分享,有编译好的可执行文件吗?

链接: https://pan.baidu.com/s/1kPTvZZpB66O2JkFGT2KTVg 提取码: kau2
里面的MemChange是编译好的文件,然后tester是用来测试的,每按一次回车数字就会变

深水夜藏 发表于 2020-8-27 22:12

谢谢分享,正好可以用来学习

ps122 发表于 2020-8-28 07:59

笙若 发表于 2020-8-27 20:36
链接: https://pan.baidu.com/s/1kPTvZZpB66O2JkFGT2KTVg 提取码: kau2
里面的MemChange是编译好的文件 ...

非常感谢!

xiaohe_nh 发表于 2020-11-14 08:28

ComparePage中里面是从头按照4个字节比较的。有没有可能,程序因为存储类型等有两个字节或1个字节的空间占用。这样导致后续数据的错位,使得无法真正找到4个字节的数据?

天天54123 发表于 2021-1-19 16:26

没有提示这个 请输入要修改的程序

笙若 发表于 2021-1-19 17:28

天天54123 发表于 2021-1-19 16:26
没有提示这个 请输入要修改的程序

如果带有参数的话,第一个参数会被当做要修改的程序,就不会提示输入程序了

AA_BB_2AB 发表于 2021-4-2 15:26

谢谢,祝您身体健康!
页: [1]
查看完整版本: 自制一个极简的内存修改器