📄 memorydump.pas
字号:
unit MemoryDump;
{$I JCL.INC}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, ToolWin, ActnList, ExtCtrls, ViewTemplate, Menus;
type
TMemoryInfo = packed record
MemInfo: TMemoryBasicInformation;
RepeatedItem, MappedFile: Boolean;
end;
TMemoryDumpForm = class(TViewForm)
StatusBar: TStatusBar;
PagesListView: TListView;
Splitter1: TSplitter;
DumpListView: TListView;
ToolButton5: TToolButton;
ToolButton6: TToolButton;
MemoryTreeView: TTreeView;
Splitter2: TSplitter;
ToolButton1: TToolButton;
ToolButton2: TToolButton;
ToolButton3: TToolButton;
ToolButton4: TToolButton;
Refresh2: TMenuItem;
N1: TMenuItem;
Copy2: TMenuItem;
Save1: TMenuItem;
N2: TMenuItem;
Selectall2: TMenuItem;
ViewAsText1: TAction;
ToolButton7: TToolButton;
ToolButton8: TToolButton;
N3: TMenuItem;
Viewastext2: TMenuItem;
SaveData1: TAction;
ToolButton9: TToolButton;
Savedata2: TMenuItem;
SaveDataDialog: TSaveDialog;
ToolButton10: TToolButton;
procedure Refresh1Execute(Sender: TObject);
procedure DumpListViewData(Sender: TObject; Item: TListItem);
procedure PagesListViewSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
procedure FormDestroy(Sender: TObject);
procedure PagesListViewCustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
procedure FormCreate(Sender: TObject);
procedure PagesListViewData(Sender: TObject; Item: TListItem);
procedure MemoryTreeViewChange(Sender: TObject; Node: TTreeNode);
procedure MemoryTreeViewGetSelectedIndex(Sender: TObject;
Node: TTreeNode);
procedure ViewAsText1Execute(Sender: TObject);
procedure SaveData1Update(Sender: TObject);
procedure SaveData1Execute(Sender: TObject);
private
FDumpBytesPerLine: Integer;
FProcessID: DWORD;
FProcess: THandle;
FFileName: TFileName;
FMemoryInfo: array of TMemoryInfo;
FModulesList: TStringList;
procedure BuildPagesList;
procedure BuildModulesList;
procedure UpdateDumpList;
public
procedure SetParams(ProcessID: DWORD; const FileName: TFileName);
end;
var
MemoryDumpForm: TMemoryDumpForm;
implementation
uses Global, TLHelp32, ToolsUtils, FindDlg, JclBase;
{$R *.DFM}
resourcestring
sAllocations = 'Allocations';
sCaption = 'Virtual Memory list - %s';
sCommited = 'Comitted: %.0n';
sCount = 'Count: %d';
sModules = 'Modules';
sReserved = 'Reserved: %.0n';
function AllocationProtectStr(P: DWORD): string;
begin
case P of
PAGE_NOACCESS:
Result := 'NoAccess';
PAGE_READONLY:
Result := 'ReadOnly';
PAGE_READWRITE:
Result := 'ReadWrite';
PAGE_WRITECOPY:
Result := 'WriteCopy';
PAGE_EXECUTE:
Result := 'Exec';
PAGE_EXECUTE_READ:
Result := 'ExecRead';
PAGE_EXECUTE_READWRITE:
Result := 'ExecReadWrite';
PAGE_EXECUTE_WRITECOPY:
Result := 'ExecWriteCopy';
PAGE_GUARD:
Result := 'Guard';
PAGE_NOCACHE:
Result := 'NoCache';
else
Result := '';
end;
end;
function StateStr(P: DWORD): string;
begin
case P of
MEM_COMMIT:
Result := 'Commit';
MEM_FREE:
Result := 'Free';
MEM_RESERVE:
Result := 'Reserve';
else
Result := Format('%x', [P]);
end;
end;
function TypeStr(P: DWORD): string;
begin
case P of
MEM_IMAGE:
Result := 'Image';
MEM_MAPPED:
Result := 'Mapped';
MEM_PRIVATE:
Result := 'Private';
else
Result := Format('%x', [P]);
end;
end;
function ImageIndexFromInfo(MemInfo: TMemoryInfo): Integer;
begin
with MemInfo do
if MappedFile then Result := 6 else
if RepeatedItem then Result := 21 else
Result := 19;
end;
{ TMemoryDumpForm }
procedure TMemoryDumpForm.FormCreate(Sender: TObject);
begin
inherited;
FModulesList := TStringList.Create;
end;
procedure TMemoryDumpForm.FormDestroy(Sender: TObject);
begin
FModulesList.Free;
if FProcess <> 0 then CloseHandle(FProcess);
end;
procedure TMemoryDumpForm.BuildPagesList;
var
AllocationsNode, ModulesNode, TempNode: TTreeNode;
LastAllocationBase: Pointer;
LastMappedFile: Boolean;
I, N, TotalCommit, TotalReserve: Integer;
procedure EnumAllocations;
var
P: PChar;
MI: TMemoryBasicInformation;
Res: DWORD;
Count: Integer;
begin
FMemoryInfo := nil;
Count := 0;
P := Pointer(0);
Res := VirtualQueryEx(FProcess, P, MI, SizeOf(MI));
if Res <> SizeOf(MI) then RaiseLastOSError;
while Res = SizeOf(MI) do
begin
if MI.AllocationBase <> nil then
begin
SetLength(FMemoryInfo, Count + 1);
FMemoryInfo[Count].MemInfo := MI;
Inc(Count);
end;
Inc(P, MI.RegionSize);
Res := VirtualQueryEx(FProcess, P, MI, SizeOf(MI));
end;
end;
begin
Screen.Cursor := crHourGlass;
try
PagesListView.Items.BeginUpdate;
PagesListView.Items.Count := 0;
MemoryTreeView.Items.BeginUpdate;
StatusBar.Panels.BeginUpdate;
try
EnumAllocations;
PagesListView.Items.Count := Length(FMemoryInfo);
with MemoryTreeView.Items do
begin
Clear;
AllocationsNode := AddFirst(nil, sAllocations);
AllocationsNode.ImageIndex := 19;
ModulesNode := Add(nil, sModules);
ModulesNode.ImageIndex := 6;
LastAllocationBase := nil;
LastMappedFile := False;
for I := 0 to Length(FMemoryInfo) - 1 do
with FMemoryInfo[I] do
if LastAllocationBase <> MemInfo.AllocationBase then
begin
TempNode := AddChildObject(AllocationsNode, Format('%p', [MemInfo.AllocationBase]), Pointer(I));
with TempNode do ImageIndex := Parent.ImageIndex;
LastAllocationBase := MemInfo.AllocationBase;
RepeatedItem := False;
N := FModulesList.IndexOfObject(LastAllocationBase);
if N <> -1 then
begin
TempNode := AddChildObject(ModulesNode, FModulesList[N], Pointer(I));
with TempNode do ImageIndex := Parent.ImageIndex;
MappedFile := True;
end else
MappedFile := False;
LastMappedFile := MappedFile;
end else
begin
RepeatedItem := True;
MappedFile := LastMappedFile;
end;
end;
AllocationsNode.AlphaSort;
ModulesNode.AlphaSort;
TotalCommit := 0;
TotalReserve := 0;
for I := 0 to Length(FMemoryInfo) - 1 do with FMemoryInfo[I].MemInfo do
case State of
MEM_COMMIT: Inc(TotalCommit, RegionSize);
MEM_RESERVE: Inc(TotalReserve, RegionSize);
end;
with StatusBar do
begin
Panels[0].Text := Format(sCount, [Length(FMemoryInfo)]);
Panels[1].Text := Format(sCommited, [IntToExtended(TotalCommit)]);
Panels[2].Text := Format(sReserved, [IntToExtended(TotalReserve)]);
end;
ListViewFocusFirstItem(PagesListView);
finally
PagesListView.Items.EndUpdate;
MemoryTreeView.Items.EndUpdate;
StatusBar.Panels.EndUpdate;
end;
finally
Screen.Cursor := crDefault;
end;
end;
procedure TMemoryDumpForm.BuildModulesList;
var
SnapProcHandle: THandle;
ModuleEntry: TModuleEntry32;
Next: Boolean;
begin
FModulesList.Clear;
SnapProcHandle := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, FProcessID);
if SnapProcHandle <> THandle(-1) then
begin
ModuleEntry.dwSize := SizeOf(ModuleEntry);
Next := Module32First(SnapProcHandle, ModuleEntry);
while Next do
begin
FModulesList.AddObject(ModuleEntry.szModule, Pointer(ModuleEntry.modBaseAddr));
Next := Module32Next(SnapProcHandle, ModuleEntry);
end;
CloseHandle(SnapProcHandle);
end;
end;
procedure TMemoryDumpForm.SetParams(ProcessID: DWORD; const FileName: TFileName);
begin
FProcessID := ProcessID;
FFileName := FileName;
Caption := Format(sCaption, [FFileName]);
Refresh1.Execute;
end;
procedure TMemoryDumpForm.UpdateDumpList;
begin
with DumpListView do
begin
if ViewAsText1.Checked then
begin
FDumpBytesPerLine := 64;
Columns[1].Caption := 'Ansi text';
Columns[2].Caption := 'Unicode text';
end else
begin
FDumpBytesPerLine := 16;
Columns[1].Caption := 'Data';
Columns[2].Caption := 'ASCII';
end;
Items.Count := Integer(PagesListView.Selected.SubItems.Objects[3]) div FDumpBytesPerLine;
Invalidate;
end;
end;
procedure TMemoryDumpForm.Refresh1Execute(Sender: TObject);
begin
if FProcess <> 0 then CloseHandle(FProcess);
FProcess := OpenProcess(PROCESS_ALL_ACCESS, False, FProcessID);
if FProcess = 0 then
begin
Close;
RaiseLastOSError;
end;
BuildModulesList;
BuildPagesList;
end;
procedure TMemoryDumpForm.DumpListViewData(Sender: TObject; Item: TListItem);
var
Address: Pointer;
LineData: packed array[0..63] of Byte;
NR: DWORD;
Hex, Ascii, S: string;
I: Integer;
W: PWideChar;
begin
with TListView(Sender) do
if PagesListView.Selected <> nil then
begin
Address := Pointer(DWORD(FMemoryInfo[PagesListView.Selected.Index].MemInfo.BaseAddress) + DWORD(Item.Index * FDumpBytesPerLine));
SetLength(Hex, 3 * SizeOf(LineData));
SetLength(Ascii, 3 * SizeOf(LineData));
Hex := '';
Ascii := '';
if ReadProcessMemory(FProcess, Address, @LineData, SizeOf(LineData), NR) and (NR = SizeOf(LineData)) then
begin
if ViewAsText1.Checked then
begin
for I := 0 to FDumpBytesPerLine - 1 do
begin
if LineData[I] >= 32 then
Hex := Hex + Chr(LineData[I])
else
Hex := Hex + '.';
end;
W := PWideChar(@LineData);
for I := 0 to FDumpBytesPerLine div 2 - 1 do
begin
SetLength(S, 1);
WideCharToMultiByte(CP_ACP, 0, W, 1, PChar(S), 1, nil, nil);
S := PChar(S);
if Length(S) = 0 then S := '.';
Ascii := Ascii + S;
Inc(W);
end;
end else
begin
for I := 0 to FDumpBytesPerLine - 1 do
begin
Hex := Hex + Format('%.2x ', [LineData[I]]);
if LineData[I] >= 32 then
Ascii := Ascii + Chr(LineData[I])
else
Ascii := Ascii + '.';
end;
end;
end;
Item.Caption := Format('%p', [Address]);
Item.SubItems.Add(Hex);
Item.SubItems.Add(Ascii);
end;
end;
procedure TMemoryDumpForm.PagesListViewSelectItem(Sender: TObject;
Item: TListItem; Selected: Boolean);
begin
if Selected then
begin
if (DWORD(Item.SubItems.Objects[0]) = PAGE_NOACCESS) or
(DWORD(Item.SubItems.Objects[2]) = 0) then
begin
DumpListView.Items.Count := 0;
DumpListView.Invalidate;
end else
UpdateDumpList;
end;
end;
procedure TMemoryDumpForm.PagesListViewData(Sender: TObject; Item: TListItem);
var
I: Integer;
begin
with Item, FMemoryInfo[Item.Index].MemInfo do
begin
Caption := Format('%p', [BaseAddress]);
SubItems.AddObject(AllocationProtectStr(Protect), Pointer(Protect));
SubItems.AddObject(Format('%p', [AllocationBase]), AllocationBase);
SubItems.AddObject(AllocationProtectStr(AllocationProtect), Pointer(AllocationProtect));
SubItems.AddObject(Format('%.0n', [IntToExtended(RegionSize)]), Pointer(RegionSize));
SubItems.AddObject(StateStr(State), Pointer(State));
I := FModulesList.IndexOfObject(AllocationBase);
if I <> - 1 then SubItems.Add(FModulesList[I]) else SubItems.Add('');
SubItems.AddObject(TypeStr(Type_9), Pointer(Type_9));
end;
Item.ImageIndex := ImageIndexFromInfo(FMemoryInfo[Item.Index]);
end;
procedure TMemoryDumpForm.PagesListViewCustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
if DWORD(Item.SubItems.Objects[0]) = PAGE_NOACCESS then
Sender.Canvas.Font.Color := clBtnFace;
end;
procedure TMemoryDumpForm.MemoryTreeViewChange(Sender: TObject; Node: TTreeNode);
begin
if Node.Level = 1 then
with PagesListView do
begin
while Assigned(Selected) do Selected.Selected := False;
ItemFocused := PagesListView.Items[Integer(Node.Data)];
ItemFocused.Selected := True;
ItemFocused.MakeVisible(False);
end;
end;
procedure TMemoryDumpForm.MemoryTreeViewGetSelectedIndex(Sender: TObject;
Node: TTreeNode);
begin
Node.SelectedIndex := Node.ImageIndex;
end;
procedure TMemoryDumpForm.ViewAsText1Execute(Sender: TObject);
begin
with ViewAsText1 do
Checked := not Checked;
UpdateDumpList;
end;
procedure TMemoryDumpForm.SaveData1Update(Sender: TObject);
begin
TAction(Sender).Enabled := (ActiveControl = PagesListView) and
(PagesListView.Selected <> nil) and
(DWORD(PagesListView.Selected.SubItems.Objects[0]) <> PAGE_NOACCESS);
end;
procedure TMemoryDumpForm.SaveData1Execute(Sender: TObject);
var
MS: TMemoryStream;
NR: DWORD;
begin
with SaveDataDialog, FMemoryInfo[PagesListView.Selected.Index].MemInfo do
begin
FileName := '';
if Execute then
begin
MS := TMemoryStream.Create;
try
MS.Size := RegionSize;
if ReadProcessMemory(FProcess, BaseAddress, MS.Memory, RegionSize, NR) and
(NR = RegionSize) then
MS.SaveToFile(FileName)
else
RaiseLastOSError;
finally
MS.Free;
end;
end;
end;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -