2720 lines
69 KiB
ObjectPascal
Executable File
2720 lines
69 KiB
ObjectPascal
Executable File
unit frmstructurecompareunit;
|
|
|
|
{$mode delphi}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$ifdef darwin}
|
|
macport,
|
|
{$else}
|
|
windows,
|
|
{$endif}
|
|
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, ComCtrls,
|
|
cefuncproc, newkernelhandler, frmStringMapUnit, MemFuncs, AvgLvlTree, Menus,
|
|
bigmemallochandler, math, maps, oldRegExpr, symbolhandler, commonTypeDefs,
|
|
pagemap, syncobjs2, syncobjs, Clipbrd, betterControls;
|
|
|
|
const compareversion=1;
|
|
|
|
type
|
|
|
|
{ TfrmStructureCompare }
|
|
TPointerpath=array of dword;
|
|
TDiffkind=(dkDontCare, dkMustBeDifferent, dkMustBeSame);
|
|
|
|
|
|
|
|
type
|
|
|
|
PMappedRegion=^TMappedRegion;
|
|
TMappedRegion=record
|
|
baseaddress: ptruint; //if a new section overlaps, append to this
|
|
size: ptruint; //if a new section ovrlaps increase this
|
|
previous: PMappedRegion;
|
|
next: PMappedRegion;
|
|
end;
|
|
|
|
PPointerListEntry=^TPointerListEntry;
|
|
TPointerListEntry=record
|
|
address: ptruint;
|
|
pointsto: ptruint;
|
|
previous: PPointerListEntry;
|
|
next: PPointerListEntry;
|
|
end;
|
|
|
|
|
|
type TPointerRecord=packed record
|
|
level: integer;
|
|
offset: TDwordArray;
|
|
end;
|
|
PPointerRecord=^TPointerRecord;
|
|
|
|
|
|
|
|
TPointerfileReader=class
|
|
private
|
|
files: array of record
|
|
f: Tfilestream;
|
|
startindex: qword;
|
|
lastindex: qword;
|
|
end;
|
|
entrysize: integer;
|
|
fcount: qword;
|
|
pointerfileLevelwidth: integer;
|
|
|
|
bufferindex: integer;
|
|
buffersize: integer;
|
|
pointerrecords: PPointerRecord; //actually an array...
|
|
|
|
stringbuf: pchar;
|
|
widestringbuf: pwidechar;
|
|
|
|
pointermap: TMap;
|
|
ffilename: string;
|
|
fhexadecimal: boolean;
|
|
|
|
function getByteFromAddress(address: ptruint; var error: boolean): byte;
|
|
function getWordFromAddress(address: ptruint; var error: boolean): word;
|
|
function getDWordFromAddress(address: ptruint; var error: boolean): dword;
|
|
function getQWordFromAddress(address: ptruint; var error: boolean): qword;
|
|
function getSingleFromAddress(address: ptruint; var error: boolean): single;
|
|
function getDoubleFromAddress(address: ptruint; var error: boolean): double;
|
|
function getPointerFromAddress(address: ptruint; var error: boolean): ptruint;
|
|
|
|
procedure writeRecord(filestream: TfileStream; precord: PPointerRecord); //for sorting
|
|
public
|
|
vartype: TVariableType;
|
|
procedure clearPointerCache;
|
|
function getPointerRec(index: qword): PPointerRecord;
|
|
function getAddressFromPointerRecord(p: ppointerrecord; baseaddress: ptruint; shadow: ptruint; shadowsize: integer): ptruint;
|
|
function getStringFromPointerRecord(p: ppointerrecord; address: ptruint; shadow: ptruint; shadowsize: integer): string;
|
|
function getStringAndAddress(index: qword; var address: ptruint; out p: PPointerRecord; shadow: ptruint; shadowsize: integer): string;
|
|
|
|
procedure sort(offsetnr: integer);
|
|
|
|
procedure CloseFile;
|
|
procedure LoadFile(filename: string);
|
|
constructor create(filename: string);
|
|
destructor destroy; override;
|
|
|
|
property levelWidth: integer read pointerfileLevelwidth;
|
|
property count: qword read fcount;
|
|
property filename: string read ffilename;
|
|
property hexadecimal: boolean read fhexadecimal write fhexadecimal;
|
|
end;
|
|
|
|
TAddressWithShadow=record
|
|
address: ptruint;
|
|
shadow: ptruint;
|
|
shadowsize: ptruint;
|
|
end;
|
|
|
|
TAddressWithShadowList=array of TAddressWithShadow;
|
|
|
|
|
|
TStructureCompareWorkItem=record
|
|
currentLevel: integer;
|
|
path: TPointerPath;
|
|
LookingForList: array of ptruint;
|
|
NotLookingForList: array of ptruint;
|
|
end;
|
|
|
|
|
|
TfrmStructureCompare=class;
|
|
TStructCompareRescan=class(tthread)
|
|
private
|
|
LFList: TAddressWithShadowList;
|
|
NLFList: TAddressWithShadowList;
|
|
|
|
outputfile: tfilestream;
|
|
pointerfilereader: TPointerfileReader;
|
|
|
|
outputfilename: string;
|
|
oldpointerfilename: string;
|
|
|
|
results: TMemorystream;
|
|
fcount: qword;
|
|
fCurrentPosition: qword;
|
|
fMaxPosition: qword;
|
|
sameonly: boolean;
|
|
|
|
lastwrite: qword;
|
|
|
|
|
|
ownerFrmStructureCompare: TfrmStructureCompare;
|
|
comparesize: integer;
|
|
|
|
procedure addPointer(p: PPointerRecord);
|
|
procedure flushresults;
|
|
|
|
|
|
|
|
public
|
|
progressbar: TProgressBar;
|
|
errorstring:string;
|
|
|
|
procedure execute; override;
|
|
constructor create(comparesize: integer; sameonly: boolean; const LFL: TAddressWithShadowList; const NLFL: TAddressWithShadowList; oldpointerfilename: string; outputfilename: string; ownerFrmStructureCompare: TfrmStructureCompare );
|
|
destructor destroy; override;
|
|
property count: qword read fcount;
|
|
property currentPosition: qword read fCurrentPosition;
|
|
property maxPosition: qword read fMaxPosition;
|
|
end;
|
|
|
|
|
|
|
|
TStructCompareController=class;
|
|
|
|
TStructCompareScanner=class(TThread)
|
|
private
|
|
owner: TStructCompareController;
|
|
is64bittarget: boolean;
|
|
maxlevel: integer;
|
|
structsize: integer;
|
|
alignment: integer;
|
|
LookingForShadowList: array of TAddressWithShadow;
|
|
NotLookingForShadowList: array of TAddressWithShadow;
|
|
|
|
memoryblockLF: array of PByteArray;
|
|
memoryblockNLF: array of PByteArray;
|
|
blocksize: integer;
|
|
|
|
newwi: TStructureCompareWorkItem;
|
|
|
|
readmemorycache: array of record
|
|
pageindex: ptruint;
|
|
data: pointer;
|
|
lasttimeaccess: qword;
|
|
end;
|
|
|
|
filename: string;
|
|
lastwrite: qword;
|
|
results: TMemorystream;
|
|
resultfile: TFileStream;
|
|
|
|
sameonly: boolean;
|
|
|
|
|
|
procedure flushresults;
|
|
procedure writeResult(path: TPointerpath; level: integer);
|
|
function readMemory(address: ptruint; destination: pointer; size: integer): boolean;
|
|
|
|
function getWorkItem(var wi: TStructureCompareWorkItem): boolean; //gets a workitem from the queue, waits if empty
|
|
procedure addNewWorkItemToQueue; //add the new workitem to the queue (handles it itself if the que is full)
|
|
procedure HandleWorkItem(wi: TStructureCompareWorkItem);
|
|
public
|
|
foundcount: integer;
|
|
|
|
idle: boolean;
|
|
procedure execute; override;
|
|
destructor destroy; override;
|
|
end;
|
|
|
|
TStructCompareController=class(tthread)
|
|
private
|
|
LFList: TAddressWithShadowList;
|
|
NLFList: TAddressWithShadowList;
|
|
|
|
structsize: ptruint;
|
|
maxlevel: integer;
|
|
|
|
foundcount: integer;
|
|
|
|
//levelblock: array of Puint64Array; //I think this is obsolete
|
|
|
|
// block64: array of Pint64Array;
|
|
sameonly: boolean;
|
|
|
|
results: TMemorystream;
|
|
resultfile: tfilestream;
|
|
lastwrite: dword;
|
|
|
|
alignment: integer;
|
|
|
|
mustbeinregion: boolean;
|
|
pointerstart: ptruint;
|
|
pointerstop: ptruint;
|
|
owner: TfrmStructureCompare;
|
|
|
|
is64bittarget: boolean;
|
|
public
|
|
workqueue: array [0..63] of TStructureCompareWorkItem;
|
|
workqueueCS: TCriticalSection;
|
|
workqueueSemaphore: TSemaphore;
|
|
workqueuepos: integer;
|
|
|
|
workers: array of TStructCompareScanner;
|
|
|
|
memorymap: TPageMap;
|
|
memorymapCS: TCriticalSection;
|
|
filename: string;
|
|
|
|
configfile: tfilestream;
|
|
errorstring: string;
|
|
|
|
procedure execute; override;
|
|
constructor create(alignment: integer; sameonly: boolean; const LFL: TAddressWithShadowList; const NLFL: TAddressWithShadowList; structsize: integer; maxlevel: integer; filename: string; owner: TfrmStructureCompare);
|
|
destructor destroy; override;
|
|
end;
|
|
|
|
TfrmStructureCompare = class(TForm)
|
|
btnAddAddressLF: TButton;
|
|
btnAddAddressNLF: TButton;
|
|
btnNewScan: TButton;
|
|
btnScan: TButton;
|
|
cbHexadecimal: TCheckBox;
|
|
cbSameOnly: TCheckBox;
|
|
comboType: TComboBox;
|
|
edtAlignsize: TEdit;
|
|
edtMaxLevel: TEdit;
|
|
edtStructsize: TEdit;
|
|
FindDialog1: TFindDialog;
|
|
gbLF: TGroupBox;
|
|
gbNLF: TGroupBox;
|
|
scImageList: TImageList;
|
|
lblAlign: TLabel;
|
|
lblInfo: TLabel;
|
|
lblMaxLevel: TLabel;
|
|
lblStructsize: TLabel;
|
|
lblvds: TLabel;
|
|
lvResults: TListView;
|
|
MainMenu1: TMainMenu;
|
|
MenuItem1: TMenuItem;
|
|
MenuItem2: TMenuItem;
|
|
miMaxAddresses: TMenuItem;
|
|
miDeleteAddress: TMenuItem;
|
|
miShadow: TMenuItem;
|
|
miCut: TMenuItem;
|
|
miCopy: TMenuItem;
|
|
miPaste: TMenuItem;
|
|
MenuItem9: TMenuItem;
|
|
miFind: TMenuItem;
|
|
miFindNext: TMenuItem;
|
|
miNewScan: TMenuItem;
|
|
miOpen: TMenuItem;
|
|
MenuItem4: TMenuItem;
|
|
MenuItem7: TMenuItem;
|
|
miClearCache: TMenuItem;
|
|
MenuItem5: TMenuItem;
|
|
MenuItem6: TMenuItem;
|
|
OpenDialog1: TOpenDialog;
|
|
Panel1: TPanel;
|
|
Panel2: TPanel;
|
|
Panel3: TPanel;
|
|
Panel4: TPanel;
|
|
Panel5: TPanel;
|
|
Panel9: TPanel;
|
|
pnlNLF: TPanel;
|
|
pnlLF: TPanel;
|
|
Panel7: TPanel;
|
|
pmPointerfile: TPopupMenu;
|
|
pmAddressPopup: TPopupMenu;
|
|
SaveDialog1: TSaveDialog;
|
|
sbLF: TScrollBox;
|
|
sbNLF: TScrollBox;
|
|
Splitter1: TSplitter;
|
|
statusupdater: TTimer;
|
|
tRefresher: TTimer;
|
|
procedure btnAddAddressClick(Sender: TObject);
|
|
procedure btnScanClick(Sender: TObject);
|
|
procedure cbHexadecimalChange(Sender: TObject);
|
|
procedure comboTypeChange(Sender: TObject);
|
|
procedure FindDialog1Find(Sender: TObject);
|
|
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormDestroy(Sender: TObject);
|
|
procedure FormShow(Sender: TObject);
|
|
procedure gbNLFClick(Sender: TObject);
|
|
procedure lvResultsColumnClick(Sender: TObject; Column: TListColumn);
|
|
procedure lvResultsCustomDrawSubItem(Sender: TCustomListView;
|
|
Item: TListItem; SubItem: Integer; State: TCustomDrawState;
|
|
var DefaultDraw: Boolean);
|
|
procedure lvResultsData(Sender: TObject; Item: TListItem);
|
|
procedure lvResultsDblClick(Sender: TObject);
|
|
procedure miCopyClick(Sender: TObject);
|
|
procedure miCutClick(Sender: TObject);
|
|
procedure miDeleteAddressClick(Sender: TObject);
|
|
procedure miFindClick(Sender: TObject);
|
|
procedure miMaxAddressesClick(Sender: TObject);
|
|
procedure miNewScanClick(Sender: TObject);
|
|
procedure miOpenClick(Sender: TObject);
|
|
procedure MenuItem7Click(Sender: TObject);
|
|
procedure miClearCacheClick(Sender: TObject);
|
|
procedure miPasteClick(Sender: TObject);
|
|
procedure miShadowClick(Sender: TObject);
|
|
procedure pmAddressPopupPopup(Sender: TObject);
|
|
procedure statusupdaterTimer(Sender: TObject);
|
|
procedure tRefresherTimer(Sender: TObject);
|
|
private
|
|
{ private declarations }
|
|
scanner: TStructCompareController;
|
|
rescanner: TStructCompareRescan;
|
|
pointerfilereader: TPointerfilereader;
|
|
|
|
btncancel: TButton;
|
|
maxaddresses: integer;
|
|
|
|
function mapCompare(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
|
|
function pointerCompare(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
|
|
procedure cleanup;
|
|
procedure OpenPointerfile(filename: string);
|
|
procedure scanDone;
|
|
procedure rescanDone;
|
|
procedure scanError;
|
|
|
|
function getStringFromPointer(address: ptruint; offsets: TDwordArray; level, bytesize: integer; unicode: boolean; var a: ptruint): string;
|
|
procedure setGUIStateEnabled(state: boolean);
|
|
procedure reloadlistviewcolumns;
|
|
procedure AddressEditChange(Sender: TObject);
|
|
procedure btnCancelClick(sender: TObject);
|
|
public
|
|
{ public declarations }
|
|
|
|
edtLF: TList;
|
|
edtNLF: TList;
|
|
|
|
donotfreeonclose: boolean;
|
|
procedure disableGui;
|
|
procedure enablegui;
|
|
procedure addAddress(address: ptruint; shadow: qword; shadowsize: integer; group: integer);
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
frmStructureCompare: TfrmStructureCompare;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
{ TfrmStructureCompare }
|
|
|
|
uses frmStructPointerRescanUnit, MemoryBrowserFormUnit, ProcessHandlerUnit,
|
|
Parsers, addressedit, PointerscanresultReader, DPIHelper, ceregistry;
|
|
|
|
|
|
resourcestring
|
|
rsGeneratingStringmap = 'Generating stringmap';
|
|
rsGeneratedScanning = 'Generated. Scanning...';
|
|
rsThisAddressIsNotAccessible = 'This address is not accessible';
|
|
rsStop = 'Stop';
|
|
rsTerminating = 'Terminating...';
|
|
rsAreYouSureYo = 'Are you sure you wish to start a new scan?';
|
|
rsScan = 'Scan';
|
|
rsScanningFoun = 'Scanning... Found %s';
|
|
rsSPSUNotYetImplemented = 'Not yet implemented';
|
|
rsSPSUFUUUU = 'FUUUU';
|
|
rsSPSUFUUUUU = 'FUUUUU';
|
|
rsSPSUException = 'Exception:';
|
|
rsSPSUOffset = 'Offset ';
|
|
rsSPSUAddress = 'Address';
|
|
rsSPSUAddress2 = 'Address 2';
|
|
rsSPSURescan = 'Rescan';
|
|
rsSPSUFound = 'Found:';
|
|
rsSPSUScanDoneFound = 'Scan done! Found ';
|
|
rsSPSUErrorduringScanNoScanresults = 'Error during scan. No scanresults available';
|
|
rsInvalidGroup = 'Group %d address %s (%s) is not valid';
|
|
rsTooSmallAlign = 'Alignsize must be greater than 0';
|
|
rsSPSInvalidstruct = 'Invalid structure pointerfile';
|
|
rsSPSNeedNewCE = 'You''ll need a newer CE version to open this file';
|
|
rsSPSNoError = 'No error';
|
|
rsSPSUnlock = 'Unlock (%.8x-%.8x)';
|
|
rsSPSLock = 'Lock';
|
|
rsStructureCompare = 'Structure Compare';
|
|
rsMaxAddressesShown = 'Max addresses shown :';
|
|
rsMaxReached = '...<max reached>...';
|
|
rsSameAddress = 'This will result in 0 results as address %s appears multiple times';
|
|
//----------TPointerfileReader---------
|
|
|
|
|
|
type
|
|
TShadow=class
|
|
address: ptruint;
|
|
size: ptruint;
|
|
donotfree: boolean;
|
|
end;
|
|
|
|
|
|
function TPointerfilereader.getByteFromAddress(address: ptruint; var error: boolean): byte;
|
|
var x: ptruint;
|
|
begin
|
|
error:=not readprocessmemory(processhandle, pointer(address), @result, 1, x);
|
|
error:=error or (x<>1);
|
|
end;
|
|
|
|
function TPointerfilereader.getWordFromAddress(address: ptruint; var error: boolean): word;
|
|
var x: ptruint;
|
|
begin
|
|
error:=not readprocessmemory(processhandle, pointer(address), @result, 2, x);
|
|
error:=error or (x<>2);
|
|
end;
|
|
|
|
function TPointerfilereader.getDWordFromAddress(address: ptruint; var error: boolean): dword;
|
|
var x: ptruint;
|
|
begin
|
|
error:=not readprocessmemory(processhandle, pointer(address), @result, 4, x);
|
|
error:=error or (x<>4);
|
|
end;
|
|
|
|
function TPointerfilereader.getQWordFromAddress(address: ptruint; var error: boolean): qword;
|
|
var x: ptruint;
|
|
begin
|
|
error:=not readprocessmemory(processhandle, pointer(address), @result, 8, x);
|
|
error:=error or (x<>8);
|
|
end;
|
|
|
|
function TPointerfilereader.getSingleFromAddress(address: ptruint; var error: boolean): single;
|
|
var x: ptruint;
|
|
begin
|
|
error:=not readprocessmemory(processhandle, pointer(address), @result, 4, x);
|
|
error:=error or (x<>4);
|
|
end;
|
|
|
|
function TPointerfilereader.getDoubleFromAddress(address: ptruint; var error: boolean): double;
|
|
var x: ptruint;
|
|
begin
|
|
error:=not readprocessmemory(processhandle, pointer(address), @result, 8, x);
|
|
error:=error or (x<>8);
|
|
end;
|
|
|
|
function TPointerfilereader.getPointerFromAddress(address: ptruint; var error: boolean): ptruint;
|
|
var x: ptruint;
|
|
begin
|
|
result:=0;
|
|
error:=not readprocessmemory(processhandle, pointer(address), @result, processhandler.pointersize, x);
|
|
error:=error or (x<>processhandler.pointersize);
|
|
end;
|
|
|
|
|
|
procedure TPointerfilereader.clearPointerCache;
|
|
begin
|
|
pointermap.Clear;
|
|
end;
|
|
|
|
function TPointerfileReader.getPointerRec(index: qword): PPointerRecord;
|
|
var
|
|
blocksize: integer;
|
|
i: integer;
|
|
|
|
relativeindex: qword;
|
|
found: boolean;
|
|
|
|
pos: int64;
|
|
begin
|
|
if index>fcount then exit(nil);
|
|
|
|
result:=nil;
|
|
|
|
|
|
if (buffersize=0) or (not InRangeQ(index, bufferindex, bufferindex+buffersize-1)) then
|
|
begin
|
|
//find the file this index belongs to
|
|
found:=false;
|
|
for i:=0 to length(files)-1 do
|
|
if InRangeQ(index, files[i].startindex, files[i].lastindex) then
|
|
begin
|
|
relativeindex:=index-files[i].startindex;
|
|
|
|
blocksize:=files[i].lastindex-files[i].startindex+1-relativeindex;
|
|
blocksize:=min(blocksize, 4096);
|
|
|
|
pos:=relativeindex*entrysize;
|
|
files[i].f.position:=pos;
|
|
if files[i].f.Read(pointerrecords^, entrysize*blocksize)=entrysize*blocksize then
|
|
begin
|
|
bufferindex:=index;
|
|
buffersize:=blocksize;
|
|
end else exit(nil);
|
|
|
|
found:=true;
|
|
break;
|
|
end;
|
|
|
|
if not found then exit(nil);
|
|
end;
|
|
|
|
result:=PPointerRecord(ptruint(pointerrecords)+(index-bufferindex)*entrysize);
|
|
|
|
end;
|
|
|
|
function TPointerfilereader.getAddressFromPointerRecord(p: ppointerrecord; baseaddress: ptruint; shadow: ptruint; shadowsize: integer): ptruint;
|
|
var address: ptruint;
|
|
a: ptruint;
|
|
i: integer;
|
|
x: ptruint;
|
|
dp: pptruint;
|
|
begin
|
|
|
|
result:=0;
|
|
if p=nil then exit;
|
|
|
|
address:=baseaddress+p.offset[0];
|
|
|
|
if (shadow<>0) and inrangeq(address, baseaddress, baseaddress+shadowsize) then
|
|
address:=address+(shadow-baseaddress);
|
|
|
|
for i:=1 to p.level do
|
|
begin
|
|
dp:=pointermap.GetDataPtr(address);
|
|
if dp<>nil then
|
|
begin
|
|
address:=dp^;
|
|
if address=0 then
|
|
exit; //unreadable
|
|
end
|
|
else
|
|
begin
|
|
a:=0;
|
|
if not readprocessmemory(processhandle, pointer(address), @a, processhandler.pointersize, x) then
|
|
a:=0;
|
|
|
|
|
|
if (shadow<>0) and (inrangeq(a, baseaddress, baseaddress+shadowsize)) then
|
|
a:=a+(shadow-baseaddress);
|
|
|
|
pointermap.Add(address, a);
|
|
address:=a;
|
|
|
|
if a=0 then
|
|
exit; //unreadable
|
|
end;
|
|
|
|
|
|
address:=address+p.offset[i]
|
|
end;
|
|
|
|
result:=address;
|
|
end;
|
|
|
|
function TPointerfileReader.getStringFromPointerRecord(p: ppointerrecord; address: ptruint; shadow: ptruint; shadowsize: integer): string;
|
|
var i,j: integer;
|
|
x: ptruint;
|
|
e: boolean;
|
|
begin
|
|
e:=false;
|
|
result:='';
|
|
address:=getAddressFromPointerRecord(p, address, shadow, shadowsize);
|
|
|
|
case vartype of
|
|
vtByte, vtWord, vtDword, vtQword, vtSingle, vtDouble, vtPointer:
|
|
begin
|
|
e:=false;
|
|
case vartype of
|
|
vtByte: if fhexadecimal then result:=inttohex(getByteFromAddress(address,e),2) else result:=inttostr(getByteFromAddress(address,e));
|
|
vtWord: if fhexadecimal then result:=inttohex(getWordFromAddress(address,e),4) else result:=inttostr(getWordFromAddress(address,e));
|
|
vtDword: if fhexadecimal then result:=inttohex(getDwordFromAddress(address,e),8) else result:=inttostr(getDwordFromAddress(address,e));
|
|
vtQword: if fhexadecimal then result:=inttohex(getQwordFromAddress(address,e),16) else result:=inttostr(getQwordFromAddress(address,e));
|
|
vtSingle: result:=format('%.3f',[getSingleFromAddress(address,e)]);
|
|
vtDouble: result:=format('%.3f',[getDoubleFromAddress(address,e)]);
|
|
vtPointer: result:=IntToHex(getPointerFromAddress(address,e), processhandler.pointersize*2);
|
|
end;
|
|
end;
|
|
|
|
vtString:
|
|
begin
|
|
|
|
//string
|
|
i:=16;
|
|
if readprocessmemory(processhandle, pointer(address), stringbuf, i, x) then
|
|
begin
|
|
stringbuf[i]:=#0;
|
|
stringbuf[i+1]:=#0;
|
|
|
|
result:=stringbuf;
|
|
end;
|
|
end;
|
|
|
|
vtUnicodeString:
|
|
begin
|
|
//string
|
|
i:=16;
|
|
if readprocessmemory(processhandle, pointer(address), stringbuf, i, x) then
|
|
begin
|
|
stringbuf[i]:=#0;
|
|
stringbuf[i+1]:=#0;
|
|
|
|
result:=widestringbuf;
|
|
end;
|
|
end
|
|
else
|
|
result:=rsSPSUNotYetImplemented;
|
|
|
|
end;
|
|
|
|
|
|
if e then
|
|
result:='???';
|
|
end;
|
|
|
|
function TPointerfileReader.getStringAndAddress(index: qword; var address: ptruint; out p: PPointerRecord; shadow: ptruint; shadowsize: integer): string;
|
|
begin
|
|
result:='';
|
|
address:=0;
|
|
p:=getPointerRec(index);
|
|
if p<>nil then
|
|
begin
|
|
result:=getStringFromPointerRecord(p, address, shadow, shadowsize);
|
|
address:=getAddressFromPointerRecord(p, address, shadow, shadowsize);
|
|
end;
|
|
end;
|
|
|
|
procedure TPointerfileReader.writeRecord(filestream: TfileStream; precord: PPointerRecord);
|
|
begin
|
|
filestream.WriteBuffer(precord^,entrysize);
|
|
end;
|
|
|
|
procedure TPointerfileReader.sort(offsetnr: integer); //sorts more on level than on offsetvalues
|
|
var
|
|
configfile: TFilestream=nil;
|
|
|
|
results: Array of Tfilestream;
|
|
resultcount: integer;
|
|
|
|
tempfilename: string;
|
|
i: qword;
|
|
j: integer;
|
|
pr: PPointerRecord;
|
|
|
|
fl: Tstringlist;
|
|
|
|
s1,s2: string;
|
|
begin
|
|
if count=0 then exit;
|
|
if offsetnr>pointerfilelevelwidth then exit;
|
|
|
|
tempfilename:=filename+'.sorting';
|
|
configfile:=tfilestream.create(tempfilename, fmCreate);
|
|
|
|
configfile.WriteByte($ec); //HEADER
|
|
configfile.WriteByte(compareversion);
|
|
configfile.WriteDWord(pointerfileLevelwidth);
|
|
configfile.free;
|
|
configfile:=nil;
|
|
|
|
setlength(results,pointerfileLevelwidth);
|
|
resultcount:=pointerfileLevelwidth;
|
|
for j:=0 to length(results)-1 do
|
|
results[j]:=tfilestream.create(tempfilename+'.results.'+inttostr(j+1), fmCreate);
|
|
|
|
i:=0;
|
|
while (i <= count-1) do
|
|
begin
|
|
pr:=getPointerRec(i);
|
|
writeRecord(results[pr^.level], pr);
|
|
inc(i);
|
|
end;
|
|
|
|
for j:=0 to length(results)-1 do
|
|
begin
|
|
results[j].free;
|
|
results[j]:=nil
|
|
end;
|
|
setlength(results,0);
|
|
|
|
closeFile;
|
|
|
|
fl:=tstringlist.create;
|
|
findAllResultFilesForThisPtr(filename,fl);
|
|
for j:=0 to fl.Count-1 do
|
|
DeleteFile(fl[j]);
|
|
|
|
DeleteFile(filename);
|
|
|
|
fl.free;
|
|
RenameFile(tempfilename, filename);
|
|
for j:=1 to resultcount do
|
|
begin
|
|
s1:=tempfilename+'.results.'+inttostr(j);
|
|
s2:=filename+'.results.'+inttostr(j);
|
|
if RenameFile(s1, s2)=false then
|
|
OutputDebugString('Failed to rename sorted results');
|
|
end;
|
|
|
|
loadFile(filename);
|
|
end;
|
|
|
|
procedure TPointerFileReader.closeFile;
|
|
var i: integer;
|
|
begin
|
|
if pointerrecords<>nil then
|
|
FreeMemAndNil(pointerrecords);
|
|
|
|
if stringbuf<>nil then
|
|
FreeMemAndNil(stringbuf);
|
|
|
|
widestringbuf:=nil;
|
|
|
|
if pointermap<>nil then
|
|
freeandnil(pointermap);
|
|
|
|
for i:=0 to length(files)-1 do
|
|
begin
|
|
if files[i].f<>nil then
|
|
freeandnil(files[i].f);
|
|
end;
|
|
|
|
setlength(files,0);
|
|
|
|
fcount:=0;
|
|
entrysize:=0;
|
|
end;
|
|
|
|
procedure TPointerfileReader.LoadFile(filename: string);
|
|
var
|
|
f: Tstringlist=nil;
|
|
configfile: TFilestream=nil;
|
|
i,j: integer;
|
|
begin
|
|
ffilename:=filename;
|
|
|
|
try
|
|
configfile:=TFileStream.Create(filename, fmOpenRead);
|
|
if configfile.ReadByte<>$ec then raise exception.create(rsSPSInvalidstruct);
|
|
if configfile.readbyte>compareversion then raise exception.create(rsSPSNeedNewCE);
|
|
pointerfileLevelwidth:=configfile.ReadDWord;
|
|
entrysize:=sizeof(pointerfileLevelwidth)+pointerfileLevelwidth*sizeof(dword);
|
|
|
|
f:=tstringlist.create;
|
|
findAllResultFilesForThisPtr(ffilename, f);
|
|
|
|
setlength(files, f.count);
|
|
for i:=0 to f.count-1 do
|
|
files[i].f:=nil;
|
|
|
|
fcount:=0;
|
|
|
|
try
|
|
for i:=0 to f.count-1 do
|
|
begin
|
|
files[i].f:=tfilestream.create(f[i], fmOpenRead);
|
|
files[i].startindex:=fcount;
|
|
|
|
if files[i].f.Size>0 then
|
|
begin
|
|
inc(fcount, files[i].f.Size div entrysize);
|
|
files[i].lastindex:=fcount-1;
|
|
end
|
|
else
|
|
freeandnil(files[i].f);
|
|
end;
|
|
|
|
i:=0;
|
|
while i<length(files) do
|
|
begin
|
|
if files[i].f=nil then
|
|
begin
|
|
for j:=i to length(files)-2 do
|
|
files[j]:=files[j+1];
|
|
|
|
setlength(files, length(files)-1);
|
|
end
|
|
else
|
|
inc(i);
|
|
end;
|
|
|
|
except
|
|
for i:=0 to f.count-1 do
|
|
if files[i].f<>nil then
|
|
freeandnil(files[i].f);
|
|
raise;
|
|
end;
|
|
|
|
getmem(pointerrecords, entrysize*4096);
|
|
|
|
getmem(stringbuf, 512);
|
|
widestringbuf:=pwidechar(stringbuf);
|
|
|
|
pointermap:=TMap.Create(ituPtrSize,sizeof(pointer))
|
|
|
|
finally
|
|
if f<>nil then
|
|
freeandnil(f);
|
|
|
|
if configfile<>nil then
|
|
freeandnil(configfile);
|
|
end;
|
|
end;
|
|
|
|
constructor TPointerfileReader.create(filename: string);
|
|
begin
|
|
loadFile(filename);
|
|
end;
|
|
|
|
destructor TPointerfileReader.destroy;
|
|
var i: integer;
|
|
begin
|
|
closefile;
|
|
|
|
|
|
//cleanup the maps
|
|
inherited destroy;
|
|
end;
|
|
|
|
|
|
|
|
//--------------TStructCompareRescan----------------
|
|
procedure TStructCompareRescan.flushResults;
|
|
begin
|
|
lastwrite:=GetTickCount;
|
|
outputfile.WriteBuffer(results.Memory^, results.position);
|
|
results.position:=0;
|
|
end;
|
|
|
|
procedure TStructCompareRescan.addPointer(p: PPointerRecord);
|
|
begin
|
|
inc(fcount);
|
|
results.WriteBuffer(p^, pointerfilereader.entrysize);
|
|
|
|
if ((gettickcount64-lastwrite)>5*60*1000) or (results.Position>=(15*1024*1024)) then
|
|
flushResults;
|
|
end;
|
|
|
|
|
|
procedure TStructCompareRescan.execute;
|
|
var index, i,j: integer;
|
|
p: PPointerRecord;
|
|
|
|
passed: boolean;
|
|
|
|
g1: array of pointer;
|
|
g2: array of pointer;
|
|
|
|
g1same, g2same: boolean;
|
|
|
|
a: ptruint;
|
|
x: ptruint;
|
|
|
|
r: tstringlist=nil;
|
|
begin
|
|
setlength(g1, length(LFList));
|
|
setlength(g2, length(NLFList));
|
|
for i:=0 to length(g1)-1 do
|
|
getmem(g1[i], comparesize);
|
|
|
|
for i:=0 to length(g2)-1 do
|
|
getmem(g2[i], comparesize);
|
|
|
|
|
|
try
|
|
try
|
|
for index:=0 to pointerfilereader.count-1 do
|
|
begin
|
|
fCurrentPosition:=index;
|
|
p:=pointerfilereader.getPointerRec(index);
|
|
if p=nil then continue;
|
|
|
|
passed:=true;
|
|
|
|
for j:=0 to length(LFList)-1 do
|
|
begin
|
|
a:=pointerfilereader.getAddressFromPointerRecord(p,LFList[j].address,LFList[j].shadow, LFList[j].shadowsize);
|
|
if a=0 then
|
|
begin
|
|
passed:=false;
|
|
break;
|
|
end;
|
|
|
|
if not readprocessmemory(processhandle, pointer(a),g1[j],comparesize, x) then
|
|
begin
|
|
passed:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
if not passed then continue;
|
|
|
|
for j:=0 to length(NLFList)-1 do
|
|
begin
|
|
a:=pointerfilereader.getAddressFromPointerRecord(p,NLFList[j].address,NLFList[j].shadow, NLFList[j].shadowsize);
|
|
if a=0 then
|
|
begin
|
|
passed:=false;
|
|
break;
|
|
end;
|
|
|
|
if not readprocessmemory(processhandle, pointer(a),g2[j],comparesize, x) then
|
|
begin
|
|
passed:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
if not passed then continue;
|
|
|
|
//still here so readable
|
|
//compare the groups
|
|
g1same:=true;
|
|
g2same:=true;
|
|
for i:=0 to length(g2)-1 do
|
|
begin
|
|
//check if these values are in g1. If so, not valid
|
|
for j:=0 to length(g1)-1 do
|
|
begin
|
|
//first iteration checks if g1 is the same or not
|
|
if (i=0) and (j>0) and (CompareMem(g1[0], g1[j],comparesize)=false) then
|
|
begin
|
|
g1same:=false;
|
|
if sameonly then
|
|
begin
|
|
passed:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
if CompareMem(g2[i], g1[j], comparesize) then
|
|
begin
|
|
passed:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
if passed=false then break;
|
|
|
|
//also check if g2 is the same during this loop
|
|
if (i>0) and (CompareMem(g2[0], g2[i],comparesize)=false) then
|
|
begin
|
|
g2same:=false;
|
|
if sameonly then
|
|
begin
|
|
passed:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if passed and ((g1same=false) and (g2same=false)) then
|
|
passed:=false;
|
|
|
|
|
|
if passed then //add it
|
|
addPointer(p);
|
|
|
|
//check for forced exit
|
|
if terminated then exit;
|
|
end;
|
|
|
|
flushresults;
|
|
|
|
if outputfile<>nil then
|
|
freeandnil(outputfile);
|
|
|
|
if results<>nil then
|
|
freeandnil(results);
|
|
|
|
if pointerfilereader<>nil then
|
|
freeandnil(pointerfilereader);
|
|
|
|
|
|
//delete all old files
|
|
r:=tstringlist.create;
|
|
findAllResultFilesForThisPtr(outputfilename, r);
|
|
|
|
for i:=0 to r.count-1 do
|
|
DeleteFile(r[i]);
|
|
|
|
freeandnil(r);
|
|
|
|
if deletefile(outputfilename)=false then
|
|
OutputDebugString('Failure deleting '+outputfilename);
|
|
|
|
RenameFile(outputfilename+'.temp', outputfilename);
|
|
RenameFile(outputfilename+'.results.0.temp', outputfilename+'.results.0');
|
|
|
|
Queue(ownerFrmStructureCompare.rescandone);
|
|
finally
|
|
if r<>nil then
|
|
freeandnil(r);
|
|
|
|
if outputfile<>nil then
|
|
freeandnil(outputfile);
|
|
|
|
if results<>nil then
|
|
freeandnil(results);
|
|
|
|
if pointerfilereader<>nil then
|
|
freeandnil(pointerfilereader);
|
|
|
|
for i:=0 to length(g1)-1 do
|
|
FreeMemAndNil(g1[i]);
|
|
|
|
for i:=0 to length(g2)-1 do
|
|
FreeMemAndNil(g2[i]);
|
|
end;
|
|
except
|
|
on e:exception do
|
|
begin
|
|
errorstring:=e.message;
|
|
Queue(ownerFrmStructureCompare.scanerror);
|
|
end;
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
constructor TStructCompareRescan.create(comparesize: integer; sameonly: boolean; const LFL: TAddressWithShadowList; const NLFL: TAddressWithShadowList; oldpointerfilename: string; outputfilename: string; ownerFrmStructureCompare: TfrmStructureCompare );
|
|
var
|
|
i: integer;
|
|
regflags: tregexprflags;
|
|
lw: integer;
|
|
configfile: tfilestream;
|
|
begin
|
|
progressbar:=tprogressbar.create(ownerFrmStructureCompare);
|
|
progressbar.align:=alBottom;
|
|
progressbar.parent:=ownerFrmStructureCompare;
|
|
|
|
self.comparesize:=comparesize;
|
|
|
|
setlength(LFList, length(LFL));
|
|
for i:=0 to length(LFL)-1 do
|
|
LFList[i]:=LFL[i];
|
|
|
|
setlength(NLFList, length(NLFL));
|
|
for i:=0 to length(NLFL)-1 do
|
|
NLFList[i]:=NLFL[i];
|
|
|
|
self.oldpointerfilename:=oldpointerfilename;
|
|
pointerfilereader:=TPointerfileReader.create(oldpointerfilename);
|
|
fmaxPosition:=pointerfilereader.count;
|
|
|
|
self.outputfilename:=outputfilename;
|
|
self.ownerFrmStructureCompare:=ownerFrmStructureCompare;
|
|
self.sameonly:=sameonly;
|
|
pointerfilereader.clearPointercache;
|
|
|
|
//build the configfile
|
|
configfile:=TFileStream.Create(outputfilename+'.temp', fmCreate);
|
|
configfile.WriteByte($ec); //header (not to be confused with pointerscan)
|
|
configfile.WriteByte(compareversion);
|
|
configfile.WriteDWord(pointerfilereader.levelWidth);
|
|
configfile.free;
|
|
|
|
outputfile:=Tfilestream.create(outputfilename+'.results.0.temp', fmcreate);
|
|
|
|
lastwrite:=GetTickCount64;
|
|
results:=TMemoryStream.Create;
|
|
results.size:=16*1024*1024;
|
|
inherited create(suspended);
|
|
end;
|
|
|
|
|
|
destructor TStructCompareRescan.destroy;
|
|
begin
|
|
if pointerfilereader<>nil then
|
|
freeandnil(pointerfilereader);
|
|
|
|
if outputfile<>nil then
|
|
freeandnil(outputfile);
|
|
|
|
if results<>nil then
|
|
freeandnil(results);
|
|
|
|
if progressbar<>nil then
|
|
freeandnil(progressbar);
|
|
|
|
inherited destroy;
|
|
end;
|
|
|
|
//--------------TStructCompareScanner------------------
|
|
|
|
procedure TStructCompareScanner.flushresults;
|
|
begin
|
|
resultfile.WriteBuffer(results.Memory^, results.position);
|
|
results.position:=0;
|
|
lastwrite:=GetTickCount;
|
|
end;
|
|
|
|
procedure TStructCompareScanner.writeResult(path: TPointerpath; level: integer);
|
|
begin
|
|
results.WriteBuffer(level, sizeof(level));
|
|
results.WriteBuffer(path[0], sizeof(path[0])*(maxlevel+1));
|
|
|
|
if (getTickCount-lastwrite>5*60*1000) or (results.Position>=(15*1024*1024)) then
|
|
flushResults;
|
|
|
|
inc(foundcount);
|
|
end;
|
|
|
|
function TStructCompareScanner.readMemory(address: ptruint; destination: pointer; size: integer): boolean;
|
|
var
|
|
pi: PPageInfo;
|
|
pageindex: ptruint;
|
|
i: integer;
|
|
x: ptruint;
|
|
readok: boolean;
|
|
|
|
data: PByteArray;
|
|
offset: word;
|
|
|
|
block: integer;
|
|
begin
|
|
|
|
while size>0 do
|
|
begin
|
|
pageindex:=address shr 12;
|
|
offset:=address and $fff;
|
|
|
|
pi:=owner.memorymap.GetPageInfo(pageindex);
|
|
if pi=nil then
|
|
begin
|
|
getmem(data,4096);
|
|
x:=0;
|
|
readok:=ReadProcessMemory(processhandle, pointer(pageindex shl 12), data, 4096, x);
|
|
|
|
if (not readok) or (x<>4096) then
|
|
begin
|
|
//unreadable memory or buggy rpm hook
|
|
FreeMemAndNil(data);
|
|
|
|
exit(false)
|
|
end;
|
|
|
|
owner.memorymapCS.Enter;
|
|
try
|
|
owner.memorymap.Add(pageindex, data);
|
|
finally
|
|
owner.memorymapCS.Leave;
|
|
end;
|
|
|
|
|
|
end
|
|
else
|
|
data:=pi^.data;
|
|
|
|
block:=4096-offset;
|
|
|
|
if block>size then
|
|
block:=size;
|
|
|
|
copymemory(destination,@data[offset],block);
|
|
|
|
dec(size,block);
|
|
destination:=pointer(ptruint(destination)+block);
|
|
end;
|
|
|
|
result:=true;
|
|
end;
|
|
|
|
function TStructCompareScanner.getWorkItem(var wi: TStructureCompareWorkItem): boolean;
|
|
{pre: wi has the proper array lengths setup}
|
|
var i: integer;
|
|
begin
|
|
result:=false;
|
|
if owner.workqueueSemaphore.tryAcquire=false then
|
|
begin
|
|
//the list is empty, wait
|
|
idle:=true;
|
|
owner.workqueueSemaphore.Acquire;
|
|
end;
|
|
idle:=false;
|
|
|
|
owner.workqueueCS.Enter;
|
|
try
|
|
if owner.workqueuepos>0 then
|
|
begin
|
|
wi.currentLevel:=owner.workqueue[owner.workqueuepos-1].currentLevel;
|
|
for i:=0 to length(wi.LookingForList)-1 do
|
|
wi.LookingForList[i]:=owner.workqueue[owner.workqueuepos-1].LookingForList[i];
|
|
|
|
for i:=0 to length(wi.NotLookingForList)-1 do
|
|
wi.NotLookingForList[i]:=owner.workqueue[owner.workqueuepos-1].NotLookingForList[i];
|
|
|
|
for i:=0 to length(wi.path)-1 do
|
|
wi.path[i]:=owner.workqueue[owner.workqueuepos-1].path[i];
|
|
|
|
dec(owner.workqueuepos);
|
|
result:=true;
|
|
end;
|
|
finally
|
|
owner.workqueueCS.Leave;
|
|
end;
|
|
|
|
if terminated then result:=false;
|
|
end;
|
|
|
|
procedure TStructCompareScanner.addNewWorkItemToQueue;
|
|
var
|
|
addedtoqueue: boolean=false;
|
|
i: integer;
|
|
begin
|
|
if owner.workqueuepos<60 then
|
|
begin
|
|
if owner.workqueueCS.tryenter then
|
|
begin
|
|
try
|
|
if (owner.workqueuepos<64) then
|
|
begin
|
|
for i:=0 to length(newwi.NotLookingForList)-1 do
|
|
owner.workqueue[owner.workqueuepos].NotLookingForList[i]:=newwi.NotLookingForList[i];
|
|
|
|
for i:=0 to length(newwi.LookingForList)-1 do
|
|
owner.workqueue[owner.workqueuepos].LookingForList[i]:=newwi.LookingForList[i];
|
|
|
|
for i:=0 to length(newwi.path)-1 do
|
|
owner.workqueue[owner.workqueuepos].path[i]:=newwi.path[i];
|
|
|
|
owner.workqueue[owner.workqueuepos].currentLevel:=newwi.currentLevel;
|
|
|
|
inc(owner.workqueuepos);
|
|
addedtoqueue:=true;
|
|
owner.workqueueSemaphore.Release;
|
|
end;
|
|
finally
|
|
owner.workqueueCS.Leave;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if (not addedtoqueue) and (not terminated) then
|
|
handleWorkItem(newwi); //do it yourself
|
|
end;
|
|
|
|
procedure TStructCompareScanner.handleWorkItem(wi: TStructureCompareWorkItem);
|
|
var
|
|
i,j: integer;
|
|
a: ptruint;
|
|
pi: PPageInfo;
|
|
pageindex: integer;
|
|
|
|
pos: integer;
|
|
valid: boolean;
|
|
|
|
t1same: boolean;
|
|
t2same: boolean;
|
|
|
|
potentialpointer: boolean;
|
|
allreadablepointers: boolean;
|
|
p: ptruint;
|
|
begin
|
|
//do stuff
|
|
|
|
//matching rules: (looking for is table 1, not looking for is table 2)
|
|
//if any value of table 2 is in table 1, then not valid
|
|
//if above passes then
|
|
//if table 1 fields are all the same and table 2 fields are all the same: match
|
|
//if table 1 fields are all the same and table 2 fields are not all the same: match
|
|
//if table 1 fields are all different and table 2 fields are all the same: match
|
|
|
|
//if no match and ALL fields represent an address then add it as a new workitem (fields where the player is null and the enemy is filled in have no further use besides checking if it's valid or not)
|
|
|
|
pos:=0;
|
|
for i:=0 to wi.currentLevel-1 do
|
|
newwi.path[i]:=wi.path[i];
|
|
|
|
|
|
while pos<structsize do
|
|
begin
|
|
for i:=0 to length(memoryblockLF)-1 do
|
|
begin
|
|
a:=wi.LookingForList[i]+pos;
|
|
if (LookingForShadowList[i].shadow<>0) and inrangeq(a, LookingForShadowList[i].address, LookingForShadowList[i].address+LookingForShadowList[i].shadowsize) then
|
|
begin
|
|
//make it a relative address in the shadow copy
|
|
a:=a-LookingForShadowList[i].address+LookingForShadowList[i].shadow;
|
|
if readMemory(a, memoryblockLF[i], blocksize)=false then
|
|
begin
|
|
inc(pos,alignment);
|
|
continue;
|
|
end;
|
|
end
|
|
else
|
|
if readMemory(a, memoryblockLF[i], blocksize)=false then
|
|
exit; //unreadable
|
|
end;
|
|
|
|
for i:=0 to length(memoryblockNLF)-1 do
|
|
begin
|
|
a:=wi.NotLookingForList[i]+pos;
|
|
if (NotLookingForShadowList[i].shadow<>0) and inrangeq(a, NotLookingForShadowList[i].address, NotLookingForShadowList[i].address+NotLookingForShadowList[i].shadowsize) then
|
|
begin
|
|
//make it a relative address in the shadow copy
|
|
a:=(a-NotLookingForShadowList[i].address)+NotLookingForShadowList[i].shadow;
|
|
if readMemory(a, memoryblockNLF[i], blocksize)=false then
|
|
begin
|
|
inc(pos,alignment);
|
|
continue;
|
|
end;
|
|
end
|
|
else
|
|
if readMemory(a, memoryblockNLF[i], blocksize)=false then
|
|
exit; //unreadable
|
|
end;
|
|
|
|
//do the check
|
|
|
|
valid:=true;
|
|
t1same:=true;
|
|
t2same:=true;
|
|
potentialpointer:=true;
|
|
for i:=0 to length(memoryblockNLF)-1 do
|
|
begin
|
|
//check if these values are in memoryblockLF. If so, not valid
|
|
for j:=0 to length(memoryblockLF)-1 do
|
|
begin
|
|
//use the first iteration to see of LF is all the same nor not
|
|
if (i=0) and (j>0) and (CompareMem(memoryblockLF[0], memoryblockLF[j],alignment)=false) then
|
|
begin
|
|
t1same:=false;
|
|
if sameonly then
|
|
begin
|
|
valid:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
if CompareMem(memoryblockNLF[i], memoryblockLF[j], alignment) then
|
|
begin
|
|
//a value in table2 matches table1
|
|
valid:=false;
|
|
potentialpointer:=false;
|
|
break;
|
|
end;
|
|
|
|
end;
|
|
if valid=false then break;
|
|
|
|
if (i>0) and (CompareMem(memoryblockNLF[0], memoryblockNLF[i],alignment)=false) then
|
|
begin
|
|
t2same:=false;
|
|
if sameonly then
|
|
begin
|
|
valid:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if valid then
|
|
if (t1same=false) and (t2same=false) then valid:=false;
|
|
|
|
if valid then
|
|
begin
|
|
//found a possible identifier to use
|
|
wi.path[wi.currentLevel]:=pos;
|
|
writeResult(wi.path,wi.currentLevel);
|
|
end;
|
|
|
|
if (potentialpointer) then
|
|
begin
|
|
allreadablepointers:=(wi.currentLevel<maxlevel) and potentialpointer;
|
|
|
|
if allreadablepointers then
|
|
begin
|
|
for i:=0 to length(memoryblockLF)-1 do
|
|
begin
|
|
p:=0;
|
|
if is64bittarget then
|
|
begin
|
|
newwi.LookingForList[i]:=pqword(memoryblockLF[i])^;
|
|
end
|
|
else
|
|
begin
|
|
newwi.LookingForList[i]:=pdword(memoryblockLF[i])^;
|
|
end;
|
|
|
|
if readMemory(newwi.LookingForList[i], @p, 1)=false then
|
|
begin
|
|
allreadablepointers:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if allreadablepointers then
|
|
begin
|
|
for i:=0 to length(memoryblockNLF)-1 do
|
|
begin
|
|
p:=0;
|
|
if is64bittarget then
|
|
begin
|
|
newwi.NotLookingForList[i]:=pqword(memoryblockNLF[i])^;
|
|
end
|
|
else
|
|
begin
|
|
newwi.NotLookingForList[i]:=pdword(memoryblockNLF[i])^;
|
|
end;
|
|
|
|
if readMemory(newwi.NotLookingForList[i], @p, 1)=false then
|
|
begin
|
|
allreadablepointers:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
if allreadablepointers then
|
|
begin
|
|
newwi.currentLevel:=wi.currentLevel+1;
|
|
|
|
newwi.path[wi.currentLevel]:=pos;
|
|
addNewWorkItemToQueue;
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
inc(pos,alignment);
|
|
end;
|
|
|
|
//profit
|
|
end;
|
|
|
|
procedure TStructCompareScanner.execute;
|
|
var
|
|
wi: TStructureCompareWorkItem;
|
|
i: integer;
|
|
begin
|
|
//initialize the temp and new workitem object (saves on allocs)
|
|
results:=tmemorystream.create;
|
|
|
|
lastwrite:=gettickcount64;
|
|
resultfile:=TFileStream.Create(filename,fmCreate);
|
|
|
|
|
|
setlength(newwi.LookingForList,length(LookingForShadowList));
|
|
setlength(newwi.NotLookingForList,length(NotLookingForShadowList));
|
|
setlength(newwi.path,maxlevel+1); //for the final offset
|
|
|
|
setlength(memoryblockLF,length(LookingForShadowList));
|
|
setlength(memoryblockNLF,length(NotLookingForShadowList));
|
|
|
|
blocksize:=max(alignment, processhandler.pointersize);
|
|
for i:=0 to length(memoryblockLF)-1 do
|
|
getmem(memoryblockLF[i],blocksize);
|
|
|
|
for i:=0 to length(memoryblockNLF)-1 do
|
|
getmem(memoryblockNLF[i],blocksize);
|
|
|
|
setlength(readmemorycache,length(LookingForShadowList)+length(NotLookingForShadowList));
|
|
for i:=0 to length(readmemorycache)-1 do
|
|
begin
|
|
readmemorycache[i].pageindex:=0;
|
|
readmemorycache[i].lasttimeaccess:=0;
|
|
readmemorycache[i].data:=nil;
|
|
end;
|
|
|
|
try
|
|
setlength(wi.path,maxlevel+1);
|
|
setlength(wi.LookingForList,length(LookingForShadowList));
|
|
setlength(wi.NotLookingForList,length(NotLookingForShadowList));
|
|
while not terminated and GetWorkItem(wi) do
|
|
HandleWorkItem(wi);
|
|
except
|
|
on e:exception do
|
|
OutputDebugString('TStructCompareScanner.Error:'+e.message);
|
|
end;
|
|
|
|
idle:=true;
|
|
flushresults;
|
|
|
|
freeandnil(results);
|
|
freeandnil(resultfile);
|
|
end;
|
|
|
|
destructor TStructCompareScanner.destroy;
|
|
var i: integer;
|
|
begin
|
|
for i:=0 to length(memoryblockLF)-1 do
|
|
FreeMemAndNil(memoryblockLF[i]);
|
|
|
|
for i:=0 to length(memoryblockNLF)-1 do
|
|
FreeMemAndNil(memoryblockNLF[i]);
|
|
|
|
if results<>nil then
|
|
freeandnil(results);
|
|
|
|
if resultfile<>nil then
|
|
freeandnil(resultfile);
|
|
inherited destroy;
|
|
end;
|
|
|
|
//--------------TStructCompareController---------------
|
|
procedure TStructCompareController.execute;
|
|
var
|
|
count: integer;
|
|
pointerpath: TPointerpath;
|
|
i,j: integer;
|
|
|
|
t: TStructCompareScanner;
|
|
|
|
released: boolean;
|
|
allidle: boolean;
|
|
begin
|
|
try
|
|
errorstring:=rsSPSNoError;
|
|
|
|
memorymap:=TPageMap.create;
|
|
memorymapCS:=TCriticalSection.create;;
|
|
try
|
|
count:=GetCPUCount;
|
|
workqueueCS:=TCriticalSection.Create;
|
|
workqueueSemaphore:=TSemaphore.create(64, true);
|
|
|
|
configfile:=tfilestream.create(filename, fmCreate);
|
|
|
|
setlength(workers, count);
|
|
for i:=0 to length(workers)-1 do
|
|
begin
|
|
workers[i]:=TStructCompareScanner.Create(true);
|
|
workers[i].LookingForShadowList:=LFList;
|
|
workers[i].NotLookingForShadowList:=NLFList;
|
|
workers[i].maxlevel:=maxlevel;
|
|
workers[i].structsize:=structsize;
|
|
workers[i].alignment:=alignment;
|
|
workers[i].is64bittarget:=is64bittarget;
|
|
workers[i].filename:=filename+'.results.'+inttostr(i);
|
|
workers[i].sameonly:=sameonly;
|
|
workers[i].owner:=self;
|
|
workers[i].Start;
|
|
end;
|
|
|
|
//allocate space for the workitems
|
|
for i:=0 to 63 do
|
|
begin
|
|
setlength(workqueue[i].path, maxlevel+1);
|
|
for j:=0 to maxlevel do
|
|
workqueue[i].path[j]:=0;
|
|
|
|
setlength(workqueue[i].LookingForList, length(LFList));
|
|
setlength(workqueue[i].NotLookingForList, length(NLFList));
|
|
end;
|
|
|
|
//input the first workitem
|
|
for i:=0 to length(LFList)-1 do
|
|
workqueue[0].LookingForList[i]:=LFList[i].address;
|
|
|
|
for i:=0 to length(NLFList)-1 do
|
|
workqueue[0].NotLookingForList[i]:=NLFList[i].address;
|
|
|
|
workqueue[0].currentLevel:=0;
|
|
|
|
workqueuepos:=1;
|
|
workqueueSemaphore.Release; //the first worker will now add new items to the queue launching other workers
|
|
|
|
|
|
//create the results config file (pointerscan file, not compressed config)
|
|
configfile.WriteByte($ec); //header (not to be confused with pointerscan)
|
|
configfile.WriteByte(compareversion);
|
|
configfile.WriteDword(maxlevel+1); //number of offsets in each row (with level 0 that is 1 offset)
|
|
freeandnil(configfile);
|
|
|
|
allidle:=false;
|
|
released:=false;
|
|
|
|
while (not terminated) and ((not allidle) or (workqueuepos>0)) do
|
|
begin
|
|
j:=0;
|
|
allidle:=true;
|
|
for i:=0 to length(workers)-1 do
|
|
begin
|
|
inc(j,workers[i].foundcount);
|
|
if workers[i].idle=false then
|
|
allidle:=false;
|
|
end;
|
|
|
|
foundcount:=j;
|
|
|
|
end;
|
|
|
|
|
|
finally
|
|
for i:=0 to length(workers)-1 do
|
|
workers[i].Terminate;
|
|
|
|
while workqueueSemaphore.Release<>-1 do;
|
|
|
|
for i:=0 to length(workers)-1 do
|
|
begin
|
|
workers[i].WaitFor;
|
|
workers[i].Free;
|
|
end;
|
|
|
|
setlength(workers,0);
|
|
|
|
//reached the end, tell the main thread that the scan is done
|
|
if configfile<>nil then
|
|
freeandnil(configfile);
|
|
|
|
Queue(owner.scandone);
|
|
|
|
freeandnil(workqueueCS);
|
|
freeandnil(workqueueSemaphore);
|
|
freeandnil(memorymapCS);
|
|
freeandnil(memorymap);
|
|
end;
|
|
|
|
except
|
|
on e:exception do
|
|
begin
|
|
errorstring:=e.message;
|
|
Queue(owner.scanerror);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
constructor TStructCompareController.create(alignment: integer; sameonly: boolean; const LFL: TAddressWithShadowList; const NLFL: TAddressWithShadowList; structsize: integer; maxlevel: integer; filename: string; owner: TfrmStructureCompare);
|
|
var
|
|
i: integer;
|
|
r: tstringlist;
|
|
begin
|
|
setlength(LFList, length(LFL));
|
|
for i:=0 to length(LFL)-1 do
|
|
LFList[i]:=LFL[i];
|
|
|
|
setlength(NLFList, length(NLFL));
|
|
for i:=0 to length(NLFL)-1 do
|
|
NLFList[i]:=NLFL[i];
|
|
|
|
self.is64bittarget:=processhandler.is64Bit;
|
|
self.structsize:=structsize;
|
|
self.maxlevel:=maxlevel;
|
|
|
|
self.alignment:=alignment;
|
|
self.mustbeinregion:=mustbeinregion;
|
|
|
|
self.owner:=owner;
|
|
self.filename:=filename;
|
|
self.sameonly:=sameonly;
|
|
|
|
r:=tstringlist.create;
|
|
findAllResultFilesForThisPtr(filename, r);
|
|
for i:=0 to r.count-1 do
|
|
DeleteFile(r[i]);
|
|
|
|
r.free;
|
|
inherited create(false); //let's get started...
|
|
end;
|
|
|
|
destructor TStructCompareController.destroy;
|
|
var i: integer;
|
|
begin
|
|
if resultfile<>nil then
|
|
freeandnil(resultfile);
|
|
|
|
inherited destroy;
|
|
end;
|
|
|
|
|
|
//----------------------------
|
|
|
|
function TfrmStructureCompare.getStringFromPointer(address: ptruint; offsets: TDwordArray; level, bytesize: integer; unicode: boolean; var a: ptruint): string;
|
|
var i: integer;
|
|
x: ptruint;
|
|
|
|
b: pchar;
|
|
wb: pwidechar absolute b;
|
|
begin
|
|
result:='';
|
|
a:=address+offsets[0];
|
|
for i:=1 to level do
|
|
begin
|
|
if readprocessmemory(processhandle, pointer(a), @a, processhandler.pointersize, x) then
|
|
begin
|
|
a:=a+offsets[i];
|
|
end
|
|
else
|
|
begin
|
|
result:='???';
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
getmem(b, bytesize+2);
|
|
if ReadProcessMemory(processhandle, pointer(a), b, bytesize, x) then
|
|
begin
|
|
b[bytesize]:=#0;
|
|
b[bytesize+1]:=#0;
|
|
|
|
if unicode then
|
|
result:=wb
|
|
else
|
|
result:=b;
|
|
|
|
end;
|
|
FreeMemAndNil(b);
|
|
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.lvResultsColumnClick(Sender: TObject;
|
|
Column: TListColumn);
|
|
begin
|
|
if pointerfilereader<>nil then
|
|
begin
|
|
if (pointerfilereader.count>10000000) and (MessageDlg('There are over 10000000 entries in this list. This may take a while. Are you sure?', mtConfirmation,[mbyes,mbno],0)<>mryes) then exit;
|
|
|
|
pointerfilereader.sort(column.Index);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.lvResultsData(Sender: TObject; Item: TListItem);
|
|
var
|
|
i: integer;
|
|
s,s2: string;
|
|
a: ptruint;
|
|
|
|
a2: ptruint;
|
|
|
|
p: PPointerRecord;
|
|
|
|
count: integer;
|
|
|
|
procedure HandleAddress(edt: TAddressEdit);
|
|
var
|
|
shadow: ptruint;
|
|
shadowsize: integer;
|
|
begin
|
|
a:=edt.address;
|
|
|
|
if edt.tag<>0 then
|
|
begin
|
|
shadow:=tshadow(edt.tag).address;
|
|
shadowsize:=tshadow(edt.tag).size;
|
|
end
|
|
else
|
|
begin
|
|
shadow:=0;
|
|
shadowsize:=0;
|
|
end;
|
|
|
|
if not edt.invalidAddress then
|
|
begin
|
|
s:=pointerfilereader.getStringFromPointerRecord(p, a, shadow, shadowsize);
|
|
a:=pointerfilereader.getAddressFromPointerRecord(p, a, shadow, shadowsize);
|
|
|
|
//check if a is in the shadow, and if so, change it back (for visual purpose)
|
|
if (shadow<>0) and inrangeq(a,shadow,shadow+shadowsize-1) then
|
|
a:=(a-shadow)+edt.address;
|
|
|
|
item.SubItems.Add(inttohex(a,8)+' : '+s)
|
|
end
|
|
else
|
|
begin
|
|
item.subitems.add(' ');
|
|
end;
|
|
end;
|
|
begin
|
|
if pointerfilereader<>nil then
|
|
begin
|
|
item.data:=nil;
|
|
|
|
p:=pointerfilereader.getPointerRec(item.index);
|
|
|
|
if p<>nil then
|
|
begin
|
|
item.caption:=inttohex(p.offset[0],1);
|
|
for i:=1 to p.level do
|
|
item.SubItems.add(inttohex(p.offset[i],1));
|
|
|
|
for i:=p.level+1 to pointerfilereader.levelWidth-1 do
|
|
item.SubItems.Add(' ');
|
|
|
|
|
|
count:=0;
|
|
|
|
for i:=0 to edtLF.count-1 do
|
|
begin
|
|
inc(count);
|
|
if count>maxAddresses then
|
|
begin
|
|
item.subitems.add(rsMaxReached);
|
|
exit;
|
|
end;
|
|
|
|
HandleAddress(TAddressEdit(edtLF[i]));
|
|
end;
|
|
|
|
for i:=0 to edtNLF.count-1 do
|
|
begin
|
|
inc(count);
|
|
if count>maxAddresses then
|
|
begin
|
|
item.subitems.add(rsMaxReached);
|
|
exit;
|
|
end;
|
|
HandleAddress(TAddressEdit(edtNLF[i]));
|
|
end;
|
|
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.lvResultsDblClick(Sender: TObject);
|
|
var
|
|
p: tpoint;
|
|
i,j: integer;
|
|
x: integer;
|
|
a: ptruint;
|
|
begin
|
|
if (lvResults.Selected<>nil) and (pointerfilereader<>nil) then
|
|
begin
|
|
if edtLF.count>1 then
|
|
a:=TAddressEdit(edtLF[0]).address
|
|
else
|
|
if edtNLF.count>1 then
|
|
a:=TAddressEdit(edtNLF[0]).address
|
|
else
|
|
exit;
|
|
|
|
|
|
//get the mousecursor
|
|
p:=lvResults.ScreenToClient(mouse.CursorPos);
|
|
x:=0;
|
|
for i:=0 to lvResults.Columns.Count-1 do
|
|
begin
|
|
if (p.x>x) and (p.x<x+lvResults.Column[i].Width) then
|
|
begin
|
|
//found the columns
|
|
j:=i-pointerfilereader.pointerfileLevelwidth;
|
|
if j>=0 then
|
|
begin
|
|
if j>=edtLF.count then
|
|
begin
|
|
dec(j, edtLF.count);
|
|
a:=TAddressEdit(edtNLF[j]).address;
|
|
end
|
|
else
|
|
a:=TAddressEdit(edtLF[j]).address;
|
|
end;
|
|
break;
|
|
end;
|
|
|
|
inc(x, lvResults.Column[i].Width);
|
|
end;
|
|
MemoryBrowser.hexview.address:=pointerfilereader.getAddressFromPointerRecord(pointerfilereader.getPointerRec(lvResults.Selected.Index), a, 0, 0);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miCopyClick(Sender: TObject);
|
|
var
|
|
e: TAddressEdit;
|
|
i: integer;
|
|
begin
|
|
e:=TAddressEdit(pmAddressPopup.PopupComponent);
|
|
|
|
for i:=0 to edtLF.Count-1 do
|
|
if TAddressEdit(edtLF[i]).Focused then
|
|
begin
|
|
e:=edtLF[i];
|
|
break;
|
|
end;
|
|
|
|
for i:=0 to edtNLF.Count-1 do
|
|
if TAddressEdit(edtNLF[i]).Focused then
|
|
begin
|
|
e:=edtNLF[i];
|
|
break;
|
|
end;
|
|
|
|
if e=nil then exit;
|
|
|
|
if e.SelLength>0 then
|
|
e.CopyToClipboard;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miCutClick(Sender: TObject);
|
|
var
|
|
e: TAddressEdit;
|
|
i: integer;
|
|
begin
|
|
e:=TAddressEdit(pmAddressPopup.PopupComponent);
|
|
|
|
for i:=0 to edtLF.Count-1 do
|
|
if TAddressEdit(edtLF[i]).Focused then
|
|
begin
|
|
e:=edtLF[i];
|
|
break;
|
|
end;
|
|
|
|
for i:=0 to edtNLF.Count-1 do
|
|
if TAddressEdit(edtNLF[i]).Focused then
|
|
begin
|
|
e:=edtNLF[i];
|
|
break;
|
|
end;
|
|
|
|
if e=nil then exit;
|
|
|
|
if e.SelLength>0 then
|
|
begin
|
|
if e.ReadOnly then
|
|
e.CopyToClipboard
|
|
else
|
|
e.CutToClipboard;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miDeleteAddressClick(Sender: TObject);
|
|
var
|
|
e: TAddressEdit;
|
|
i: integer;
|
|
begin
|
|
e:=TAddressEdit(pmAddressPopup.PopupComponent);
|
|
i:=edtLF.IndexOf(e);
|
|
if i<>-1 then
|
|
begin
|
|
if edtLF.count=1 then exit;
|
|
edtLF.delete(i);
|
|
end;
|
|
|
|
i:=edtNLF.IndexOf(e);
|
|
if i<>-1 then
|
|
begin
|
|
if edtNLF.Count=1 then exit;
|
|
edtNLF.delete(i);
|
|
end;
|
|
|
|
e.free;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miFindClick(Sender: TObject);
|
|
begin
|
|
finddialog1.execute;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miMaxAddressesClick(Sender: TObject);
|
|
var
|
|
s: string;
|
|
i: integer;
|
|
begin
|
|
s:=inttostr(maxaddresses);
|
|
if InputQuery(rsStructureCompare, rsMaxAddressesShown, s) then
|
|
begin
|
|
maxaddresses:=strtoint(s);
|
|
if maxaddresses<0 then
|
|
maxaddresses:=0;
|
|
|
|
miMaxAddresses.caption:=format(rsMaxAddressesShown+' %d',[maxaddresses]);
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.OpenPointerfile(filename: string);
|
|
begin
|
|
cleanup;
|
|
|
|
pointerfilereader:=TPointerfileReader.Create(filename);
|
|
comboType.OnChange(comboType);
|
|
cbHexadecimal.OnChange(cbHexadecimal);
|
|
|
|
reloadlistviewcolumns;
|
|
|
|
|
|
|
|
lvResults.items.count:=dword(min(dword(1000000), dword(pointerfilereader.count)));
|
|
lblInfo.caption:=inttostr(pointerfilereader.count);
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.scanerror;
|
|
begin
|
|
if rescanner<>nil then
|
|
messagedlg(rescanner.errorstring,mtError,[mbok],0);
|
|
|
|
if scanner<>nil then
|
|
messagedlg(scanner.errorstring,mtError,[mbok],0);
|
|
|
|
cleanup;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.rescanDone;
|
|
begin
|
|
if rescanner<>nil then
|
|
lblInfo.caption:=rsSPSUFound+inttostr(rescanner.count);
|
|
|
|
cleanup;
|
|
beep;
|
|
|
|
btnScan.Visible:=true;
|
|
btnNewScan.Visible:=true;
|
|
|
|
OpenPointerfile(SaveDialog1.FileName);
|
|
|
|
if btncancel<>nil then
|
|
freeandnil(btncancel);
|
|
|
|
EnableGui;
|
|
beep;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.scanDone;
|
|
begin
|
|
if scanner<>nil then
|
|
lblInfo.caption:=rsSPSUFound+inttostr(scanner.foundcount);
|
|
|
|
cleanup;
|
|
|
|
OpenPointerfile(SaveDialog1.FileName);
|
|
|
|
btnScan.visible:=true;
|
|
btnScan.caption:=rsSPSURescan;
|
|
btnScan.tag:=1;
|
|
|
|
btnNewScan.visible:=true;
|
|
btnNewScan.enabled:=true;
|
|
|
|
if btncancel<>nil then
|
|
freeandnil(btncancel);
|
|
|
|
EnableGui;
|
|
beep;
|
|
end;
|
|
|
|
function TfrmStructureCompare.mapCompare(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
|
|
begin
|
|
result:=CompareValue(PMappedRegion(Data1).baseaddress, PMappedRegion(Data2).baseaddress);
|
|
end;
|
|
|
|
function TfrmStructureCompare.pointerCompare(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
|
|
begin
|
|
result:=CompareValue(PPointerListEntry(Data1).address, PPointerListEntry(Data2).address);
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.cleanup;
|
|
var r: TAvgLvlTreeNode;
|
|
p,m: PMappedRegion;
|
|
begin
|
|
statusupdater.enabled:=false;
|
|
|
|
if rescanner<>nil then
|
|
begin
|
|
rescanner.terminate;
|
|
rescanner.waitfor;
|
|
freeandnil(rescanner);
|
|
end;
|
|
|
|
if scanner<>nil then
|
|
begin
|
|
scanner.terminate;
|
|
scanner.WaitFor;
|
|
freeandnil(scanner);
|
|
end;
|
|
|
|
lvResults.items.count:=0;
|
|
while lvResults.ColumnCount>0 do
|
|
lvResults.Columns.Delete(0);
|
|
|
|
if pointerfilereader<>nil then
|
|
freeandnil(pointerfilereader);
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.btnCancelClick(sender: TObject);
|
|
begin
|
|
if scanner<>nil then
|
|
scanner.Terminate;
|
|
|
|
if rescanner<>nil then
|
|
rescanner.terminate;
|
|
|
|
btncancel.enabled:=false;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.btnScanClick(Sender: TObject);
|
|
var baseaddress: ptruint;
|
|
baseaddress2: ptruint;
|
|
structsize: integer;
|
|
maxlevel: integer;
|
|
alignsize: integer;
|
|
pointerstart: ptruint;
|
|
pointerstop: ptruint;
|
|
|
|
diffkind: TDiffkind;
|
|
vartype: TVariableType;
|
|
|
|
shadow, shadow2: ptruint;
|
|
shadowsize, shadowsize2: ptruint;
|
|
|
|
oldpointerfile: string;
|
|
|
|
i,j: integer;
|
|
LF: array of TAddressWithShadow;
|
|
NLF: array of TAddressWithShadow;
|
|
|
|
s: string;
|
|
begin
|
|
|
|
if pointerfilereader<>nil then
|
|
oldpointerfile:=pointerfilereader.filename
|
|
else
|
|
oldpointerfile:='';
|
|
|
|
setlength(LF, edtLF.count);
|
|
|
|
for i:=0 to edtLF.count-1 do
|
|
begin
|
|
lf[i].address:=TAddressEdit(edtLF[i]).address;
|
|
|
|
if TAddressEdit(edtLF[i]).tag<>0 then
|
|
begin
|
|
lf[i].shadow:=TShadow(TAddressEdit(edtLF[i]).tag).Address;
|
|
lf[i].shadowsize:=TShadow(TAddressEdit(edtLF[i]).tag).Size;
|
|
end
|
|
else
|
|
begin
|
|
lf[i].shadow:=0;
|
|
lf[i].shadowsize:=0;
|
|
end;
|
|
|
|
if TAddressEdit(edtLF[i]).invalidAddress then raise exception.create(Format(rsInvalidGroup, [1, inttostr(i+1), TAddressEdit(edtLF[i]).text]));
|
|
|
|
for j:=0 to i-1 do
|
|
if (lf[j].address=lf[i].address) and (lf[i].shadow=lf[j].shadow) then raise exception.create(Format(rsSameAddress, [TAddressEdit(edtLF[i]).text]));
|
|
end;
|
|
|
|
setlength(NLF, edtNLF.count);
|
|
for i:=0 to edtNLF.count-1 do
|
|
begin
|
|
nlf[i].address:=TAddressEdit(edtNLF[i]).address;
|
|
|
|
if TAddressEdit(edtNLF[i]).tag<>0 then
|
|
begin
|
|
nlf[i].shadow:=TShadow(TAddressEdit(edtNLF[i]).tag).Address;
|
|
nlf[i].shadowsize:=TShadow(TAddressEdit(edtNLF[i]).tag).Size;
|
|
end
|
|
else
|
|
begin
|
|
nlf[i].shadow:=0;
|
|
nlf[i].shadowsize:=0;
|
|
end;
|
|
if TAddressEdit(edtNLF[i]).invalidAddress then raise exception.create(Format(rsInvalidGroup, [2, inttostr(i+1), TAddressEdit(edtNLF[i]).text]));
|
|
|
|
for j:=0 to i-1 do
|
|
begin
|
|
if (nlf[j].address=nlf[i].address) and (nlf[i].shadow=nlf[j].shadow) then
|
|
begin
|
|
s:=TAddressEdit(edtNLF[i]).text;
|
|
raise exception.create(Format(rsSameAddress, [s]));
|
|
end;
|
|
end;
|
|
|
|
for j:=0 to length(lf)-1 do
|
|
begin
|
|
if (lf[j].address=nlf[i].address) and (lf[i].shadow=nlf[j].shadow) then
|
|
begin
|
|
s:=TAddressEdit(edtNLF[i]).text;
|
|
raise exception.create(Format(rsSameAddress, [s]));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
maxlevel:=strtoint(edtMaxLevel.text);
|
|
structsize:=strtoint(edtStructsize.text);
|
|
alignsize:=strtoint(edtAlignsize.text);
|
|
|
|
vartype:=vtPointer;
|
|
|
|
if alignsize<=0 then raise exception.create(rsTooSmallAlign);
|
|
|
|
if savedialog1.execute=false then exit;
|
|
|
|
|
|
cleanup;
|
|
disableGui;
|
|
|
|
if btnscan.tag=1 then
|
|
begin
|
|
if oldpointerfile<>'' then
|
|
rescanner:=TStructCompareRescan.create(alignsize,cbSameOnly.checked, lf,nlf, oldpointerfile, savedialog1.filename, self)
|
|
else
|
|
rescandone;
|
|
end
|
|
else
|
|
begin
|
|
scanner:=TStructCompareController.create(alignsize,cbSameOnly.checked, lf,nlf, structsize, maxlevel, savedialog1.filename, self);
|
|
statusupdater.enabled:=true;
|
|
end;
|
|
|
|
btnscan.visible:=false;
|
|
btnNewScan.visible:=false;
|
|
|
|
btncancel:=TButton.create(self);
|
|
btncancel.parent:=panel9;
|
|
btncancel.caption:=rsStop;
|
|
btncancel.AutoSize:=true;
|
|
btncancel.OnClick:=btnCancelClick;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.cbHexadecimalChange(Sender: TObject);
|
|
begin
|
|
if pointerfilereader<>nil then
|
|
pointerfilereader.hexadecimal:=cbHexadecimal.checked;
|
|
|
|
lvResults.Refresh;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.reloadlistviewcolumns;
|
|
var
|
|
nr: integer;
|
|
lc: TListColumn;
|
|
i: integer;
|
|
begin
|
|
if pointerfilereader=nil then
|
|
begin
|
|
if lvResults.Columns.Count>0 then
|
|
lvResults.Columns.Clear;
|
|
end
|
|
else
|
|
begin
|
|
nr:=0;
|
|
for i:=0 to pointerfilereader.levelWidth-1 do
|
|
begin
|
|
if lvResults.Columns.count<=nr then
|
|
begin
|
|
lc:=lvResults.Columns.Add;
|
|
lc.MinWidth:=2;
|
|
lc.Width:=70;
|
|
end
|
|
else
|
|
lc:=lvResults.Column[nr];
|
|
|
|
lc.Caption:=rsSPSUOffset+inttostr(i);
|
|
inc(nr);
|
|
end;
|
|
|
|
|
|
for i:=0 to edtLF.Count-1 do
|
|
begin
|
|
if lvResults.Columns.count<=nr then
|
|
begin
|
|
lc:=lvResults.Columns.Add;
|
|
lc.MinWidth:=2;
|
|
lc.Width:=120;
|
|
end
|
|
else
|
|
lc:=lvResults.Column[nr];
|
|
|
|
lc.Caption:='G1:'+TAddressEdit(edtLF[i]).Text;
|
|
inc(nr);
|
|
end;
|
|
|
|
for i:=0 to edtNLF.Count-1 do
|
|
begin
|
|
if lvResults.Columns.count<=nr then
|
|
begin
|
|
lc:=lvResults.Columns.Add;
|
|
lc.MinWidth:=2;
|
|
lc.Width:=120;
|
|
end
|
|
else
|
|
lc:=lvResults.Column[nr];
|
|
|
|
lc.Caption:='G2:'+TAddressEdit(edtNLF[i]).Text;
|
|
inc(nr);
|
|
end;
|
|
|
|
while lvResults.columns.count>nr do
|
|
lvResults.Columns.Delete(lvResults.columns.count-1);
|
|
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.AddressEditChange(Sender: TObject);
|
|
begin
|
|
reloadlistviewcolumns;
|
|
lvResults.Refresh;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.btnAddAddressClick(Sender: TObject);
|
|
var e: TaddressEdit;
|
|
begin
|
|
e:=TAddressEdit.Create(self);
|
|
e.OnChange:=AddressEditChange;
|
|
e.PopupMenu:=pmAddressPopup;
|
|
|
|
if (sender as Tbutton).tag=0 then
|
|
begin
|
|
e.parent:=pnlLF;
|
|
edtLF.Add(e);
|
|
end
|
|
else
|
|
begin
|
|
e.parent:=pnlNLF;
|
|
edtNLF.Add(e);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TfrmStructureCompare.comboTypeChange(Sender: TObject);
|
|
begin
|
|
if pointerfilereader<>nil then
|
|
begin
|
|
case comboType.itemindex of
|
|
0: pointerfilereader.vartype:=vtString;
|
|
1: pointerfilereader.vartype:=vtUnicodeString;
|
|
2: pointerfilereader.vartype:=vtByte;
|
|
3: pointerfilereader.vartype:=vtWord;
|
|
4: pointerfilereader.vartype:=vtDword;
|
|
5: pointerfilereader.vartype:=vtQword;
|
|
6: pointerfilereader.vartype:=vtSingle;
|
|
7: pointerfilereader.vartype:=vtDouble;
|
|
8: pointerfilereader.vartype:=vtPointer;
|
|
end;
|
|
|
|
lvResults.Refresh;
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TfrmStructureCompare.FindDialog1Find(Sender: TObject);
|
|
var
|
|
i,start: integer;
|
|
s: string;
|
|
FindText: string;
|
|
cs: boolean;
|
|
begin
|
|
FindText:=finddialog1.FindText;
|
|
if FindText='' then exit;
|
|
|
|
miFindNext.enabled:=true;
|
|
|
|
cs:=frMatchCase in finddialog1.Options;
|
|
if not cs then findtext:=uppercase(findtext);
|
|
|
|
for i:=lvResults.ItemIndex+1 to lvResults.Items.Count-1 do
|
|
begin
|
|
s:=lvResults.items[i].SubItems.Text;
|
|
if not cs then s:=uppercase(s);
|
|
|
|
if pos(FindText, s)>0 then
|
|
begin
|
|
lvResults.ItemIndex:=i;
|
|
lvResults.Items[i].MakeVisible(false);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
beep;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.FormClose(Sender: TObject; var CloseAction: TCloseAction);
|
|
begin
|
|
cleanup;
|
|
|
|
if donotfreeonclose then exit;
|
|
|
|
if self<>frmStructureCompare then closeaction:=cafree;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.FormCreate(Sender: TObject);
|
|
begin
|
|
edtLF:=Tlist.create;
|
|
edtNLF:=Tlist.create;
|
|
autosize:=false;
|
|
LoadFormPosition(self);
|
|
|
|
maxAddresses:=cereg.readInteger('Structure Compare Max Addresses', 32);
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.FormDestroy(Sender: TObject);
|
|
begin
|
|
edtLF.free;
|
|
edtNLF.free;
|
|
|
|
SaveFormPosition(self);
|
|
cereg.writeInteger('Structure Compare Max Addresses', maxaddresses);
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.FormShow(Sender: TObject);
|
|
var w,b,mh: integer;
|
|
begin
|
|
//panel1.Constraints.MinHeight:=btnNewScan.Top+btnNewScan.Height+lblInfo.Height+4;
|
|
|
|
if edtLF.Count=0 then
|
|
btnAddAddressLF.click;
|
|
|
|
if edtNLF.count=0 then
|
|
btnAddAddressNLF.click;
|
|
|
|
AdjustComboboxSize(comboType, canvas);
|
|
|
|
// gbLF.Constraints.MinWidth:=GetEditBoxMargins(tedit(edtLF[0]))+canvas.TextWidth(' XXXXXXXXXXXXXXXX ');
|
|
w:=GetEditBoxMargins(edtMaxLevel)+canvas.TextWidth(' XXXXXXXXXXXXXXXX '); ;
|
|
|
|
|
|
b:=tedit(edtLF[0]).ClientToParent(point(0,0),gbLF).x;
|
|
|
|
|
|
gbLF.Constraints.MinWidth:=w+b;
|
|
gbNLF.Constraints.MinWidth:=w+b;
|
|
|
|
gbLF.ClientWidth:=w+b;
|
|
gbNLF.ClientWidth:=w+b;
|
|
|
|
Constraints.MinWidth:=panel7.left+panel7.Width;
|
|
|
|
mh:=panel9.top+panel9.height+lblinfo.height;
|
|
panel3.Constraints.MinHeight:=mh;
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TfrmStructureCompare.gbNLFClick(Sender: TObject);
|
|
begin
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TfrmStructureCompare.lvResultsCustomDrawSubItem(
|
|
Sender: TCustomListView; Item: TListItem; SubItem: Integer;
|
|
State: TCustomDrawState; var DefaultDraw: Boolean);
|
|
var i: integer;
|
|
begin
|
|
defaultdraw:=true;
|
|
if pointerfilereader<>nil then
|
|
begin
|
|
i:=subitem-pointerfilereader.levelWidth;
|
|
if i>=0 then
|
|
begin
|
|
|
|
sender.Canvas.Font.Color:=clBlue;
|
|
if i>=edtLF.count then
|
|
begin
|
|
sender.Canvas.Font.Color:=clRed;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miNewScanClick(Sender: TObject);
|
|
begin
|
|
if MessageDlg(rsAreYouSureYo, mtConfirmation, mbYesNo, 0)=mryes then
|
|
begin
|
|
cleanup;
|
|
btnScan.tag:=0;
|
|
btnScan.caption:=rsScan;
|
|
|
|
btnScan.Left:=(panel1.ClientWidth div 2)-(btnscan.Width div 2);
|
|
|
|
EnableGui;
|
|
|
|
edtMaxLevel.Enabled:=true;
|
|
edtStructsize.enabled:=true;
|
|
lblMaxLevel.enabled:=true;
|
|
lblStructsize.enabled:=true;
|
|
|
|
btnNewScan.Visible:=false;
|
|
end;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miOpenClick(Sender: TObject);
|
|
begin
|
|
if (scanner=nil) and (rescanner=nil) and OpenDialog1.Execute then
|
|
begin
|
|
OpenPointerfile(opendialog1.filename);
|
|
enablegui;
|
|
|
|
btnScan.caption:=rsSPSURescan;
|
|
btnScan.tag:=1;
|
|
|
|
btnNewScan.visible:=true;
|
|
btnNewScan.enabled:=true;
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.MenuItem7Click(Sender: TObject);
|
|
begin
|
|
with TfrmStructureCompare.create(Application) do show;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miClearCacheClick(Sender: TObject);
|
|
begin
|
|
if pointerfilereader<>nil then
|
|
pointerfilereader.clearPointerCache;
|
|
|
|
lvResults.Refresh;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miPasteClick(Sender: TObject);
|
|
var e: TAddressEdit;
|
|
i: integer;
|
|
begin
|
|
e:=TAddressEdit(pmAddressPopup.PopupComponent);
|
|
|
|
for i:=0 to edtLF.Count-1 do
|
|
if TAddressEdit(edtLF[i]).Focused then
|
|
begin
|
|
e:=edtLF[i];
|
|
break;
|
|
end;
|
|
|
|
for i:=0 to edtNLF.Count-1 do
|
|
if TAddressEdit(edtNLF[i]).Focused then
|
|
begin
|
|
e:=edtNLF[i];
|
|
break;
|
|
end;
|
|
|
|
if e=nil then exit; //?
|
|
if e.readonly then exit;
|
|
|
|
if Clipboard.AsText<>'' then
|
|
e.PasteFromClipboard;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.miShadowClick(Sender: TObject);
|
|
var
|
|
e: TAddressEdit;
|
|
a: ptruint;
|
|
|
|
temp: pointer;
|
|
size: integer;
|
|
x: ptruint;
|
|
s: tshadow;
|
|
begin
|
|
e:=TAddressEdit(pmAddressPopup.PopupComponent);
|
|
if e=nil then exit;
|
|
|
|
a:=e.address;
|
|
if e.invalidAddress then exit;
|
|
|
|
if e.tag<>0 then
|
|
begin
|
|
s:=TShadow(e.tag);
|
|
if s.donotfree=false then
|
|
VirtualFreeEx(processhandle, pointer(TShadow(e.tag).address), 0, MEM_DECOMMIT);
|
|
|
|
s.free;
|
|
e.tag:=0;
|
|
e.ReadOnly:=false;
|
|
|
|
lvResults.Refresh;
|
|
exit;
|
|
end;
|
|
|
|
|
|
size:=strtoint(edtStructsize.Text);
|
|
getmem(temp, size);
|
|
|
|
if ReadProcessMemory(processhandle, pointer(a), temp, size,x) then
|
|
begin
|
|
a:=ptruint(VirtualAllocEx(processhandle, nil, size,mem_Commit or mem_Reserve, PAGE_READWRITE));
|
|
if a<>0 then
|
|
begin
|
|
WriteProcessMemory(processhandle, pointer(a),temp,size,x);
|
|
s:=tshadow.create;
|
|
s.address:=a;
|
|
s.size:=size;
|
|
e.tag:=ptruint(s);
|
|
e.readonly:=true;
|
|
end;
|
|
end;
|
|
|
|
FreeMemAndNil(temp);
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.pmAddressPopupPopup(Sender: TObject);
|
|
var e: TAddressEdit;
|
|
begin
|
|
e:=TAddressEdit(pmAddressPopup.PopupComponent);
|
|
miPaste.enabled:=Clipboard.AsText<>'';
|
|
miCut.enabled:=e.SelLength>0;
|
|
miCopy.enabled:=e.SelLength>0;
|
|
miShadow.enabled:=(e.invalidAddress=false) or (e.tag<>0);
|
|
|
|
if e.tag<>0 then
|
|
miShadow.Caption:=format(rsSPSUnlock,[TShadow(e.tag).address, TShadow(e.tag).address+TShadow(e.tag).size])
|
|
else
|
|
miShadow.caption:=rsSPSLock;
|
|
end;
|
|
|
|
|
|
|
|
procedure TfrmStructureCompare.statusupdaterTimer(Sender: TObject);
|
|
var
|
|
scannerprogress: double;
|
|
scannerTotal: double;
|
|
i: integer;
|
|
x: integer;
|
|
begin
|
|
{
|
|
if (rescanner<>nil) and (pointerfilereader<>nil) then
|
|
progressbar1.position:=trunc((rescanner.currentPosition / pointerfilereader.count) * 1000);
|
|
}
|
|
if scanner<>nil then
|
|
begin
|
|
lblInfo.caption:=Format(rsScanningFoun, [inttostr(scanner.foundcount)]);
|
|
{
|
|
scannerTotal:=power(scanner.structsize, scanner.maxlevel+1);
|
|
|
|
scannerprogress:=0;
|
|
for i:=0 to scanner.maxlevel-1 do
|
|
begin
|
|
x:=scanner.progress[i];
|
|
scannerprogress:=scannerprogress+x*power(scanner.structsize, (scanner.maxlevel+1)-i-1);
|
|
end;
|
|
|
|
progressbar1.position:=trunc((scannerprogress / scannertotal) * 1000);
|
|
|
|
}
|
|
end
|
|
else
|
|
if rescanner<>nil then
|
|
begin
|
|
if rescanner.progressbar<>nil then
|
|
rescanner.progressbar.position:=(rescanner.currentPosition*100) div rescanner.maxPosition;
|
|
|
|
lblinfo.caption:=Format(rsScanningFoun, [inttostr(rescanner.count)])
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.tRefresherTimer(Sender: TObject);
|
|
begin
|
|
lvResults.refresh;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.setGUIStateEnabled(state: boolean);
|
|
begin
|
|
lblvds.enabled:=state;
|
|
comboType.enabled:=state;
|
|
|
|
btnNewScan.enabled:=state;
|
|
btnScan.enabled:=state;
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.disableGui;
|
|
begin
|
|
setGUIStateEnabled(false);
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.enableGui;
|
|
begin
|
|
setGUIStateEnabled(true);
|
|
end;
|
|
|
|
procedure TfrmStructureCompare.addAddress(address: ptruint; shadow: qword; shadowsize: integer; group: integer);
|
|
var
|
|
e: TAddressEdit;
|
|
s: Tshadow;
|
|
begin
|
|
if group=0 then
|
|
begin
|
|
btnAddAddressLF.click;
|
|
e:=TAddressEdit(edtLF[edtLF.count-1]);
|
|
end
|
|
else
|
|
begin
|
|
btnAddAddressNLF.click;
|
|
e:=TAddressEdit(edtNLF[edtNLF.count-1]);
|
|
end;
|
|
|
|
e.Text:=inttohex(address,8);
|
|
|
|
if shadow<>0 then
|
|
begin
|
|
s:=tshadow.create;
|
|
s.address:=shadow;
|
|
s.size:=shadowsize;
|
|
s.donotfree:=true;
|
|
e.tag:=ptruint(s);
|
|
e.ReadOnly:=true;
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
{$I frmstructurecompareunit.lrs}
|
|
|
|
end.
|
|
|