一、原理
设计一个名为MemChange的应用程序,让它修改Test.exe进程里面的内容。
每个进程占用的内存空间是离散的,所以要访问进程内存种的一个值,就需要遍历整个64K---2G的地址空间。
难点1.MemChange如何访问Test的进程空间
解决方案:MemChange程序中使用CreateProcess把Test.exe拉起来,这样MemChange就可以访问Test.exe的进程空间
难点2.如何搜索进程空间
Windows采用分页机制管理内存,每页的大小为4K(X86处理器上),也就是说Windows以4K为单位为应用程序分配内存。依次遍历每个页面,在一个页面中逐字节进行比对,就可以查询到需要搜索的值。
二、Test.exe程序
#include <stdio.h>
int g_nNum;
int main(void)
{
int i = 198;
g_nNum = 1000;
while (1)
{
// 输出两个变量的值与地址供我们对比
printf("i = %d,addr = %p ; g_nNum = %d , addr = %p\n",
i, &i, g_nNum, &g_nNum);
i++;
g_nNum--;
getchar();
}
return 0;
}
三、MemChange程序
1、头文件
#include <stdio.h>
#include <Windows.h>
// 查找数据的地址列表
DWORD g_arList[1024];
// 有效地址的个数
int g_nListCnt;
// 目标进程的句柄
HANDLE g_hProcess;
// 对目标进程空间进行查找
BOOL FindFirst(DWORD dwValue);
// 对目标进程空间进行2,3,4/。。。查找
BOOL FindNext(DWORD dwValue);
// 用来交互显示
void ShowList();
// 修改目标进程的值
BOOL WriteMemory(DWORD dwAddr, DWORD dwValue);
BOOL CompareAPage(DWORD dwBaseAddr, DWORD dwValue);
源文件
CompareAPage函数用从进程中读取4K大小的字节,并在其中查找匹配的dwValue,并将成功的地址存放在g_arList中
BOOL CompareAPage(DWORD dwBaseAddr, DWORD dwValue)
{
BYTE arBytes[4096]; // 4K大小
if (!::ReadProcessMemory(g_hProcess, (LPVOID)dwBaseAddr, arBytes,4096,NULL))
return FALSE;
// 当前页面可读
DWORD *pdw;
for (int i = 0; i < (int)4 * 1024 - 3; i++)
{
pdw = (DWORD *)&arBytes[i];
if (pdw[0] == dwValue)
{
// 保存1024个,大于1024,则不进行比较
if (g_nListCnt >= 1024)
return FALSE;
// 找到,并存放当前的地址
g_arList[g_nListCnt++] = dwBaseAddr + i;
}
}
return TRUE;
}
FindFirst函数 用来在2GB的空间以4K的大小来查找与dwValue匹配的地址,而且系统的前64K是不允许被访问的,因此查找的起始位置位64K;
BOOL FindFirst(DWORD dwValue)
{
const DWORD dwOneGB = 1024 * 1024 * 1024;
const DWORD dwOnePage = 4 * 1024;
// 如果当前进行不存在,则返回FALSE
if (g_hProcess == NULL)
return FALSE;
// 64K是不允许被访问的
DWORD dwBase = 640 * 1024; // 起始地址
//BOOL bRet = FALSE;
for (;dwBase < 2 * dwOneGB;dwBase+=dwOnePage)
{
CompareAPage(dwBase, dwValue);
}
return TRUE;
}
FindNext函数用来在FindFirst函数查找的出来的匹配数组g_arList中再次查找,直到找到与dwValue匹配的地址,找到第一个就直接返回;
BOOL FindNext(DWORD dwValue)
{
// 保存全局有效地址个数
int nOrgCnt = g_nListCnt;
g_nListCnt = 0; // 置为空
BOOL bRet = FALSE; // 假定查找失败
DWORD dwReadVal;
for (int i =0;i<nOrgCnt;i++)
{
if (::ReadProcessMemory(g_hProcess, (LPVOID)g_arList[i],
&dwReadVal, sizeof(DWORD), NULL))
{
if (dwReadVal == dwValue)
{
g_arList[g_nListCnt++] = g_arList[i];
bRet = TRUE;
}
}
}
return bRet;
}
ShowList函数用来显示每次查找找到的所有的匹配地址,用来进行用户交互;
void ShowList()
{
for (int i =0;i<g_nListCnt;i++)
{
printf("%p\n", g_arList[i]);
}
}
WriteMemory函数将查找到的地址的值用需要修改的值进行替换;
BOOL WriteMemory(DWORD dwAddr, DWORD dwValue)
{
return ::WriteProcessMemory(g_hProcess,(LPVOID)dwAddr,&dwValue,sizeof(DWORD),NULL);
}
主函数
int main()
{
// 首先,我们用CreateProcess把我们的目标进程Testor拉起来
// 进而,我们就可以访问Testor的进程空间
char szFileName[] = "Testor.exe";
STARTUPINFO si;
memset(&si, 0, sizeof(si));
PROCESS_INFORMATION pi;
BOOL bRet = ::CreateProcess(
NULL,
szFileName,
NULL,
NULL,
NULL,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi);
g_hProcess = pi.hProcess;
// 首先,我们需要将当前的memchange程序接收一个目标值
// 并将这个目标值进行搜索比对
int iVal;
printf("请输入你要修改的变量数值\n");
scanf("%d", &iVal);
// 进行第一次查找
FindFirst(iVal);
// 进行交互显示
ShowList();
while (g_nListCnt > 1)
{
printf("请再次输入你要修改的变量数值\n");
scanf("%d", &iVal);
// 进行下一次搜索
FindNext(iVal);
ShowList();
}
// 得到唯一值
// 将用户希望修改的数值输入,我们进行修改替换
printf("目标地址是%p\n", g_arList[0]);
printf("请输入新数据\n");
scanf("%d", &iVal);
// 写入新数值,修改完毕
if (WriteMemory(g_arList[0],iVal))
{
printf("修改成功!\n");
}
system("pause");
return 0;
}
四、总结
此程序在Win7的电脑上运行是正常的,但是在Win10上运行存在一定的问题,不一定可以修改成功!
转载自原文链接, 如需删除请联系管理员。
原文链接:进程内存修改器,转载请注明来源!