首页 » 技术分享 » 进程内存修改器

进程内存修改器

 

一、原理

设计一个名为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上运行存在一定的问题,不一定可以修改成功!

转载自原文链接, 如需删除请联系管理员。

原文链接:进程内存修改器,转载请注明来源!

0