mirror of
https://github.com/cheat-engine/cheat-engine
synced 2026-08-15 02:26:08 -04:00
1032 lines
27 KiB
ObjectPascal
Executable file
1032 lines
27 KiB
ObjectPascal
Executable file
unit frmMemoryAllocHandlerUnit;
|
|
|
|
{$MODE Delphi}
|
|
|
|
{
|
|
ZwVirtualAllocEx
|
|
}
|
|
interface
|
|
|
|
uses
|
|
{$ifdef darwin}
|
|
macport,
|
|
{$endif}
|
|
{$ifdef windows}
|
|
windows,
|
|
{$endif}
|
|
LCLIntf, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
|
Dialogs, StdCtrls, symbolhandler, symbolhandlerstructs, CEFuncProc,NewKernelHandler,
|
|
autoassembler, ExtCtrls, ComCtrls, stacktrace2, math, Menus, syncobjs, Contnrs, circularbuffer,
|
|
LResources, commonTypeDefs, betterControls;
|
|
|
|
|
|
const
|
|
HOOKEVENT_ALLOC=0;
|
|
HOOKEVENT_FREE=1;
|
|
HOOKEVENT_HEAPALLOC=2;
|
|
HOOKEVENT_HEAPFREE=3;
|
|
HOOKEVENT_HEAPDESTROY=4;
|
|
|
|
type
|
|
TAllocData=record
|
|
handle: qword;
|
|
baseaddress: qword;
|
|
allocationType: dword;
|
|
protect: dword;
|
|
size: dword;
|
|
esp: qword;
|
|
end;
|
|
|
|
TFreeData=record
|
|
handle: qword;
|
|
baseaddress: qword;
|
|
size: dword;
|
|
FreeType: dword;
|
|
end;
|
|
|
|
THeapAllocData=record
|
|
HeapHandle: qword;
|
|
Flags: DWORD;
|
|
Size: dword;
|
|
esp:qword;
|
|
address: qword;
|
|
end;
|
|
|
|
THeapFreeData=record
|
|
HeapHandle: qword;
|
|
Flags: dword;
|
|
HeapBase: qword;
|
|
end;
|
|
|
|
THeapDestroyData=record
|
|
HeapHandle: qword;
|
|
end;
|
|
|
|
THookEvent = record
|
|
eventtype: integer;
|
|
case integer of
|
|
HOOKEVENT_ALLOC: (AllocEvent: TAllocData);
|
|
HOOKEVENT_FREE: (FreeEvent: TFreeData);
|
|
HOOKEVENT_HEAPALLOC: (HeapAllocEvent: THeapAllocData);
|
|
HOOKEVENT_HEAPFREE: (HeapFreeEvent: THeapFreeData);
|
|
HOOKEVENT_HEAPDESTROY: (HeapDestroyEvent: THeapDestroyData);
|
|
end;
|
|
|
|
type TmemoryAllocevent=class
|
|
private
|
|
public
|
|
HookEvent: THookEvent;
|
|
|
|
BaseAddress: ptrUint;
|
|
//stack: array [0..4095] of byte;
|
|
//stacksize: dword;
|
|
|
|
procedure assign(o: TMemoryAllocEvent);
|
|
end;
|
|
|
|
type
|
|
PMemRecTable=^TMemRecTable;
|
|
PMemRecTableArray=^TMemRecTableArray;
|
|
TMemRecTable=record
|
|
case integer of
|
|
1: (memallocevent: TmemoryAllocevent); //if this is the last level (7) this is a memallocevent pointer
|
|
2: (MemrecArray: PMemRecTableArray); //else it's a PMemrectablearray
|
|
end;
|
|
TMemRecTableArray=array [0..15] of TMemRecTable;
|
|
|
|
type TDisplayThread=class(tthread)
|
|
private
|
|
maxlevel: integer;
|
|
procedure AddAddress(addresslist: PMemrecTableArray; memallocevent: TmemoryAllocevent );
|
|
procedure removeaddress(addresslist: PMemrecTableArray; memallocevent: TmemoryAllocevent );
|
|
public
|
|
csObjectList: TCriticalSection;
|
|
ObjectQueue: TCircularObjectBuffer;
|
|
ListContainsEntries: TEvent;
|
|
|
|
errorcount: dword; //debug variable
|
|
heapcount: integer;
|
|
//-----
|
|
procedure AddObject_Free(o: TMemoryAllocEvent);
|
|
procedure AddObject_Alloc(o: TMemoryAllocEvent);
|
|
procedure AddObject_HeapAlloc(o: TMemoryAllocEvent);
|
|
procedure AddObject_HeapFree(o: TMemoryAllocEvent);
|
|
procedure AddObject_HeapDestroy(o: TMemoryAllocEvent);
|
|
procedure AddObject_HeapDestroy2(a: PMemrectableArray; level: integer; heaphandle: qword);
|
|
procedure addobject(objecttoadd: TMemoryAllocEvent);
|
|
procedure execute; override;
|
|
constructor create(suspended: boolean);
|
|
destructor destroy; override;
|
|
end;
|
|
|
|
type TAllocWatcher=class(TThread)
|
|
private
|
|
HasSetupDataEvent: THandle;
|
|
CEHasHandledItEvent: THandle;
|
|
HookEventDataAddress: ptrUint;
|
|
|
|
errorcount: dword; //debug variable
|
|
|
|
HookEvent: THookEvent;
|
|
maxlevel: integer;
|
|
procedure allocevent;
|
|
procedure freeEvent;
|
|
procedure heapAllocEvent;
|
|
procedure heapFreeEvent;
|
|
procedure headpDestroyEvent;
|
|
// procedure addObject;
|
|
procedure addObjectToList(o: TmemoryAllocevent);
|
|
public
|
|
procedure execute; override;
|
|
constructor create(suspended: boolean; HasSetupDataEvent: THandle; CEHasHandledItEvent: THandle; HookEventDataAddress: ptrUint);
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
{ TfrmMemoryAllocHandler }
|
|
|
|
TfrmMemoryAllocHandler = class(TForm)
|
|
Label1: TLabel;
|
|
Label2: TLabel;
|
|
Label3: TLabel;
|
|
Label4: TLabel;
|
|
lblBaseAddress: TLabel;
|
|
lblFlags: TLabel;
|
|
lblHeapHandle: TLabel;
|
|
lblSize: TLabel;
|
|
Panel2: TPanel;
|
|
StatusBar1: TStatusBar;
|
|
Timer1: TTimer;
|
|
GroupBox1: TGroupBox;
|
|
Panel1: TPanel;
|
|
Edit1: TEdit;
|
|
Button1: TButton;
|
|
lblErr: TLabel;
|
|
btnReload: TButton;
|
|
cbHookAllocs: TCheckBox;
|
|
procedure cbHookAllocsChange(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormDestroy(Sender: TObject);
|
|
procedure FormShow(Sender: TObject);
|
|
procedure GroupBox1Click(Sender: TObject);
|
|
procedure Timer1Timer(Sender: TObject);
|
|
procedure Button1Click(Sender: TObject);
|
|
procedure btnReloadClick(Sender: TObject);
|
|
procedure cbHookAllocsClick(Sender: TObject);
|
|
private
|
|
{ Private declarations }
|
|
HasSetupDataEvent: THandle;
|
|
CEHasHandledItEvent: THandle;
|
|
CEInitializationFinished: THandle;
|
|
watcher: TAllocWatcher;
|
|
HookEventDataAddress: ptrUint;
|
|
|
|
hookscript: tstringlist;
|
|
|
|
disableinfo: TDisableInfo;
|
|
|
|
maxlevel: integer;
|
|
pointermask: integer;
|
|
public
|
|
{ Public declarations }
|
|
|
|
//level0 (0000000-10000000-20000000-30000000-40000000-50000000-60000000-70000000-80000000-90000000-a0000000-b0000000-c0000000-d0000000-e0000000-f0000000
|
|
//level1.0 (01000000-02000000-03000000-04000000-....
|
|
//level1.1 (11000000-12000000-13000000-14000000-....
|
|
//max level (7/15) is the actual record
|
|
memrecCS: TCriticalSection;
|
|
HeapBaselevel: TMemRecTableArray;
|
|
AllocBaseLevel: TMemRecTableArray;
|
|
|
|
hookedprocessid: dword;
|
|
displaythread: TDisplayThread;
|
|
|
|
function FindAddress(addresslist: PMemrecTableArray; address: ptrUint): TMemoryAllocEvent;
|
|
function WaitForInitializationToFinish: boolean;
|
|
end;
|
|
|
|
var
|
|
frmMemoryAllocHandler: TfrmMemoryAllocHandler;
|
|
|
|
implementation
|
|
|
|
|
|
uses frmautoinjectunit, MemoryBrowserFormUnit, processhandlerunit, Globals;
|
|
|
|
resourcestring
|
|
rsQueuedMemoryEventsWaiting = 'Queued memory events waiting: %s';
|
|
rsHeapcount = 'Heapcount=%s';
|
|
rsFailureToInitialize = 'Failure to initialize';
|
|
rsFailureToHook = 'Failure to hook';
|
|
rsEvent1Failure = 'Event1 failure:';
|
|
rsEvent2Failure = 'Event2 failure:';
|
|
rsEvent3Failure = 'Event3 failure:';
|
|
|
|
procedure TDisplayThread.removeaddress(addresslist: PMemrecTableArray; memallocevent: TmemoryAllocevent );
|
|
var
|
|
level: integer;
|
|
entrynr: integer;
|
|
currentarray: PMemrectablearray;
|
|
i: integer;
|
|
temp: TmemoryAllocevent;
|
|
begin
|
|
level:=0;
|
|
currentarray:=addresslist;
|
|
while level<maxlevel do
|
|
begin
|
|
entrynr:=memallocevent.baseaddress shr ((maxlevel-level)*4) and $f;
|
|
if currentarray[entrynr].MemrecArray=nil then
|
|
exit; //not found
|
|
|
|
currentarray:=currentarray[entrynr].MemrecArray;
|
|
|
|
inc(level);
|
|
end;
|
|
|
|
entrynr:=memallocevent.baseaddress shr ((maxlevel-level)*4) and $f;
|
|
if currentarray[entrynr].memallocevent<>nil then
|
|
begin
|
|
//remove it
|
|
temp:=currentarray[entrynr].memallocevent;
|
|
currentarray[entrynr].memallocevent:=nil;
|
|
|
|
if addresslist=@frmMemoryAllocHandler.HeapBaselevel then
|
|
dec(heapcount);
|
|
|
|
temp.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TDisplayThread.AddAddress(addresslist: PMemrecTableArray; memallocevent: TmemoryAllocevent );
|
|
var
|
|
level: integer;
|
|
entrynr: integer;
|
|
temp,currentarray: PMemrectablearray;
|
|
temp2: TmemoryAllocevent;
|
|
i: integer;
|
|
begin
|
|
level:=0;
|
|
currentarray:=addresslist;
|
|
while level<maxlevel do
|
|
begin
|
|
|
|
entrynr:=memallocevent.baseaddress shr ((maxlevel-level)*4) and $f;
|
|
if currentarray[entrynr].MemrecArray=nil then //allocate the array
|
|
begin
|
|
GetMem(temp, sizeof(TMemRecTableArray));
|
|
ZeroMemory(temp, sizeof(TMemRecTableArray));
|
|
currentarray[entrynr].MemrecArray:=temp;
|
|
end;
|
|
|
|
currentarray:=currentarray[entrynr].MemrecArray;
|
|
|
|
inc(level);
|
|
end;
|
|
|
|
entrynr:=memallocevent.baseaddress shr ((maxlevel-level)*4) and $f;
|
|
if currentarray[entrynr].memallocevent<>nil then
|
|
begin
|
|
temp2:=currentarray[entrynr].memallocevent;
|
|
currentarray[entrynr].memallocevent:=nil;
|
|
temp2.free;
|
|
end;
|
|
|
|
currentarray[entrynr].memallocevent:=memallocevent;
|
|
|
|
if addresslist=@frmMemoryAllocHandler.HeapBaselevel then
|
|
inc(heapcount);
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TDisplayThread.AddObject_HeapAlloc(o: TMemoryAllocEvent);
|
|
begin
|
|
addaddress(@frmMemoryAllocHandler.HeapBaseLevel, o);
|
|
end;
|
|
|
|
procedure TDisplayThread.AddObject_HeapFree(o: TMemoryAllocEvent);
|
|
var
|
|
first,last: integer;
|
|
j,k: integer;
|
|
x: integer;
|
|
temp: dword;
|
|
begin
|
|
removeaddress(@frmMemoryAllocHandler.HeapBaseLevel, o);
|
|
o.Free;
|
|
end;
|
|
|
|
procedure TDisplayThread.AddObject_HeapDestroy2(a: PMemrectableArray; level: integer; heaphandle: qword);
|
|
var i: integer;
|
|
temp: TMemoryAllocEvent;
|
|
begin
|
|
if level=maxlevel then
|
|
begin
|
|
for i:=0 to 15 do
|
|
if (a[i].memallocevent<>nil) and (a[i].memallocevent.HookEvent.HeapAllocEvent.HeapHandle=heaphandle) then
|
|
begin
|
|
//delete this one
|
|
temp:=a[i].memallocevent;
|
|
a[i].memallocevent:=nil;
|
|
dec(heapcount);
|
|
temp.free;
|
|
end;
|
|
end
|
|
else
|
|
for i:=0 to 15 do
|
|
begin
|
|
if a[i].MemrecArray<>nil then
|
|
AddObject_HeapDestroy2(a[i].MemrecArray,level+1,heaphandle);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TDisplayThread.AddObject_HeapDestroy(o: TMemoryAllocEvent);
|
|
var
|
|
i: integer;
|
|
level: integer;
|
|
destroyedHandle: pointer;
|
|
begin
|
|
AddObject_HeapDestroy2(@frmMemoryAllocHandler.HeapBaseLevel,0,o.HookEvent.HeapDestroyEvent.HeapHandle);
|
|
end;
|
|
|
|
procedure TDisplayThread.AddObject_Alloc(o: TMemoryAllocEvent);
|
|
var
|
|
s: string;
|
|
first,last: integer;
|
|
x,i,j,k,l: integer;
|
|
temp: dword;
|
|
insert: boolean;
|
|
begin
|
|
addaddress(@frmMemoryAllocHandler.AllocBaseLevel, o);
|
|
end;
|
|
|
|
procedure TDisplayThread.AddObject_Free(o: TMemoryAllocEvent);
|
|
var
|
|
first,last: integer;
|
|
x,j,k: integer;
|
|
temp: dword;
|
|
begin
|
|
removeaddress(@frmMemoryAllocHandler.AllocBaseLevel, o);
|
|
o.Free;
|
|
end;
|
|
|
|
procedure TDisplayThread.addobject(objecttoadd: TMemoryAllocEvent);
|
|
//Handles the current object with the gui (add, delete, etc...)
|
|
begin
|
|
try
|
|
case objecttoadd.HookEvent.eventtype of
|
|
HOOKEVENT_FREE: AddObject_Free(objecttoadd);
|
|
HOOKEVENT_ALLOC: AddObject_Alloc(objecttoadd);
|
|
HOOKEVENT_HEAPALLOC: AddObject_HeapAlloc(objecttoadd);
|
|
HOOKEVENT_HEAPFREE: AddObject_HeapFree(objecttoadd);
|
|
HOOKEVENT_HEAPDESTROY:AddObject_HeapDestroy(objecttoadd);
|
|
end;
|
|
except
|
|
inc(errorcount);
|
|
OutputDebugString('Exception in TDisplayThread.addobject2. objecttoadd='+inttohex(ptrUint(objecttoadd),8));
|
|
end;
|
|
end;
|
|
|
|
procedure TDisplayThread.execute;
|
|
var o: TMemoryAllocEvent;
|
|
begin
|
|
while not terminated do
|
|
begin
|
|
try
|
|
o:=nil;
|
|
csObjectList.Enter;
|
|
try
|
|
if ObjectQueue.Count>0 then
|
|
o:=TMemoryAllocEvent(ObjectQueue.Read);
|
|
finally
|
|
csObjectList.Leave;
|
|
end;
|
|
|
|
if o<>nil then
|
|
begin
|
|
frmMemoryAllocHandler.memrecCS.enter;
|
|
try
|
|
addobject(o)
|
|
finally
|
|
frmMemoryAllocHandler.memrecCS.leave;
|
|
end;
|
|
end
|
|
else
|
|
ListContainsEntries.WaitFor(1000); //nothing to add to the list, wait a second and check again
|
|
|
|
except
|
|
inc(errorcount);
|
|
OutputDebugString('Error in TDisplayThread.execute');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
constructor TDisplayThread.create(suspended: boolean);
|
|
begin
|
|
csObjectList:=TCriticalSection.Create;
|
|
ObjectQueue:=TCircularObjectBuffer.Create(5000000, 1000); //arround 20MB of of pointe storage
|
|
|
|
ListContainsEntries:=TEvent.Create(nil,false,false,'');
|
|
|
|
if processhandler.is64Bit then
|
|
maxlevel:=15
|
|
else
|
|
maxlevel:=7;
|
|
|
|
inherited create(suspended);
|
|
end;
|
|
|
|
destructor TDisplayThread.destroy;
|
|
begin
|
|
if csObjectList<>nil then
|
|
freeandnil(csObjectList);
|
|
|
|
if ListContainsEntries<>nil then
|
|
freeandnil(ListContainsEntries);
|
|
|
|
if ObjectQueue<>nil then
|
|
freeandnil(ObjectQueue);
|
|
|
|
inherited destroy;
|
|
end;
|
|
|
|
procedure TAllocWatcher.addObjectToList(o: TmemoryAllocevent);
|
|
//Will add this object to a list that will later on be read out by the display thread
|
|
begin
|
|
frmMemoryAllocHandler.displaythread.csObjectList.Enter;
|
|
try
|
|
frmMemoryAllocHandler.displaythread.ObjectQueue.Write(o)
|
|
finally
|
|
frmMemoryAllocHandler.displaythread.csObjectList.Leave;
|
|
end;
|
|
frmMemoryAllocHandler.displaythread.ListContainsEntries.SetEvent;
|
|
end;
|
|
|
|
procedure TmemoryAllocevent.assign(o: TMemoryAllocEvent);
|
|
var i: integer;
|
|
begin
|
|
self.HookEvent:=o.HookEvent;
|
|
|
|
{ for i:=0 to o.stacksize-1 do
|
|
self.stack[i]:=o.stack[i];
|
|
|
|
self.stacksize:=o.stacksize; }
|
|
end;
|
|
|
|
|
|
{
|
|
procedure TAllocWatcher.addObject;
|
|
|
|
end; }
|
|
|
|
procedure TAllocWatcher.allocEvent;
|
|
var s: integer;
|
|
o: TMemoryAllocEvent;
|
|
begin
|
|
{$IFDEF windows}
|
|
o:=TmemoryAllocevent.create;
|
|
o.HookEvent:=hookevent;
|
|
o.BaseAddress:=hookevent.AllocEvent.baseaddress;
|
|
|
|
{
|
|
|
|
if not ReadProcessMemory(processhandle,pointer(o.HookEvent.AllocEvent.esp),@o.stack[0],4096,o.stacksize) then
|
|
begin
|
|
//probably couldn't read because of pageerror
|
|
//read the remaining of the page
|
|
s:=4096-(o.HookEvent.AllocEvent.esp mod 4096);
|
|
ReadProcessMemory(processhandle,pointer(o.HookEvent.AllocEvent.esp),@o.stack[0],s,o.stacksize);
|
|
end; }
|
|
|
|
setevent(CEHasHandledItEvent); //tell the hooked app to continue
|
|
addObjectToList(o);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TAllocWatcher.freeEvent;
|
|
var o: TMemoryAllocEvent;
|
|
begin
|
|
{$IFDEF windows}
|
|
o:=TmemoryAllocevent.create;
|
|
o.HookEvent:=hookevent;
|
|
o.BaseAddress:=hookevent.FreeEvent.baseaddress;
|
|
|
|
setevent(CEHasHandledItEvent);
|
|
addObjectToList(o);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TAllocWatcher.heapAllocEvent;
|
|
var o: TMemoryAllocEvent;
|
|
var s: integer;
|
|
begin
|
|
{$IFDEF windows}
|
|
o:=TmemoryAllocevent.create;
|
|
o.HookEvent:=hookevent;
|
|
o.BaseAddress:=ptrUint(hookevent.HeapAllocEvent.address);
|
|
{
|
|
if not ReadProcessMemory(processhandle,pointer(o.HookEvent.HeapAllocEvent.esp),@o.stack[0],4096,o.stacksize) then
|
|
begin
|
|
//probably couldn't read because of pageerror
|
|
//read the remaining of the page
|
|
s:=4096-(o.HookEvent.AllocEvent.esp mod 4096);
|
|
ReadProcessMemory(processhandle,pointer(o.HookEvent.HeapAllocEvent.esp),@o.stack[0],s,o.stacksize);
|
|
end; }
|
|
|
|
setevent(CEHasHandledItEvent);
|
|
addObjectToList(o);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TAllocWatcher.heapFreeEvent;
|
|
var o: TMemoryAllocEvent;
|
|
begin
|
|
{$IFDEF windows}
|
|
o:=TmemoryAllocevent.create;
|
|
o.HookEvent:=hookevent;
|
|
o.BaseAddress:=ptrUint(hookevent.HeapFreeEvent.HeapBase);
|
|
setevent(CEHasHandledItEvent);
|
|
addObjectToList(o);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TAllocWatcher.headpDestroyEvent;
|
|
var o: TMemoryAllocEvent;
|
|
begin
|
|
{$IFDEF windows}
|
|
o:=TmemoryAllocevent.create;
|
|
o.HookEvent:=hookevent;
|
|
o.BaseAddress:=$0;
|
|
|
|
setevent(CEHasHandledItEvent);
|
|
addObjectToList(o);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TAllocWatcher.execute;
|
|
var x: PtrUInt;
|
|
begin
|
|
{$IFDEF windows}
|
|
while not terminated do
|
|
begin
|
|
try
|
|
if waitforsingleobject(HasSetupDataEvent,500)=WAIT_OBJECT_0 then
|
|
begin
|
|
if readprocessmemory(processhandle, pointer(HookEventDataAddress),@HookEvent,sizeof(HookEvent),x) then
|
|
begin
|
|
case HookEvent.eventtype of
|
|
HOOKEVENT_ALLOC : allocEvent;
|
|
HOOKEVENT_FREE : freeEvent;
|
|
HOOKEVENT_HEAPALLOC : heapAllocEvent;
|
|
HOOKEVENT_HEAPFREE : heapFreeEvent;
|
|
HOOKEVENT_HEAPDESTROY : headpDestroyEvent;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
except
|
|
inc(errorcount);
|
|
OutputDebugString('Error in TAllocWatcher.execute');
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
constructor TAllocWatcher.create(suspended: boolean; HasSetupDataEvent: THandle; CEHasHandledItEvent: THandle; HookEventDataAddress: ptrUint);
|
|
begin
|
|
self.HasSetupDataEvent:=HasSetupDataEvent;
|
|
self.CeHasHandledItEvent:=CeHasHandledItEvent;
|
|
|
|
self.HookEventDataAddress:=HookEventDataAddress;
|
|
|
|
if processhandler.is64Bit then
|
|
maxlevel:=15
|
|
else
|
|
maxlevel:=7;
|
|
|
|
inherited create(suspended);
|
|
end;
|
|
|
|
|
|
|
|
procedure TfrmMemoryAllocHandler.FormCreate(Sender: TObject);
|
|
var
|
|
injectionscript: TStringlist;
|
|
x,y,z: THandle;
|
|
mi: tmoduleinfo;
|
|
mname: string;
|
|
begin
|
|
{$ifdef windows}
|
|
|
|
if processhandler.is64Bit then
|
|
begin
|
|
maxlevel:=15;
|
|
pointermask:=7; //AND the value/address with this value. If the result=0 it's aligned
|
|
end
|
|
else
|
|
begin
|
|
maxlevel:=7;
|
|
pointermask:=3;
|
|
end;
|
|
|
|
|
|
memrecCS:=TCriticalSection.Create;
|
|
displaythread:=TDisplayThread.create(false);
|
|
injectionscript:=tstringlist.Create;
|
|
try
|
|
//inject allochook.dll
|
|
|
|
if processhandler.is64bit then
|
|
mname:='allochook-x86_64.dll'
|
|
else
|
|
mname:='allochook-i386.dll';
|
|
|
|
|
|
if not symhandler.getmodulebyname(mname,mi) then
|
|
begin
|
|
injectdll(CheatEngineDir+mname);
|
|
symhandler.reinitialize(True);
|
|
end;
|
|
|
|
|
|
|
|
|
|
HasSetupDataEvent:=CreateEvent(nil, false, false, nil);
|
|
CEHasHandledItEvent:=CreateEvent(nil,false,false,nil);
|
|
CEInitializationFinished:=CreateEvent(nil,true,false,nil);
|
|
|
|
x:=0;
|
|
if not DuplicateHandle(GetCurrentProcess, HasSetupDataEvent, processhandle, @x, 0, false, DUPLICATE_SAME_ACCESS ) then
|
|
raise exception.Create(rsEvent1Failure+inttostr(getlasterror));
|
|
|
|
y:=0;
|
|
if not DuplicateHandle(GetCurrentProcess, CEHasHandledItEvent, processhandle, @y, 0, false, DUPLICATE_SAME_ACCESS ) then
|
|
raise exception.Create(rsEvent2Failure+inttostr(getlasterror));
|
|
|
|
z:=0;
|
|
if not DuplicateHandle(GetCurrentProcess, CEInitializationFinished, processhandle, @z, 0, false, DUPLICATE_SAME_ACCESS ) then
|
|
raise exception.Create(rsEvent3Failure+inttostr(getlasterror));
|
|
|
|
//set event handles
|
|
injectionscript.Add('HasSetupDataEvent:');
|
|
injectionscript.Add('DQ '+inttohex(x,8));
|
|
injectionscript.Add('CEHasHandledItEvent:');
|
|
injectionscript.Add('DQ '+inttohex(y,8));
|
|
injectionscript.Add('CEInitializationFinished:');
|
|
injectionscript.Add('DQ '+inttohex(z,8));
|
|
|
|
|
|
if not autoassemble(injectionscript,false) then raise exception.Create(rsFailureToHook);
|
|
|
|
HookEventDataAddress:=symhandler.getAddressFromName('HookEventData');
|
|
|
|
//everything configured successful, start thread that watches for HasSetupDataEvent events
|
|
watcher:=TAllocWatcher.create(false, HasSetupDataEvent, CEHasHandledItEvent, HookEventDataAddress);
|
|
|
|
hookedprocessid:=processid;
|
|
|
|
injectionscript.Clear;
|
|
injectionscript.Add('CreateThread(CeInitializeAllocHook)');
|
|
if not autoassemble(injectionscript,false) then raise exception.Create(rsFailureToInitialize);
|
|
|
|
|
|
finally
|
|
injectionscript.Free;
|
|
end;
|
|
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TfrmMemoryAllocHandler.cbHookAllocsChange(Sender: TObject);
|
|
begin
|
|
|
|
end;
|
|
|
|
|
|
procedure TfrmMemoryAllocHandler.FormDestroy(Sender: TObject);
|
|
begin
|
|
if watcher<>nil then
|
|
freeandnil(watcher);
|
|
|
|
if HasSetupDataEvent>0 then
|
|
closehandle(HasSetupDataEvent);
|
|
|
|
if CEHasHandledItEvent>0 then
|
|
closehandle(CEHasHandledItEvent);
|
|
|
|
//cleanup memory allocs
|
|
end;
|
|
|
|
procedure TfrmMemoryAllocHandler.FormShow(Sender: TObject);
|
|
begin
|
|
if processhandler.is64Bit then
|
|
edit1.ClientWidth:=canvas.TextWidth(' DDDDDDDD ')
|
|
else
|
|
edit1.ClientWidth:=canvas.TextWidth(' DDDDDDDDDDDDD ')
|
|
end;
|
|
|
|
procedure TfrmMemoryAllocHandler.GroupBox1Click(Sender: TObject);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TfrmMemoryAllocHandler.Timer1Timer(Sender: TObject);
|
|
begin
|
|
StatusBar1.Panels[0].Text:=Format(rsQueuedMemoryEventsWaiting, [inttostr(displaythread.ObjectQueue.Count)]);
|
|
Statusbar1.Panels[1].Text:=Format(rsHeapcount, [inttostr(displaythread.heapcount)]);
|
|
end;
|
|
|
|
procedure TfrmMemoryAllocHandler.Button1Click(Sender: TObject);
|
|
var result: TMemoryAllocEvent;
|
|
begin
|
|
result:=FindAddress(@HeapBaselevel, strToInt('$'+edit1.Text));
|
|
|
|
if result<>nil then
|
|
begin
|
|
lblHeapHandle.caption:=inttohex(ptrUint(result.HookEvent.HeapAllocEvent.HeapHandle),8);
|
|
lblflags.Caption:=heapflagstostring(result.HookEvent.HeapAllocEvent.Flags);
|
|
lblbaseaddress.caption:=inttohex(ptrUint(result.HookEvent.HeapAllocEvent.address),8);
|
|
lblsize.Caption:=inttostr(result.HookEvent.HeapAllocEvent.Size);
|
|
if lblErr.Visible then
|
|
lblErr.Visible:=false;
|
|
end else
|
|
begin
|
|
lblHeapHandle.caption:='-';
|
|
lblflags.Caption:='-';
|
|
|
|
|
|
if FindAddress(@AllocBaseLevel,strtoint('$'+edit1.Text))<>nil then
|
|
begin
|
|
lblbaseaddress.caption:=inttohex(ptrUint(result.HookEvent.HeapAllocEvent.address),8);
|
|
lblsize.Caption:=inttostr(result.HookEvent.HeapAllocEvent.Size);
|
|
lblErr.Visible:=true;
|
|
end
|
|
else
|
|
begin
|
|
lblbaseaddress.caption:='-';
|
|
lblsize.Caption:='-';
|
|
if lblErr.Visible then
|
|
lblErr.Visible:=false;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
type TMemrectablearraylist = array [0..15] of record
|
|
arr:PMemrectablearray;
|
|
entrynr: integer;
|
|
end;
|
|
|
|
function findMaxOfPath(a: PMemrectablearray; level: integer; maxlevel: integer):TMemoryAllocEvent;
|
|
var
|
|
i: integer;
|
|
begin
|
|
result:=nil;
|
|
if level=maxlevel then
|
|
begin
|
|
for i:=15 downto 0 do
|
|
begin
|
|
if a[i].memallocevent<>nil then
|
|
begin
|
|
result:=a[i].memallocevent;
|
|
exit;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
for i:=15 downto 0 do
|
|
begin
|
|
if a[i].MemrecArray<>nil then
|
|
begin
|
|
result:=findMaxOfPath(a[i].MemrecArray,level+1, maxlevel);
|
|
if result<>nil then exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function findprevious(lvl: TMemrectablearraylist; level: integer; maxlevel: integer):TMemoryAllocEvent;
|
|
var
|
|
i: integer;
|
|
currentarray: PMemrectablearray;
|
|
begin
|
|
result:=nil;
|
|
if level=maxlevel then
|
|
begin
|
|
currentarray:=lvl[level].arr;
|
|
for i:=lvl[level].entrynr-1 downto 0 do
|
|
begin
|
|
if currentarray[i].memallocevent<>nil then
|
|
begin
|
|
result:=currentarray[i].memallocevent;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
end
|
|
else
|
|
begin
|
|
currentarray:=lvl[level].arr;
|
|
for i:=lvl[level].entrynr-1 downto 0 do
|
|
begin
|
|
if currentarray[i].MemrecArray<>nil then
|
|
begin
|
|
result:=findMaxOfPath(currentarray[i].MemrecArray,level+1, maxlevel);
|
|
if result<>nil then exit;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
//still here
|
|
if level>0 then
|
|
begin
|
|
lvl[level].entrynr:=$f;
|
|
result:=findprevious(lvl,level-1, maxlevel);
|
|
end;
|
|
end;
|
|
|
|
function TfrmMemoryAllocHandler.WaitForInitializationToFinish: boolean;
|
|
begin
|
|
{$IFDEF windows}
|
|
result:=WaitForSingleObject(CEInitializationFinished,5000)=WAIT_OBJECT_0;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TfrmMemoryAllocHandler.FindAddress(addresslist: PMemrecTableArray; address: ptrUint): TMemoryAllocEvent;
|
|
//only call this when displaythread is suspended
|
|
var
|
|
level: integer;
|
|
entrynr: integer;
|
|
currentarray: PMemrectablearray;
|
|
|
|
lvl: TMemrectablearraylist;
|
|
i: integer;
|
|
begin
|
|
zeromemory(@lvl,sizeof(TMemrectablearraylist));
|
|
|
|
result:=nil;
|
|
|
|
//memrecCS.Enter;
|
|
// try
|
|
level:=0;
|
|
currentarray:=addresslist;
|
|
|
|
while level<maxlevel do
|
|
begin
|
|
entrynr:=address shr ((7-level)*4) and $f;
|
|
lvl[level].arr:=currentarray;
|
|
lvl[level].entrynr:=entrynr;
|
|
|
|
if currentarray[entrynr].MemrecArray=nil then
|
|
begin
|
|
|
|
|
|
//not a direct match (this will happen almost every time)
|
|
//try to find a previous entry
|
|
result:=findprevious(lvl, level, maxlevel);
|
|
if (result<>nil) and (not InRangeX(address,result.BaseAddress,result.BaseAddress+result.HookEvent.HeapAllocEvent.Size)) then
|
|
result:=nil;
|
|
|
|
exit;
|
|
end;
|
|
|
|
currentarray:=currentarray[entrynr].MemrecArray;
|
|
|
|
inc(level);
|
|
end;
|
|
|
|
entrynr:=address shr ((maxlevel-level)*4) and $f;
|
|
lvl[level].arr:=currentarray;
|
|
lvl[level].entrynr:=entrynr;
|
|
|
|
if currentarray[entrynr].memallocevent<>nil then
|
|
result:=currentarray[entrynr].memallocevent
|
|
else
|
|
begin
|
|
//not a direct match
|
|
//try to find a previous entry
|
|
result:=findprevious(lvl,level, maxlevel);
|
|
end;
|
|
|
|
|
|
if (result<>nil) and (not InRangeX(address,result.BaseAddress,result.BaseAddress+result.HookEvent.HeapAllocEvent.Size)) then
|
|
result:=nil;
|
|
{
|
|
finally
|
|
//memrecCS.Leave;
|
|
end;}
|
|
end;
|
|
|
|
procedure DeletePath(addresslist: PMemrecTableArray; level: integer; maxlevel: integer);
|
|
var
|
|
i: integer;
|
|
begin
|
|
if level=maxlevel then
|
|
begin
|
|
for i:=0 to 15 do
|
|
begin
|
|
if addresslist[i].memallocevent<>nil then
|
|
freeandnil(addresslist[i].memallocevent);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
for i:=0 to 15 do
|
|
begin
|
|
if addresslist[i].MemrecArray<>nil then
|
|
begin
|
|
deletepath(addresslist[i].MemrecArray,level+1, maxlevel);
|
|
freememandnil(addresslist[i].MemrecArray);
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
end;
|
|
|
|
procedure TfrmMemoryAllocHandler.btnReloadClick(Sender: TObject);
|
|
var
|
|
injectionscript: TStringlist;
|
|
i: integer;
|
|
begin
|
|
{$IFDEF windows}
|
|
WaitForInitializationToFinish;
|
|
|
|
injectionscript:=TStringList.Create;
|
|
memrecCS.Enter;
|
|
displaythread.csObjectList.Enter;
|
|
try
|
|
//whipe the old list
|
|
DeletePath(@HeapBaselevel, 0, maxlevel);
|
|
displaythread.heapcount:=0;
|
|
|
|
|
|
injectionscript.Add('CreateThread(CeInitializeAllocHook)');
|
|
ResetEvent(CEInitializationFinished);
|
|
if not autoassemble(injectionscript, false) then raise exception.Create(rsFailureToInitialize);
|
|
finally
|
|
displaythread.csObjectList.Leave;
|
|
memrecCS.Leave;
|
|
injectionscript.Free;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
end;
|
|
|
|
procedure TfrmMemoryAllocHandler.cbHookAllocsClick(Sender: TObject);
|
|
begin
|
|
{$ifdef windows}
|
|
if cbHookAllocs.Checked then
|
|
begin
|
|
|
|
if hookscript=nil then
|
|
hookscript:=tstringlist.Create;
|
|
|
|
hookscript.Clear;
|
|
hookscript.Add('[Enable]');
|
|
hookscript.Add('');
|
|
hookscript.Add('[Disable]');
|
|
hookscript.Add('');
|
|
|
|
|
|
//hook apis
|
|
|
|
generateAPIHookScript(hookscript,'NtAllocateVirtualMemory','CeAllocateVirtualMemory', 'NtAllocateVirtualMemoryOrig','0');
|
|
generateAPIHookScript(hookscript,'NtFreeVirtualMemory','CeFreeVirtualMemory', 'NtFreeVirtualMemoryOrig','1');
|
|
generateAPIHookScript(hookscript,'RtlAllocateHeap','CeRtlAllocateHeap', 'RtlAllocateHeapOrig','2');
|
|
generateAPIHookScript(hookscript,'RtlFreeHeap','CeRtlFreeHeap', 'RtlFreeHeapOrig','3');
|
|
generateAPIHookScript(hookscript,'RtlDestroyHeap','CeRtlDestroyHeap', 'RtlDestroyHeapOrig','4');
|
|
|
|
if not autoassemble(hookscript,false,true,false,false,disableinfo) then raise exception.Create(rsFailureToHook);
|
|
end
|
|
else
|
|
begin
|
|
//unload
|
|
if hookscript=nil then exit; //should never happen
|
|
|
|
if not autoassemble(hookscript,false,false,false,false,disableinfo) then raise exception.Create(rsFailureToHook);
|
|
|
|
freeandnil(hookscript);
|
|
end;
|
|
{$endif}
|
|
|
|
|
|
|
|
end;
|
|
|
|
initialization
|
|
{$i frmMemoryAllocHandlerUnit.lrs}
|
|
|
|
end.
|
|
|