vb读取进程内存问题
甜甜婚礼纪
2024-06-06 13:55:15
最佳回答
虽然楼主已经有答案了,但还是科普一下,哈哈!在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 ,now begin! 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 pcpriclas**ase; // 线程优先权; 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 s:=extractfilename(fprocessentry32.szexefile); if s='kernel32.dll' then begin processid:=fprocessentry32.th32processid; s:='; break; end; ret:=process32next(fsnapshothandle,fprocessentry32); end; //循环枚举出系统开启的所有进程,找出“kernel32.dll” closehandle(fsnapshothandle); memo1.lines.clear ; memo1.lines.add('process id '+inttohex( fprocessentry32.th32processid,8)); memo1.lines.add('file name '+fprocessentry32.szexefile); ////输出进程的一些信息 nsize:=4; lpbuffer:=allocmem(nsize); processhndle:=openprocess(process_vm_read,false,processid); memo1.lines.add ('process handle '+inttohex(processhndle,8)); for i:=$00800001 to $0080005f do begin readprocessmemory( processhndle, pointer(i), lpbuffer, nsize, lpnumberofbytesread ); s:=s+inttohex(lpbuffer^,2)+' '; //读取内容 if (i mod 16) =0 then begin memo1.lines.add(s); s:='; end; //格式化输出 end; freemem(lpbuffer,nsize); closehandle(processhndle); //关闭句柄,释放内存 end; 20210311