自制一个极简的内存修改器
本帖最后由 笙若 于 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
谢谢分享,有编译好的可执行文件吗?
链接: https://pan.baidu.com/s/1kPTvZZpB66O2JkFGT2KTVg 提取码: kau2
里面的MemChange是编译好的文件,然后tester是用来测试的,每按一次回车数字就会变 谢谢分享,正好可以用来学习 笙若 发表于 2020-8-27 20:36
链接: https://pan.baidu.com/s/1kPTvZZpB66O2JkFGT2KTVg 提取码: kau2
里面的MemChange是编译好的文件 ...
非常感谢! ComparePage中里面是从头按照4个字节比较的。有没有可能,程序因为存储类型等有两个字节或1个字节的空间占用。这样导致后续数据的错位,使得无法真正找到4个字节的数据? 没有提示这个 请输入要修改的程序 天天54123 发表于 2021-1-19 16:26
没有提示这个 请输入要修改的程序
如果带有参数的话,第一个参数会被当做要修改的程序,就不会提示输入程序了 谢谢,祝您身体健康!
页:
[1]