一、结论:
只可以间接修改
二、原因:
32位字长的Windows 环境是一个“多任务单用户”操作系统,运行在X86或者兼容CPU的保护模式上,此模式使每个正在运行的程序(准确的叫法是“进程”,即Windows所调度的“任务”)都有自己独立的4GB逻辑内存空间,所谓保护就是每个进程的4GB内存地址逻辑上彼此独立,CPU和Windows保护每个独立进程的4GB逻辑内存空间不被其他用户进程直接修改。
而且4GB中只有2GB属于本进程专有地址,另外2GB属于Windows系统保护的系统内存空间,受windows系统严格保护,不允许用户进程直接读写只能通过“内核对象”间接访问。
所以您通过某个软件看到了内存地址0x0467A618的内容并试图用自编的VC程序修改时,实际上只能修改自编VC程序运行实例(即“进程”)的同编号进程地址的内容,而不能修改原来的那个进程的内存地址内容。
三、解决方案:
1)思路:
a.启动您希望修改其内容的进程,记下您希望修改的地址的内容;
b.编写一个VC程序,先按照进程名字获取进程ID,然后搜索这个ID的进程中,非系统内存区域中匹配这个内容的内存地址(注意:从Windows 2000/XP开始的版本,系统保留区域为64K~2GB,2GB以上的才是用户进程内存区域)
c.找到内容匹配的内存地址后,将其修改为你希望的值。
2)具体编程方法:
a.建立一个名字为MemRepair的VC工程(选择32位控制台工程,使用预编译头)
b.陆续加入以下三个文件:
MemRepair.h MemRepair.cpp MainProg.cpp,分别键入以下源代码:
/*(完整VC工程源代码,已经在WinXP+VC2008Express环境调试通过)*/
/* *************** 新文件 ********************* */
/* ******************************************** */
//StdAfx.h
#if !defined(AFX_STDAFX_H__7438D592_DA27_443E_824E_D280AF259D3F__INCLUDED_)
#define AFX_STDAFX_H__7438D592_DA27_443E_824E_D280AF259D3F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// TODO: reference additional headers your program requires here
#include "MemRepair.h"
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_STDAFX_H__7438D592_DA27_443E_824E_D280AF259D3F__INCLUDED_)
/* **************新文件**************** */
/* ************************************ */
//MemRepair.h
#ifndef __MEMFINDER_H__
#define __MEMFINDER_H__
#include <windows.h>
class CMemFinder
{
public:
CMemFinder(DWORD dwProcessId);
virtual ~CMemFinder();
// 属性
public:
BOOL IsFirst() const { return m_bFirst; }
BOOL IsValid() const { return m_hProcess != NULL; }
int GetListCount() const { return m_nListCnt; }
DWORD operator [](int nIndex) { return m_arList[nIndex]; }
// 操作
virtual BOOL FindFirst(DWORD dwValue);
virtual BOOL FindNext(DWORD dwValue);
virtual BOOL WriteMemory(DWORD dwAddr, DWORD dwValue);
// 实现
protected:
virtual BOOL CompareAPage(DWORD dwBaseAddr, DWORD dwValue);
DWORD m_arList[1024]; // 地址列表
int m_nListCnt; // 有效地址的个数
HANDLE m_hProcess; // 目标进程句柄
BOOL m_bFirst; // 是不是第一次搜索
};
#endif // __MEMFINDER_H__
/* ************** 新文件 ************** */
/* ************************************ */
//MemRepair.cpp
#include "MemRepair.h"
#include "StdAfx.h"
CMemFinder::CMemFinder(DWORD dwProcessId)
{
m_nListCnt = 0;
m_bFirst = TRUE;
m_hProcess = ::OpenProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, FALSE, dwProcessId);
}
CMemFinder::~CMemFinder()
{
if(m_hProcess != NULL)
::CloseHandle(m_hProcess);
}
BOOL CMemFinder::FindFirst(DWORD dwValue)
{
const DWORD dwOneGB = 1024*1024*1024; // 1GB
const DWORD dwOnePage = 4*1024; // 4KB
if(m_hProcess == NULL)
return FALSE;
// 查看操作系统类型,以决定开始地址
DWORD dwBase;
OSVERSIONINFO vi = { sizeof(vi) };
::GetVersionEx(&vi);
if (vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
dwBase = 4*1024*1024; // Windows 98系列,4MB
else
dwBase = 640*1024; // Windows NT系列,64KB
// 在开始地址到2GB的地址空间进行查找
for(; dwBase < 2*dwOneGB; dwBase += dwOnePage)
{
// 比较1页大小的内存
CompareAPage(dwBase, dwValue);
}
m_bFirst = FALSE;
return TRUE;
}
BOOL CMemFinder::CompareAPage(DWORD dwBaseAddr, DWORD dwValue)
{
// 读取1页内存
BYTE arBytes[4096];
if(!::ReadProcessMemory(m_hProcess, (LPVOID)dwBaseAddr, arBytes, 4096, NULL))
return FALSE; // 此页不可读
// 在这1页内存中查找
DWORD* pdw;
for(int i=0; i<(int)4*1024-3; i++)
{
pdw = (DWORD*)&arBytes[i];
if(pdw[0] == dwValue) // 等于要查找的值?
{
if(m_nListCnt >= 1024)
return FALSE;
// 添加到全局变量中
m_arList[m_nListCnt++] = dwBaseAddr + i;
}
}
return TRUE;
}
BOOL CMemFinder::FindNext(DWORD dwValue)
{
// 保存m_arList数组中有效地址的个数,初始化新的m_nListCnt值
int nOrgCnt = m_nListCnt;
m_nListCnt = 0;
// 在m_arList数组记录的地址处查找
BOOL bRet = FALSE; // 假设失败
DWORD dwReadValue;
for(int i=0; i<nOrgCnt; i++)
{
if(::ReadProcessMemory(m_hProcess, (LPVOID)m_arList[i], &dwReadValue, sizeof(DWORD), NULL))
{
if(dwReadValue == dwValue)
{
m_arList[m_nListCnt++] = m_arList[i];
bRet = TRUE;
}
}
}
return bRet;
}
BOOL CMemFinder::WriteMemory(DWORD dwAddr, DWORD dwValue)
{
return ::WriteProcessMemory(m_hProcess, (LPVOID)dwAddr, &dwValue, sizeof(DWORD), NULL);
}
/* ************** 新文件 ************** */
/* ************************************ */
//主程序mainprog.cpp
#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
#include <iostream>
using namespace std;
BOOL FindFirst(DWORD dwValue); // 在目标进程空间进行第一次查找
BOOL FindNext(DWORD dwValue); // 在目标进程地址空间进行第2、3、4……次查找
DWORD g_arList[1024]; // 地址列表
int g_nListCnt; // 有效地址的个数
HANDLE g_hProcess; // 目标进程句柄
//////////////////////
BOOL WriteMemory(DWORD dwAddr, DWORD dwValue);
void ShowList();
int main(int argc, char* argv[])
{
// 启动02testor进程
char szFileName[] = "..\\02testor\\debug\\02testor.exe";
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
::CreateProcess(NULL, szFileName, NULL, NULL, FALSE,
CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
// 关闭线程句柄,既然我们不使用它
::CloseHandle(pi.hThread);
g_hProcess = pi.hProcess;
// 输入要修改的值
int iVal;
printf(" Input val = ");
scanf("%d", &iVal);
// 进行第一次查找
FindFirst(iVal);
// 打印出搜索的结果
ShowList();
while(g_nListCnt > 1)
{
printf(" Input val = ");
scanf("%d", &iVal);
// 进行下次搜索
FindNext(iVal);
// 显示搜索结果
ShowList();
}
// 取得新值
printf(" New value = ");
scanf("%d", &iVal);
// 写入新值
if(WriteMemory(g_arList[0], iVal))
printf(" Write data success \n");
::CloseHandle(g_hProcess);
return 0;
}
BOOL CompareAPage(DWORD dwBaseAddr, DWORD dwValue)
{
// 读取1页内存
BYTE arBytes[4096];
if(!::ReadProcessMemory(g_hProcess, (LPVOID)dwBaseAddr, arBytes, 4096, NULL))
return FALSE; // 此页不可读
// 在这1页内存中查找
DWORD* pdw;
for(int i=0; i<(int)4*1024-3; i++)
{
pdw = (DWORD*)&arBytes[i];
if(pdw[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; // 1GB
const DWORD dwOnePage = 4*1024; // 4KB
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; // Windows 98系列,4MB
else
dwBase = 640*1024; // Windows NT系列,64KB
// 在开始地址到2GB的地址空间进行查找
for(; dwBase < 2*dwOneGB; dwBase += dwOnePage)
{
// 比较1页大小的内存
CompareAPage(dwBase, dwValue);
}
return TRUE;
}
BOOL FindNext(DWORD dwValue)
{
// 保存m_arList数组中有效地址的个数,初始化新的m_nListCnt值
int nOrgCnt = g_nListCnt;
g_nListCnt = 0;
// 在m_arList数组记录的地址处查找
BOOL bRet = FALSE; // 假设失败
DWORD dwReadValue;
for(int i=0; i<nOrgCnt; i++)
{
if(::ReadProcessMemory(g_hProcess, (LPVOID)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]);
}
}
BOOL WriteMemory(DWORD dwAddr, DWORD dwValue)
{
return ::WriteProcessMemory(g_hProcess, (LPVOID)dwAddr, &dwValue, sizeof(DWORD), NULL);
}
参考资料:人民邮电出版出版、王艳平编著的《Windows程序设计》一书的第二章第五节