理论
Windows 是多任务的操作系统, 一个任务就是一个应用(应用程序)、一个应用占一个进程; 在一个进程里面, 又可以运行多个线程(所以就有了很多"多线程编程"的话题).
--------------------------------------------------------------------------------
对 Win32 来讲, 系统给每个进程 4GB 的地址空间:
低端 2GB($00000000 - $7FFFFFFF) 给用户支配;
高端 2GB($80000000 - $FFFFFFFF) 留给系统使用.
--------------------------------------------------------------------------------
文件或程序要调入内存才能工作, 先看看我们的内存到底有多大吧.
在系统盘根目录下有个 pagefile.sys 文件, 这就是我们的 "虚拟内存"(虚拟内存是以文件的形式存在的).
把 pagefile.sys 叫做 "虚拟内存" 似乎不妥, 所谓的 "虚拟" 只是相对真实的物理内存(RAM)来讲的; 很多书上的 "物理内存" 指的其实是: RAM + 虚拟内存, 也就是所有可用内存.
"虚拟内存" 在有些书上也被称作 "页文件" 、"页面文件" 或 "交换文件". "虚拟内存" 的大小可以从 "控制面板" 里设置, 默认是由系统自动管理的.
使用 "虚拟内存" 是系统的机制, 不管 RAM 有多大, 也应该使用 "虚拟内存".
RAM 大了, 系统就会少用 "虚拟内存", 从而提高速度; 但 RAM 也不是越大越好, 如果你真的放 4G 的内存条, 系统能够识别并使用的也就是 3G 左右, 因为 Win32 只有 4G 的管理能力(寻址能力), 当然这在 Win64 下要另当别论.
--------------------------------------------------------------------------------
所谓系统给每个程序 4G, 是给 4G 的 "虚拟的地址表", 绝不是真实的内存, 不然一个记事本、一个计算器就得需要 8G.
这个 "虚拟的地址表" 在有些书上叫 "虚地址表"、"页映射表" 或 "虚内存地址", 也有叫 "虚拟内存地址", 很容易和 "虚拟内存" 的概念混淆.
这个 "虚拟的地址表" 上有 4G 个(4294967296 个)地址(0 - $FFFFFFFF), 虽然每个程序都有这样一个表, 但它们并不会冲突, 就因为这些地址是虚拟的, 系统在需要的时候会把它们映射成具体的真实内存的地址. 这样就阻断了一个进程对另一个进程的访问.
在 Win2000 以前的版本中, 用 GlobalAlloc 申请公用内存, 用 LocalAlloc 申请私有内存; 现在通过 "虚拟的地址表" 使用内存, 在进程中申请的内存都是私有的, 现在的 GlobalAlloc、LocalAlloc 没有区别, 都是执行同样的代码.
如果需要跨进程的公用内存空间, 需要用 "内存映射" 等手段, 这需要再专题学习.
--------------------------------------------------------------------------------
总结概念: 物理内存、虚拟内存、虚地址表.
函数 GlobalMemoryStatus 可以获取它们的信息, 获取后放在 TMemoryStatus 结构中.
--------------------------------------------------------------------------------
//TMemoryStatus 是 _MEMORYSTATUS 的重命名:_MEMORYSTATUS = record dwLength: DWORD; {结构长度} dwMemoryLoad: DWORD; {表示已使用的内存比例的一个整数} dwTotalPhys: DWORD; {物理内存总数} dwAvailPhys: DWORD; {可用物理内存总数} dwTotalPageFile: DWORD; {虚拟内存总数} dwAvailPageFile: DWORD; {可用虚拟内存总数} dwTotalVirtual: DWORD; {虚地址表中的地址总数} dwAvailVirtual: DWORD; {虚地址表中可用的地址总数}end;
--------------------------------------------------------------------------------
做个小程序看看内存情况:
--------------------------------------------------------------------------------
unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;type TForm1 = class(TForm) Memo1: TMemo; procedure FormCreate(Sender: TObject); end;var Form1: TForm1;implementation{$R *.dfm}procedure TForm1.FormCreate(Sender: TObject);var m: TMemoryStatus;const num = 1024 * 1024;begin GlobalMemoryStatus(m); Memo1.Clear; with Memo1.Lines do begin Add(Format('dwLength:' + #9 + '%d', [m.dwLength])); Add(Format('dwMemoryLoad:' + #9 + '%d', [m.dwMemoryLoad])); Add(Format('dwTotalPhys:' + #9 + '%d', [m.dwTotalPhys div num])); Add(Format('dwAvailPhys:' + #9 + '%d', [m.dwAvailPhys div num])); Add(Format('dwTotalPageFile:' + #9 + '%d', [m.dwTotalPageFile div num])); Add(Format('dwAvailPageFile:' + #9 + '%d', [m.dwAvailPageFile div num])); Add(Format('dwTotalVirtual:' + #9 + '%d', [m.dwTotalVirtual div num])); Add(Format('dwAvailVirtual:' + #9 + '%d', [m.dwAvailVirtual div num])); end;end;end.
--------------------------------------------------------------------------------
系统给程序的地址数是 4G, 为什么不是 3G 或 5G? 因为 32 位的指针的最大值就是 $FFFFFFFF, 它不能表示更多了, 究其根源这要回到 CPU 的寻址能力、地址总线等等.
在 Win64 下, 系统给程序的地址数达到了 16EB(0 - $FFFFFFFFFFFFFFFF), 也就是 18446744073709551616 个. 不过 Win64 还没有普及, 我们还得回到实际的 Win32.
就这 4G 的地址, 系统还要留下一半($80000000 - $FFFFFFFF, 这 2G 是各进程共享的)用作宏观管理; 只给程序 2G(0 - $7FFFFFFF).
就这 2G 的地址, 也不是全给用户的, 低端的 0 - $FFFF 是用于空指针分配, 禁止访问; 高端的 $7FFF0000 - $7FFFFFFF 也留出来作为进程的临界区, 也禁止访问. 其实进程的私有空间地址只有 $10000 - $7FEFFFF.
上面这个结果, 我们可以通过 GetSystemInfo 函数得到证实, 通过 GetSystemInfo 函数能获取一个 TSystemInfo 结构, 结构中的 lpMinimumApplicationAddress 和 lpMaximumApplicationAddress 分别表示程序(或动态链接库)可以访问的最低与最高的内存地址.
--------------------------------------------------------------------------------
var
si: TSystemInfo;
begin
GetSystemInfo(si);
ShowMessageFmt('%p-%p', [si.lpMinimumApplicationAddress, si.lpMaximumApplicationAddress]);
{结果是: 00010000-7FFEFFFF}
end;
--------------------------------------------------------------------------------
通过 GetSystemInfo 还能得到一个内存相关的重要参数: 页大小(PageSize)
--------------------------------------------------------------------------------
var
si: TSystemInfo;
begin
GetSystemInfo(si);
ShowMessage(IntToStr(si.dwPageSize)); {4096; 4096 字节也就是 4K}
end;
--------------------------------------------------------------------------------
PageSize 是系统管理内存的基本单位, 之所以需要用 GetSystemInfo 获取不同系统的 PageSize 也会有区别.
我们需要知道的是, 用 VirtualAlloc 函数分配的内存就是以 PageSize(4K) 为最小单位的; 假如我们用 VirtualAlloc 给一个整数(4个字节)分配内存, 将会浪费 4092 个字节, 也就是说 VirtualAlloc 不适合分配小内存, 因而也有了多种分配内存的函数.
--------------------------------------------------------------------------------
暂时放下这个话题, 先了解一下 "栈".
说到 "栈", 就想到 "堆", 还有 "堆栈" 指的也是 "栈"; "栈" 与 "堆" 都是程序可操作的内存区域($10000 - $FFEFFFF)中的某一小段.
系统函数中有 HeapReAlloc、GlobalAlloc 等分配 "堆" 的函数, 却没有分配 "栈" 的函数, 这是因为 "栈" 是程序自动管理的; 每个程序都从自己的可用地址范围内留出一块作为 "栈", 程序根据需要可以自动调节它的大小, 但咱们可以设置它的最大值与最小值. 在Delphi 中可以从这里设置:
Project -> Options -> Linker -> [Min stack size 和 Max stack size]
"栈" 用来暂存局部变量和函数参数, 由程序在需要时申请, 用完就释放.
因为 "栈" 的空间一般不是很大, 所以咱们一般不要把局部变量弄得太大(特别是在使用数组的时候);
因为访问 "栈" 比访问 "堆" 来的简洁, 速度快, 所以要尽量多用局部变量、少用全局变量.
VirtualAlloc 分配的内存是以 4K 为最小单位、连续的内存地址(但映射到真实的内存时它不一定是连续的), 前面说了, 它不适合分配小内存(譬如只有几个字节的变量); 局部的变量在 "栈" 中有程序自动管理, 那么那些全局的小变量怎么办呢? 这就要用到 "堆".
这样看来, VirtualAlloc 分配的内存既不是 "栈" 也不是 "堆"; VirtualAlloc 分配的内存地址是连续的, "堆" 中内容一般是不连续的, 所以管理 "堆" 比较麻烦, 它是通过双线链表的结构方式管理的; 程序可以拥有若干个 "堆", 每一个 "堆" 都会有一个句柄, 访问 "堆" 中的内容时先要找到这个 "堆", 然后再遍历链表, 这可能就是 "堆" 比 "栈" 慢的根本原因.
--------------------------------------------------------------------------------
在 "堆" 中分配内存(HeapAlloc)前先要建立 "堆"(HeapCreate), 就像程序有默认的 "栈" 一样, 每一个程序都有一个默认建立的 "堆"(可以用 GetProcessHeap 获取这个 "默认堆" 的句柄), 我们在 Delphi 中用到 "堆" 时, 使用的就是这个 "默认堆". 如果让程序更灵活地拥有多个 "堆", 必须要用到 API 函数.
建立 "堆" 时会同时提交真实内存的, 这在申请大内存时会很慢, 所以默认堆也只有 1M, 但 "默认堆" 并没有限制大小, 它会根据需要动态增长.
有了 "默认堆" 还有必要申请其他的 "堆" 吗? 这只有在多线程中才能体现出来, 和 "栈" 不一样, 程序会给每个线程分配一个 "栈区"; 而 "默认堆" 是进程中的所有线程公用的, 当一个线程使用 "默认堆" 时, 另一个需要使用 "堆" 的线程就要先挂起等待, 也就是它们不能同时使用; 只有通过 API 函数重新建立的私有堆才是互不干涉、最有效率的.
--------------------------------------------------------------------------------
先了解一下 "堆" 相关的函数.
--------------------------------------------------------------------------------
//建立堆; 注意建立时指定的尺寸也是按页大小(PageSize)对齐的, 譬如指定 15k, 实际会分配 16K.
HeapCreate(
flOptions: DWORD; {堆属性选项, 见下表}
dwInitialSize: DWORD; {初始尺寸, 单位是字节; 该大小会被直接提交到实际的内存}
dwMaximumSize: DWORD {最大尺寸, 如果不限定最大值就设为 0}
): THandle; {返回堆句柄; 失败返回 0, 但如果参数 flOptions 允许了异常, 失败会返回异常标识}
//flOptions 参数可选值:
HEAP_NO_SERIALIZE = 1; {非互斥, 此标记可允许多个线程同时访问此堆}
HEAP_GENERATE_EXCEPTIONS = 4; {当建立堆出错时, 此标记可激发一个异常并返回异常标识}
HEAP_ZERO_MEMORY = 8; {把分配的内存初始化为 0}
//flOptions 参数指定有 HEAP_GENERATE_EXCEPTIONS 时, 可能返回的异常:
STATUS_ACCESS_VIOLATION = DWORD($C0000005); {参数错误}
STATUS_NO_MEMORY = DWORD($C0000017); {内存不足}
--------------------------------------------------------------------------------
//销毁堆
HeapDestroy(
hHeap: THandle {堆句柄}
): BOOL; {}
--------------------------------------------------------------------------------
//从堆中申请内存
HeapAlloc(
hHeap: THandle; {堆句柄}
dwFlags: DWORD; {内存属性选项, 见下表}
dwBytes: DWORD {申请内存的大小, 单位是字节}
): Pointer; {返回内存指针; 失败返回 0 或异常, 情况和建立堆是一样}
//dwFlags 参数可选值:
HEAP_NO_SERIALIZE = 1; {非互斥, 此标记可允许多个线程同时访问此堆}
HEAP_GENERATE_EXCEPTIONS = 4; {当建立堆出错时, 此标记可激发一个异常并返回异常标识}
HEAP_ZERO_MEMORY = 8; {把分配的内存初始化为 0}
{能看出这和堆的属性选项是一样的; 如果 dwFlags 参数设为 0, 将使用堆的属性; 如果重新指定将覆盖堆的属性}
{另外: 如果堆是默认堆, 也就是堆句柄来自 GetProcessHeap, dwFlags 参数会被忽略}
--------------------------------------------------------------------------------
//改变堆内存的大小, 也就是重新分配
HeapReAlloc(
hHeap: THandle; {句柄}
dwFlags: DWORD; {内存属性选项; 该参数比 HeapAlloc 多出一个选项, 见下表}
lpMem: Pointer; {原内存指针}
dwBytes: DWORD {新的尺寸}
): Pointer; {同 HeapAlloc}
//dwFlags 参数可选值:
HEAP_NO_SERIALIZE = 1; {非互斥, 此标记可允许多个线程同时访问此堆}
HEAP_GENERATE_EXCEPTIONS = 4; {当建立堆出错时, 此标记可激发一个异常并返回异常标识}
HEAP_ZERO_MEMORY = 8; {把分配的内存初始化为 0}
HEAP_REALLOC_IN_PLACE_ONLY = 16; {此标记不允许改变原来的内存位置}
--------------------------------------------------------------------------------
//获取堆中某块内存的大小
HeapSize(
hHeap: THandle; {堆句柄}
dwFlags: DWORD; {内存属性; 可选值是 0 或 HEAP_NO_SERIALIZE, 后者可确保同步访问}
lpMem: Pointer {内存指针}
): DWORD; {成功返回字节为单位的大小; 失败返回 $FFFFFFFF}
--------------------------------------------------------------------------------
//释放堆中指定的内存块
HeapFree(
hHeap: THandle; {堆句柄}
dwFlags: DWORD; {内存属性; 可选值是 0 或 HEAP_NO_SERIALIZE}
lpMem: Pointer {内存指针}
): BOOL; {}
--------------------------------------------------------------------------------
//验证堆
HeapValidate(
hHeap: THandle; {}
dwFlags: DWORD; {}
lpMem: Pointer {}
): BOOL; {}
--------------------------------------------------------------------------------
//整理堆
HeapCompact(
hHeap: THandle; {}
dwFlags: DWORD {}
): UINT; {}
--------------------------------------------------------------------------------
//锁定堆
HeapLock(
hHeap: THandle {}
): BOOL; {}
--------------------------------------------------------------------------------
//锁定后的解锁
HeapUnlock(
hHeap: THandle {}
): BOOL; {}
--------------------------------------------------------------------------------
//列举堆中的内存块
HeapWalk(
hHeap: THandle; {}
var lpEntry: TProcessHeapEntry {}
): BOOL; {}
delphi 内存读取、修改
在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
{=============================================}
{=============的到进程的ID=====================}
{=============================================}
function TReadMemory.GetProcessInfo: TList;
var
ProcessInfoList : TList;
ProcessInfo : PProcessInfo;
hSnapShot : THandle;
mProcessEntry32 : TProcessEntry32;
bFound : Boolean;
begin
ProcessInfoList:=TList.Create;
ProcessInfoList.Clear;
hSnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
mProcessEntry32.dwSize := Sizeof(mProcessEntry32);
bFound := Process32First(hSnapShot, mProcessEntry32);
while bFound do
begin
New(ProcessInfo);
ProcessInfo.ProcessExe := mProcessEntry32.szExeFile;
ProcessInfo.ProcessId := mProcessEntry32.th32ProcessID;
ProcessInfoList.Add(ProcessInfo);
bFound := Process32Next(hSnapShot, mProcessEntry32);
end;
Result := ProcessInfoList;
end;
这里用到
1:CreateToolhelp32Snapshot()创建系统快照句柄(hSnapShot是我们声明用来保存
创建的快照句柄)
2:Process32First、Process32Next是用来枚举进程
{=============================================}
{=============内存查找=========================}
{=============================================}
function TReadMemoryFrm.StartSearch: Boolean;
var
ProcHandle:Integer;
begin
Result:=False;
ReadMemoryProgress.Position:=0;
if Not CheckInput then Exit;
if File>begin
PParameter.FirstSearch:=False;
PParameter.Data:=StrToInt(EdtSearchData.Text);
end else
begin //------------------------------第一次搜索
PParameter.FirstSearch:=True;
if PParameter.ProcessHandle>0 then
CloseHandle(PParameter.ProcessHandle);
ProcHandle:=OpenProcess(PROCESS_ALL_ACCESS,false,StrToInt(EdtProcID.Text));
if ProcHandle>0 then
begin
PParameter.Data:=StrToInt(EdtSearchData.Text);
Case DataType.ItemIndex of
0:PParameter.DataType:=1;
1:PParameter.DataType:=2;
2:PParameter.DataType:=4;
end;
end else Exit;
FileName:=TabSheet1.Caption;
PParameter.ProcessHandle:=ProcHandle;
end;
SearchButton.Enabled:=False;
ToolSearchMemory.Enabled:=False;
MemoryAddrList.Clear;
PReadMemory.StartSearch;
Result:=True;
end;
1:
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 希望获得的访问权限
BOOL bInheritHandle, // 指明是否希望所获得的句柄可以继承
DWORD dwProcessId // 要访问的进程ID
);
分析内存块
//----------------------------------------------------分析内存块
function TReadMemoryThread.GetMemoryRegion: Boolean;
var
TempStartAddress : DWord;
TempEndAddress : DWord;
I,J,k : Integer;
NewMemoryRegions : array [0..40000] of TmemoryRegion;
begin
Result:=False;
MemoryRegionsIndex := 0;
TempStartAddress := 1*1024*1024;
TempEndAddress := 2*1024*1024;
TempEndAddress := TempEndAddress*1024;
While (VirtualQueryEx(PParameter.ProcessHandle,
pointer(TempStartAddress),
MBI,
sizeof(MBI))>0) and (TempStartAddress<TempEndAddress) do
begin
if (MBI.State=MEM_COMMIT) then
begin
if (MBI.Protect=PAGE_READWRITE) or
(MBI.Protect=PAGE_WRITECOPY) or
(MBI.Protect=PAGE_EXECUTE_READWRITE) or
(MBI.Protect=PAGE_EXECUTE_WRITECOPY)
then
begin
PMemoryRegion[MemoryRegionsIndex].BaseAddress:=Dword(MBI.BaseAddress);
PMemoryRegion[MemoryRegionsIndex].MemorySize:=MBI.RegionSize;
Inc(MemoryRegionsIndex);
end;
end;
TempStartAddress:=Dword(MBI.BaseAddress)+MBI.RegionSize;
end;
if MemoryRegionsIndex=0 then Exit;
//---------------------------------------------判断内存块是否过大
J:=0;
for i:=0 to MemoryRegionsIndex-1 do
begin
if PMemoryRegion.MemorySize>$FFFF then
begin
for K:=0 to PMemoryRegion.MemorySize div $FFFF do
begin
if K=PMemoryRegion.MemorySize div $FFFF+1 then
begin
NewMemoryRegions[j].BaseAddress:=PMemoryRegion.BaseAddress+K*$FFFF;
NewMemoryRegions[j].MemorySize:=PMemoryRegion.MemorySize Mod $FFFF;
end else
begin
NewMemoryRegions[j].BaseAddress:=PMemoryRegion.BaseAddress+K*$FFFF;
NewMemoryRegions[j].MemorySize:=$FFFF;
end;
Inc(J);
end;
end else
begin
NewMemoryRegions[j].BaseAddress:=PMemoryRegion.BaseAddress;
NewMemoryRegions[j].MemorySize:=PMemoryRegion.MemorySize;
Inc(J);
end;
end;
//---------------------------------------------------数据转换
MemoryRegionsIndex:=j;
for i:=0 to MemoryRegionsIndex-1 do
begin
PMemoryRegion.MemorySize:=NewMemoryRegions.MemorySize;
PMemoryRegion.BaseAddress:=NewMemoryRegions.BaseAddress;
end;
Result:=True;
end;
1:查找的内存大小
TempStartAddress := 1*1024*1024;
TempEndAddress := 2*1024*1024;
TempEndAddress := TempEndAddress*1024;
2:VirtualQueryEx :查询地址空间中内存地址的信息。
参数:
hProcess 进程句柄。
LpAddress 查询内存的地址。
LpBuffer 指向MEMORY_BASIC_INFORMATION结构的指针,用于接收内存信息。
DwLength MEMORY_BASIC_INFORMATION结构的大小。
返回值:
函数写入lpBuffer的字节数,如果不等于sizeof(MEMORY_BASIC_INFORMATION)表示失败。
http://www.vckbase.com/vckbase/function/viewfunc.asp?id=139 讲了这个API的详细信息
{=============================================}
{=============开始查找=========================}
{=============================================}
procedure TReadMemoryThread.Execute;
var
//StopAddr,StartAddr:Dword;
BeginTime,EndTime:String;
I:Integer;
begin
inherited;
while Not Terminated do
begin
AddrCount := 0;
if PParameter.FirstSearch then
begin
if Not GetMemoryRegion then Exit;
GetMaxMemoryRange;
GetMinMemoryRange;
SendMessage(APPHandle,WM_READMEMORY,RM_MAXPROGRESS,MemoryRegionsIndex);
BeginTime:=FloatToStr(CPUTimeCounterQPC);
for I:=0 to MemoryRegionsIndex-1 do
begin
FirstCheckMemory(PMemoryRegion.BaseAddress,PMemoryRegion.MemorySize);
end;
EndTime:=FloatToStr(CPUTimeCounterQPC);
SendMessage(APPHandle,
WM_READMEMORY,
RM_USETIME,
StrToInt(Copy(EndTime,1,Pos('.',EndTime)-1))-StrToInt(Copy(BeginTime,1,Pos('.',BeginTime)-1)));
SendMessage(APPHandle,WM_READMEMORY,RM_ADDRCOUNT,AddrCount);
SendMessage(APPHandle,WM_READMEMORY,RM_FINISH,RM_FINISH);
end else
begin
SendMessage(APPHandle,WM_READMEMORY,RM_MAXPROGRESS,100);
BeginTime:=FloatToStr(CPUTimeCounterQPC);
for i:=0 to High(PSearchAgain) do
begin
SecondCheckMemory(PSearchAgain.DataAddr);
end;
EndTime:=FloatToStr(CPUTimeCounterQPC);
SendMessage(APPHandle,
WM_READMEMORY,
RM_USETIME,
StrToInt(Copy(EndTime,1,Pos('.',EndTime)-1))-StrToInt(Copy(BeginTime,1,Pos('.',BeginTime)-1)));
SendMessage(APPHandle,WM_READMEMORY,RM_ADDRCOUNT,AddrCount);
SendMessage(APPHandle,WM_READMEMORY,RM_FINISH,RM_FINISH);
end;
Suspend;
end;
end;
delphi 内存补丁
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Mask,ComCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
NewData : array[0..1] of byte = ($00,$00);
Bytesread : DWORD;
procedure TForm1.Timer1Timer(Sender: TObject);
var
hwin, pid: DWORD;
hprocess: DWORD;
begin
hwin := FindWindow(nil,'外挂窗口');
if hwin = 0 then
Form1.Label1.Caption:='目标程序未运行'
else
Form1.Label1.Caption:='目标程序已运行';
GetWindowThreadProcessId(hwin, pid);
hprocess := OpenProcess(PROCESS_ALL_ACCESS, False, pid);
if WriteProcessMemory(hProcess ,Pointer($00410000),@newdata,1 ,BytesRead ) then
begin
CloseHandle(hProcess);
Form1.Label2.Caption:='目标程序解密成功 ';
end
else
begin
CloseHandle(hProcess);
Form1.Label2.Caption:='目标程序解密失败 ';
end;
end;
end.
查看内存数据的函数
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
end;var
Form1: TForm1;implementation
{$R *.dfm}
{用十六进制查看内存的函数; 参数1是内存起点, 参数2是以字节为单位的长度}
function ToHex(p: PByteArray; b: Integer): string;
var
i: Integer;
begin
for i := 0 to b - 1 do
Result := IntToHex(p^, 2) + Chr(32) + Result;
Result := TrimRight(Result);
end;{用二进制查看内存的函数; 参数1是内存起点, 参数2是以字节为单位的长度}
function ToBin(p: PByteArray; b: Integer): string;
const
Convert: array['0'..'F'] of string = (
'0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001',
'', '', '', '', '', '', '', '1010', '1011', '1100', '1101', '1110', '1111');
var
i: Integer;
s: string;
begin
s := ToHex(p, b);
for i := 1 to Length(s) do
if s <> Chr(32) then
Result := Result + Convert[s]
else
Result := Result + Chr(32);
end;{测试一}
procedure TForm1.Button1Click(Sender: TObject);
var
num: Integer;
begin
Randomize;
num := Random(MaxInt);
ShowMessage(IntToStr(num) + #10#13#10#13 +
ToHex(@num, 4) + #10#13#10#13 +
ToBin(@num, 4));
end;{测试二}
procedure TForm1.Button2Click(Sender: TObject);
var
str: string;
begin
str := 'Delphi 2010';
ShowMessage(str + #10#13#10#13 +
ToHex(@str[1], Length(str)*SizeOf(str[1])) + #10#13#10#13 +
ToBin(@str[1], Length(str)*SizeOf(str[1])));
end;end.
--------------------------------------------------------------------------------重写:
--------------------------------------------------------------------------------
function ToBin2(p: PByteArray; b: Integer): string;
var
i,j: Integer;
begin
Result := StringOfChar('0', b * 8);
for i := 0 to b - 1 do for j := 0 to 7 do
if Odd(p^[b-1-i] shr j) then Result[i*8 + 8 - j] := '1';
end;procedure TForm1.Button1Click(Sender: TObject);
var
f: Single;
str: string;
begin
f := -31.4;
str := ToBin2(@f, SizeOf(f));
Memo1.Lines.Add(str);
end;