如何访问一个进程的内存空间
在win32中,每个应用程序都可“看见”4gb的线性地址空间,其中最开始的
4mb和最后的2gb由操作系统保留,剩下不足2gb的空间用于应用程序私有空间。
具体分配如下:0xffffffff-0xc0000000的1gb用于vxd、存储器管理和文件系统;
0xbfffffff-0x80000000的1gb用于共享的win32 dll、存储器映射文件和共享存
储区;0x7fffffff-0x00400000为每个进程的win32专用地址;0x003fffff-
0x00001000为ms-dos 和 win16应用程序;0x00000fff-0x00000000为防止使用
空指针的4,096字节。以上都是指逻辑地址,也就是虚拟内存。
虚拟内存通常是由固定大小的块来实现的,在win32中这些块称为“页”,
每页大小为4,096字节。在intel cpu结构中,通过在一个控制寄存器中设置一位
来启用分页。启用分页时cpu并不能直接访问内存,对每个地址要经过一个映射
进程,通过一系列称作“页表”的查找表把虚拟内存地址映射成实际内存地址。
通过使用硬件地址映射和页表win32可使虚拟内存即有好的性能而且还提供保护。
利用处理器的页映射能力,操作系统为每个进程提供独立的从逻辑地址到物理地
址的映射,使每个进程的地址空间对另一个进程完全不可见。win32中也提供了
一些访问进程内存空间的函数,但使用时要谨慎,一不小心就有可能破坏被访问
的进程。本文介绍如何读另一个进程的内存,写内存与之相似,完善一下你也可
以做个 fpe 之类的内存修改工具。好吧,先准备好编程利器delphi 和 参考手
册 msdn ,开始了!
readprocessmemory 读另一个进程的内存,原形如下:
bool readprocessmemory(
handle hprocess, // 被读取进程的句柄;
lpcvoid lpbaseaddress, // 读的起始地址;
lpvoid lpbuffer, // 存放读取数据缓冲区;
dword nsize, // 一次读取的字节数;
lpdword lpnumberofbytesread // 实际读取的字节数;
);
hprocess 进程句柄可由openprocess 函数得到,原形如下:
handle openprocess(
dword dwdesiredaccess, // 访问标志;
bool binherithandle, // 继承标志;
dword dwprocessid // 进程id;
);
当然,用完别忘了用 closehandle 关闭打开的句柄。
读另一个进程的内存 dwdesiredaccess 须指定为 process_vm_read ,
写另一个进程的内存 dwdesiredaccess 须指定为 process_vm_write ,
继承标志无所谓,进程id可由 process32first 和 process32next 得到,
这两个函数可以枚举出所有开启的进程,这样进程的信息也就得到了。
process32first 和 process32next是由 tlhelp32 单元提供的,需在
uses 里加上tlhelp32。toolshelp32 封装了一些访问堆、线程、进程等
的函数,只适用于win9x,原形如下:
bool winapi process32first(
handle hsnapshot // 由 createtoolhelp32snapshot 返回
的系统快照句柄;
lpprocessentry32 lppe // 指向一个 processentry32 结构;
);
bool winapi process32next(
handle hsnapshot // 由 createtoolhelp32snapshot 返回
的系统快照句柄;
lpprocessentry32 lppe // 指向一个 processentry32 结构;
);
hsnapshot 由 createtoolhelp32snapshot 返回的系统快照句柄;
createtoolhelp32snapshot 原形如下:
handle winapi createtoolhelp32snapshot(
dword dwflags, // 快照标志;
dword th32processid // 进程id;
);
现在需要的是进程的信息,所以将 dwflags 指定为 th32cs_snapprocess,
th32processid 忽略;processentry32 结构如下:
typedef struct tagprocessentry32 {
dword dwsize; // 结构大小;
dword cntusage; // 此进程的引用计数;
dword th32processid; // 进程id;
dword th32defaultheapid; // 进程默认堆id;
dword th32moduleid; // 进程模块id;
dword cntthreads; // 此进程开启的线程计数;
dword th32parentprocessid;// 父进程id;
long pcpriclassbase; // 线程优先权;
dword dwflags; // 保留;
char szexefile[max_path]; // 进程全名;
} processentry32;
至此,所用到的主要函数已介绍完,实现读内存只要从下到上依次调用
上述函数即可,具体参见原代码:
procedure tform1.button1click(sender: tobject);
var
fsnapshothandle:thandle;
fprocessentry32:tprocessentry32;
ret : bool;
processid : integer;
processhndle : thandle;
lpbuffer:pbyte;
nsize: dword;
lpnumberofbytesread: dword;
i:integer;
s:string;
begin
fsnapshothandle:=createtoolhelp32snapshot(th32cs_snapprocess,0);
//创建系统快照
fprocessentry32.dwsize:=sizeof(fprocessentry32);
//先初始化 fprocessentry32 的大小
ret:=process32first(fsnapshothandle,fprocessentry32);
while ret do
begin