cheat-engine/Cheat Engine/frmCodecaveScannerUnit.pas
2020-12-10 23:39:40 +01:00

384 lines
10 KiB
ObjectPascal
Executable file

unit frmCodecaveScannerUnit;
{$MODE Delphi}
interface
uses
{$ifdef darwin}
macport,
{$endif}
{$ifdef windows}
jwawindows, windows,
{$endif}
LCLIntf, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls,{$ifndef net}NewKernelHandler,{$endif}CEFuncProc,
ExtCtrls, Menus, Clipbrd, LResources, commonTypeDefs, symbolhandler, betterControls;
type TCodeCaveScanner=class(tthread)
private
found:qword;
progress:integer;
curraddr:qword;
procedure updatelabel;
procedure updateprogressbar;
procedure done;
procedure foundone;
public
startaddress:ptrUint;
stopaddress:ptrUint;
size:qword;
alsonx:boolean;
procedure execute; override;
end;
type
{ TfrmCodecaveScanner }
TfrmCodecaveScanner = class(TForm)
btnStart: TButton;
editSize: TEdit;
editStart: TEdit;
editStop: TEdit;
sfcImageList: TImageList;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
lbCodecaveList: TListBox;
Panel1: TPanel;
Panel2: TPanel;
cbNoExecute: TCheckBox;
Panel3: TPanel;
ProgressBar1: TProgressBar;
PopupMenu1: TPopupMenu;
Copytoclipboard1: TMenuItem;
procedure btnStartClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormShow(Sender: TObject);
procedure lbCodecaveListDblClick(Sender: TObject);
procedure Copytoclipboard1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
codecavescanner: TCodecavescanner;
end;
var
frmCodecaveScanner: TfrmCodecaveScanner;
implementation
uses MainUnit2, MemoryBrowserFormUnit, ProcessHandlerUnit, Globals, Parsers,
DPIHelper, Math;
resourcestring
rsPleaseProvideAValidStartAddress = 'Please provide a valid start address';
rsPleaseProvideAValidStopAddress = 'Please provide a valid stop address';
rsPleaseTellMeYouDonTNeedACodeCaveThisSmall = 'Please tell me you don''t need a code cave this small!!!';
rsPleaseProvideAValidSizeForTheWantedCodeCave = 'Please provide a valid size for the wanted code cave';
rsClosingThisWindowWillAlsoStopTheScannerAreYouSure = 'Closing this window will also stop the scanner. Are you sure?';
procedure TCodecavescanner.updatelabel;
begin
if frmcodecavescanner<>nil then
frmcodecavescanner.Label4.Caption:=inttohex(curraddr,8);
end;
procedure TCodecavescanner.updateprogressbar;
begin
if frmcodecavescanner<>nil then
frmcodecavescanner.ProgressBar1.Position:=progress;
end;
procedure TCodecavescanner.foundone;
begin
if frmCodecaveScanner<>nil then
begin
frmCodecaveScanner.lbCodecaveList.Items.Add(IntToHex(found,8));
if frmCodecaveScanner.lbCodecaveList.Items.Count>30000 then terminate; //too many found
end;
end;
procedure TCodecavescanner.done;
begin
if frmCodecaveScanner<>nil then
begin
frmCodecaveScanner.btnStart.caption:=strStart;
frmCodecaveScanner.codecavescanner:=nil;
frmCodecaveScanner.progressbar1.Position:=0;
frmCodecaveScanner.Label4.Caption:='';
end;
end;
procedure TCodecavescanner.execute;
var mbi: _MEMORY_BASIC_INFORMATION;
currentpos: ptrUint;
a: boolean;
i,j: integer;
x:ptrUint;
memoryregion:array of tmemoryregion;
currentbyte: byte;
bytecount:dword;
buf: array of byte;
begin
//not going to add much optimization to the scanroutines here
freeonterminate:=true;
try
currentpos:=startaddress;
setlength(buf,buffersize);
while (not terminated) and (currentpos<stopaddress) do
begin
progress:=trunc(currentpos/stopaddress*1000);
curraddr:=currentpos;
synchronize(updateprogressbar);
synchronize(updatelabel);
//find the memoryranges to scan
virtualqueryEx(processhandle,pointer(currentpos),mbi,sizeof(mbi));
if alsonx then
a:=(mbi.Protect and (PAGE_EXECUTE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY or PAGE_READONLY or PAGE_READWRITE))>0
else
a:=(mbi.Protect and (PAGE_EXECUTE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY))>0;
if not a then
begin
inc(currentpos,mbi.RegionSize);
continue;
end;
//now scan the memory in chunks of "buffersize"
x:=mbi.RegionSize;
setlength(memoryregion,1+(x div buffersize));
i:=0;
while x>0 do
begin
memoryregion[i].BaseAddress:=ptrUint(mbi.BaseAddress)+i*buffersize;
if x<buffersize then
memoryregion[i].MemorySize:=x
else
memoryregion[i].MemorySize:=buffersize;
if i>0 then memoryregion[i].IsChild:=true;
dec(x,memoryregion[i].MemorySize);
inc(i);
end;
//stop the
//now scan the memory
currentbyte:=0;
bytecount:=0;
i:=0;
while (not terminated) and (i<length(memoryregion)) do
begin
progress:=trunc(memoryregion[i].BaseAddress/stopaddress*1000);
curraddr:=memoryregion[i].BaseAddress;
synchronize(updateprogressbar);
synchronize(updatelabel);
//read the mem
if ReadProcessmemory(processhandle,pointer(memoryregion[i].BaseAddress),@buf[0],memoryregion[i].MemorySize,x) then
begin
//everything ok
for j:=0 to memoryregion[i].MemorySize-1 do
begin
if buf[j]=currentbyte then
inc(bytecount)
else
begin
currentbyte:=buf[j];
bytecount:=1;
end;
if bytecount=size then //found one
begin
found:=memoryregion[i].BaseAddress+j-bytecount+1;
synchronize(foundone);
end;
end;
end;
inc(i);
end;
inc(currentpos,mbi.RegionSize);
end;
finally
synchronize(done);
end;
end;
procedure TfrmCodecaveScanner.btnStartClick(Sender: TObject);
var startaddress,stopaddress:ptrUint;
bytelength: Qword;
begin
{
start the thread that scans the memory for a array of the same bytes in read-
only memory
}
if codecavescanner=nil then
begin
try
startaddress:=StrToQWordEx('$'+editstart.text);
except
startaddress:=symhandler.getAddressFromName(editstart.text);
end;
try
stopaddress:=StrToQWordEx('$'+editstop.text);
except
stopaddress:=symhandler.getAddressFromName(editstop.text);
end;
try
bytelength:=StrToQWordEx('$'+editsize.Text);
if bytelength<3 then raise exception.Create(rsPleaseTellMeYouDonTNeedACodeCaveThisSmall);
except
raise exception.Create(rsPleaseProvideAValidSizeForTheWantedCodeCave);
end;
if startaddress>stopaddress then
begin //xor swap
startaddress:=startaddress xor stopaddress;
stopaddress:=stopaddress xor startaddress;
startaddress:=startaddress xor stopaddress;
end;
codecavescanner:=TCodecavescanner.create(true);
codecavescanner.startaddress:=startaddress;
codecavescanner.stopaddress:=stopaddress;
codecavescanner.size:=bytelength;
//execute protection on by default on 64bit targets
if not processhandler.is64bit then
codecavescanner.AlsoNX:=cbnoexecute.checked
else
codecavescanner.AlsoNX:=false;
progressbar1.Min:=0;
progressbar1.Max:=1000;
progressbar1.Position:=0;
btnStart.caption:=strStop;
lbCodecaveList.Clear;
codecavescanner.start;
end else
begin
codecavescanner.terminate;
codecavescanner:=nil;
progressbar1.Position:=0;
btnstart.Caption:=strStart;
end;
end;
procedure TfrmCodecaveScanner.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
if codecavescanner<>nil then
codecavescanner.Terminate;
codecavescanner:=nil;
end;
procedure TfrmCodecaveScanner.FormCloseQuery(Sender: TObject;
var CanClose: Boolean);
begin
if codecavescanner<>nil then
canclose:=messagedlg(rsClosingThisWindowWillAlsoStopTheScannerAreYouSure, mtconfirmation, [mbyes, mbno], 0)=mryes;
end;
procedure TfrmCodecaveScanner.FormShow(Sender: TObject);
var
fh: integer;
b: TBitmap;
preferedwidth: integer;
begin
fh:=GetFontData(font.reference.Handle).Height;
editstart.font.height:=fh;
editstop.font.height:=fh;
editsize.font.height:=fh;
//execute protection on by default on 64bit targets
if processhandler.is64bit then
begin
//init just once if needed
if (editstop.Text = '') or (editstart.Text = '') then // if not initialized
begin
editstop.text:='7FFFFFFFFFFFFFFF';
editstart.Text:='0000000000000000';
end;
cbNoExecute.checked:=false;
cbNoExecute.Enabled:=false;
cbNoExecute.visible:=false;
end
else
begin
//init just once if needed
if (editstop.Text = '') or (editstart.Text = '') then // if not initialized
begin
editstop.text:='7FFFFFFF';
editstart.Text:='00000000';
end;
end;
b:=tbitmap.Create;
b.canvas.Font:=editstart.font;
editStart.Constraints.MinWidth:=dpihelper.GetEditBoxMargins(editstart)+canvas.GetTextWidth(' XXXXXXXXXXXXXXXX ');
editStop.Constraints.MinWidth:= editStart.Constraints.MinWidth;
editStart.Width:=editStart.Constraints.MinWidth;
editStop.Width:=editStart.Constraints.MinWidth;
editSize.Constraints.MinWidth:=dpihelper.GetEditBoxMargins(editstart)+canvas.GetTextWidth(' XXXXX ');
editSize.Width:=editSize.Constraints.MinWidth;
progressbar1.height:=ceil(progressbar1.height*dpihelper.getDPIScaleFactor);
DoAutoSize;
autosize:=false;
preferedwidth:=canvas.GetTextWidth('XXXXXXXXXXXXXX - XXXXXX')+panel1.width;
if clientwidth<preferedwidth then
clientwidth:=preferedwidth;
end;
procedure TfrmCodecaveScanner.lbCodecaveListDblClick(Sender: TObject);
begin
if lbCodecaveList.ItemIndex<>-1 then
memorybrowser.memoryaddress:=StrToQwordEx('$'+lbCodecaveList.Items[lbCodecaveList.itemindex]);
end;
procedure TfrmCodecaveScanner.Copytoclipboard1Click(Sender: TObject);
var i: integer;
s: string;
begin
S:='';
for i:=0 to lbCodecaveList.count-1 do
if lbCodecaveList.Selected[i] then
s:=s+lbCodecaveList.Items[i]+#13#10;
if s<>'' then
begin
s:=copy(s,1,length(s)-2);
Clipboard.SetTextBuf(pchar(s));
end;
end;
initialization
{$i frmCodecaveScannerUnit.lrs}
end.