cheat-engine/Cheat Engine/formChangedAddresses.pas
2023-10-20 17:32:15 +02:00

1541 lines
41 KiB
ObjectPascal
Executable file

unit formChangedAddresses;
{$MODE Delphi}
interface
uses
{$ifdef darwin}
macport,
{$endif}
{$ifdef windows}
windows,
{$endif}
LCLIntf, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,CEFuncProc, ExtCtrls, ComCtrls, Menus, NewKernelHandler, LResources,
disassembler, symbolhandler, byteinterpreter, CustomTypeHandler, maps, math, Clipbrd,
addressparser, commonTypeDefs, DBK32functions, vmxfunctions, betterControls,
syncobjs, contexthandler;
const
cbDisplayTypeIndexByte=0;
cbDisplayTypeIndexWord=1;
cbDisplayTypeIndexDword=2;
cbDisplayTypeIndexQword=3;
cbDisplayTypeIndexSingle=4;
cbDisplayTypeIndexDouble=5;
cbDisplayTypeIndexPointer=6;
type
TfrmChangedAddresses=class;
TAddressEntry=class
private
fcontext: Pointer;
contexthandler: TContextInfo;
procedure setContext(c: pointer);
public
address: ptruint; //for whatever reason it could be used in the future
base: ptruint;
stack: record
stack: pbyte;
savedsize: PtrUInt;
end;
{$IFDEF WINDOWS}
ipt: record
log: pointer;
size: integer;
end;
{$ENDIF}
count: integer;
group: integer;
owner: TfrmChangedAddresses;
changed: boolean;
procedure fillBase;
procedure savestack;
constructor create(AOwner: TfrmChangedAddresses);
destructor destroy; override;
property context: pointer read fContext write setContext;
end;
{ TfrmChangedAddresses }
{$ifdef windows}
TDBVMWatchExecutePollThread=class(TThread)
private
results: PPageEventListDescriptor;
resultsize: integer;
cr3disassembler: Tcr3Disassembler;
procedure addEntriesToList;
public
id: integer;
fca: TfrmChangedAddresses;
procedure execute; override;
end;
{$endif}
TfrmChangedAddresses = class(TForm)
caImageList: TImageList;
editCodeAddress: TEdit;
labelCodeAddress: TLabel;
lblInfo: TLabel;
miShowIPTLog: TMenuItem;
micbShowAsSigned: TMenuItem;
miCodeAddressCopy: TMenuItem;
miCopyValueToClipboard: TMenuItem;
miCopyAddressToClipboard: TMenuItem;
MenuItem2: TMenuItem;
miChangeValue: TMenuItem;
miSetFilter: TMenuItem;
miCodeAddressDisassembleMemoryRegion: TMenuItem;
miCodeAddressBrowseMemoryRegion: TMenuItem;
N1: TMenuItem;
miCommonalitiesSubgroup: TMenuItem;
miMarkAsGroup1: TMenuItem;
miMarkAsGroup2: TMenuItem;
miScanForCommonalities: TMenuItem;
MenuItem7: TMenuItem;
MenuItem8: TMenuItem;
miGroupClear: TMenuItem;
miResetCount: TMenuItem;
miDeleteSelectedEntries: TMenuItem;
miCopyToAddresslist: TMenuItem;
miDissect: TMenuItem;
micbShowAsHexadecimal: TMenuItem;
Panel1: TPanel;
OKButton: TButton;
Changedlist: TListView;
cbDisplayType: TComboBox;
PanelContent: TPanel;
PanelAddressInfo: TPanel;
pmCodeAddress: TPopupMenu;
Timer1: TTimer;
PopupMenu1: TPopupMenu;
Showregisterstates1: TMenuItem;
Browsethismemoryregion1: TMenuItem;
procedure ChangedlistColumnClick(Sender: TObject; Column: TListColumn);
procedure ChangedlistCompare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
procedure ChangedlistCustomDrawItem(Sender: TCustomListView;
Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
procedure miCodeAddressBrowseMemoryRegionClick(Sender: TObject);
procedure miCodeAddressCopyClick(Sender: TObject);
procedure miCodeAddressDisassembleMemoryRegionClick(Sender: TObject);
procedure miCopyAddressToClipboardClick(Sender: TObject);
procedure miChangeValueClick(Sender: TObject);
procedure miCopyValueToClipboardClick(Sender: TObject);
procedure miSetFilterClick(Sender: TObject);
procedure miGroupClearClick(Sender: TObject);
procedure miMarkAsGroupClick(Sender: TObject);
procedure miDeleteSelectedEntriesClick(Sender: TObject);
procedure micbShowAsHexadecimalClick(Sender: TObject);
procedure miDissectClick(Sender: TObject);
procedure miResetCountClick(Sender: TObject);
procedure miScanForCommonalitiesClick(Sender: TObject);
procedure miShowIPTLogClick(Sender: TObject);
procedure OKButtonClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormShow(Sender: TObject);
procedure ChangedlistDblClick(Sender: TObject);
procedure PopupMenu1Popup(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Showregisterstates1Click(Sender: TObject);
procedure Browsethismemoryregion1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
hassetsizes: boolean;
faddress: ptruint;
defaultcolor: TColor;
fdebuggerinterfacewatchid: integer;
{$ifdef windows}
dbvmwatchpollthread: TDBVMWatchExecutePollThread;
{$endif}
currentFilter: string;
currentFilterFunc: integer;
filterExtraRegs: boolean;
function checkFilter(entry: TaddressEntry): boolean;
procedure rebuildListWithFilter;
procedure stopdbvmwatch;
procedure refetchValues(specificaddress: ptruint=0;countonly: boolean=false);
procedure setAddress(a: ptruint);
procedure setdebuggerinterfacewatchid(id: integer);
public
{ Public declarations }
equation: string;
foundcodedialog: pointer;
dbvmwatch_unlock: qword;
breakpoint: pointer;
addresslist: TMap;
addresslistCS: TCriticalSection;
foundcount: integer;
newRecord: TaddressEntry;
procedure clearFilter;
procedure AddRecord;
property address: ptruint read fAddress write setAddress;
property debuggerinterfacewatchid: integer read fdebuggerinterfacewatchid write setdebuggerinterfacewatchid;
end;
implementation
uses CEDebugger, MainUnit, frmRegistersunit, MemoryBrowserFormUnit, debughelper,
debugeventhandler, debuggertypedefinitions, FoundCodeUnit, StructuresFrm2,
ProcessHandlerUnit, Globals, Parsers, frmStackViewUnit, frmSelectionlistunit,
frmChangedAddressesCommonalityScannerUnit, LastDisassembleData, lua, lauxlib,
lualib, luahandler, BreakpointTypeDef, DebuggerInterfaceAPIWrapper, iptlogdisplay;
resourcestring
rsStop='Stop';
rsClose='Close';
rsNoDescription = 'No Description';
rsChangedAddressesBy = 'Accessed addresses by %x';
rsDesignateSomeAddresses = 'Please designate a group to at least some '
+'addresses';
rsNoAddressesLeftForGroup = 'There are no addresses left for group %d';
rsInvalidGroups = 'Invalid groups';
rsDeleteAddresses = 'Delete addresses';
rsAreYouWishToDelete = 'Are you sure you wish to delete these entries(s)?';
rsCodeAccessesSingleAddress = 'This address has been accessed by the code '
+'you selected';
rsCodeAccessesAddresses = 'The following %d addresses have been accessed by '
+'the code you selected';
rsValueChange = 'Value Change';
rsGiveTheNewValue = 'Give the new value';
rsFilter = 'Filter';
rsEnterLuaFormula = 'Enter a Lua formula to use to filter. E.g RCX==0x2301'
+'adc0. Empty for no filter';
rsNeedsIPTFindWhat = 'The debugger did not collect any IPT data. This may be due to the Intel PT feature not being enabled in settings/malfunctioning, or that the option to also log the trace in "find what..." results was disabled. Do you wish to enable this for this debugging session? You will need to recollect this information again though.'#13#10'(This is for this session only. If you wish to always turn it on from the start, go to settings->debugger options, and enable "Use Intel-PT feature" and the suboption for recording elements in "find what ..." routines)';
destructor TAddressEntry.destroy;
begin
if stack.stack<>nil then
freememandnil(stack.stack);
{$IFDEF WINDOWS}
if ipt.log<>nil then
freememandnil(ipt.log);
{$ENDIF}
if fcontext<>nil then
freememandnil(fcontext);
inherited destroy;
end;
constructor TAddressEntry.create(AOwner: TfrmChangedAddresses);
begin
owner:=aowner;
end;
procedure TAddressEntry.setContext(c: pointer);
begin
if fcontext<>nil then
freememandnil(fcontext);
contexthandler:=getBestContextHandler;
fcontext:=contexthandler.getcopy(c);
end;
procedure TAddressEntry.fillBase;
var ap: TAddressParser;
begin
if (base=0) and (owner<>nil) then
begin
ap:=TAddressParser.Create;
ap.setSpecialContext(fcontext);
base:=ap.getBaseAddress(owner.equation);
ap.free;
end;
end;
procedure TAddressEntry.savestack;
var stackbase: ptruint;
begin
getmem(stack.stack, savedStackSize);
stackbase:=contexthandler.StackPointerRegister^.getValue(fcontext);
if ReadProcessMemory(processhandle, pointer(stackbase), stack.stack, savedStackSize, stack.savedsize)=false then
begin
//for some reason this sometimes returns 0 bytes read even if some of the bytes are readable.
stack.savedsize:=4096-(stackbase mod 4096);
ReadProcessMemory(processhandle, pointer(stackbase), stack.stack, stack.savedsize, stack.savedsize);
end;
end;
//-----------------------------
{$IFDEF windows}
procedure TDBVMWatchExecutePollThread.addEntriesToList;
var
c: TContext;
i,j: integer;
opcode,desc: string;
li: TListItem;
ldi: TLastDisassembleData;
basicinfo: TPageEventBasic;
basic: PPageEventBasicArray;
extended: PPageEventExtendedArray absolute basic;
basics: PPageEventBasicWithStackArray absolute basic;
extendeds: PPageEventExtendedWithStackArray absolute basic;
address, address2: ptruint;
skip: boolean;
debug,debug2: pointer;
x: TaddressEntry;
haserror: boolean;
s: string;
begin
outputdebugstring('addEntriesToList ('+inttostr(results^.numberOfEntries)+')');
try
basic:=PPageEventBasicArray(ptruint(results)+sizeof(TPageEventListDescriptor));
for i:=0 to results^.numberOfEntries-1 do
begin
case results^.entryType of
0: basicinfo:=basic^[i];
1: basicinfo:=extended^[i].basic;
2: basicinfo:=basics^[i].basic;
3: basicinfo:=extendeds^[i].basic;
end;
ZeroMemory(@c,sizeof(c));
c.{$ifdef cpu64}Rax{$else}Eax{$endif}:=basicinfo.RAX;
c.{$ifdef cpu64}Rbx{$else}Ebx{$endif}:=basicinfo.RBX;
c.{$ifdef cpu64}Rcx{$else}Ecx{$endif}:=basicinfo.RCX;
c.{$ifdef cpu64}Rdx{$else}Edx{$endif}:=basicinfo.RDX;
c.{$ifdef cpu64}Rsi{$else}Esi{$endif}:=basicinfo.RSI;
c.{$ifdef cpu64}Rdi{$else}Edi{$endif}:=basicinfo.RDI;
c.{$ifdef cpu64}Rbp{$else}Ebp{$endif}:=basicinfo.RBP;
c.{$ifdef cpu64}Rsp{$else}Esp{$endif}:=basicinfo.Rsp;
c.{$ifdef cpu64}Rip{$else}Eip{$endif}:=basicinfo.Rip;
{$ifdef cpu64}
c.R8:=basicinfo.R8;
c.R9:=basicinfo.R9;
c.R10:=basicinfo.R10;
c.R11:=basicinfo.R11;
c.R12:=basicinfo.R12;
c.R13:=basicinfo.R13;
c.R14:=basicinfo.R14;
c.R15:=basicinfo.R15;
{$endif}
address:=symhandler.getAddressFromName(fca.equation, false, haserror, @c);
if haserror=false then
begin
//check if address is in the list
if fca.addresslist.GetData(address,x) then
begin
inc(x.count,basicinfo.Count+1);
fca.refetchValues(x.address, true);
continue;
end;
//still here so not in the list, get more info and add it
c.EFlags:=basicinfo.FLAGS;
c.SegCs:=basicinfo.CS;
c.SegSs:=basicinfo.SS;
c.SegDs:=basicinfo.DS;
c.SegEs:=basicinfo.ES;
c.SegFs:=basicinfo.FS;
c.SegGs:=basicinfo.GS;
x:=TAddressEntry.Create(fca);
x.address:=address;
x.count:=basicinfo.Count+1;
case results^.entryType of
1:
begin
//fpu
copymemory(@c.{$ifdef cpu64}FltSave{$else}ext{$endif}, @extended^[i].fpudata, sizeof(c.{$ifdef cpu64}FltSave{$else}ext{$endif}));
end;
2:
begin
//stack
getmem(x.stack.stack, 4096);
copymemory(x.stack.stack, @basics^[i].stack[0], 4096);
x.stack.savedsize:=4096;
end;
3:
begin
//fpu/stack
copymemory(@c.{$ifdef cpu64}FltSave{$else}ext{$endif}, @extended^[i].fpudata, sizeof(c.{$ifdef cpu64}FltSave{$else}ext{$endif}));
getmem(x.stack.stack, 4096);
copymemory(x.stack.stack, @basics^[i].stack[0], 4096);
x.stack.savedsize:=4096;
end;
end;
x.context:=@c;
OutputDebugString('adding to the lists');
s:=inttohex(address,8);
fca.addresslist.Add(address, x);
if fca.checkFilter(x) then
begin
li:=fca.changedlist.Items.add;
li.caption:=s;
li.SubItems.Add('');
li.subitems.add('1');
li.Data:=x;
end;
fca.refetchValues(x.address);
end
else
begin
OutputDebugString(fca.equation+' did not end up into an address');
end;
end;
except
on e: exception do
outputdebugstring('TDBVMWatchPollThread.addEntriesToList error: '+e.message);
end;
end;
procedure TDBVMWatchExecutePollThread.execute;
var
i: integer;
size: integer;
begin
cr3disassembler:=TCR3Disassembler.create;
getmem(results,4096);
resultsize:=4096;
zeromemory(results,resultsize);
try
while not terminated do
begin
size:=resultsize;
i:=dbvm_watch_retrievelog(id, results, size);
if i=0 then
begin
//OutputDebugString('TDBVMWatchExecutePollThread returned 0. results^.numberOfEntries='+inttostr(results^.numberOfEntries));
//process data
if results^.numberOfEntries>0 then
begin
//OutputDebugString('calling addEntriesToList');
synchronize(addEntriesToList);
sleep(10);
end
else
sleep(50);
end
else
if i=2 then
begin
//not enough memory. Allocate twice the needed amount
outputdebugstring(inttostr(resultsize)+' is too small for the buffer. It needs to be at least '+inttostr(size));
freememandnil(results);
resultsize:=size*2;
getmem(results, resultsize);
zeromemory(results,resultsize);
continue; //try again, no sleep
end else
begin
outputdebugstring('dbvm_watch_retrievelog returned '+inttostr(i)+' which is not supported');
exit;
end;
end;
except
on e: exception do
outputdebugstring('TDBVMWatchExecutePollThread crash:'+e.Message);
end;
freememandnil(results);
freeandnil(cr3disassembler);
end;
{$ENDIF}
//-----------------------------
procedure TfrmChangedAddresses.AddRecord;
var
li: tlistitem;
begin
inc(foundcount);
if checkFilter(newRecord) then
begin
li:=changedlist.Items.add;
li.caption:=inttohex(newRecord.address,8);
li.SubItems.Add('');
li.subitems.add('1');
li.Data:=newRecord;
end;
refetchValues(newRecord.address);
if foundcodedialog<>nil then
begin
//TFoundCodeDialog(foundcodedialog).setChangedAddressCount(contexthan
TFoundCodeDialog(foundcodedialog).setChangedAddressCount(newRecord.contexthandler.InstructionPointerRegister^.getValue(newrecord.context));
if (changedlist.Items.Count>=8) then //remove this breakpoint
debuggerthread.FindWhatCodeAccessesStop(self);
end;
debuggerthread.guiupdate:=true;
end;
procedure TfrmChangedAddresses.OKButtonClick(Sender: TObject);
var temp: dword;
i: integer;
begin
stopdbvmwatch;
if OKButton.caption=rsStop then
begin
debuggerthread.FindWhatCodeAccessesStop(self);
okButton.Caption:=rsClose;
end
else
close;
end;
procedure TfrmChangedAddresses.micbShowAsHexadecimalClick(Sender: TObject);
begin
refetchvalues;
end;
procedure TfrmChangedAddresses.miDissectClick(Sender: TObject);
var
i: integer;
ae: TAddressEntry;
ap: TAddressParser;
address: ptruint;
s: tstringlist;
f: TfrmSelectionList;
structurefrm: TfrmStructures2;
new: boolean;
maxoffset: dword;
begin
ap:=TAddressParser.Create;
if changedlist.SelCount>0 then
begin
//find out which data dissect windows are open
s:=tstringlist.create;
if frmStructures2=nil then
raise exception.create(rsTheStructuresListIsBroken);
for i:=0 to frmStructures2.Count-1 do
s.add(TfrmStructures2(frmStructures2[i]).Caption);
s.add(rsNewWindow);
f:=TfrmSelectionList.Create(self, s);
f.caption:=rsLockAndAddToStructureDissect;
f.label1.Caption:=rsSelectTheStructureDissectWindowYouWishToAddThisReg;
if f.showmodal=mrok then
begin
if f.itemindex=-1 then f.itemindex:=0;
if f.itemindex>=frmStructures2.Count then //new window
begin
structurefrm:=tfrmstructures2.create(application);
structurefrm.show;
end
else
structurefrm:=TfrmStructures2(frmStructures2[f.itemindex]);
//add the addresses
maxoffset:=4096;
for i:=0 to changedlist.Items.Count-1 do
begin
if changedlist.Items[i].Selected then
begin
ae:=changedlist.items[i].data;
ap.setSpecialContext(ae.fcontext);
address:=ap.getBaseAddress(equation);
maxoffset:=max(maxoffset, 8+strtoint64('$'+changedlist.Items[i].Caption)-address);
if address<>0 then
structurefrm.addColumn.Address:=address;
end;
end;
structurefrm.show;
if structurefrm.mainStruct=nil then //if no structure is selected define it then
structurefrm.DefineNewStructure(maxoffset);
end;
end;
ap.free;
end;
procedure TfrmChangedAddresses.ChangedlistColumnClick(Sender: TObject;
Column: TListColumn);
begin
changedlist.SortColumn:=Column.Index;
changedlist.SortType:=stData;
end;
procedure TfrmChangedAddresses.ChangedlistCompare(Sender: TObject; Item1,
Item2: TListItem; Data: Integer; var Compare: Integer);
var i1, i2: TAddressEntry;
hex, hex2: qword;
s: single absolute hex;
s2: single absolute hex2;
d: double absolute hex;
d2: double absolute hex2;
x: PtrUInt;
varsize: integer;
signedcompare: boolean;
begin
signedcompare:=micbShowAsSigned.checked;
compare:=0;
i1:=TAddressEntry(item1.data);
i2:=TAddressEntry(item2.data);
if (i1=nil) or (i2=nil) then exit;
case changedlist.SortColumn of
0: //sort by address
begin
compare:=CompareValue(i1.address, i2.address);
end;
1: //sort by value
begin
hex:=0;
hex2:=0;
case cbDisplayType.itemindex of
cbDisplayTypeIndexByte: varsize:=1;
cbDisplayTypeIndexWord: varsize:=2;
cbDisplayTypeIndexDword: varsize:=4;
cbDisplayTypeIndexQword: varsize:=8;
cbDisplayTypeIndexSingle: varsize:=4;
cbDisplayTypeIndexDouble: varsize:=8;
cbDisplayTypeIndexPointer:
begin
if processhandler.is64Bit then
varsize:=8
else
varsize:=4;
end
else
begin
Compare:=0;
exit;
end;
end;
if not ReadProcessMemory(processhandle, pointer(i1.address), @hex, VarSize, x) then
begin
compare:=-1;//1<2
exit;
end;
if not ReadProcessMemory(processhandle, pointer(i2.address), @hex2, VarSize, x) then
begin
compare:=1;//1>2
exit;
end;
if micbShowAsHexadecimal.checked then
begin
compare:=hex-hex2;
end
else
begin
case cbDisplayType.itemindex of
cbDisplayTypeIndexByte: compare:=ifthen(signedcompare, CompareValue(Int8(hex), Int8(hex2)), CompareValue(Uint8(hex),Uint8(hex2)));
cbDisplayTypeIndexWord: compare:=ifthen(signedcompare, CompareValue(Int16(hex), Int16(hex2)), CompareValue(Uint16(hex),Uint16(hex2)));
cbDisplayTypeIndexDword: compare:=ifthen(signedcompare, CompareValue(Int32(hex), Int32(hex2)), CompareValue(Uint32(hex),Uint32(hex2)));
cbDisplayTypeIndexQword: compare:=ifthen(signedcompare, CompareValue(Int64(hex), Int64(hex2)), CompareValue(Uint64(hex),Uint64(hex2)));
cbDisplayTypeIndexPointer:
begin
if processhandler.is64Bit then
compare:=ifthen(signedcompare, CompareValue(Int64(hex), Int64(hex2)), CompareValue(Uint64(hex),Uint64(hex2)))
else
compare:=ifthen(signedcompare, CompareValue(Int32(hex), Int32(hex2)), CompareValue(Uint32(hex),Uint32(hex2)));
end;
cbDisplayTypeIndexSingle:
begin
try
if s>s2 then
compare:=1
else
if s=s2 then
compare:=0
else
compare:=-1;
except
compare:=-1;
end;
end;
cbDisplayTypeIndexDouble:
begin
try
if d>d2 then
compare:=1
else
if d=d2 then
compare:=0
else
compare:=-1;
except
compare:=-1;
end;
end;
end;
end;
end;
2: //sort by count
begin
compare:=CompareValue(i1.count, i2.count);
end;
end;
end;
procedure TfrmChangedAddresses.ChangedlistCustomDrawItem(
Sender: TCustomListView; Item: TListItem; State: TCustomDrawState;
var DefaultDraw: Boolean);
var
e: TAddressEntry;
i: integer;
begin
i:=item.index;
if tobject(item.Data) is TAddressEntry then
begin
e:=TAddressEntry(item.data);
case e.group of
1: sender.Canvas.Font.color:=clBlue;
2: sender.Canvas.Font.color:=clRed;
else
sender.Canvas.Font.color:=clWindowtext;
end;
end
else
begin
asm
nop
end;
end;
DefaultDraw:=true;
end;
procedure TfrmChangedAddresses.miCodeAddressCopyClick(Sender: TObject);
begin
clipboard.AsText:=editCodeAddress.Text;
end;
procedure TfrmChangedAddresses.miCodeAddressBrowseMemoryRegionClick(
Sender: TObject);
begin
memorybrowser.hexview.address := faddress;
memorybrowser.Show;
end;
procedure TfrmChangedAddresses.miCodeAddressDisassembleMemoryRegionClick(
Sender: TObject);
begin
memorybrowser.disassemblerview.SelectedAddress := faddress;
memorybrowser.Show;
end;
procedure TfrmChangedAddresses.miCopyAddressToClipboardClick(Sender: TObject);
var
list: Tstringlist;
i: integer;
begin
list:=tstringlist.create;
for i:=0 to changedlist.Items.Count-1 do
if changedlist.Items[i].Selected then
list.add(changedlist.Items[i].Caption);
clipboard.AsText:=list.text;
list.free;
end;
procedure TfrmChangedAddresses.miCopyValueToClipboardClick(Sender: TObject);
var
list: Tstringlist;
i: integer;
begin
list:=tstringlist.create;
for i:=0 to changedlist.Items.Count-1 do
if changedlist.Items[i].Selected then
list.add(changedlist.Items[i].SubItems[0]);
clipboard.AsText:=list.text;
list.free;
end;
procedure TfrmChangedAddresses.miChangeValueClick(Sender: TObject);
var
i: integer;
a: ptruint;
value: string;
vartype: TVariableType;
ct: TCustomType;
begin
value:='';
if changedlist.ItemIndex<>-1 then
value:=changedlist.Items[changedlist.ItemIndex].SubItems[0];
if InputQuery(rsValueChange, rsGiveTheNewValue, value)=false then exit;
for i:=0 to changedlist.Items.Count-1 do
begin
if changedlist.items[i].Selected then
begin
a:=symhandler.getAddressFromName(changedlist.items[i].Caption);
vartype:=vtDword;
ct:=nil;
ct:=TCustomType(cbDisplayType.Items.Objects[cbDisplayType.ItemIndex]);
if ct=nil then
begin
case cbDisplayType.ItemIndex of
cbDisplayTypeIndexByte: vartype:=vtByte;
cbDisplayTypeIndexWord: vartype:=vtWord;
cbDisplayTypeIndexDword: vartype:=vtDWord;
cbDisplayTypeIndexQword: vartype:=vtQWord;
cbDisplayTypeIndexSingle: vartype:=vtSingle;
cbDisplayTypeIndexDouble: vartype:=vtDouble;
cbDisplayTypeIndexPointer:
begin
if processhandler.is64Bit then
vartype:=vtQWord
else
vartype:=vtDWord;
end
end;
end
else
vartype:=vtCustom;
ParseStringAndWriteToAddress(value,a, vartype,false,ct);
end;
end;
end;
function TfrmChangedAddresses.checkFilter(entry: TaddressEntry): boolean;
begin
if currentFilterFunc=-1 then exit(true);
LUA_SetCurrentContextState(0,entry.fcontext, filterExtraRegs);
lua_rawgeti(LuaVM, LUA_REGISTRYINDEX, currentFilterFunc);
if lua_pcall(LuaVM,0,1,0)=0 then
begin
result:=lua_toboolean(LuaVM,-1);
lua_pop(LuaVM,1);
end
else
begin
lua_settop(LuaVM,0);
result:=false;
end;
end;
procedure TfrmChangedAddresses.rebuildListWithFilter;
var
mi: TMapIterator;
entry: TaddressEntry;
address: ptruint;
li: TListItem;
begin
changedlist.Clear; //don't delete the data
mi:=tmapiterator.Create(addresslist);
mi.First;
while not mi.eom do
begin
mi.GetData(entry);
if checkFilter(entry) then
begin
mi.GetID(address);
li:=changedlist.Items.add;
li.caption:=inttohex(address,8);
li.SubItems.Add('');
li.subitems.add(inttostr(entry.count));
li.Data:=entry;
end;
mi.Next;
end;
refetchValues;
end;
procedure TfrmChangedAddresses.clearFilter;
begin
if currentFilterFunc<>-1 then
begin
luaL_unref(luaVM, LUA_REGISTRYINDEX, currentFilterFunc);
currentFilterFunc:=-1;
end;
currentFilter:='';
miSetFilter.Caption:=rsFilter;
end;
procedure TfrmChangedAddresses.miSetFilterClick(Sender: TObject);
var
f: string;
i: integer;
begin
f:=currentFilter;
if InputQuery(rsFilter, rsEnterLuaFormula, f) then
begin
if trim(f)<>'' then
begin
//parse it
if luaL_loadstring(LuaVM,pchar('return '+f))=0 then
begin
clearfilter;
filterExtraRegs:=(pos('XMM',f)>0);
if filterExtraRegs=false then
begin
i:=pos('FP',f);
filterExtraRegs:=(i>0) and (i+2<=length(f)) and (f[i+2] in ['0'..'7']);
end;
currentFilterFunc:=luaL_ref(LuaVM,LUA_REGISTRYINDEX);
currentFilter:=f;
miSetFilter.Caption:=rsFilter+' : '+currentFilter;
end
else
MessageDlg('"'+f+'" is invalid',mtError,[mbOK],0);
lua_settop(LuaVM,0);
end
else
clearfilter;
rebuildlistwithfilter;
end;
end;
procedure TfrmChangedAddresses.miGroupClearClick(Sender: TObject);
var
i: integer;
e: TAddressEntry;
begin
for i:=0 to Changedlist.Items.count-1 do
begin
e:=TAddressEntry(changedlist.items[i].Data);
e.group:=0;
end;
end;
procedure TfrmChangedAddresses.miMarkAsGroupClick(Sender: TObject);
var
i: integer;
e: TAddressEntry;
begin
for i:=0 to Changedlist.Items.count-1 do
if changedlist.items[i].Selected then
begin
e:=TAddressEntry(changedlist.items[i].Data);
e.group:=tmenuitem(sender).tag;
end;
changedlist.Refresh;
end;
procedure TfrmChangedAddresses.miResetCountClick(Sender: TObject);
var
i: integer;
ae: TAddressEntry;
begin
for i:=changedlist.items.Count-1 downto 0 do
begin
ae:=TAddressEntry(changedlist.Items[i].Data);
ae.count:=0;
end;
refetchValues;
end;
procedure TfrmChangedAddresses.miScanForCommonalitiesClick(Sender: TObject);
var
i,j,k: integer;
g0: array of TAddressEntry;
g1: array of TAddressEntry;
g2: array of TAddressEntry;
e: TAddressEntry;
gs: array of TAddressEntry;
f:TfrmChangedAddressesCommonalityScanner;
begin
setlength(g0,0);
setlength(g1,0);
setlength(g2,0);
setlength(gs,0);
for i:=0 to changedlist.items.Count-1 do
begin
e:=TAddressEntry(changedlist.items[i].data);
e.fillBase;
case e.group of
0:
begin
setlength(g0,length(g0)+1);
g0[length(g0)-1]:=e;
end;
1:
begin
setlength(g1,length(g1)+1);
g1[length(g1)-1]:=e;
end;
2:
begin
setlength(g2,length(g2)+1);
g2[length(g2)-1]:=e;
end;
else raise exception.create(rsInvalidGroups);
end;
if changedlist.items[i].Selected then
begin
setlength(gs,length(gs)+1);
gs[length(gs)-1]:=e;
end;
end;
if (length(g1)=0) and (length(g2)=0) then
begin
if length(gs)>1 then //nothing marked, but more than 1 address selected
begin
g1:=gs;
//delete all g0 entries that are in g1(gs)
for i:=0 to length(g1)-1 do
begin
for j:=0 to length(g0)-1 do
if g0[j]=g1[i] then
begin
//found one
for k:=j to length(g0)-2 do
g0[k]:=g0[k+1];
setlength(g0,length(g0)-1);
end;
end;
end
else
raise exception.create(rsDesignateSomeAddresses);
end;
if (length(g1)>0) and (length(g2)=0) then
begin
if length(g0)=0 then raise exception.create(format(rsNoAddressesLeftForGroup, [1]));
g2:=g0;
end
else
if (length(g1)=0) and (length(g2)>0) then
begin
if length(g0)=0 then raise exception.create(format(rsNoAddressesLeftForGroup, [1]));
g2:=g0;
end;
//process g1 and g2
f:=TfrmChangedAddressesCommonalityScanner.Create(application); //don't destroy when you close this window
f.setgroup(1, g1);
f.setgroup(2, g2);
f.show;
f.initlist;
end;
procedure TfrmChangedAddresses.miShowIPTLogClick(Sender: TObject);
var
f: TfrmIPTLogDisplay;
ae: TAddressEntry;
begin
{$IFDEF WINDOWS}
if changedlist.Selected=nil then exit;
ae:=TAddressEntry(changedlist.Selected.Data);
if (ae.ipt.log=nil) then
begin
if debuggerthread<>nil then
begin
if (useintelptfordebug=false) or (inteliptlogfindwhatroutines=false) then
begin
if messagedlg(rsNeedsIPTFindWhat, mtConfirmation, [mbyes,mbno],0)=mryes then
begin
useintelptfordebug:=true;
inteliptlogfindwhatroutines:=true;
debuggerthread.initIntelPTTracing;
end;
end;
end;
end
else
begin
f:=TfrmIPTLogDisplay.create(application);
f.show;
f.loadlog('log'+faddress.ToHexString+GetTickCount64.ToHexString, ae.ipt.log, ae.ipt.size, faddress);
end;
{$ENDIF}
end;
procedure TfrmChangedAddresses.miDeleteSelectedEntriesClick(Sender: TObject);
var
i: integer;
ae: TAddressEntry;
begin
if changedlist.SelCount>=1 then
begin
if MessageDlg(rsDeleteAddresses, rsAreYouWishToDelete, mtConfirmation, [mbyes,mbno],0) = mryes then
begin
i:=0;
while i<changedlist.items.Count do
begin
if changedlist.items[i].Selected then
begin
ae:=TAddressEntry(changedlist.Items[i].Data);
if addresslist<>nil then
addresslist.Delete(ae.address);
if ae<>nil then
ae.free;
changedlist.Items[i].data:=nil;
changedlist.Items[i].Delete;
end
else
inc(i);
end;
end;
end;
end;
procedure TfrmChangedAddresses.FormClose(Sender: TObject;
var Action: TCloseAction);
var temp:dword;
i: integer;
ae: TAddressEntry;
x: array of integer;
begin
if OKButton.caption=rsStop then
OKButton.Click;
if breakpoint=nil then
action:=caFree
else
begin
dec(pbreakpoint(breakpoint).referencecount);
action:=cahide;
end;
setlength(x,3);
x[0]:=changedlist.Column[0].Width;
x[1]:=changedlist.Column[1].Width;
x[2]:=changedlist.Column[2].Width;
saveformposition(self,x);
try
//rename so that the next created dialog can have this name
self.name:=self.name+'_tobedeleted'+inttohex(random(65535),4)+inttohex(random(65535),4)+inttohex(random(65535),4)+inttohex(random(65535),4)+'_'+inttohex(GetTickCount64,1);
except
end;
end;
procedure TfrmChangedAddresses.FormShow(Sender: TObject);
begin
// defaultcolor:=
defaultcolor:=Changedlist.GetDefaultColor(dctFont);
OKButton.Caption:=rsStop;
if not hassetsizes then
begin
changedlist.Column[0].Width:=max(changedlist.Column[0].Width, canvas.TextWidth('DDDDDDDDFFFFF'));
changedlist.Column[1].Width:=max(changedlist.Column[1].Width, canvas.TextWidth('9999999.999'));
changedlist.Column[2].Width:=max(changedlist.Column[2].Width, canvas.TextWidth('999999'));
ClientWidth:=max(clientwidth, integer(changedlist.Column[0].Width+changedlist.Column[1].Width+changedlist.Column[2].Width+20));
hassetsizes:=true;
end;
end;
procedure TfrmChangedAddresses.ChangedlistDblClick(Sender: TObject);
var i: integer;
ad: dword;
vartype: TVariableType;
ct: TCustomType;
begin
vartype:=vtDword;
ct:=nil;
ct:=TCustomType(cbDisplayType.Items.Objects[cbDisplayType.ItemIndex]);
if ct=nil then
begin
case cbDisplayType.ItemIndex of
cbDisplayTypeIndexByte: vartype:=vtByte;
cbDisplayTypeIndexWord: vartype:=vtWord;
cbDisplayTypeIndexDword: vartype:=vtDword;
cbDisplayTypeIndexQword: vartype:=vtQWord;
cbDisplayTypeIndexSingle: vartype:=vtSingle;
cbDisplayTypeIndexDouble: vartype:=vtDouble;
cbDisplayTypeIndexPointer:
begin
if processhandler.is64Bit then
vartype:=vtQWord
else
vartype:=vtDWord;
end
end;
end
else
vartype:=vtCustom;
for i:=0 to changedlist.Items.Count-1 do
if changedlist.Items[i].Selected then
mainform.addresslist.addaddress(rsNoDescription, changedlist.Items[i].caption, [], 0, vartype, cbDisplayType.Text );
end;
procedure TfrmChangedAddresses.PopupMenu1Popup(Sender: TObject);
begin
Showregisterstates1.enabled:=changedlist.selected<>nil;
Browsethismemoryregion1.enabled:=changedlist.selected<>nil;
miDeleteSelectedEntries.enabled:=changedlist.SelCount>0;
miDissect.enabled:=changedlist.SelCount>0;
micbShowAsSigned.Visible:=cbDisplayType.ItemIndex in [cbDisplayTypeIndexByte, cbDisplayTypeIndexWord, cbDisplayTypeIndexDword, cbDisplayTypeIndexQword];
micbShowAsSigned.Enabled:=not micbShowAsHexadecimal.checked;
end;
procedure TfrmChangedAddresses.refetchValues(specificaddress: ptruint=0; countonly: boolean=false);
var i: integer;
s: string;
handled: boolean;
startindex: integer;
stopindex: integer;
e: TAddressEntry;
begin
s:='';
if changedlist.Items.Count>0 then
begin
if Changedlist.TopItem=nil then exit;
startindex:=Changedlist.TopItem.Index;
stopindex:=min(Changedlist.TopItem.Index+changedlist.VisibleRowCount, Changedlist.Items.Count-1);
for i:=startindex to stopindex do
begin
e:=TAddressEntry(changedlist.items[i].Data);
if (specificaddress<>0) and (e.address <> specificaddress) then //check if this is the line to update, if not, don't read and parse
continue;
if countonly=false then
begin
case cbDisplayType.ItemIndex of
cbDisplayTypeIndexByte: s:=ReadAndParseAddress(e.address, vtByte, nil, micbShowAsHexadecimal.checked, micbShowAsSigned.checked);
cbDisplayTypeIndexWord: s:=ReadAndParseAddress(e.address, vtWord, nil, micbShowAsHexadecimal.checked, micbShowAsSigned.checked);
cbDisplayTypeIndexDword: s:=ReadAndParseAddress(e.address, vtDWord, nil, micbShowAsHexadecimal.checked, micbShowAsSigned.checked);
cbDisplayTypeIndexQword: s:=ReadAndParseAddress(e.address, vtQWord, nil, micbShowAsHexadecimal.checked, micbShowAsSigned.checked);
cbDisplayTypeIndexSingle: s:=ReadAndParseAddress(e.address, vtSingle,nil, micbShowAsHexadecimal.checked);
cbDisplayTypeIndexDouble: s:=ReadAndParseAddress(e.address, vtDouble,nil, micbShowAsHexadecimal.checked);
cbDisplayTypeIndexPointer:
begin
if processhandler.is64Bit then
s:=ReadAndParseAddress(e.address, vtQWord, nil, true)
else
s:=ReadAndParseAddress(e.address, vtDWord, nil, true);
end;
else
begin
//custom type
s:=ReadAndParseAddress(e.address, vtCustom, TCustomType(cbDisplayType.Items.Objects[cbDisplayType.ItemIndex]), micbShowAsHexadecimal.checked, micbShowAsSigned.checked);
end;
end;
end;
while Changedlist.Items[i].SubItems.Count<2 do
Changedlist.Items[i].SubItems.Add('');
changedlist.items[i].SubItems[1]:=inttostr(TAddressEntry(changedlist.items[i].Data).count);
if countonly=false then
Changedlist.Items[i].SubItems[0]:=s;
end;
end;
end;
procedure TfrmChangedAddresses.Timer1Timer(Sender: TObject);
var
c: integer;
s: string;
begin
refetchValues;
c:=changedlist.Items.Count;
if c=1 then
s:=rsCodeAccessesSingleAddress
else
s:=format(rsCodeAccessesAddresses, [c]);
lblInfo.caption:=s;
end;
procedure TfrmChangedAddresses.Showregisterstates1Click(Sender: TObject);
var ae: TAddressEntry;
begin
if changedlist.Selected<>nil then
begin
with TRegisters.create(self) do
begin
//borderstyle:=bsSingle;
ae:=TAddressEntry(changedlist.Selected.Data);
SetContextPointer(ae.context, ae.stack.stack, ae.stack.savedsize);
show;
end;
end;
end;
procedure TfrmChangedAddresses.Browsethismemoryregion1Click(
Sender: TObject);
begin
if changedlist.Selected<>nil then
begin
memorybrowser.memoryaddress:=StrToQWordEx('$'+changedlist.Selected.Caption);
if not memorybrowser.visible then
memorybrowser.show;
end;
end;
procedure TfrmChangedAddresses.setAddress(a: ptruint);
var
a2: ptruint;
ins: string;
begin
faddress:=a;
caption:=format(rsChangedAddressesBy, [a]);
a2:=a;
disassemble(a2);
ins:=defaultDisassembler.LastDisassembleData.opcode+defaultDisassembler.LastDisassembleData.parameters;
editCodeAddress.Text:=format('%x',[a]);
end;
procedure TfrmChangedAddresses.FormDestroy(Sender: TObject);
var
i: integer;
ae: TAddressEntry;
begin
stopDBVMWatch();
for i:=0 to changedlist.Items.Count-1 do
begin
ae:=TAddressEntry(changedlist.Items[i].Data);
ae.free;
end;
if addresslist<>nil then
freeandnil(addresslist);
if addresslistCS<>nil then
freeandnil(addresslistCS);
end;
procedure TfrmChangedAddresses.FormCreate(Sender: TObject);
var x: array of integer;
i: integer;
begin
fdebuggerinterfacewatchid:=-1;
currentFilterFunc:=-1;
okbutton.caption:=rsStop;
setlength(x, 0);
if loadformposition(self,x) then
begin
if length(x)>0 then
begin
changedlist.Column[0].Width:=x[0];
changedlist.Column[1].Width:=x[1];
changedlist.Column[2].Width:=x[2];
hassetsizes:=true;
end;
end;
//fill in the custom types
for i:=0 to customTypes.count-1 do
cbDisplayType.Items.AddObject(TCustomType(customTypes[i]).name, customTypes[i]);
addresslistCS:=TCriticalSection.Create;
addresslist:=TMap.Create(ituPtrSize,sizeof(pointer));
miShowIPTLog.Visible:=systemSupportsIntelPT and not hideiptcapability and (CurrentDebuggerInterface<>nil) and CurrentDebuggerInterface.canUseIPT;
end;
procedure TfrmChangedAddresses.stopdbvmwatch;
begin
{$IFDEF windows}
if dbvmwatchpollthread<>nil then
begin
dbvmwatchpollthread.Terminate;
dbvmwatchpollthread.WaitFor;
freeandnil(dbvmwatchpollthread);
end;
if debuggerinterfacewatchid<>-1 then
begin
dbvm_watch_delete(debuggerinterfacewatchid);
debuggerinterfacewatchid:=-1;
end;
if dbvmwatch_unlock<>0 then
begin
UnlockMemory(dbvmwatch_unlock);
dbvmwatch_unlock:=0;
end;
{$ENDIF}
end;
procedure TfrmChangedAddresses.setdebuggerinterfacewatchid(id: integer);
begin
{$IFDEF windows}
fdebuggerinterfacewatchid:=id;
if id<>-1 then
begin
if dbvmwatchpollthread<>nil then
freeandnil(dbvmwatchpollthread);
dbvmwatchpollthread:=TDBVMWatchExecutePollThread.Create(true);
dbvmwatchpollthread.id:=id;
dbvmwatchpollthread.fca:=self;
dbvmwatchpollthread.Start;
end;
{$ENDIF}
end;
initialization
{$i formChangedAddresses.lrs}
end.