{
todo:
This has shown a usefull method to dissect structures
wat can be done is add every dword offset in a structure as an offset and compare against the 4 bytes there
}
unit frmStringPointerScanUnit;
{$mode delphi}
interface
uses
windows, Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, ComCtrls,
cefuncproc, newkernelhandler, frmStringMapUnit, MemFuncs, AvgLvlTree, Menus,
bigmemallochandler, math, maps, oldRegExpr, symbolhandler, commonTypeDefs;
const
wm_sps_done=wm_user+1;
type
{ TfrmStringPointerScan }
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;
stringsize: integer;
unicode: BOOL;
offset: TDwordArray;
end;
PPointerRecord=^TPointerRecord;
TPointerfileReader=class
private
pointerfile: TFilestream;
entrysize: integer;
fcount: qword;
pointerfileLevelwidth: integer;
bufferindex: integer;
buffersize: integer;
pointerrecords: PPointerRecord; //actually an array...
stringbuf: pchar;
widestringbuf: pwidechar;
pointermap: TMap;
ffilename: string;
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;
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;
constructor create(filename: string);
destructor destroy; override;
property levelWidth: integer read pointerfileLevelwidth;
property count: qword read fcount;
property filename: string read ffilename;
end;
Trescan=class(tthread)
private
outputfile: tfilestream;
isstringscan: boolean;
mustbestart: boolean;
regEx: TRegExprEngine;
diffkind: TDiffkind;
pointerfilereader: TPointerfileReader;
address, address2: ptruint;
shadow, shadow2: ptruint;
shadowsize, shadowsize2: integer;
mustbeinregion: boolean;
pointerstart, pointerend: ptruint;
outputfilename: string;
oldpointerfilename: string;
results: TMemorystream;
fcount: qword;
fCurrentPosition: qword;
vartype: TVariableType;
lastwrite: dword;
ownerFrmStringPointerScan: TCustomForm;
procedure addPointer(p: PPointerRecord);
procedure flushresults;
function checkByte(p: PPointerRecord): boolean;
function checkWord(p: PPointerRecord): boolean;
function checkDWord(p: PPointerRecord): boolean;
function checkQWord(p: PPointerRecord): boolean;
function checkSingle(p: PPointerRecord): boolean;
function checkDouble(p: PPointerRecord): boolean;
function checkPointer(p: PPointerRecord): boolean;
public
procedure execute; override;
constructor create(suspended: boolean; address, address2: ptruint; mustbeinregion: boolean; pointerstart, pointerend: ptruint; isstringscan, caseSensitive, mustbestart: boolean; regExstr: string; diffkind: TDiffkind; vartype: TVariableType; oldpointerfilename: string; outputfilename: string; ownerFrmStringPointerScan: TCustomForm );
destructor destroy; override;
property count: qword read fcount;
property currentPosition: qword read fCurrentPosition;
end;
TScanner=class(tthread)
private
baseaddress: ptruint;
shadow: ptruint;
shadowsize: ptruint;
baseaddress2: ptruint;
shadow2: ptruint;
shadowsize2: ptruint;
diffkind: Tdiffkind;
vartype: TVariableType;
structsize: ptruint;
maxlevel: ptruint;
mappedregions: TAvgLvlTree;
pointerlist: TAvgLvlTree;
tempvariablebuffer, tempvariablebuffer2: pbytearray; //storage for the value compare. Preallocate so no need to call getmem/freemem each time
variablesize: integer;
valuemap: tmap;
bma: TBigMemoryAllocHandler;
count: integer;
//levelblock: array of Puint64Array; //I think this is obsolete
fillpointerblock: pointer; //memory block allocated for the fuillpointers function
// block64: array of Pint64Array;
results: TMemorystream;
resultfile: tfilestream;
lastwrite: dword;
//datascan variables
isDataScan: boolean;
alignment: integer;
mustbeinregion: boolean;
pointerstart: ptruint;
pointerstop: ptruint;
ownerFrmStringPointerScan: TCustomForm;
is64bittarget: boolean;
procedure handleBlock(blockaddress: ptruint; level: integer; path: TPointerpath);
function addStringPath(level: integer; path: tpointerpath; stringsize: integer; unicode: bool): boolean;
function comparePath(level: integer; path: tpointerpath; stringsize: integer): boolean;
function getAddressFromPath(base :ptruint; column: integer; level: integer; const path: TPointerPath): ptruint;
procedure mapRegionIfNeeded(blockaddress: ptruint; size: integer);
procedure fillPointers(base: ptruint; size: integer);
function getFirstPointerEntry(base: ptruint): PPointerListEntry;
function getPointerValue(address: ptruint; column: integer): ptruint;
procedure flushResults;
public
progress: TPointerpath;
procedure execute; override;
constructor create(isDataScan, mustbeinregion: boolean; alignment: integer; pointerstart, pointerstop: ptruint; baseaddress: ptruint; shadow: ptruint; shadowsize: ptruint; baseaddress2: ptruint; shadow2: ptruint; shadowsize2: integer; diffkind: TDiffkind; vartype: TVariableType; mapvalues: boolean; structsize: integer; maxlevel: integer; mappedregions,pointerlist: TAvgLvlTree; bma: TBigMemoryAllocHandler; filename: string; ownerFrmStringPointerScan: TCustomForm);
destructor destroy; override;
end;
TfrmStringPointerScan = class(TForm)
btnNewScan: TButton;
btnScan: TButton;
cbCaseSensitive: TCheckBox;
cbHasShadow: TCheckBox;
cbHasShadow2: TCheckBox;
cbMustBeStart: TCheckBox;
cbRegExp: TCheckBox;
cbPointerInRange: TCheckBox;
cbMapPointerValues: TCheckBox;
cbReuseStringmap: TCheckBox;
comboCompareType: TComboBox;
comboType: TComboBox;
edtBase: TEdit;
edtMaxLevel: TEdit;
edtShadowAddress: TEdit;
edtShadowAddress2: TEdit;
edtPointerStart: TEdit;
edtPointerStop: TEdit;
edtAlignsize: TEdit;
edtExtra: TEdit;
edtRegExp: TEdit;
edtShadowSize: TEdit;
edtShadowSize2: TEdit;
edtStructsize: TEdit;
FindDialog1: TFindDialog;
lblBaseRegion: TLabel;
lblInfo: TLabel;
lblMaxLevel: TLabel;
lblSize: TLabel;
lblsize2: TLabel;
lblStructsize: TLabel;
lblCompare: TLabel;
lblAlign: TLabel;
lblAnd: TLabel;
lblString: TLabel;
lblExtra: TLabel;
lblvds: TLabel;
ListView1: TListView;
MainMenu1: TMainMenu;
MenuItem1: 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;
Panel6: TPanel;
Panel7: TPanel;
Panel8: TPanel;
Panel9: TPanel;
pmPointerfile: TPopupMenu;
ProgressBar1: TProgressBar;
rbDiffDontCare: TRadioButton;
rbMustBeDifferent: TRadioButton;
rbMustBeSame: TRadioButton;
rbStringscan: TRadioButton;
rbDatascan: TRadioButton;
SaveDialog1: TSaveDialog;
statusupdater: TTimer;
procedure btnScanClick(Sender: TObject);
procedure cbHasShadowChange(Sender: TObject);
procedure cbRegExpChange(Sender: TObject);
procedure cbPointerInRangeChange(Sender: TObject);
procedure comboTypeChange(Sender: TObject);
procedure edtBaseChange(Sender: TObject);
procedure edtExtraChange(Sender: TObject);
procedure FindDialog1Find(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormShow(Sender: TObject);
procedure ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
procedure ListView1Data(Sender: TObject; Item: TListItem);
procedure ListView1DblClick(Sender: TObject);
procedure miFindClick(Sender: TObject);
procedure miNewScanClick(Sender: TObject);
procedure miOpenClick(Sender: TObject);
procedure MenuItem7Click(Sender: TObject);
procedure miClearCacheClick(Sender: TObject);
procedure MenuItem6Click(Sender: TObject);
procedure Panel3Click(Sender: TObject);
procedure rbDatascanChange(Sender: TObject);
procedure rbDiffDontCareChange(Sender: TObject);
procedure statusupdaterTimer(Sender: TObject);
private
{ private declarations }
mappedRegions: TAvgLvlTree; //holds the map of the regions that have been mapped
pointerlist: TAvgLvlTree; //holds the pointers in the app of the mapped regions
bma: TBigMemoryAllocHandler;
scanner: TScanner;
rescanner: TRescan;
pointerfilereader: TPointerfilereader;
address, address2: ptruint;
shadow, shadow2: ptruint;
shadowsize, shadowsize2: integer;
hasAddress2: boolean;
function mapCompare(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
function pointerCompare(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
procedure cleanup;
procedure OpenPointerfile(filename: string);
procedure scanDone;
function getStringFromPointer(address: ptruint; offsets: TDwordArray; level, bytesize: integer; unicode: boolean; var a: ptruint): string;
procedure setGUIStateEnabled(state: boolean);
public
{ public declarations }
procedure disableGui;
procedure enablegui;
end;
var
frmStringPointerScan: TfrmStringPointerScan;
implementation
{ TfrmStringPointerScan }
uses frmStructPointerRescanUnit, MemoryBrowserFormUnit, ProcessHandlerUnit, Parsers;
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';
//----------TPointerfileReader---------
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;
begin
result:=nil;
if (buffersize=0) or (not InRangeQ(index, bufferindex, bufferindex+buffersize-1)) then
begin
blocksize:=count-index;
blocksize:=min(blocksize, 4096);
pointerfile.Position:=sizeof(pointerfileLevelwidth)+index*entrysize;
if pointerfile.Read(pointerrecords^, entrysize*blocksize)=entrysize*blocksize then
begin
bufferindex:=index;
buffersize:=blocksize;
end;
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;
address:=baseaddress+p.offset[0];
if shadow<>0 then
begin
if inrangeq(address, baseaddress, baseaddress+shadowsize) then
address:=address+(shadow-baseaddress);
end;
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
result:='';
address:=getAddressFromPointerRecord(p, address, shadow, shadowsize);
if address>0 then
begin
case vartype of
vtByte, vtWord, vtDword, vtQword, vtSingle, vtDouble, vtPointer:
begin
e:=false;
case vartype of
vtByte: result:=inttostr(getByteFromAddress(address,e));
vtWord: result:=inttostr(getWordFromAddress(address,e));
vtDword: result:=inttostr(getDwordFromAddress(address,e));
vtQword: 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
if p.stringsize>0 then
i:=min(p.stringsize, 510)
else
i:=16; //it was a data type
if readprocessmemory(processhandle, pointer(address), stringbuf, i, x) then
begin
stringbuf[i]:=#0;
stringbuf[i+1]:=#0;
if p.stringsize<=0 then
begin
//guess the stringtype (if it is one to begin with)
if stringbuf[1]=#0 then
p.unicode:=true;
end;
if p.unicode then
result:=widestringbuf
else
result:=stringbuf;
end;
end
else
result:=rsSPSUNotYetImplemented;
end;
end;
end;
function TPointerfileReader.getStringAndAddress(index: qword; var address: ptruint; out p: PPointerRecord; shadow: ptruint; shadowsize: integer): string;
begin
p:=getPointerRec(index);
if p<>nil then
result:=getStringFromPointerRecord(p, address, shadow, shadowsize);
address:=getAddressFromPointerRecord(p, address, shadow, shadowsize);
end;
constructor TPointerfileReader.create(filename: string);
begin
ffilename:=filename;
pointerfile:=TFileStream.Create(filename, fmOpenRead or fmShareDenyNone);
pointerfile.ReadBuffer(pointerfileLevelwidth, sizeof(pointerfileLevelwidth));
entrysize:=(pointerfileLevelwidth+4)*sizeof(dword); //+4 for : Levelsize of pointer, stringsize and the isunicode boolean and levelwidth is based on 0 (0=1 offset, 1=2 offsets, etc..)
fcount:=(pointerfile.size-sizeof(pointerfileLevelwidth)) div (entrysize);
getmem(pointerrecords, entrysize*4096);
getmem(stringbuf, 512);
widestringbuf:=pwidechar(stringbuf);
pointermap:=TMap.Create(ituPtrSize,sizeof(pointer))
end;
destructor TPointerfileReader.destroy;
begin
if pointerfile<>nil then
freeandnil(pointerfile);
if pointerrecords<>nil then
freemem(pointerrecords);
if stringbuf<>nil then
freemem(stringbuf);
if pointermap<>nil then
freeandnil(pointermap);
//cleanup the maps
inherited destroy;
end;
//--------------TRescan----------------
procedure TRescan.flushResults;
begin
lastwrite:=GetTickCount;
outputfile.WriteBuffer(results.Memory^, results.position);
results.position:=0;
end;
procedure TRescan.addPointer(p: PPointerRecord);
begin
inc(fcount);
results.WriteBuffer(p^, pointerfilereader.entrysize);
if ((gettickcount-lastwrite)>5*60*1000) or (results.Position>=(15*1024*1024)) then
flushResults;
end;
function TRescan.checkByte(p: PPointerRecord): boolean;
var error: boolean;
a: ptruint;
v,v2: byte;
begin
result:=false;
a:=pointerfilereader.getAddressFromPointerRecord(p, address, shadow, shadowsize);
if (a<>0) and ((not mustbeinregion) or (InRangeX(a, pointerstart, pointerend))) then
begin
v:=pointerfilereader.getByteFromAddress(a, error);
result:=not error;
if result and (address2<>0) then
begin
a:=pointerfilereader.getAddressFromPointerRecord(p, address2, shadow2, shadowsize2);
if a<>0 then
begin
v2:=pointerfilereader.getByteFromAddress(a, error);
result:=not error;
if result and (diffkind<>dkDontcare) then
begin
if diffkind=dkMustBeDifferent then
result:=v<>v2
else
result:=v=v2; //must be equal
end;
end
else
result:=false;
end;
end;
end;
function TRescan.checkWord(p: PPointerRecord): boolean;
var error: boolean;
a: ptruint;
v,v2: word;
begin
result:=false;
a:=pointerfilereader.getAddressFromPointerRecord(p, address, shadow, shadowsize);
if (a<>0) and ((not mustbeinregion) or (InRangeX(a, pointerstart, pointerend))) then
begin
v:=pointerfilereader.getWordFromAddress(a, error);
result:=not error;
if result and (address2<>0) then
begin
a:=pointerfilereader.getAddressFromPointerRecord(p, address2, shadow2, shadowsize2);
if a<>0 then
begin
v2:=pointerfilereader.getWordFromAddress(a, error);
result:=not error;
if result and (diffkind<>dkDontCare) then
begin
if diffkind=dkMustBeDifferent then
result:=v<>v2
else
result:=v=v2; //must be equal
end;
end
else
result:=false;
end;
end;
end;
function TRescan.checkDWord(p: PPointerRecord): boolean;
var error: boolean;
a: ptruint;
v,v2: Dword;
begin
result:=false;
a:=pointerfilereader.getAddressFromPointerRecord(p, address, shadow, shadowsize);
if (a<>0) and ((not mustbeinregion) or (InRangeX(a, pointerstart, pointerend))) then
begin
v:=pointerfilereader.getDwordFromAddress(a, error);
result:=not error;
if result and (address2<>0) then
begin
a:=pointerfilereader.getAddressFromPointerRecord(p, address2, shadow2, shadowsize2);
if a<>0 then
begin
v2:=pointerfilereader.getDwordFromAddress(a, error);
result:=not error;
if result and (diffkind<>dkDontCare) then
begin
if diffkind=dkMustBeDifferent then
result:=v<>v2
else
result:=v=v2; //must be equal
end;
end
else
result:=false;
end;
end;
end;
function TRescan.checkQWord(p: PPointerRecord): boolean;
var error: boolean;
a: ptruint;
v,v2: Qword;
begin
result:=false;
a:=pointerfilereader.getAddressFromPointerRecord(p, address, shadow, shadowsize);
if (a<>0) and ((not mustbeinregion) or (InRangeX(a, pointerstart, pointerend))) then
begin
v:=pointerfilereader.getQwordFromAddress(a, error);
result:=not error;
if result and (address2<>0) then
begin
a:=pointerfilereader.getAddressFromPointerRecord(p, address2, shadow2, shadowsize2);
if a<>0 then
begin
v2:=pointerfilereader.getQwordFromAddress(a, error);
result:=not error;
if result and (diffkind<>dkDontCare) then
begin
if diffkind=dkMustBeDifferent then
result:=v<>v2
else
result:=v=v2; //must be equal
end;
end
else
result:=false;
end;
end;
end;
function TRescan.checkSingle(p: PPointerRecord): boolean;
var error: boolean;
a: ptruint;
v,v2: Single;
begin
result:=false;
a:=pointerfilereader.getAddressFromPointerRecord(p, address, shadow, shadowsize);
if (a<>0) and ((not mustbeinregion) or (InRangeX(a, pointerstart, pointerend))) then
begin
v:=pointerfilereader.getSingleFromAddress(a, error);
result:=not error;
if result and (address2<>0) then
begin
a:=pointerfilereader.getAddressFromPointerRecord(p, address2, shadow2, shadowsize2);
if a<>0 then
begin
v2:=pointerfilereader.getSingleFromAddress(a, error);
result:=not error;
if result and (diffkind<>dkDontCare) then
begin
if diffkind=dkMustBeDifferent then
result:=v<>v2
else
result:=v=v2; //must be equal
end;
end
else
result:=false;
end;
end;
end;
function TRescan.checkDouble(p: PPointerRecord): boolean;
var error: boolean;
a: ptruint;
v,v2: Double;
begin
result:=false;
a:=pointerfilereader.getAddressFromPointerRecord(p, address, shadow, shadowsize);
if (a<>0) and ((not mustbeinregion) or (InRangeX(a, pointerstart, pointerend))) then
begin
v:=pointerfilereader.getDoubleFromAddress(a, error);
result:=not error;
if result and (address2<>0) then
begin
a:=pointerfilereader.getAddressFromPointerRecord(p, address2, shadow2, shadowsize2);
if a<>0 then
begin
v2:=pointerfilereader.getDoubleFromAddress(a, error);
result:=not error;
if result and (diffkind<>dkDontCare) then
begin
if diffkind=dkMustBeDifferent then
result:=v<>v2
else
result:=v=v2; //must be equal
end;
end
else
result:=false;
end;
end;
end;
function TRescan.checkPointer(p: PPointerRecord): boolean;
var error: boolean;
a: ptruint;
v,v2: PtrUint;
begin
result:=false;
a:=pointerfilereader.getAddressFromPointerRecord(p, address, shadow, shadowsize);
if (a<>0) and ((not mustbeinregion) or (InRangeX(a, pointerstart, pointerend))) then
begin
v:=pointerfilereader.getPointerFromAddress(a, error);
result:=not error;
if result and (address2<>0) then
begin
a:=pointerfilereader.getAddressFromPointerRecord(p, address2, shadow2, shadowsize2);
if a<>0 then
begin
v2:=pointerfilereader.getPointerFromAddress(a, error);
result:=not error;
if result and (diffkind<>dkDontCare) then
begin
if diffkind=dkMustBeDifferent then
result:=v<>v2
else
result:=v=v2; //must be equal
end;
end
else
result:=false;
end;
end;
end;
procedure TRescan.execute;
var i,j: integer;
p: PPointerRecord;
s,s2: string;
passed: boolean;
index,len: integer;
begin
try
for i:=0 to pointerfilereader.count-1 do
begin
p:=pointerfilereader.getPointerRec(i);
case vartype of
vtByte: passed:=checkByte(p);
vtWord: passed:=checkWord(p);
vtDword: passed:=checkDword(p);
vtQword: passed:=checkQword(p);
vtSingle: passed:=checkSingle(p);
vtDouble: passed:=checkDouble(p);
vtPointer: passed:=checkPointer(p);
vtString: //todo, move to checkString
begin
s:=pointerfilereader.getStringFromPointerRecord(p,address, shadow, shadowsize);
if (address<>0) and ((not mustbeinregion) or (InRangeX(address, pointerstart, pointerend))) then
begin
if s<>'' then
begin
if regex<>nil then
begin
//use the regex engine to test this string
index:=0;
len:=0;
passed:=RegExprPos(regex, pchar(s), index, len);
if passed and mustbestart then
passed:=index=0;
end
else
begin
passed:=true;
if isstringscan then
begin //check if the first 4 characters are valid
for j:=1 to min(length(s), 4) do
if not (s[j] in [#$20..#$7f]) then
begin
passed:=false;
break;
end;
end;
end;
end
else
passed:=false;
if passed and (address2<>0) then
begin
s2:=pointerfilereader.getStringFromPointerRecord(p, address2, shadow2, shadowsize2);
if (s2<>'') then
begin
if (diffkind<>dkDontCare) then
begin
if diffkind=dkMustBeDifferent then
passed:=s<>s2
else
passed:=s=s2;
end;
end
else
passed:=false;
if (passed) and (regex<>nil) then
begin
index:=0;
len:=0;
passed:=RegExprPos(regex, pchar(s2), index, len);
if passed and mustbestart then
passed:=index=0;
end;
end;
end //else the address is not in the region
end;
end;
if passed then //add it
addPointer(p);
//check for forced exit
if terminated then exit;
fCurrentPosition:=i;
end;
flushresults;
finally
if outputfile<>nil then
freeandnil(outputfile);
if results<>nil then
freeandnil(results);
if pointerfilereader<>nil then
freeandnil(pointerfilereader);
if deletefile(outputfilename)=false then
OutputDebugString('Failure deleting '+outputfilename);
RenameFile(outputfilename+'.temp', outputfilename);
Queue(TfrmStringPointerScan(ownerFrmStringPointerScan).scandone);
end;
end;
constructor TRescan.create(suspended: boolean; address, address2: ptruint; mustbeinregion: boolean; pointerstart, pointerend: ptruint; isstringscan, caseSensitive, mustbestart: boolean; regExstr: string; diffkind: TDiffkind; vartype: TVariableType; oldpointerfilename: string; outputfilename: string ; ownerFrmStringPointerScan: TCustomForm);
var regflags: tregexprflags;
lw: integer;
begin
self.vartype:=vartype;
self.isstringscan:=isstringscan;
self.mustbestart:=mustbestart;
self.diffkind:=diffkind;
self.oldpointerfilename:=oldpointerfilename;
pointerfilereader:=TPointerfileReader.create(oldpointerfilename);
pointerfilereader.vartype:=vartype;
self.address:=address;
self.address2:=address2;
self.mustbeinregion:=mustbeinregion;
self.pointerstart:=pointerstart;
self.pointerend:=pointerend;
self.outputfilename:=outputfilename;
self.ownerFrmStringPointerScan:=ownerFrmStringPointerScan;
pointerfilereader.clearPointercache;
if isstringscan then
begin
if regexstr<>'' then
begin
if CaseSensitive then
regflags:=[]
else
regflags:=[ref_caseinsensitive];
self.regex:=GenerateRegExprEngine(pchar(regexstr), regflags);
end;
end;
outputfile:=TFileStream.Create(outputfilename+'.temp', fmCreate or fmShareDenyNone);
outputfile.Free; //so it can be reopened by other processes
outputfile:=TFileStream.create(Outputfilename+'.temp', fmOpenWrite or fmShareDenyNone);
lastwrite:=GetTickCount;
lw:=pointerfilereader.levelWidth;
outputfile.WriteBuffer(lw, sizeof(lw));
results:=TMemoryStream.Create;
results.size:=16*1024*1024;
inherited create(suspended);
end;
destructor TRescan.destroy;
begin
if pointerfilereader<>nil then
freeandnil(pointerfilereader);
if outputfile<>nil then
freeandnil(outputfile);
if results<>nil then
freeandnil(results);
inherited destroy;
end;
//--------------TScanner---------------
function TScanner.getPointerValue(address: ptruint; column: integer): ptruint;
{
returns 0 if not found
}
var
search: TPointerListEntry;
pe: TAvgLvlTreeNode;
r: PPointerListEntry;
begin
if column=1 then
begin
if (shadow<>0) and (InRangeQ(address, baseaddress, baseaddress+shadowsize)) then
address:=address+(shadow-baseaddress);
end
else
begin
if (shadow2<>0) and (InRangeQ(address, baseaddress2, baseaddress2+shadowsize2)) then
address:=address+(shadow2-baseaddress2);
end;
result:=0;
mapRegionIfNeeded(address,structsize);
search.address:=address;
pe:=pointerlist.Find(@search);
if pe<>nil then
begin
r:=pe.data;
result:=r.pointsto;
end;
end;
function TScanner.getFirstPointerEntry(base: ptruint): PPointerListEntry;
var pe: TAvgLvlTreeNode;
search: TPointerListEntry;
r: PPointerListEntry;
begin
r:=nil;
zeromemory(@search, sizeof(search));
search.address:=base;
pe:=pointerlist.FindNearest(@search);
if pe<>nil then
begin
r:=pe.data;
while (r<>nil) and (r.address0 then
begin
if InRangeQ(a, baseaddress, baseaddress+shadowsize) then
a:=a+(shadow-baseaddress);
PPtrUintArray(fillpointerblock)[i]:=a;
end;
if (a>$10000) and ((a mod 8)=0) and (isreadable(a)) then //<-- isReadable can be sped up by caching in a tree
begin
p:=bma.alloc(sizeof(TPointerListEntry));
p.address:=base+(i*8);
p.pointsto:=PPtrUintArray(fillpointerblock)[i];
pe:=pointerlist.Add(p);
prev:=pointerlist.FindPrecessor(pe);
next:=pointerlist.FindSuccessor(pe);
if prev=nil then
p.previous:=nil
else
begin
p.previous:=prev.Data;
PPointerListEntry(prev.data).next:=p;
end;
if next=nil then
p.next:=nil
else
begin
p.next:=next.Data;
PPointerListEntry(next.data).previous:=p;
end;
end;
end;
end
else
begin
for i:=0 to (x div 4)-1 do
begin
a:=PDwordArray(fillpointerblock)[i];
if shadow<>0 then
begin
if InRangeQ(a, baseaddress, baseaddress+shadowsize) then
a:=a+(shadow-baseaddress);
PDwordArray(fillpointerblock)[i]:=a;
end;
if (a>$10000) and ((a mod 4)=0) and (isreadable(a)) then
begin
p:=bma.alloc(sizeof(TPointerListEntry));
p.address:=base+(i*4);
p.pointsto:=PDwordArray(fillpointerblock)[i];
// debug code
{ if pointerlist.Find(p)<>nil then
begin
// freemem(p);
messagebox(0,rsSPSUFUUUU,rsSPSUFUUUUU,0);
continue;
end; }
pe:=pointerlist.Add(p);
prev:=pointerlist.FindPrecessor(pe);
next:=pointerlist.FindSuccessor(pe);
if prev=nil then
p.previous:=nil
else
begin
p.previous:=prev.Data;
PPointerListEntry(prev.data).next:=p;
end;
if next=nil then
p.next:=nil
else
begin
p.next:=next.Data;
PPointerListEntry(next.data).previous:=p;
end;
end;
end;
end;
end;
end;
procedure TScanner.mapRegionIfNeeded(blockaddress: ptruint; size: integer);
var search: TMappedRegion;
result: TAvgLvlTreeNode;
map, oldmap: PMappedRegion;
prev, next: TAvgLvlTreeNode;
size2: integer;
begin
search.baseaddress:=blockaddress;
result:=mappedregions.FindNearest(@search);
if result<>nil then
map:=result.data
else
map:=nil;
if (map<>nil) and (not ((map.baseaddress<=blockaddress) and (map.baseaddress+map.size>=blockaddress))) then
begin
//if the found map isn't a good enough match then check the next and previous one
if (map.next<>nil) and ((map.next.baseaddress<=blockaddress) and (map.next.baseaddress+map.next.size>=blockaddress)) then
map:=map.next
else
if (map.previous<>nil) and ((map.previous.baseaddress<=blockaddress) and (map.previous.baseaddress+map.previous.size>=blockaddress)) then
map:=map.previous
else
map:=nil;
end;
if (map<>nil) then
begin
//this map can contain or add the extra bytes needed
size2:=((blockaddress+size) - (map.baseaddress+map.size)); //size now contains the total bytes that need to appended to this map
if size2>0 then
begin
inc(blockaddress, size-size2);
size:=size2;
if (map.next<>nil) and (map.next.baseaddress0 then
begin
fillPointers(map.baseaddress+map.size, size2);
map.size:=map.size+size2;
dec(size, size2);
end;
if (size>0) then //this map has been filled as much as it can, try the next one
mapRegionIfNeeded(blockaddress, size);
end; //else it's already fully mapped
end
else
begin
//create a new one
// map:=bma.alloc(sizeof(TMappedRegion));
//no bma, a map can be freed
map:=getmem(sizeof(TMappedRegion));
//ZeroMemory(map, sizeof(TMappedRegion));
map.baseaddress:=blockaddress;
result:=mappedregions.Add(map);
prev:=mappedregions.FindPrecessor(result);
next:=mappedregions.FindSuccessor(result);
if prev=nil then
map.previous:=nil
else
begin
map.previous:=prev.Data;
PMappedRegion(prev.data).next:=map;
end;
if next=nil then
map.next:=nil
else
begin
map.next:=next.Data;
PMappedRegion(next.data).previous:=map;
end;
map.size:=structsize;
//adjust the size so it won't overlap with the next block
if (map.next<>nil) and (map.next.baseaddressnil) and (map.next.baseaddress=map.baseaddress+map.size) then
begin
//merge (delte old one)
result:=mappedregions.Find(map.next);
if result<>nil then
begin
oldmap:=result.data; //adjust the linked list
map.next:=oldmap.next;
if map.next<>nil then
map.next.previous:=map;
map.size:=map.size+oldmap.size;
mappedregions.Remove(oldmap);
freemem(oldmap);
end;
end;
end;
end;
procedure TScanner.flushResults;
begin
lastwrite:=GetTickCount;
resultfile.WriteBuffer(results.Memory^, results.position);
results.position:=0;
end;
function TScanner.getAddressFromPath(base :ptruint; column: integer; level: integer; const path: TPointerPath): ptruint;
var x: ptruint;
i: integer;
begin
result:=base+path[0];
if column=1 then
begin
if (shadow<>0) and (InRangeQ(result, baseaddress, baseaddress+shadowsize)) then
result:=result+(shadow-baseaddress);
end
else
begin
if (shadow2<>0) and (InRangeQ(result, baseaddress2, baseaddress2+shadowsize2)) then
result:=result+(shadow2-baseaddress2);
end;
for i:=1 to level do
begin
result:=getPointerValue(result, column);
if result=0 then exit;
result:=result+path[i];
end;
end;
function TScanner.comparePath(level: integer; path: tpointerpath; stringsize: integer): boolean;
var i: integer;
x: ptruint;
address, address2: ptruint;
e: boolean;
value,value2: pbytearray;
br: ptruint;
begin
result:=false;
address2:=getAddressFromPath(baseaddress2, 2, level, path);
if address2=0 then exit;
address:=getAddressFromPath(baseaddress, 1, level, path);
if address=0 then exit; //weird
//still here so both addresses are readable and not the same address
//
if valuemap<>nil then
begin
value:=valuemap.GetDataPtr(address);
if address2=address then //if they both have the same address assign th same map pointer
value2:=value
else
value2:=valuemap.GetDataPtr(address2);
end
else
begin
value:=nil;
value2:=nil;
end;
//check address1
if value=nil then //not found, so read it manually
begin
if readprocessmemory(processhandle, pointer(address), @tempvariablebuffer[1], variablesize, br) then
begin
tempvariablebuffer[0]:=1; //mark as readable
value:=tempvariablebuffer;
if address2=address then
value2:=value;
end
else
tempvariablebuffer[0]:=0; //mark as unreadable
if valuemap<>nil then //add it
valuemap.Add(address, tempvariablebuffer^);
end;
if value=nil then exit; //unreadable
//check address2
if value2=nil then //not found, so read it manually
begin
if readprocessmemory(processhandle, pointer(address2), @tempvariablebuffer2[1], variablesize, br) then
begin
tempvariablebuffer2[0]:=1; //mark as readable
value2:=tempvariablebuffer2;
end
else
tempvariablebuffer2[0]:=0; //mark as unreadable
if valuemap<>nil then //add it
valuemap.Add(address2, tempvariablebuffer2^);
end;
if value2=nil then exit; //unreadable
if value[0]=0 then exit; //marked as unreadable
if value2[0]=0 then exit; // " " "
if diffkind=dkDontCare then exit(true);
value:=@value[1];
value2:=@value2[1];
if address=address2 then
begin
result:=diffkind=dkMustBeSame;
exit;
end;
//still here so not the same address
if isDataScan then
begin
result:=CompareMem(value, value2, variablesize);
if diffkind=dkMustBeDifferent then result:=not result; //invert the result if it's a mustbedifferent type
end
else
begin
result:=CompareMem(value, value2, min(stringsize, variablesize));
if diffkind=dkMustBeDifferent then result:=not result; //invert the result if it's a mustbedifferent type
end;
end;
function TScanner.addStringPath(level: integer; path: tpointerpath; stringsize: integer; unicode: BOOL): boolean;
begin
result:=false;
if (baseaddress2<>0) and (not comparePath(level, path, stringsize)) then exit;
results.WriteBuffer(level, sizeof(level));
results.WriteBuffer(stringsize, sizeof(stringsize));
results.WriteBuffer(unicode, sizeof(unicode));
results.WriteBuffer(path[0], sizeof(path[0])*(maxlevel+1));
if (getTickCount-lastwrite>5*60*1000) or (results.Position>=(15*1024*1024)) then
flushResults;
inc(count);
result:=true;
end;
procedure TScanner.handleBlock(blockaddress: ptruint; level: integer; path: TPointerpath);
var
x: dword;
i: integer;
newaddress: ptruint;
p: Pstringdata;
// block: pdwordarray;
pe: PPointerListEntry;
begin
if terminated then exit;
if shadow<>0 then
begin
if inrangeq(blockaddress, baseaddress, baseaddress+shadowsize) then
blockaddress:=blockaddress+(shadow-baseaddress);
end;
if level>0 then
begin
progress[level-1]:=path[level-1];
if (diffkind<>dkDontCare) and (getAddressFromPath(baseaddress2, 2, level-1, path)=0) then exit; //nothing to be found in this block
end;
if isDataScan then
begin
i:=0;
while inil then
begin
while (p<>nil) and (p.address=blockaddress then
begin
path[level]:=p.address-blockaddress;
if (not mustbeinregion) or (InRangeX(p.address, pointerstart, pointerstop)) then
addStringPath(level, path, p.stringsize, p.unicode);
end
else
begin
path[level]:=0;
if (not mustbeinregion) or (InRangeX(blockaddress, pointerstart, pointerstop)) then
addStringPath(level, path, p.stringsize-(blockaddress-p.address), p.unicode);
end;
p:=p.next;
end;
end;
end;
if level>=maxlevel then exit; //max level reached, no need to scan for pointers in this block
//still here, so go through this block looking for pointers
//see if this block already has it's pointers mapped, if not, map them
mapRegionIfNeeded(blockaddress, structsize);
pe:=getFirstPointerEntry(blockaddress);
while (pe<>nil) and (pe.addressdkDontCare then
begin
case vartype of
vtString: variablesize:=8; //string
vtByte: variablesize:=1; //byte
vtWord: variablesize:=2; //word
vtDword: variablesize:=4; //dword
vtQword: variablesize:=8; //qword
vtSingle: variablesize:=4; //single
vtDouble: variablesize:=8; //double
vtPointer: variablesize:=processhandler.pointersize; //pointer
end;
if mapvalues then
valuemap:=TMap.create(ituPtrSize,variablesize+1);
getmem(tempvariablebuffer, variablesize+1);
getmem(tempvariablebuffer2, variablesize+1);
end;
resultfile:=TFileStream.Create(filename, fmCreate or fmShareDenyNone);
resultfile.Free;
resultfile:=TFileStream.Create(filename, fmOpenWrite or fmShareDenyNone); //make it accessible by other files
resultfile.Write(maxlevel, sizeof(maxlevel));
count:=0;
setlength(progress,maxlevel+1);
inherited create(false); //let's get started...
end;
destructor TScanner.destroy;
var i: integer;
begin
{ for i:=0 to length(levelblock)-1 do
freemem(levelblock[i]); }
if fillpointerblock<>nil then
freemem(fillpointerblock);
if resultfile<>nil then
freeandnil(resultfile);
if tempvariablebuffer<>nil then
freemem(tempvariablebuffer);
if tempvariablebuffer2<>nil then
freemem(tempvariablebuffer2);
if valuemap<>nil then
freeandnil(valuemap);
end;
//----------------------------
function TfrmStringPointerScan.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
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;
freemem(b);
end;
procedure TfrmStringPointerScan.ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
defaultdraw:=true;
if item.data<>nil then
sender.Canvas.Font.Color:=clblue;
end;
procedure TfrmStringPointerScan.ListView1Data(Sender: TObject; Item: TListItem);
var
i: integer;
s,s2: string;
a: ptruint;
a2: ptruint;
p: PPointerRecord;
begin
if pointerfilereader<>nil then
begin
item.data:=nil;
a:=address;
s:=pointerfilereader.getStringAndAddress(item.index, a, p, shadow, shadowsize);
if (shadow<>0) and (inrangeq(a, address, address+shadowsize)) then
a:=a+(shadow-address);
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 do
item.SubItems.Add('');
end;
if a>0 then
item.SubItems.Add(inttohex(a,8)+' : '+s)
else
item.subitems.add('??? : ???');
if hasAddress2 then
begin
a:=address2;
s2:=pointerfilereader.getStringAndAddress(item.index, a, p, shadow2, shadowsize2);
if a>0 then
item.SubItems.Add(inttohex(a,8)+' : '+s2)
else
item.subitems.add('??? : ???');
if s<>s2 then
item.data:=pointer(1);
end;
end;
end;
procedure TfrmStringPointerScan.ListView1DblClick(Sender: TObject);
begin
if listview1.Selected<>nil then
MemoryBrowser.hexview.address:=pointerfilereader.getAddressFromPointerRecord(pointerfilereader.getPointerRec(listview1.Selected.Index), address, shadow, shadowsize2);
end;
procedure TfrmStringPointerScan.miFindClick(Sender: TObject);
begin
finddialog1.execute;
end;
procedure TfrmStringPointerScan.OpenPointerfile(filename: string);
var lc: TListColumn;
i: integer;
begin
cleanup;
pointerfilereader:=TPointerfileReader.Create(filename);
comboType.OnChange(comboType);
for i:=0 to pointerfilereader.levelWidth do
begin
lc:=listview1.Columns.Add;
lc.MinWidth:=2;
lc.Width:=70;
lc.Caption:=rsSPSUOffset+inttostr(i);
end;
lc:=listview1.Columns.Add;
lc.MinWidth:=2;
lc.Width:=120;
lc.Caption:=rsSPSUAddress;
lc:=listview1.Columns.Add;
lc.MinWidth:=2;
lc.Width:=120;
lc.Caption:=rsSPSUAddress2;
lblExtra.enabled:=true;
edtExtra.Enabled:=true;
edtExtraChange(edtExtra);
listview1.items.count:=min(1000000, pointerfilereader.count);
//setup rescan mode
btnScan.caption:=rsSPSURescan;
btnScan.tag:=1;
end;
procedure TfrmStringPointerScan.scanDone;
begin
if scanner<>nil then
lblInfo.caption:=rsSPSUFound+inttostr(scanner.count);
if rescanner<>nil then
lblInfo.caption:=rsSPSUFound+inttostr(rescanner.count);
cleanup;
OpenPointerfile(SaveDialog1.FileName);
btnScan.caption:=rsSPSURescan;
btnScan.tag:=1;
btnScan.Left:=panel1.clientwidth-btnScan.width-btnNewScan.left;
btnNewScan.visible:=true;
btnNewScan.enabled:=true;
if not rbDiffDontCare.checked then
comboType.itemindex:=comboCompareType.itemindex;
if pointerfilereader<>nil then
showmessage(rsSPSUScanDoneFound+inttostr(pointerfilereader.count))
else
raise exception.create(rsSPSUErrorduringScanNoScanresults);
//restore the gui
EnableGui;
end;
function TfrmStringPointerScan.mapCompare(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
begin
result:=CompareValue(PMappedRegion(Data1).baseaddress, PMappedRegion(Data2).baseaddress);
end;
function TfrmStringPointerScan.pointerCompare(Tree: TAvgLvlTree; Data1, Data2: Pointer): integer;
begin
result:=CompareValue(PPointerListEntry(Data1).address, PPointerListEntry(Data2).address);
end;
procedure tfrmStringPointerScan.cleanup;
var r: TAvgLvlTreeNode;
p,m: PMappedRegion;
begin
statusupdater.enabled:=false;
progressbar1.Visible:=false;
progressbar1.Position:=0;
if rescanner<>nil then
begin
rescanner.terminate;
rescanner.waitfor;
freeandnil(rescanner);
end;
if scanner<>nil then
begin
scanner.terminate;
scanner.WaitFor;
freeandnil(scanner);
end;
listview1.items.count:=0;
while listview1.ColumnCount>0 do
listview1.Columns.Delete(0);
if bma<>nil then
freeAndNil(bma);
if pointerlist<>nil then
freeandnil(pointerlist);
{
if pointerlistMemManager<>nil then
freeandnil(pointerlistMemManager); }
if pointerlist<>nil then
freeandnil(pointerlist);
if mappedRegions<>nil then
begin
r:=mappedRegions.FindLowest;
if r<>nil then
begin
m:=PMappedRegion(r.data);
while m<>nil do
begin
p:=m;
m:=m.next;
freemem(p);
end;
end;
freeandnil(mappedRegions);
end;
if pointerfilereader<>nil then
freeandnil(pointerfilereader);
end;
procedure TfrmStringPointerScan.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;
begin
vartype:=vtPointer;
if (scanner=nil) and (rescanner=nil) then
begin
baseaddress:=symhandler.getAddressFromName(edtBase.text);
baseaddress2:=0;
shadow:=0;
shadowsize:=0;
shadow2:=0;
shadowsize2:=0;
pointerstart:=0;
pointerstop:=0;
alignsize:=4;
if cbHasShadow.checked then
begin
shadow:=symhandler.getAddressFromName(edtShadowAddress.text);
shadowsize:=strtoint(edtShadowSize.text);
end;
if ((shadow<>0) and (not isreadable(shadow))) or
((shadow=0) and (not isreadable(baseaddress))) then
raise exception.create(rsThisAddressIsNotAccessible);
structsize:=strtoint(edtStructsize.text);
maxlevel:=strtoint(edtMaxLevel.text);
if edtExtra.text='' then
rbDiffDontCare.Checked:=true
else
begin
baseaddress2:=symhandler.getAddressFromName(edtExtra.text);
if cbHasShadow2.checked then
begin
shadow2:=symhandler.getAddressFromName(edtShadowAddress2.text);
shadowsize2:=strtoint(edtShadowSize2.text);
end;
end;
if rbDatascan.checked then
begin
alignsize:=strtoint(edtAlignsize.text);
if cbPointerInRange.checked then
begin
pointerstart:=symhandler.getAddressFromName(edtPointerStart.text);
pointerstop:=symhandler.getAddressFromName(edtPointerStop.text);
end;
end;
if savedialog1.execute then
begin
if pointerfilereader<>nil then
oldpointerfile:=pointerfilereader.filename
else
oldpointerfile:='';
cleanup;
//we got till this point so everything is fine, disable the gui
disableGui;
if rbMustBeSame.checked then
diffkind:=dkMustBeSame
else
if rbMustBeDifferent.checked then
diffkind:=dkMustBeDifferent
else
diffkind:=dkDontCare;
if diffkind=dkDontCare then
vartype:=vtByte
else
case comboCompareType.itemindex of
0: vartype:=vtString;
1: vartype:=vtByte;
2: vartype:=vtWord;
3: vartype:=vtDword;
4: vartype:=vtQword;
5: vartype:=vtSingle;
6: vartype:=vtDouble;
7: vartype:=vtPointer;
end;
if rbStringscan.checked then
vartype:=vtString;
if btnScan.tag=0 then //first scan
begin
mappedRegions:=TAvgLvlTree.CreateObjectCompare(mapCompare);
pointerlist:=TAvgLvlTree.CreateObjectCompare(pointerCompare);
bma:=TBigMemoryAllocHandler.create;
if rbStringscan.checked then
begin
vartype:=vtString;
if (frmStringMap<>nil) and (cbReuseStringmap.checked=false) then
freeandnil(frmStringMap);
if frmStringMap=nil then
begin
frmStringMap:=tfrmStringMap.Create(application);
//fill the stringmap
frmstringmap.cbRegExp.checked:=cbRegExp.checked;
frmstringmap.cbCaseSensitive.checked:=cbCaseSensitive.checked;
frmstringmap.cbMustBeStart.checked:=cbMustBeStart.checked;
frmstringmap.edtRegExp.text:=edtRegExp.text;
frmstringmap.btnScan.click;
lblInfo.caption:=rsGeneratingStringmap;
lblInfo.Repaint;
frmstringmap.scanner.WaitFor;
end;
end;
lblInfo.caption:=rsGeneratedScanning;
//everything has been configured
scanner:=Tscanner.create(rbDatascan.checked, cbPointerInRange.checked, alignsize, pointerstart, pointerstop, baseAddress, shadow, shadowsize, baseaddress2, shadow2, shadowsize2, diffkind, vartype, cbMapPointerValues.checked, structsize, maxlevel, mappedRegions, pointerlist, bma, savedialog1.filename, self);
end
else
begin
//next scan aka Rescan
listview1.items.count:=0;
rescanner:=trescan.create(false, address, address2, cbpointerinrange.checked, pointerstart, pointerstop, rbStringscan.checked, cbCaseSensitive.checked, cbMustBeStart.checked, edtRegExp.text, diffkind, vartype, oldpointerfile, savedialog1.filename , self);
end;
btnScan.caption:=rsStop;
btnScan.enabled:=true;
progressbar1.visible:=true;
statusupdater.enabled:=true;
end;
end
else
begin
btnScan.enabled:=false;
btnScan.caption:=rsTerminating;
if scanner<>nil then
scanner.terminate;
if rescanner<>nil then
rescanner.terminate;
end;
end;
procedure TfrmStringPointerScan.cbHasShadowChange(Sender: TObject);
begin
edtShadowAddress.visible:=cbHasShadow.checked or cbHasShadow2.checked;
edtShadowAddress2.Visible:=edtShadowAddress.visible;
lblSize.visible:=edtShadowAddress.visible;
lblSize2.visible:=edtShadowAddress.visible;
edtShadowSize.visible:=edtShadowAddress.visible;
edtShadowSize2.visible:=edtShadowAddress.visible;
edtBaseChange(edtBase);
shadow:=0;
end;
procedure TfrmStringPointerScan.cbRegExpChange(Sender: TObject);
begin
cbCaseSensitive.enabled:=cbRegExp.enabled and cbRegExp.checked;
cbMustBeStart.enabled:=cbCaseSensitive.enabled;
edtRegExp.enabled:=cbCaseSensitive.enabled;
lblString.enabled:=cbCaseSensitive.enabled;
end;
procedure TfrmStringPointerScan.cbPointerInRangeChange(Sender: TObject);
begin
edtPointerStart.enabled:=cbPointerInRange.checked;
edtPointerStop.enabled:=cbPointerInRange.checked;
lbland.enabled:=cbPointerInRange.checked;
end;
procedure TfrmStringPointerScan.comboTypeChange(Sender: TObject);
begin
if pointerfilereader<>nil then
begin
case comboType.itemindex of
0: pointerfilereader.vartype:=vtString;
1: pointerfilereader.vartype:=vtByte;
2: pointerfilereader.vartype:=vtWord;
3: pointerfilereader.vartype:=vtDword;
4: pointerfilereader.vartype:=vtQword;
5: pointerfilereader.vartype:=vtSingle;
6: pointerfilereader.vartype:=vtDouble;
7: pointerfilereader.vartype:=vtPointer;
end;
listview1.Refresh;
end;
end;
procedure TfrmStringPointerScan.edtBaseChange(Sender: TObject);
var
err: boolean;
begin
try
address:=symhandler.getAddressFromName(edtBase.text);
shadow:=symhandler.getAddressFromName(edtShadowAddress.text, false, err);
if err then
shadow:=0;
try
shadowsize:=strtoint(edtShadowSize.text);
except
end;
address2:=symhandler.getAddressFromName(edtBase.text, false, err);
if err then
address2:=0;
shadow2:=symhandler.getAddressFromName(edtShadowAddress2.text, false, err);
if err then
shadow2:=0;
try
shadowsize2:=strtoint(edtShadowSize.text);
except
end;
listview1.Refresh;
except
end;
end;
procedure TfrmStringPointerScan.edtExtraChange(Sender: TObject);
begin
hasAddress2:=edtExtra.Text<>'';
rbMustBeDifferent.enabled:=hasAddress2;
rbMustBeSame.enabled:=hasAddress2;
rbDiffDontCare.enabled:=hasAddress2;
try
address2:=symhandler.getAddressFromName(edtExtra.text);
listview1.Refresh;
except
end;
rbDiffDontCareChange(nil);
end;
procedure TfrmStringPointerScan.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:=listview1.ItemIndex+1 to listview1.Items.Count-1 do
begin
s:=listview1.items[i].SubItems.Text;
if not cs then s:=uppercase(s);
if pos(FindText, s)>0 then
begin
listview1.ItemIndex:=i;
listview1.Items[i].MakeVisible(false);
exit;
end;
end;
beep;
end;
procedure TfrmStringPointerScan.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
cleanup;
end;
procedure TfrmStringPointerScan.FormShow(Sender: TObject);
begin
//panel1.Constraints.MinHeight:=btnNewScan.Top+btnNewScan.Height+lblInfo.Height+4;
cbHasShadowChange(nil);
end;
procedure TfrmStringPointerScan.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;
lblBaseRegion.enabled:=true;
lblExtra.enabled:=true;
edtBase.enabled:=true;
edtExtra.enabled:=true;
cbHasShadow.enabled:=true;
cbHasShadow2.enabled:=true;
edtShadowAddress.Enabled:=true;
edtShadowAddress2.enabled:=true;
edtShadowSize.enabled:=true;
edtShadowSize2.enabled:=true;
lblSize.enabled:=true;
lblSize2.enabled:=true;
btnNewScan.Visible:=false;
end;
end;
procedure TfrmStringPointerScan.miOpenClick(Sender: TObject);
begin
if (scanner=nil) and (rescanner=nil) and OpenDialog1.Execute then
begin
OpenPointerfile(opendialog1.filename);
enablegui;
end;
end;
procedure TfrmStringPointerScan.MenuItem7Click(Sender: TObject);
begin
with Tfrmstringpointerscan.create(Application) do show;
end;
procedure TfrmStringPointerScan.miClearCacheClick(Sender: TObject);
begin
if pointerfilereader<>nil then
pointerfilereader.clearPointerCache;
listview1.Refresh;
end;
procedure TfrmStringPointerScan.MenuItem6Click(Sender: TObject);
begin
end;
procedure TfrmStringPointerScan.Panel3Click(Sender: TObject);
begin
end;
procedure TfrmStringPointerScan.rbDatascanChange(Sender: TObject);
begin
cbRegExp.enabled:=rbStringscan.enabled and rbStringscan.checked;
cbCaseSensitive.enabled:=cbRegExp.enabled and rbStringscan.checked and cbRegExp.checked;
cbMustBeStart.enabled:=cbCaseSensitive.enabled;
lblString.enabled:=cbCaseSensitive.enabled;
edtRegExp.enabled:=cbCaseSensitive.enabled;
lblAlign.enabled:=rbDatascan.enabled and rbDatascan.checked;
edtAlignsize.enabled:=lblAlign.enabled;
edtPointerStart.enabled:=cbPointerInRange.enabled and cbPointerInRange.checked;
lblAnd.enabled:=edtPointerStart.enabled;
edtPointerStop.enabled:=edtPointerStart.enabled;
end;
procedure TfrmStringPointerScan.rbDiffDontCareChange(Sender: TObject);
begin
lblCompare.enabled:=rbDiffDontCare.enabled and (rbDiffDontCare.checked = false);
comboCompareType.enabled:=lblCompare.enabled;
cbMapPointerValues.Enabled:=comboCompareType.enabled;
end;
procedure TfrmStringPointerScan.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.count)]);
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
lblinfo.caption:=Format(rsScanningFoun, [inttostr(rescanner.count)])
end;
procedure TfrmStringPointerScan.setGUIStateEnabled(state: boolean);
begin
lblBaseRegion.enabled:=state;
lblExtra.enabled:=state;
cbPointerInRange.enabled:=state;
cbPointerInRange.OnChange(cbPointerInRange);
lblBaseRegion.enabled:=state;
lblExtra.enabled:=state;
lblvds.enabled:=state;
edtBase.enabled:=state;
edtExtra.enabled:=state;
edtExtra.OnChange(edtExtra); //makes the difftypes enabled or not
rbDatascan.enabled:=state;
rbStringscan.enabled:=state;
cbRegExpChange(rbStringscan);
comboType.enabled:=state;
rbDatascanChange(Nil);
cbHasShadow.enabled:=state;
cbHasShadow2.enabled:=state;
edtShadowAddress.Enabled:=state;
edtShadowAddress2.enabled:=state;
edtShadowSize.enabled:=state;
edtShadowSize2.enabled:=state;
lblSize.enabled:=state;
lblSize2.enabled:=state;
btnScan.enabled:=state;
end;
procedure TfrmStringPointerScan.disableGui;
begin
setGUIStateEnabled(false);
end;
procedure TfrmStringPointerScan.enableGui;
begin
setGUIStateEnabled(true);
end;
initialization
{$I frmStringPointerScanUnit.lrs}
end.