在应用程序A中读取和修改进程B的内存的方法有很多,这里介绍使用API函数(ReadProcessMemory和WriteProcessMemory)来实现的方法。只需你了解Windows的内存管理机制,再了解几个Windows API 函数,就可以做到。对于Windows的内存机制,网络上有很多的文章介绍这方面的内容,在此我不赘述。下面开始实例讲解。
1. 思路:
修改其他进程的内存,因为进程使用独立的进程空间,所以必须使用API函数调用实现此功能。读指定进程的内存使用API函数ReadProcessMemory,写则使用WriteProcessMemory函数。
具体算法:例如要修改游戏中的金钱值,首先先玩游戏,然后挣到A数量的金钱值,然后输入这个值,搜索,因为第一次搜索,可能在进程内存中有很多其他变量的值与此金钱值变量的值相同,所以记录下所有搜索到的值,然后继续玩游戏使得金钱值增加到B,然后输入B值,在上次搜索到的地址中再匹配与B值相等的地址,如此循环,直到找到唯一的地址,则完成了内存的定位,接下来就可以进行改写了。下面开始编程:
#include <stdio.h>
#include <iostream>
using namespace std;
#include <Windows.h>
HWND destWnd; //存储目标进程的窗口句柄
DWORD destThreadID=0; //存放目标进程的线程ID号
DWORD destProcessID=0; //存放目标进程的进程ID好
DWORD g_arList[1024]; //地址列表,用来记录搜索到的地址
int g_nListCnt; //搜索到的有效地址的个数
HANDLE g_hProcess; //目标进程句柄
/*
提升权限,如果不提升权限,无法打开系统安全进程或服务进程为了提升权
限,要用到RaisePrivileges()中所示的3个API函数,具体函数介绍参考MSDN
*/
void RaisePrivileges(){
HANDLE hToken; //
TOKEN_PRIVILEGES tkp;
tkp.PrivilegeCount=1;
tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
//获得该进程访问令牌的句柄
if (OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,&hToken))
{
//得到一个唯一的GUID值
if (LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tkp.Privileges[0].Luid))
{
//修改访问令牌,获得权限
AdjustTokenPrivileges(hToken,FALSE,&tkp,0x10,(PTOKEN_PRIVILEGES)NULL,0);
}
}
if(hToken)
{
printf("Success Raise Privilege\n");
CloseHandle(hToken);
}
}
///// 读进程空间 //////////////////
//搜索并比较目标进程中一页大小的内存
BOOL cmpApage(DWORD dwBaseAddr,DWORD dwValue){
//读取一页内存
BYTE arBytes[4096];
if (!::ReadProcessMemory(g_hProcess,(LPCVOID)dwBaseAddr,arBytes,4096,NULL))
{
return FALSE;
}
//在这一页内存中查找
DWORD *pwd;
for (int i=0;i<(int)4*1024-3;i++)
{
pwd=(DWORD*)&arBytes[i];
//如果等于要查找的值,且已经搜索的到和目标值相等的值的个数(即g_nListCnt的大小)小于1024,则将其记录下来
if (pwd[0]==dwValue)
{
if(g_nListCnt>=1024)
return FALSE;
g_arList[g_nListCnt++]=dwBaseAddr+i;
}
}
return TRUE;
}
//下面的函数进行第一次搜索,将所有符合条件的内存地址都记录下来
BOOL FindFirst(DWORD dwValue){
const DWORD dwOneGB=1024*1024*1024;
const DWORD dwOnePage=4*1024;
if(g_hProcess==NULL)
return FALSE;
DWORD dwBase;
//查看操作系统类型,从而确定开始地址
OSVERSIONINFO vi={sizeof(vi)};
::GetVersionEx(&vi);
if (vi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
dwBase=4*1024*1024; //Win98系列,4MB
else
dwBase=640*1024; //Window NT系列,64KB
for (;dwBase<2*dwOneGB;dwBase+=dwOnePage)
{
cmpApage(dwBase,dwValue); //比较1页大小的内存
}
return TRUE;
}
//下面的函数在第一次搜索的结果集上继续搜索
BOOL FindNext(DWORD dwValue){
int nOrgCnt=g_nListCnt;
g_nListCnt=0;
BOOL bret=FALSE;
DWORD dwReadValue;
for (int i=0;i<nOrgCnt;i++)
{
if (::ReadProcessMemory(g_hProcess,(LPCVOID)g_arList[i],&dwReadValue,sizeof(DWORD),NULL))
{
if (dwReadValue==dwValue)
{
g_arList[g_nListCnt++]=g_arList[i];
bret=TRUE;
}
}
}
return bret;
}
void showList(){//显示结果(集)
for (int i=0;i<g_nListCnt;i++)
{
printf("%08lX\n",g_arList[i]);
}
if(g_nListCnt<1)
cout<<"Failed Read\n";
}
////////写进程空间////////////////
//找到变量的地址后,就可以改变它的值了
BOOL WriteMemory(DWORD dwAddr,DWORD dwValue){
return::WriteProcessMemory(g_hProcess,(LPVOID)dwAddr,&dwValue,sizeof(DWORD),NULL);
}
int main(){
RaisePrivileges(); //提升权限
// //第一个参数为“窗口类名”,第二个参数为"窗口名"。可以用VC中的TOOL中的
//SPY++查看。当然,也可以通过编程如拍快照等找到目标进程的ID
destWnd=FindWindowA(NULL,"超级玛丽"); //找到目标进程的窗口句柄
if(destWnd!=NULL)
{
//根据窗口句柄找到进程ID,存储在destProcessID中
destThreadID=::GetWindowThreadProcessId(destWnd,&destProcessID);
}else
cout<<"Failed FindWnd and get ProcessID\n";
cout<<"Dest ThreadID: "<<(int)destThreadID<<"\nDest ProcessID: "<<(int)destProcessID<<endl;
//
int doRead=0,doWrite=0; //两个变量都默认为0,即默认不对目标进程进行读操作和写操作
int iVal;
g_hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,destProcessID); //获得目标进程的句柄
if(g_hProcess!=NULL){
cout<<"will you read process? 0:no, !0:yes\n";
cin>>doRead;
if(doRead!=0){
printf("Input var=");
scanf("%d",&iVal);
FindFirst(iVal);
showList();
int x=0;
while(g_nListCnt>1){
printf("input iVal=");
scanf("%d",&iVal);
FindNext(iVal);
showList();
x++;
if(x>10); break;
}
}
//write memorry
if (g_nListCnt==1)
{
cout<<"will you write process? 0:no !0:yes\n";
cin>>doWrite;
}
if (doWrite!=0)
{
printf("New Value=");
scanf("%d",&iVal);
if (WriteMemory(g_arList[0],iVal))
printf("Success write\n");
else
printf("Failed Write\n");
}
CloseHandle(g_hProcess); //如果打开了句柄,必须关闭
}
else
cout<<"Failed OpenHandle()\n";
return 0;