cheat-engine/Cheat Engine/frmautoinjectunit.pas

2703 lines
80 KiB
ObjectPascal

unit frmautoinjectunit;
{$MODE Delphi}
interface
uses
windows, LCLIntf, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, Menus, CEFuncProc, StrUtils, types, ComCtrls, LResources,
NewKernelHandler, SynEdit, SynHighlighterCpp, SynHighlighterAA, LuaSyntax, disassembler,
MainUnit2, Assemblerunit, autoassembler, symbolhandler, SynEditSearch,
MemoryRecordUnit, tablist, customtypehandler, registry, SynGutterBase, SynEditMarks,
luahandler, memscan, foundlisthelper, ProcessHandlerUnit, commonTypeDefs;
type TCallbackRoutine=procedure(memrec: TMemoryRecord; script: string; changed: boolean) of object;
type TCustomCallbackRoutine=procedure(ct: TCustomType; script:string; changed: boolean; lua: boolean) of object;
type TScripts=array of record
script: string;
filename: string;
undoscripts: array [0..4] of record
oldscript: string;
startpos: integer;
end;
currentundo: integer;
end;
type TBooleanArray = Array of Boolean;
{
The TDisassemblyLine originates from jgoemat ( http://forum.cheatengine.org/viewtopic.php?t=566415 )
Originally it was just an Object but I changed it to a TObject because I think a
standalone TDisassembler object might be more efficient reducing the amount of
string parsing
}
type TDisassemblyLine = class(TObject)
Address: ptrUint; // actual address value
AddressString: String; // module+offset if specified
Comment: String; // comment part (second parameter of disassembly)
OriginalHexBytes : String; // original hex from disassembly (grouped)
Code: String; // code portion of disassembly
Size: Integer; // number of bytes for this instruction
Disassembler: TDisassembler; // The disassembler used to disassemble (free by caller)
procedure Init(_address: ptrUint; _mi: TModuleInfo);
procedure Shorten(_newsize: Integer); // if we overran our injection point, change to 'db'
function IsStarter : Boolean;
function IsEnder : Boolean;
function IsValid : Boolean;
function GetHexBytes : String; // hex bytes with spaces between each byte
function GetMaskFlags : TBooleanArray;
constructor create;
destructor destroy; override;
end;
type TAOBFind = Object
Address: ptrUint; // address where AOB was found
CodeSize: Integer; // size of code we will always use
Size: Integer;
Bytes: Array of Byte; // bytes we'll read from memory
procedure Init(_address: ptrUint; _codesize: Integer);
function IsMatch(var maskBytes: Array Of Byte; var maskFlags : TBooleanArray; startIndex, endIndex: Integer): Boolean;
end;
type
{ TfrmAutoInject }
TfrmAutoInject = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
menuAOBInjection: TMenuItem;
menuFullInjection: TMenuItem;
mifindNext: TMenuItem;
miCallLua: TMenuItem;
miNewWindow: TMenuItem;
Panel1: TPanel;
Button1: TButton;
Load1: TMenuItem;
Panel2: TPanel;
Save1: TMenuItem;
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
Exit1: TMenuItem;
Assigntocurrentcheattable1: TMenuItem;
emplate1: TMenuItem;
Codeinjection1: TMenuItem;
CheatTablecompliantcodee1: TMenuItem;
APIHook1: TMenuItem;
SaveAs1: TMenuItem;
PopupMenu1: TPopupMenu;
Coderelocation1: TMenuItem;
New1: TMenuItem;
N2: TMenuItem;
Syntaxhighlighting1: TMenuItem;
closemenu: TPopupMenu;
Close1: TMenuItem;
Inject1: TMenuItem;
Injectincurrentprocess1: TMenuItem;
Injectintocurrentprocessandexecute1: TMenuItem;
Find1: TMenuItem;
Paste1: TMenuItem;
Copy1: TMenuItem;
Cut1: TMenuItem;
Undo1: TMenuItem;
N6: TMenuItem;
FindDialog1: TFindDialog;
undotimer: TTimer;
View1: TMenuItem;
AAPref1: TMenuItem;
procedure Button1Click(Sender: TObject);
procedure Load1Click(Sender: TObject);
procedure menuAOBInjectionClick(Sender: TObject);
procedure menuFullInjectionClick(Sender: TObject);
procedure mifindNextClick(Sender: TObject);
procedure miCallLuaClick(Sender: TObject);
procedure miNewWindowClick(Sender: TObject);
procedure Save1Click(Sender: TObject);
procedure Exit1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Codeinjection1Click(Sender: TObject);
procedure Panel1Resize(Sender: TObject);
procedure CheatTablecompliantcodee1Click(Sender: TObject);
procedure Assigntocurrentcheattable1Click(Sender: TObject);
procedure APIHook1Click(Sender: TObject);
procedure SaveAs1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure assemblescreenKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure Coderelocation1Click(Sender: TObject);
procedure New1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure TabControl1Change(Sender: TObject);
procedure Syntaxhighlighting1Click(Sender: TObject);
procedure TabControl1ContextPopup(Sender: TObject; MousePos: TPoint;
var Handled: Boolean);
procedure Close1Click(Sender: TObject);
procedure Injectincurrentprocess1Click(Sender: TObject);
procedure Injectintocurrentprocessandexecute1Click(Sender: TObject);
procedure Cut1Click(Sender: TObject);
procedure Copy1Click(Sender: TObject);
procedure Paste1Click(Sender: TObject);
procedure Find1Click(Sender: TObject);
procedure FindDialog1Find(Sender: TObject);
procedure AAPref1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Undo1Click(Sender: TObject);
private
{ Private declarations }
AAHighlighter: TSynAASyn;
CPPHighlighter: TSynCppSyn;
LuaHighlighter: TSynLuaSyn;
assembleSearch: TSynEditSearch;
oldtabindex: integer;
scripts: TScripts;
selectedtab: integer;
fluamode: boolean;
fCustomTypeScript: boolean;
procedure setluamode(state: boolean);
procedure injectscript(createthread: boolean);
procedure tlistOnTabChange(sender: TObject; oldselection: integer);
procedure setCustomTypeScript(x: boolean);
procedure gutterclick(Sender: TObject; X, Y, Line: integer; mark: TSynEditMark);
procedure assemblescreenchange(sender: TObject);
function GetUniqueAOB(mi: TModuleInfo; address: ptrUint; codesize: Integer; var resultOffset: Integer) : string;
public
{ Public declarations }
assemblescreen: TSynEdit;
tlist: TTablist;
editscript: boolean;
editscript2: boolean;
memrec: TMemoryRecord;
customtype: TCustomType;
callbackroutine: TCallbackroutine;
CustomTypeCallback: TCustomCallbackroutine;
injectintomyself: boolean;
property luamode: boolean read fluamode write setluamode;
property CustomTypeScript: boolean read fCustomTypeScript write setCustomTypeScript;
end;
procedure Getjumpandoverwrittenbytes(address,addressto: ptrUINT; jumppart,originalcodepart: tstrings);
procedure generateAPIHookScript(script: tstrings; address: string; addresstogoto: string; addresstostoreneworiginalfunction: string=''; nameextension:string='0');
implementation
uses frmAAEditPrefsUnit,MainUnit,memorybrowserformunit,APIhooktemplatesettingsfrm;
resourcestring
rsExecuteScript = 'Execute script';
rsLuaFilter = 'LUA Script (*.LUA)|*.LUA|All Files ( *.* )|*.*';
rsLUAScript = 'LUA Script';
rsWriteCode = 'Write code';
rsCEAFilter = 'Cheat Engine Assembly (*.CEA)|*.CEA|All Files ( *.* )|*.*';
rsAutoAssembler = 'Auto assembler';
rsCodeNeedsEnableAndDisable = 'The code needs an [ENABLE] and a [DISABLE] section if you want to use this script as a table entry';
rsNotAllCodeIsInjectable = 'Not all code is injectable.'#13#10'%s'#13#10'Are you sure you wan''t to edit it to this?';
rsCodeInjectTemplate = 'Code inject template';
rsOnWhatAddressDoYouWantTheJump = 'On what address do you want the jump?';
rsFailedToAddToTableNotAllCodeIsInjectable = 'Failed to add to table. Not all code is injectable';
rsStartAddress = 'Start address';
rsCodeRelocationTemplate = 'Code relocation template';
rsEndAddressLastBytesAreIncludedIfNecesary = 'End address (last bytes are included if necessary)';
rsAreYouSureYouWantToClose = 'Are you sure you want to close %s ?';
rsWhatIdentifierDoYouWantToUse = 'What do you want to name the symbol for the injection point?';
procedure TfrmAutoInject.setCustomTypeScript(x: boolean);
begin
fCustomTypeScript:=x;
if x then
editscript:=true;
end;
procedure TfrmAutoInject.setluamode(state: boolean);
begin
fluamode:=state;
if state then
begin
assemblescreen.Highlighter:=LuaHighlighter;
//change gui to lua style
button1.Caption:=rsExecuteScript;
opendialog1.DefaultExt:='LUA';
opendialog1.Filter:=rsLuaFilter;
savedialog1.DefaultExt:='LUA';
savedialog1.Filter:=rsLuaFilter;
Assigntocurrentcheattable1.visible:=false;
emplate1.Visible:=false;
caption:=rsLUAScript;
// inject1.Visible:=true;
helpcontext:=19; //c-script help
end
else
begin
assemblescreen.Highlighter:=AAHighlighter;
//change gui to autoassembler style
button1.caption:=rsWriteCode;
opendialog1.DefaultExt:='CEA';
opendialog1.Filter:=rsCEAFilter;
savedialog1.DefaultExt:='CEA';
savedialog1.Filter:=rsCEAFilter;
Assigntocurrentcheattable1.Visible:=true;
emplate1.Visible:=true;
caption:=rsAutoAssembler;
inject1.Visible:=false;
helpcontext:=18; //auto asm help
end;
end;
procedure TfrmAutoInject.Button1Click(Sender: TObject);
var
a,b: integer;
aa: TCEAllocArray;
//variables for injectintomyself:
check: boolean;
registeredsymbols: TStringlist;
errmsg: string;
begin
{$ifndef standalonetrainerwithassembler}
registeredsymbols:=tstringlist.Create;
registeredsymbols.CaseSensitive:=false;
registeredsymbols.Duplicates:=dupIgnore;
if luamode then
begin
//execute
LUA_DoScript(assemblescreen.Text);
modalresult:=mrok; //not modal anymore, but can still be used to pass info
if editscript2 or CustomTypeScript then close;
end
else
begin
if editscript then
begin
//check if both scripts are valid before allowing the edit
setlength(aa,1);
getenableanddisablepos(assemblescreen.Lines,a,b);
if not CustomTypeScript then
if (a=-1) and (b=-1) then raise exception.create(rsCodeNeedsEnableAndDisable);
try
check:=autoassemble(assemblescreen.lines,false,true,true,injectintomyself,aa,registeredsymbols) and
autoassemble(assemblescreen.lines,false,false,true,injectintomyself,aa,registeredsymbols);
if not check then
errmsg:=format(rsNotAllCodeIsInjectable,['']);
except
on e: exception do
begin
check:=false;
errmsg:=format(rsNotAllCodeIsInjectable,['('+e.Message+')']);
end;
end;
if check then
begin
modalresult:=mrok; //not modal anymore, but can still be used to pass info
if editscript2 or CustomTypeScript then close; //can only be used when not modal
end
else
begin
if messagedlg(errmsg, mtWarning, [mbyes, mbno], 0)=mryes then
begin
modalresult:=mrok; //not modal anymore, but can still be used to pass info
if editscript2 or CustomTypeScript then close;
end;
end;
end else autoassemble(assemblescreen.lines,true);
end;
registeredsymbols.free;
{$endif}
end;
procedure TfrmAutoInject.Load1Click(Sender: TObject);
begin
{$ifndef standalonetrainerwithassembler}
if opendialog1.Execute then
begin
assemblescreen.Lines.Clear;
assemblescreen.Lines.LoadFromFile(opendialog1.filename);
savedialog1.FileName:=opendialog1.filename;
assemblescreen.AfterLoadFromFile;
end;
{$endif}
end;
procedure TfrmAutoInject.mifindNextClick(Sender: TObject);
begin
finddialog1.OnFind(finddialog1);
end;
procedure TfrmAutoInject.miNewWindowClick(Sender: TObject);
var f: TfrmAutoInject;
begin
f:=TfrmAutoInject.Create(application);
f.luamode:=luamode;
f.show;
end;
procedure TfrmAutoInject.Save1Click(Sender: TObject);
var f: tfilestream;
s: string;
begin
if (savedialog1.filename='') and (not savedialog1.Execute) then exit; //filename was empty and the user clicked cancel
f:=tfilestream.Create(savedialog1.filename,fmcreate);
try
s:=assemblescreen.text;
f.Write(s[1],length(assemblescreen.text));
assemblescreen.MarkTextAsSaved;
finally
f.Free;
end;
end;
procedure TfrmAutoInject.Exit1Click(Sender: TObject);
begin
close;
end;
procedure TfrmAutoInject.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
{$ifndef standalonetrainerwithassembler}
if not editscript then
begin
if self<>MainForm.frmLuaTableScript then //don't free the lua table script
action:=cafree;
end
else
begin
try
if editscript2 then
begin
//call finish routine with script
if modalresult=mrok then
callbackroutine(memrec, assemblescreen.text,true)
else
callbackroutine(memrec, assemblescreen.text,false);
action:=cafree;
end
else
if CustomTypeScript then
begin
if modalresult=mrok then
CustomTypeCallback(customtype, assemblescreen.text,true,luamode)
else
CustomTypeCallback(customtype, assemblescreen.text,false,luamode);
action:=cafree;
end;
except
on e: exception do
begin
modalresult:=mrNone;
raise exception.create(e.message);
end;
end;
end;
{$endif}
end;
procedure TfrmAutoInject.Codeinjection1Click(Sender: TObject);
function inttostr(i:int64):string;
begin
if i=0 then result:='' else result:=sysutils.IntToStr(i);
end;
var address: string;
originalcode: array of string;
originalbytes: array of byte;
codesize: integer;
a: ptrUint;
br: ptruint;
c: ptrUint;
x: string;
i,j,k: integer;
injectnr: integer;
enablepos: integer;
disablepos: integer;
enablecode: tstringlist;
disablecode: tstringlist;
mi: TModuleInfo;
begin
{$ifndef standalonetrainerwithassembler}
if parent is TMemoryBrowser then
a:=TMemoryBrowser(parent).disassemblerview.SelectedAddress
else
a:=memorybrowser.disassemblerview.SelectedAddress;
if symhandler.getmodulebyaddress(a,mi) then
address:='"'+mi.modulename+'"+'+inttohex(a-mi.baseaddress,1)
else
address:=symhandler.getNameFromAddress(a);
if inputquery(rsCodeInjectTemplate, rsOnWhatAddressDoYouWantTheJump, address) then
begin
try
a:=StrToQWordEx('$'+address);
except
a:=symhandler.getaddressfromname(address);
end;
c:=a;
injectnr:=0;
for i:=0 to assemblescreen.Lines.Count-1 do
begin
j:=pos('alloc(newmem',lowercase(assemblescreen.lines[i]));
if j<>0 then
begin
x:=copy(assemblescreen.Lines[i],j+12,length(assemblescreen.Lines[i]));
x:=copy(x,1,pos(',',x)-1);
try
k:=strtoint(x);
if injectnr<=k then
injectnr:=k+1;
except
inc(injectnr);
end;
end;
end;
//disassemble the old code
setlength(originalcode,0);
codesize:=0;
while codesize<5 do
begin
setlength(originalcode,length(originalcode)+1);
originalcode[length(originalcode)-1]:=disassemble(c,x);
i:=posex('-',originalcode[length(originalcode)-1]);
i:=posex('-',originalcode[length(originalcode)-1],i+1);
originalcode[length(originalcode)-1]:=copy(originalcode[length(originalcode)-1],i+2,length(originalcode[length(originalcode)-1]));
codesize:=c-a;
end;
setlength(originalbytes,codesize);
ReadProcessMemory(processhandle, pointer(a), @originalbytes[0], codesize, br);
enablecode:=tstringlist.Create;
disablecode:=tstringlist.Create;
try
with enablecode do
begin
if processhandler.is64bit then
add('alloc(newmem'+inttostr(injectnr)+',2048,'+address+') ')
else
add('alloc(newmem'+inttostr(injectnr)+',2048)');
add('label(returnhere'+inttostr(injectnr)+')');
add('label(originalcode'+inttostr(injectnr)+')');
add('label(exit'+inttostr(injectnr)+')');
add('');
add('newmem'+inttostr(injectnr)+': //this is allocated memory, you have read,write,execute access');
add('//place your code here');
add('');
add('originalcode'+inttostr(injectnr)+':');
for i:=0 to length(originalcode)-1 do
add(originalcode[i]);
add('');
add('exit'+inttostr(injectnr)+':');
add('jmp returnhere'+inttostr(injectnr)+'');
add('');
add(address+':');
add('jmp newmem'+inttostr(injectnr)+'');
while codesize>5 do
begin
add('nop');
dec(codesize);
end;
add('returnhere'+inttostr(injectnr)+':');
add('');
end;
with disablecode do
begin
add('dealloc(newmem'+inttostr(injectnr)+')');
add(address+':');
for i:=0 to length(originalcode)-1 do
add(originalcode[i]);
x:='db';
for i:=0 to length(originalbytes)-1 do
x:=x+' '+inttohex(originalbytes[i],2);
add('//Alt: '+x);
end;
getenableanddisablepos(assemblescreen.lines,enablepos,disablepos);
//skip first comment(s)
if enablepos>=0 then
begin
while enablepos<assemblescreen.lines.Count-1 do
begin
if pos('//',trim(assemblescreen.Lines[enablepos+1]))=1 then inc(enablepos) else break;
end;
end;
for i:=enablecode.Count-1 downto 0 do
assemblescreen.Lines.Insert(enablepos+1,enablecode[i]);
getenableanddisablepos(assemblescreen.lines,enablepos,disablepos);
//skip first comment(s)
if disablepos>=0 then
begin
while disablepos<assemblescreen.lines.Count-1 do
begin
if pos('//',trim(assemblescreen.Lines[disablepos+1]))=1 then inc(enablepos) else break;
inc(disablepos);
end;
//only if there actually is a disable section place this code
for i:=disablecode.Count-1 downto 0 do
assemblescreen.Lines.Insert(disablepos+1,disablecode[i]);
end;
finally
enablecode.free;
disablecode.Free;
end;
end;
{$endif}
end;
procedure TfrmAutoInject.Panel1Resize(Sender: TObject);
begin
button1.Left:=panel1.Width div 2-button1.Width div 2;
end;
procedure TfrmAutoInject.CheatTablecompliantcodee1Click(Sender: TObject);
var e,d: integer;
begin
{$ifndef standalonetrainerwithassembler}
getenableanddisablepos(assemblescreen.lines,e,d);
if e=-1 then //-2 is 2 or more, so bugged, and >=0 is has one
begin
assemblescreen.Lines.Insert(0,'[ENABLE]');
assemblescreen.Lines.Insert(1,'//code from here to ''[DISABLE]'' will be used to enable the cheat');
assemblescreen.Lines.Insert(2,'');
end;
if d=-1 then
begin
assemblescreen.Lines.Add(' ');
assemblescreen.Lines.Add(' ');
assemblescreen.Lines.Add('[DISABLE]');
assemblescreen.Lines.Add('//code from here till the end of the code will be used to disable the cheat');
end;
{$endif}
end;
procedure TfrmAutoInject.assemblescreenChange(Sender: TObject);
begin
if self=mainform.frmLuaTableScript then
mainform.editedsincelastsave:=true;
end;
procedure TfrmAutoInject.Assigntocurrentcheattable1Click(Sender: TObject);
var a,b: integer;
aa:TCEAllocArray;
registeredsymbols: TStringlist;
begin
{$ifndef standalonetrainerwithassembler}
{$ifndef net}
registeredsymbols:=tstringlist.Create;
registeredsymbols.CaseSensitive:=false;
registeredsymbols.Duplicates:=dupIgnore;
try
setlength(aa,0);
getenableanddisablepos(assemblescreen.Lines,a,b);
if (a=-1) and (b=-1) then raise exception.create(rsCodeNeedsEnableAndDisable);
if autoassemble(assemblescreen.lines,false,true,true,false,aa,registeredsymbols) and
autoassemble(assemblescreen.lines,false,false,true,false,aa,registeredsymbols) then
begin
//add a entry with type 255
mainform.AddAutoAssembleScript(assemblescreen.text);
end
else showmessage(rsFailedToAddToTableNotAllCodeIsInjectable);
finally
registeredsymbols.Free;
end;
{$endif}
{$endif}
end;
procedure Getjumpandoverwrittenbytes(address,addressto: ptrUint; jumppart,originalcodepart: tstrings);
//pre: jumppart and originalcodepart are declared objects
var x,y: ptrUint;
z: string;
i: integer;
ab: TAssemblerBytes;
jumpsize: integer;
begin
{$ifndef standalonetrainerwithassembler}
Assemble('jmp '+inttohex(addressto,8),address,ab);
jumpsize:=length(ab);
x:=address;
y:=address;
while x-y<jumpsize do
begin
z:=disassemble(x);
z:=copy(z,pos('-',z)+1,length(z));
z:=copy(z,pos('-',z)+1,length(z));
originalcodepart.add(z);
end;
jumppart.Add('jmp '+inttohex(addressto,8));
for i:=jumpsize to x-y-1 do
jumppart.Add('nop');
{$endif}
end;
procedure generateAPIHookScript(script: tstrings; address: string; addresstogoto: string; addresstostoreneworiginalfunction: string=''; nameextension:string='0');
var originalcode: array of string;
originaladdress: array of ptrUint;
i,j: integer;
codesize: integer;
a,b,c: ptrUint;
br: ptruint;
x: string;
enablepos,disablepos: integer;
disablescript: tstringlist;
enablescript: tstringlist;
originalcodebuffer: Pbytearray;
ab: TAssemblerBytes;
jumpsize: integer;
tempaddress: ptrUint;
specifier: array of ptrUint;
specifiernr: integer;
s,s2: string;
d: TDisassembler;
originalcodestart: integer;
isThumbOrigin: boolean;
isThumbDestination: boolean;
begin
//disassemble the old code
d:=TDisassembler.Create;
d.showmodules:=false;
d.showsymbols:=false;
setlength(specifier,0);
setlength(originalcode,0);
setlength(ab,0);
specifiernr:=0;
try
a:=symhandler.getAddressFromName(address);
except
on e: exception do
raise exception.create(address+':'+e.message);
end;
try
b:=symhandler.getAddressFromName(addresstogoto);
except
on e: exception do
raise exception.create(addresstogoto+':'+e.message);
end;
if processhandler.SystemArchitecture=archarm then
begin
isThumbOrigin:=(a and 1)=1; //assuming that a name is used and not the real address it occurs on
isThumbDestination:=(b and 1)=1;
if isThumbOrigin or isThumbDestination then
raise exception.create('The thumb instruction set is not yet suppported');
jumpsize:=8;
c:=ptruint(FindFreeBlockForRegion(a,2048));
if (c>0) and (abs(integer(c-a))<31*1024*1024) then
jumpsize:=4; //can be done with one instruction B <a>
end
else
begin
if processhandler.is64bit then
begin
//check if there is a region I can make use of for a jump trampoline
if FindFreeBlockForRegion(a,2048)=nil then
begin
Assemble('jmp '+inttohex(b,8),a,ab);
jumpsize:=length(ab);
end
else
jumpsize:=5;
end
else
jumpsize:=5;
end;
disablescript:=tstringlist.Create;
enablescript:=tstringlist.Create;
codesize:=0;
b:=a;
while codesize<jumpsize do
begin
setlength(originalcode,length(originalcode)+1);
setlength(originaladdress,length(originalcode));
originaladdress[length(originaladdress)-1]:=a;
originalcode[length(originalcode)-1]:=d.disassemble(a,x);
i:=posex('-',originalcode[length(originalcode)-1]);
i:=posex('-',originalcode[length(originalcode)-1],i+1);
originalcode[length(originalcode)-1]:=copy(originalcode[length(originalcode)-1],i+2,length(originalcode[length(originalcode)-1]));
codesize:=a-b;
end;
getmem(originalcodebuffer,codesize);
if ReadProcessMemory(processhandle,pointer(b), originalcodebuffer, codesize, br) then
begin
disablescript.Add(address+':');
x:='db';
for i:=0 to br-1 do
x:=x+' '+inttohex(originalcodebuffer[i],2);
disablescript.Add(x);
end;
freemem(originalcodebuffer);
with enablescript do
begin
if (processhandler.SystemArchitecture=archx86) and (not processhandler.is64bit) then
add('alloc(originalcall'+nameextension+',2048)')
else
begin
add('alloc(originalcall'+nameextension+',2048,'+address+')');
add('alloc(jumptrampoline'+nameextension+',64,'+address+') //special jump trampoline in the current region (64-bit)');
if processhandler.SystemArchitecture=archx86 then
add('label(jumptrampoline'+nameextension+'address)');
end;
add('label(returnhere'+nameextension+')');
add('');
if addresstostoreneworiginalfunction<>'' then
begin
add(addresstostoreneworiginalfunction+':');
if processhandler.is64Bit then
add('dq originalcall'+nameextension)
else
add('dd originalcall'+nameextension);
end;
add('');
add('originalcall'+nameextension+':');
originalcodestart:=enablescript.Count;
for i:=0 to length(originalcode)-1 do
begin
if hasAddress(originalcode[i], tempaddress, nil ) then
begin
if InRangeX(tempaddress, b,b+codesize) then
begin
s2:='specifier'+nameextension+inttostr(specifiernr);
setlength(specifier,length(specifier)+1);
specifier[specifiernr]:=tempaddress;
Insert(0,'label('+s2+')');
if has4ByteHexString(originalcode[i], s) then //should be yes
begin
s:=copy(s,2,length(s)-1);
originalcode[i]:=StringReplace(originalcode[i],s,s2,[rfIgnoreCase]);
end;
inc(specifiernr);
end;
end;
add(originalcode[i]);
end;
//now find the originalcode line that belongs to the specifier
inc(originalcodestart,specifiernr);
for i:=0 to length(specifier)-1 do
begin
for j:=0 to length(originaladdress)-1 do
begin
if specifier[i]=originaladdress[j] then
begin
enablescript[originalcodestart+j]:='specifier'+nameextension+inttostr(i)+':'+enablescript[originalcodestart+j]
end;
end;
end;
i:=0;
while i<enablescript.count do
begin
j:=pos(':',enablescript[i]);
if j>0 then
begin
s:=enablescript[i];
s2:=copy(s,j+1,length(s));
delete(i);
Insert(i,copy(s,1,j));
inc(i);
Insert(i,s2);
end;
inc(i);
end;
if processhandler.SystemArchitecture=archarm then
add('b returnhere'+nameextension)
else
add('jmp returnhere'+nameextension);
add('');
if processhandler.systemarchitecture=archarm then
begin
add('jumptrampoline'+nameextension+':');
if isThumbDestination then
begin
raise exception.create('Thumb instructions are not yet implemented');
if isThumbOrigin then
begin
add('thumb:b '+addresstogoto);
end
else
begin
add('bx jumptrampoline_armtothumb+1');
add('jumptrampoline_armtothumb:');
add('thumb:bl '+addresstogoto);
add('thumb:bx jumptrampoline_thumbtoarm');
add('jumptrampoline_thumbtoarm');
add('bx lr');
end;
end
else
add('b '+addresstogoto);
end
else
if processhandler.is64bit then
begin
add('jumptrampoline'+nameextension+':');
add('jmp [jumptrampoline'+nameextension+'address]');
add('jumptrampoline'+nameextension+'address:');
add('dq '+addresstogoto);
add('');
end;
add(address+':');
if processhandler.SystemArchitecture=archarm then
begin
add('B jumptrampoline'+nameextension);
end
else
begin
if processhandler.is64bit then
add('jmp jumptrampoline'+nameextension)
else
add('jmp '+addresstogoto);
while codesize>jumpsize do
begin
add('nop');
dec(codesize);
end;
end;
add('returnhere'+nameextension+':');
add('');
end;
getenableanddisablepos(script,enablepos,disablepos);
if disablepos<>-1 then
begin
for i:=0 to disablescript.Count-1 do
script.Insert(disablepos+i+1,disablescript[i]);
end;
getenableanddisablepos(script,enablepos,disablepos); //idiots putting disable first
if enablepos<>-1 then
begin
for i:=0 to enablescript.Count-1 do
script.Insert(enablepos+i+1,enablescript[i]);
end
else
script.AddStrings(enablescript);
disablescript.free;
enablescript.free;
d.free;
end;
procedure TfrmAutoInject.APIHook1Click(Sender: TObject);
function inttostr(i:int64):string;
begin
if i=0 then result:='' else result:=sysutils.IntToStr(i);
end;
var address: string;
a: ptrUint;
x: string;
i,j,k: integer;
injectnr: integer;
begin
if parent is TMemoryBrowser then
a:=TMemoryBrowser(parent).disassemblerview.SelectedAddress
else
a:=memorybrowser.disassemblerview.SelectedAddress;
address:=inttohex(a,8);
with tfrmapihooktemplatesettings.create(self) do
// if inputquery('Give the address of the api you want to hook',address) and inputquery('Give the address of the replacement function',address) then
begin
try
injectnr:=0;
for i:=0 to assemblescreen.Lines.Count-1 do
begin
j:=pos('alloc(newmem',lowercase(assemblescreen.lines[i]));
if j<>0 then
begin
x:=copy(assemblescreen.Lines[i],j+12,length(assemblescreen.Lines[i]));
x:=copy(x,1,pos(',',x)-1);
try
k:=strtoint(x);
if injectnr<=k then
injectnr:=k+1;
except
inc(injectnr);
end;
end;
end;
edit1.text:=address;
if showmodal<>mrok then exit;
generateAPIHookScript(assemblescreen.Lines,edit1.Text, edit2.Text, edit3.Text, inttostr(injectnr));
finally
free;
end;
end;
end;
procedure TfrmAutoInject.SaveAs1Click(Sender: TObject);
begin
if savedialog1.Execute then
save1.Click;
end;
procedure TfrmAutoInject.FormShow(Sender: TObject);
begin
if editscript then
button1.Caption:=strOK;
assemblescreen.SetFocus;
end;
procedure TfrmAutoInject.assemblescreenKeyDown(Sender: TObject;
var Key: Word; Shift: TShiftState);
begin
{ if (ssCtrl in Shift) and (key=ord('A')) then
begin
TMemo(Sender).SelectAll;
Key := 0;
end; }
end;
procedure TfrmAutoInject.miCallLuaClick(Sender: TObject);
var
luaserverinit: tstringlist;
i: integer;
needsinit1: boolean;
begin
needsinit1:=true;
for i:=0 to assemblescreen.Lines.Count-1 do
if trim(assemblescreen.lines[i])='luacall(openLuaServer(''CELUASERVER''))' then
needsinit1:=false;
if needsinit1 then
begin
luaserverinit:=tstringlist.create;
if processhandler.is64bit then
luaserverinit.add('loadlibrary(luaclient-x86_64.dll)')
else
luaserverinit.add('loadlibrary(luaclient-i386.dll)');
luaserverinit.add('luacall(openLuaServer(''CELUASERVER''))');
luaserverinit.add('globalalloc(luainit, 128)');
luaserverinit.add('globalalloc(LuaFunctionCall, 128)');
luaserverinit.add('label(luainit_exit)');
if processhandler.is64bit then
luaserverinit.add('globalalloc(luaserverinitialized, 8)')
else
luaserverinit.add('globalalloc(luaserverinitialized, 4)');
luaserverinit.add('globalalloc(luaservername, 12)');
luaserverinit.add('');
luaserverinit.add('luaservername:');
luaserverinit.add('db ''CELUASERVER'',0');
luaserverinit.add('');
luaserverinit.add('luainit:');
if processhandler.is64Bit then
luaserverinit.add('sub rsp,8 //local scratchspace (and alignment)');
luaserverinit.add('cmp [luaserverinitialized],0');
luaserverinit.add('jne luainit_exit');
if processhandler.is64Bit then
begin
luaserverinit.add('sub rsp,20 //allocate 32 bytes scratchspace for CELUA_Initialize');
luaserverinit.add('mov rcx,luaservername');
end
else
luaserverinit.add('push luaservername');
luaserverinit.add('call CELUA_Initialize //this function is defined in the luaclient dll');
if processhandler.is64Bit then
luaserverinit.add('add rsp,20');
luaserverinit.add('mov [luaserverinitialized],eax');
luaserverinit.add('luainit_exit:');
if processhandler.is64Bit then
luaserverinit.add('add rsp,8 //undo local scratchspace ');
luaserverinit.add('ret');
luaserverinit.add('');
luaserverinit.add('LuaFunctionCall:');
if processhandler.is64bit then
begin
luaserverinit.add('sub rsp,8 //private scratchspace for this function');
luaserverinit.add('mov [rsp+10],rcx //save address with function into pre-allocated scratchspace');
luaserverinit.add('mov [rsp+18],rdx //save integer val');
luaserverinit.add('sub rsp,20 //allocate 32 bytes of "shadow space" for the callee (not needed here, but good practice) ');
end
else
begin
luaserverinit.add('push ebp');
luaserverinit.add('mov ebp,esp');
end;
luaserverinit.add('call luainit');
if processhandler.is64bit then
begin
luaserverinit.add('add rsp,20');
luaserverinit.add('mov rcx,[esp+10] //restore address of function');
luaserverinit.add('mov rdx,[esp+18] //restore value');
end;
luaserverinit.add('');
if processhandler.is64Bit then
begin
luaserverinit.add('sub rsp,20');
luaserverinit.add('call CELUA_ExecuteFunction //this function is defined in the luaclient dll');
luaserverinit.add('add rsp,20');
luaserverinit.add('add rsp,8 //undo scratchpace (alignment fix) you can also combine it into add rsp,28');
luaserverinit.add('ret');
end
else
begin
luaserverinit.add('push [ebp+c]');
luaserverinit.add('push [ebp+8]');
luaserverinit.add('call CELUA_ExecuteFunction');
luaserverinit.add('pop ebp');
luaserverinit.add('ret 8');
end;
luaserverinit.add('//luacall call example:');
if processhandler.is64bit then
begin
luaserverinit.add('//Make sure rsp is aligned on a 16-byte boundary when calling this function');
luaserverinit.add('//mov rcx, addresstostringwithfunction //(The lua function will have access to the variable passed by name "parameter")');
luaserverinit.add('//mov rdx, integervariableyouwishtopasstolua');
luaserverinit.add('//sub rsp,20');
luaserverinit.add('//call LuaFunctionCall');
luaserverinit.add('//add rsp,20');
luaserverinit.add('//When done RAX will contain the result of the lua function');
end
else
begin
luaserverinit.add('//push integervariableyouwishtopasstolua');
luaserverinit.add('//push addresstostringwithfunction //(The lua function will have access to the variable passed by name "parameter")');
luaserverinit.add('//call LuaFunctionCall');
luaserverinit.add('//When done EAX will contain the result of the lua function');
end;
for i:=0 to luaserverinit.count-1 do
assemblescreen.Lines.Insert(0+i, luaserverinit[i]);
luaserverinit.free;
end;
end;
procedure TfrmAutoInject.Coderelocation1Click(Sender: TObject);
var starts,stops: string;
start,stop,current: ptrUint;
x: ptrUint;
i,j: integer;
labels: tstringlist;
output: tstringlist;
s: string;
a,b: string;
prev: ptrUint;
ok: boolean;
begin
{$ifndef standalonetrainerwithassembler}
starts:=inttohex(memorybrowser.disassemblerview.SelectedAddress,8);
stops:=inttohex(memorybrowser.disassemblerview.SelectedAddress+128,8);
if inputquery(rsStartAddress+':', rsCodeRelocationTemplate, starts) then
begin
start:=StrToQWordEx('$'+starts);
if inputquery(rsEndAddressLastBytesAreIncludedIfNecesary, rsCodeRelocationTemplate, stops) then
begin
stop:=StrToQWordEx('$'+stops);
output:=tstringlist.Create;
labels:=tstringlist.create;
labels.Duplicates:=dupIgnore;
labels.Sorted:=true;
output.add('alloc(newmem,'+inttostr(abs(integer(stop-start))*2)+')');
output.add('');
output.add('newmem:');
try
current:=start;
while current<stop do
begin
prev:=current;
s:=disassemble(current);
i:=posex('-',s);
i:=posex('-',s,i+1);
s:=copy(s,i+2,length(s));
i:=pos(' ',s);
a:=copy(s,1,i-1);
b:=copy(s,i+1,length(s));
if length(a)>1 then
begin
if (lowercase(a)='loop') or (lowercase(a[1])='j') or (lowercase(a)='call') then
begin
try
x:=symhandler.getAddressFromName(b);
if (x>=start) and (x<=stop) then
begin
labels.Add('orig_'+inttohex(x,8));
s:=a+' orig_'+inttohex(x,8);
end;
except
//nolabel
end;
end;
end;
output.add('orig_'+inttohex(prev,8)+':');
output.add(s);
end;
labels.Sort;
//now clean up output so that the result is a readable program
for i:=0 to labels.Count-1 do
output.Insert(2+i,'label('+labels[i]+')');
output.Insert(2+labels.Count,'');
i:=2+labels.Count+1;
while i<output.Count do
begin
if pos('orig_',output[i])>0 then
begin
//determine if it's valid or not
ok:=false;
for j:=0 to labels.Count-1 do
if labels[j]+':'=output[i] then
begin
ok:=true;
break;
end;
if not ok then
output.Delete(i)
else
begin
output.Insert(i,'');
inc(i,2);
end;
end
else inc(i);
end;
assemblescreen.Lines.AddStrings(output);
finally
output.free;
end;
end;
end;
{$endif}
end;
procedure TfrmAutoInject.New1Click(Sender: TObject);
var i: integer;
begin
{$ifndef standalonetrainerwithassembler}
scripts[length(scripts)-1].script:=assemblescreen.Text;
setlength(scripts,length(scripts)+1);
scripts[length(scripts)-1].script:='';
scripts[length(scripts)-1].undoscripts[0].oldscript:='';
scripts[length(scripts)-1].currentundo:=0;
assemblescreen.Text:='';
if length(scripts)=2 then //first time new
begin
tlist.AddTab('Script 1');
tlist.Visible:=true;
end;
i:=tlist.AddTab('Script '+inttostr(length(scripts)));
tlist.SelectedTab:=i;
oldtabindex:=i;
{$endif}
end;
procedure tfrmautoinject.tlistOnTabChange(sender: TObject; oldselection: integer);
begin
{$ifndef standalonetrainerwithassembler}
scripts[oldselection].script:=assemblescreen.text;
scripts[oldselection].filename:=opendialog1.FileName;
assemblescreen.text:=scripts[tlist.SelectedTab].script;
opendialog1.FileName:=scripts[tlist.SelectedTab].filename;
oldtabindex:=tlist.SelectedTab;
assemblescreen.ClearUndo;
{$endif}
end;
procedure tfrmAutoInject.gutterclick(Sender: TObject; X, Y, Line: integer; mark: TSynEditMark);
begin
if assemblescreen.Lines.Count>line then
begin
assemblescreen.CaretY:=line;
assemblescreen.CaretX:=0;
assemblescreen.SelectLine(true);
end;
end;
procedure TfrmAutoInject.FormCreate(Sender: TObject);
var x: array of integer;
reg: tregistry;
begin
{$ifndef standalonetrainerwithassembler}
setlength(scripts,1);
scripts[0].currentundo:=0;
oldtabindex:=0;
{ assemblescreen.SelStart:=0;
assemblescreen.SelLength:=0; }
AAHighlighter:=TSynAASyn.Create(self);
CPPHighlighter:=TSynCppSyn.create(self);
LuaHighlighter:=TSynLuaSyn.Create(self);
assembleSearch:=TSyneditSearch.Create;
tlist:=TTablist.Create(self);
tlist.height:=20;
tlist.Align:=alTop;
tlist.Visible:=false;
tlist.OnTabChange:=tlistOnTabChange;
tlist.Parent:=panel2;
assemblescreen:=TSynEdit.Create(self);
assemblescreen.Highlighter:=AAHighlighter;
assemblescreen.Options:=SYNEDIT_DEFAULT_OPTIONS - [eoScrollPastEol]+[eoTabIndent];
assemblescreen.Font.Quality:=fqDefault;
assemblescreen.WantTabs:=true;
assemblescreen.TabWidth:=4;
assemblescreen.Gutter.MarksPart.Visible:=false;
assemblescreen.Gutter.Visible:=true;
assemblescreen.Gutter.LineNumberPart.Visible:=true;
assemblescreen.Gutter.LeftOffset:=1;
assemblescreen.Gutter.RightOffset:=1;
assemblescreen.Align:=alClient;
assemblescreen.PopupMenu:=PopupMenu1;
assemblescreen.Parent:=panel2;
assemblescreen.Gutter.OnGutterClick:=gutterclick;
assemblescreen.name:='Assemblescreen';
assemblescreen.Text:='';
assemblescreen.OnChange:=assemblescreenchange;
setlength(x,0);
loadformposition(self,x);
reg:=tregistry.create;
try
if reg.OpenKey('\Software\Cheat Engine\Auto Assembler\',false) then
begin
if reg.valueexists('Font.name') then
assemblescreen.Font.Name:=reg.readstring('Font.name');
if reg.valueexists('Font.size') then
assemblescreen.Font.size:=reg.ReadInteger('Font.size');
if reg.valueexists('Font.quality') then
assemblescreen.Font.quality:=TFontQuality(reg.ReadInteger('Font.quality'));
if reg.valueexists('Show Line Numbers') then
assemblescreen.Gutter.linenumberpart.visible:=reg.ReadBool('Show Line Numbers');
if reg.valueexists('Show Gutter') then
assemblescreen.Gutter.Visible:=reg.ReadBool('Show Gutter');
if reg.valueexists('smart tabs') then
if reg.ReadBool('smart tabs') then assemblescreen.Options:=assemblescreen.options+[eoSmartTabs];
if reg.valueexists('tabs to spaces') then
if reg.ReadBool('tabs to spaces') then assemblescreen.Options:=assemblescreen.options+[eoTabsToSpaces];
if reg.valueexists('tab width') then
assemblescreen.tabwidth:=reg.ReadInteger('tab width');
end;
finally
reg.free;
end;
{$endif}
end;
procedure TfrmAutoInject.TabControl1Change(Sender: TObject);
begin
end;
procedure TfrmAutoInject.Syntaxhighlighting1Click(Sender: TObject);
begin
{$ifndef standalonetrainerwithassembler}
Syntaxhighlighting1.checked:=not Syntaxhighlighting1.checked;
if Syntaxhighlighting1.checked then //enable
assemblescreen.Highlighter:=AAHighlighter
else //disabl
assemblescreen.Highlighter:=nil;
{$endif}
end;
procedure TfrmAutoInject.TabControl1ContextPopup(Sender: TObject;
MousePos: TPoint; var Handled: Boolean);
begin
//selectedtab:=TabControl1.IndexOfTabAt(mousepos.x,mousepos.y);
//closemenu.Popup(mouse.CursorPos.X,mouse.cursorpos.Y);
end;
procedure TfrmAutoInject.Close1Click(Sender: TObject);
var i: integer;
begin
{$ifndef standalonetrainerwithassembler}
if messagedlg(Format(rsAreYouSureYouWantToClose, [tlist.TabText[selectedtab]]), mtConfirmation, [mbyes, mbno], 0)=mryes then
begin
scripts[oldtabindex].script:=assemblescreen.text; //save current script
tlist.RemoveTab(selectedtab);
for i:=selectedtab to length(scripts)-2 do
scripts[i]:=scripts[i+1];
setlength(scripts,length(scripts)-1);
if oldtabindex=selectedtab then //it was the current one
begin
oldtabindex:=length(scripts)-1;
tlist.SelectedTab:=oldtabindex;
assemblescreen.text:=scripts[oldtabindex].script;
assemblescreen.OnChange(assemblescreen);
end;
if (length(scripts)=1) then
begin
tlist.RemoveTab(0);
tlist.Visible:=false;
end;
// tabcontrol1.tabs[selectedtab]
end;
{$endif}
end;
procedure TfrmAutoInject.injectscript(createthread: boolean);
var i: integer;
setenvscript: tstringlist;
CEAllocArray: TCEAllocArray;
callscriptscript: tstringlist;
totalmem: dword;
totalwritten: dword;
address: pointer;
mi: TModuleInfo;
hasjustloadedundercdll: boolean;
aawindowwithstub: tfrmautoinject;
// setenv_done: dword;
// setenv_done_value: dword;
s: string;
ignore: dword;
th: thandle;
begin
{$ifndef standalonetrainerwithassembler}
{
obsolete
//this will inject the script dll and generate a assembler script the user can use to call the script
//first set the environment var for uc_home
s:=assemblescreen.text;
if not symhandler.getmodulebyname('undercdll.dll',mi) then
begin
//dll was not loaded yet
setenvscript:=tstringlist.Create;
with setenvscript do
begin
add('[enable]');
Add('alloc(envname,8)');
add('alloc(envvar,512)');
add('alloc(myscript,512)');
add('envname:');
add('db ''UC_HOME'',0');
add('envvar:');
add('db '''+cheatenginedir+''' ,0');
add('myscript:');
add('push envvar');
add('push envname');
add('call SetEnvironmentVariableA');
add('ret');
//cleanup part:
add('[disable]');
add('dealloc(myscript)');
add('dealloc(envvar)');
add('dealloc(envname)');
end;
setlength(CEAllocArray,1);
if autoassemble(setenvscript,false,true,false,false,CEAllocArray) then //enabled
begin
for i:=0 to length(ceallocarray)-1 do
if ceallocarray[i].varname='myscript' then
begin
th:=createremotethread(processhandle,nil,0,pointer(ceallocarray[i].address),nil,0,ignore);
if th<>0 then
waitforsingleobject(th,4000); //4 seconds max
break;
end;
//wait done
autoassemble(setenvscript,false,false,false,false,CEAllocArray); //disable for the deallocs
end;
setenvscript.free;
injectdll(cheatenginedir+'undercdll.dll','');
symhandler.reinitialize;
hasjustloadedundercdll:=true;
end else hasjustloadedundercdll:=false;
//now allocate memory for the script and write it to there
totalmem:=length(assemblescreen.text);
address:=VirtualAllocEx(processhandle,nil,totalmem+512,mem_commit,page_execute_readwrite);
if address=nil then raise exception.create('Failed allocating memory for the script');
if not WriteProcessMemory(processhandle,address,@s[1],totalmem,totalwritten) then
raise exception.create('failed writing the script to the process');
callscriptscript:=tstringlist.create;
try
with callscriptscript do
begin
add('label(result)');
add(inttohex((ptrUint(address)+totalmem+$20) - (ptrUint(address) mod $10),8)+':');
add('pushfd');
add('pushad');
add('push '+inttohex(ptrUint(address),8));
add('call underc_executescript');
add('mov [result],eax');
add('popad');
add('popfd');
add('mov eax,[result]');
add('ret');
add('result:');
add('dd 0');
end;
if hasjustloadedundercdll then
begin
//lets wait before injecting the callscript script
symhandler.waitforsymbolsloaded;
if not symhandler.getmodulebyname('undercdll.dll',mi) then
raise exception.Create('Failure loading undercdll');
end;
if not autoassemble(callscriptscript,false,true,false,false,CEAllocArray) then raise exception.Create('Failed creating calling stub for script located at address '+inttohex(ptrUint(address),8));
finally
callscriptscript.free;
end;
aawindowwithstub:=tfrmautoinject.create(memorybrowser);
with aawindowwithstub.assemblescreen.Lines do
begin
if createthread then
begin
add('createthread(myscript)');
add('alloc(myscript,256)');
add('myscript:');
end;
add('//Call this code to execute the script from assembler');
add('call '+inttohex((ptrUint(address)+totalmem+$20) - (ptrUint(address) mod $10),8));
add('');
add('//eax==0 when successfully executed');
add('//''call underc_geterror'' to get a pointer to the last generated error buffer');
if createthread then
add('ret //interesing thing with createthread is that the return param points to exitthread');
end;
aawindowwithstub.show;
}
{$endif}
end;
procedure TfrmAutoInject.Injectincurrentprocess1Click(Sender: TObject);
begin
injectscript(false);
end;
procedure TfrmAutoInject.Injectintocurrentprocessandexecute1Click(
Sender: TObject);
begin
injectscript(true);
end;
procedure TfrmAutoInject.Cut1Click(Sender: TObject);
begin
assemblescreen.CutToClipboard;
end;
procedure TfrmAutoInject.Copy1Click(Sender: TObject);
begin
assemblescreen.CopyToClipboard;
end;
procedure TfrmAutoInject.Paste1Click(Sender: TObject);
begin
assemblescreen.PasteFromClipboard;
end;
procedure TfrmAutoInject.Find1Click(Sender: TObject);
begin
if finddialog1.Execute then
mifindNext.visible:=true;
end;
procedure TfrmAutoInject.FindDialog1Find(Sender: TObject);
begin
//scan the text for the given text
assemblescreen.SearchReplace(finddialog1.FindText,'',[]);
FindDialog1.close;
end;
//follow is just a emergency fix since undo is messed up. At least it's better than nothing
procedure TfrmAutoInject.AAPref1Click(Sender: TObject);
var reg: tregistry;
begin
with TfrmAAEditPrefs.create(self) do
begin
try
if execute(assemblescreen) then
begin
//save these settings
reg:=tregistry.create;
try
if reg.OpenKey('\Software\Cheat Engine\Auto Assembler\',true) then
begin
reg.WriteString('Font.name', assemblescreen.Font.Name);
reg.WriteInteger('Font.size', assemblescreen.Font.size);
reg.WriteInteger('Font.quality', integer(assemblescreen.Font.Quality));
//assemblescreen.Font.
reg.WriteBool('Show Line Numbers', assemblescreen.Gutter.linenumberpart.visible);
reg.WriteBool('Show Gutter', assemblescreen.Gutter.Visible);
reg.WriteBool('smart tabs', eoSmartTabs in assemblescreen.Options);
reg.WriteBool('tabs to spaces', eoTabsToSpaces in assemblescreen.Options);
end;
finally
reg.free;
end;
end;
finally
free;
end;
end;
end;
procedure TfrmAutoInject.FormDestroy(Sender: TObject);
begin
//if editscript or editscript2 then
begin
saveformposition(self,[]);
end;
end;
procedure TfrmAutoInject.Undo1Click(Sender: TObject);
begin
assemblescreen.Undo;
end;
// \/ http://forum.cheatengine.org/viewtopic.php?t=566415 (jgoemat and some mods by db)
procedure TfrmAutoInject.menuFullInjectionClick(Sender: TObject);
var
address: string;
originalcode: array of string;
originalbytes: array of byte;
codesize: integer;
a: ptrUint;
br: ptruint;
c: ptrUint;
x: string;
i,j,k: integer;
injectnr: integer;
nr: string; // injectnr as string
aobString: string;
p: integer;
enablepos: integer;
disablepos: integer;
initialcode: tstringlist;
enablecode: tstringlist;
disablecode: tstringlist;
mi: TModuleInfo;
haveModule: boolean;
originalAddress: ptrUint;
AddressString: string;
maxBytesSize: integer;
addressList: tstringlist;
bytesList: tstringlist;
codeList: tstringlist;
startIndex: integer;
injectFirstLine: Integer;
injectLastLine: Integer;
dline: TDisassemblyLine;
ddBytes: string;
begin
{$ifndef standalonetrainerwithassembler}
// now heavily modified code from "Code injection" menu
a:=memorybrowser.disassemblerview.SelectedAddress;
mi.baseaddress := 0;
haveModule := symhandler.getmodulebyaddress(a,mi);
if haveModule then
begin
address:='"'+mi.modulename+'"+'+inttohex(a-mi.baseaddress,1);
end
else
address:=inttohex(a,8);
if inputquery(rsCodeInjectTemplate, rsOnWhatAddressDoYouWantTheJump, address) then
begin
try
a:=StrToQWordEx('$'+address);
except
a:=symhandler.getaddressfromname(address);
end;
c:=a;
injectnr:=0;
for i:=0 to assemblescreen.Lines.Count-1 do
begin
j:=pos('alloc(newmem',lowercase(assemblescreen.lines[i]));
if j<>0 then
begin
x:=copy(assemblescreen.Lines[i],j+12,length(assemblescreen.Lines[i]));
x:=copy(x,1,pos(',',x)-1);
try
k:=strtoint(x);
if injectnr<=k then
injectnr:=k+1;
except
inc(injectnr);
end;
end;
end;
if injectnr = 0 then nr := '' else nr := sysutils.IntToStr(injectnr);
// disassemble the old code, simply for putting original code in the script
// and for the bytes we assert must be there and will replace
setlength(originalcode,0);
codesize:=0;
while codesize<5 do
begin
setlength(originalcode,length(originalcode)+1);
originalcode[length(originalcode)-1]:=disassemble(c,x);
i:=posex('-',originalcode[length(originalcode)-1]);
i:=posex('-',originalcode[length(originalcode)-1],i+1);
originalcode[length(originalcode)-1]:=copy(originalcode[length(originalcode)-1],i+2,length(originalcode[length(originalcode)-1]));
codesize:=c-a;
end;
setlength(originalbytes,codesize);
ReadProcessMemory(processhandle, pointer(a), @originalbytes[0], codesize, br);
// same as menu option "Cheat Engine framework code", make sure we
// have enable and disable
getenableanddisablepos(assemblescreen.lines,enablepos,disablepos);
if enablepos=-1 then //-2 is 2 or more, so bugged, and >=0 is has one
begin
assemblescreen.Lines.Insert(0,'[ENABLE]');
assemblescreen.Lines.Insert(1,'');
end;
if disablepos=-1 then
begin
assemblescreen.Lines.Add('[DISABLE]');
assemblescreen.Lines.Add('');
end;
dline:=TDisassemblyLine.create;
initialcode:=tstringlist.Create;
enablecode:=tstringlist.Create;
disablecode:=tstringlist.Create;
addressList:=tstringlist.Create;
bytesList:=tstringlist.Create;
codeList:=tstringlist.Create;
try
aobString:='';
for i:=0 to length(originalbytes)-1 do
begin
if i > 0 then
aobString := aobString + ' ';
aobString := aobString + inttohex(originalbytes[i], 2);
end;
with initialcode do
begin
add('define(address' + nr + ',' + address + ')');
add('define(bytes' + nr + ',' + aobString + ')');
add('');
end;
with enablecode do
begin
add('assert(address'+nr+',bytes'+nr+')');
if processhandler.is64bit then
add('alloc(newmem' + nr + ',$1000,' + address + ')')
else
add('alloc(newmem' + nr + ',$1000)');
add('');
add('label(code'+nr+')');
add('label(return'+nr+')');
add('');
add('newmem'+nr+':');
add('');
add('code'+nr+':');
for i:=0 to length(originalcode)-1 do
add(' '+originalcode[i]);
add(' jmp return'+nr+'');
add('');
add('address'+nr+':');
add(' jmp code'+nr+'');
while codesize>5 do
begin
add(' nop');
dec(codesize);
end;
add('return'+nr+':');
add('');
end;
with disablecode do
begin
add('address'+nr+':');
add(' db bytes'+nr);
for i:=0 to length(originalcode)-1 do
add(' // ' + originalcode[i]);
add('');
add('dealloc(newmem'+nr+')');
end;
// add initial defines before enable
getenableanddisablepos(assemblescreen.lines,enablepos,disablepos);
p:=0;
if (enablepos>0) then
p:=enablepos;
for i:=initialcode.Count-1 downto 0 do
assemblescreen.Lines.Insert(p,initialcode[i]);
// add enable lines before disable
getenableanddisablepos(assemblescreen.lines,enablepos,disablepos);
p:=assemblescreen.lines.Count-1;
if(disablepos>0) then
p:=disablepos;
for i:=enablecode.Count-1 downto 0 do
assemblescreen.Lines.Insert(p,enablecode[i]);
// add disable lines at very end
for i:=0 to disablecode.Count-1do
assemblescreen.Lines.Add(disablecode[i]);
// finally add comment at the beginning
assemblescreen.Lines.Insert(0,'{ Game : ' + copy(mainform.ProcessLabel.Caption, pos('-', mainform.ProcessLabel.Caption) + 1, length(mainform.ProcessLabel.Caption)));
assemblescreen.Lines.Insert(1,' Version: ');
assemblescreen.Lines.Insert(2,' Date : ' + FormatDateTime('YYYY-MM-DD', Now));
assemblescreen.Lines.Insert(3,' Author : ' + UserName);
assemblescreen.Lines.Insert(4,'');
assemblescreen.Lines.Insert(5,' This script does blah blah blah');
assemblescreen.Lines.Insert(6,'}');
assemblescreen.Lines.Insert(7,'');
// now we disassemble quite a bit more code for comments at the
// bottom so someone can easily find the code again if the game
// is updated
assemblescreen.Lines.Add('');
assemblescreen.Lines.Add('{');
assemblescreen.Lines.Add('// ORIGINAL CODE - INJECTION POINT: ' + address);
assemblescreen.Lines.Add('');
injectFirstLine := 0;
injectLastLine := 0;
maxBytesSize := 0;
dline.Init(a - 128, mi);
while dline.Address < (a + 128) do
begin
if (dline.Address < a) and ((dline.Address + dline.Size) > a) then dline.Shorten((dline.Address + dline.Size) - a);
addressList.Add(dline.AddressString);
ddBytes := dline.GetHexBytes;
maxBytesSize := Max(maxBytesSize, Length(ddBytes));
bytesList.Add(ddBytes);
codeList.Add(dline.Code);
if (dline.Address >= a) and (injectFirstLine <= 0) then injectFirstLine := addressList.Count - 1;
if (dline.Address < a + codesize) then injectLastLine := addressList.Count - 1;
dline.Init(dline.Address + dline.Size, mi);
end;
for i := injectFirstLine - 10 to injectLastLine + 10 do
begin
if i = injectFirstLine then assemblescreen.Lines.Add('// ---------- INJECTING HERE ----------');
assemblescreen.Lines.Add(addressList[i] + ': ' + PadRight(bytesList[i],maxBytesSize) + ' - ' + codeList[i]);
if i = injectLastLine then assemblescreen.Lines.Add('// ---------- DONE INJECTING ----------');
end;
assemblescreen.Lines.Add('}');
finally
initialcode.free;
enablecode.free;
disablecode.Free;
addressList.Free;
bytesList.Free;
codeList.Free;
dline.free;
end;
end;
{$endif}
end;
procedure TfrmAutoInject.menuAOBInjectionClick(Sender: TObject);
var
address: string;
a: ptrUint; // pointer to injection point
originalcode: array of string; // disassembled code we're replacing
originalbytes: array of byte; // bytes we're replacing
codesize: integer; // # of bytes we're replacing
aobString: string; // hex bytes we're replacing
injectnr: integer; // # of this injection (multiple can be in 1 script)
nr: string; // injectnr as string
// lines where [ENABLE] and [DISABLE] are
enablepos: integer;
disablepos: integer;
// temp variables
br: ptruint;
c: ptrUint;
x: string;
i,j,k: integer;
p: integer;
// lines of code to inject in certain places
initialcode: tstringlist;
enablecode: tstringlist;
disablecode: tstringlist;
// these are for code in comment at bottom
maxBytesSize: Integer;
addressList: TStringList;
bytesList: TStringList;
codeList: TStringList;
ddBytes: String;
haveModule: boolean; // true if address is in a module
mi: TModuleInfo; // info on the module
dline: TDisassemblyLine; // for disassembling code in the bottom comment
injectFirstLine: Integer;
injectLastLine: Integer;
resultAOB: String;
resultOffset: Integer;
symbolName: String;
symbolNameWithOffset: String;
begin
{$ifndef standalonetrainerwithassembler}
// now heavily modified code from "Code injection" menu
a:=memorybrowser.disassemblerview.SelectedAddress;
mi.baseaddress := 0;
haveModule := symhandler.getmodulebyaddress(a,mi);
if haveModule then
begin
address:='"'+mi.modulename+'"+'+inttohex(a-mi.baseaddress,1);
end
else
address:=inttohex(a,8);
if inputquery(rsCodeInjectTemplate, rsOnWhatAddressDoYouWantTheJump, address) then
begin
try
a:=StrToQWordEx('$'+address);
except
a:=symhandler.getaddressfromname(address);
end;
c:=a;
injectnr:=0;
for i:=0 to assemblescreen.Lines.Count-1 do
begin
j:=pos('alloc(newmem',lowercase(assemblescreen.lines[i]));
if j<>0 then
begin
x:=copy(assemblescreen.Lines[i],j+12,length(assemblescreen.Lines[i]));
x:=copy(x,1,pos(',',x)-1);
try
k:=strtoint(x);
if injectnr<=k then
injectnr:=k+1;
except
inc(injectnr);
end;
end;
end;
if injectnr = 0 then nr := '' else nr := sysutils.IntToStr(injectnr);
// disassemble the old code, simply for putting original code in the script
// and for the bytes we assert must be there and will replace
setlength(originalcode,0);
codesize:=0;
while codesize<5 do
begin
setlength(originalcode,length(originalcode)+1);
originalcode[length(originalcode)-1]:=disassemble(c,x);
i:=posex('-',originalcode[length(originalcode)-1]);
i:=posex('-',originalcode[length(originalcode)-1],i+1);
originalcode[length(originalcode)-1]:=copy(originalcode[length(originalcode)-1],i+2,length(originalcode[length(originalcode)-1]));
codesize:=c-a;
end;
setlength(originalbytes, codesize);
ReadProcessMemory(processhandle, pointer(a), @originalbytes[0], codesize, br);
// same as menu option "Cheat Engine framework code", make sure we
// have enable and disable
getenableanddisablepos(assemblescreen.lines,enablepos,disablepos);
if enablepos=-1 then //-2 is 2 or more, so bugged, and >=0 is has one
begin
assemblescreen.Lines.Insert(0,'[ENABLE]');
assemblescreen.Lines.Insert(1,'');
end;
if disablepos=-1 then
begin
assemblescreen.Lines.Add('[DISABLE]');
assemblescreen.Lines.Add('');
end;
dline:=TDisassemblyLine.create;
initialcode:=tstringlist.Create;
enablecode:=tstringlist.Create;
disablecode:=tstringlist.Create;
addressList:=tstringlist.Create;
bytesList:=tstringlist.Create;
codeList:=tstringlist.Create;
try
//************************************************************************
//* Now do AOBScan and get name for injection symbol
//************************************************************************
resultAOB := GetUniqueAOB(mi, a, codesize, resultOffset);
symbolName := 'INJECT' + nr;
if not inputquery(rsCodeInjectTemplate, rsWhatIdentifierDoYouWantToUse, symbolName) then symbolName := 'INJECTION_POINT';
if resultOffset <> 0 then
symbolNameWithOffset := symbolName + '+' + IntToHex(resultOffset, 2)
else
symbolNameWithOffset := symbolName;
aobString:='';
for i:=0 to length(originalbytes)-1 do
begin
if i > 0 then
aobString := aobString + ' ';
aobString := aobString + IntToHex(originalbytes[i], 2);
end;
with enablecode do
begin
if (mi.baseAddress > 0) then
add('aobscanmodule(' + symbolName + ',' + mi.modulename + ',' + resultAOB + ') // should be unique')
else
add('aobscan(' + symbolName + ',' + resultAOB + ') // should be unique');
if processhandler.is64bit then
add('alloc(newmem' + nr + ',$1000,' + address + ')')
else
add('alloc(newmem' + nr + ',$1000)');
add('');
add('label(code'+nr+')');
add('label(return'+nr+')');
add('');
add('newmem'+nr+':');
add('');
add('code' + nr + ':');
for i:=0 to length(originalcode) - 1 do
add(' ' + originalcode[i]);
add(' jmp return'+nr+'');
add('');
add(symbolNameWithOffset + ':');
add(' jmp code' + nr + '');
for i := 6 to codesize do
add(' nop');
add('return' + nr + ':');
add('registersymbol(' + symbolName + ')');
add('');
end;
with disablecode do
begin
add(symbolNameWithOffset+':');
add(' db ' + aobString);
add('');
add('unregistersymbol(' + symbolName + ')');
add('dealloc(newmem'+nr+')');
end;
// add initial defines before enable
getenableanddisablepos(assemblescreen.lines,enablepos,disablepos);
p:=0;
if (enablepos>0) then
p:=enablepos;
for i:= initialcode.Count-1 downto 0 do
assemblescreen.Lines.Insert(p, initialcode[i]);
// add enable lines before disable
getenableanddisablepos(assemblescreen.lines, enablepos, disablepos);
p := assemblescreen.lines.Count - 1;
if(disablepos > 0) then
p := disablepos;
for i:= enablecode.Count - 1 downto 0 do
assemblescreen.Lines.Insert(p,enablecode[i]);
// add disable lines at very end
for i:= 0 to disablecode.Count - 1 do
assemblescreen.Lines.Add(disablecode[i]);
// add template comment at the beginning
assemblescreen.Lines.Insert(0,'{ Game : ' + copy(mainform.ProcessLabel.Caption, pos('-', mainform.ProcessLabel.Caption) + 1, length(mainform.ProcessLabel.Caption)));
assemblescreen.Lines.Insert(1,' Version: ');
assemblescreen.Lines.Insert(2,' Date : ' + FormatDateTime('YYYY-MM-DD', Now));
assemblescreen.Lines.Insert(3,' Author : ' + UserName);
assemblescreen.Lines.Insert(4,'');
assemblescreen.Lines.Insert(5,' This script does blah blah blah');
assemblescreen.Lines.Insert(6,'}');
assemblescreen.Lines.Insert(7,'');
// now we disassemble quite a bit more code for comments at the
// bottom so someone can easily find the code again if the game
// is updated
assemblescreen.Lines.Add('');
assemblescreen.Lines.Add('{');
assemblescreen.Lines.Add('// ORIGINAL CODE - INJECTION POINT: ' + address);
assemblescreen.Lines.Add('');
injectFirstLine := 0;
injectLastLine := 0;
maxBytesSize := 0;
dline.Init(a - 128, mi);
while dline.Address < (a + 128) do
begin
// see if we overshot our injection point
if (dline.Address < a) and ((dline.Address + dline.Size) > a) then dline.Shorten((dline.Address + dline.Size) - a);
addressList.Add(dline.AddressString);
ddBytes := dline.GetHexBytes;
maxBytesSize := Max(maxBytesSize, Length(ddBytes));
bytesList.Add(ddBytes);
codeList.Add(dline.Code);
if (dline.Address >= a) and (injectFirstLine <= 0) then injectFirstLine := addressList.Count - 1;
if (dline.Address < a + codesize) then injectLastLine := addressList.Count - 1;
dline.Init(dline.Address + dline.Size, mi);
end;
for i := injectFirstLine - 10 to injectLastLine + 10 do
begin
if i = injectFirstLine then assemblescreen.Lines.Add('// ---------- INJECTING HERE ----------');
assemblescreen.Lines.Add(addressList[i] + ': ' + PadRight(bytesList[i],maxBytesSize) + ' - ' + codeList[i]);
if i = injectLastLine then assemblescreen.Lines.Add('// ---------- DONE INJECTING ----------');
end;
assemblescreen.Lines.Add('}');
finally
initialcode.free;
enablecode.free;
disablecode.Free;
addressList.Free;
bytesList.Free;
codeList.Free;
dline.free;
end;
end;
{$ENDIF}
end;
function TfrmAutoInject.GetUniqueAOB(mi: TModuleInfo; address: ptrUint; codesize: Integer; var resultOffset: Integer) : string;
var
size: integer;
dline: TDisassemblyLine;
maskFlags : Array of Boolean; // true if we need to use **
maskBytes : Array of Byte; // bytes around code we're replacing
flags : Array of Boolean; // temp for single instruction
br : ptruint;
aob : string;
i, j, k : Integer;
// variables used for memory scan
ms : TMemScan;
minaddress: ptruint;
maxaddress: ptrUint;
foundAddress: ptrUint;
foundCount: Integer;
fl: TFoundList;
instructionOffset: Integer; // offset for copying mask flags to main list from instruction list
shortestAfter: Integer; // # of bytes, including codesize, index is 20 of course because it only counts starting at original code
shortestBeforeIndex: Integer; // index to start at, will be 0 - 20
shortestBeforeLength: Integer; // # of bytes, including before, original code, and possibly after
finds: Array of TAOBFind; // for each found address has bytes to use for comparison
// count how many found addresses match the criteria
function CountMatches(offset: Integer; size: Integer) : Integer;
var
i: Integer;
count: Integer;
flength: Integer;
begin
count := 0;
for i := 0 to Length(finds) - 1 do
begin
if finds[i].IsMatch(maskBytes, maskFlags, offset, offset + size - 1) then count := count + 1;
if count > 1 then break; // short-circuit, we only care if there is more than 1
end;
result := count;
end;
begin
size := 40 + codesize; // 20 bytes on each side of replaced code
SetLength(maskBytes, size); // setup array for bytes around code we're looking for
SetLength(maskFlags, size); // flags on whether they need masking or not
ReadProcessMemory(processhandle, pointer(address - 20), @maskBytes[0], size, br);
dline:=TDisassemblyLine.create;
// get AOB to search for using the code we're replacing
aob := '';
for i := 0 to codesize - 1 do
begin
if (i > 0) then aob := aob + ' ';
aob := aob + inttohex(maskBytes[20 + i], 2);
end;
// Do AOBSCAN for replaced code
ms := tmemscan.create(nil);
ms.parseProtectionflags('');
ms.onlyone := false;
if mi.baseaddress > 0 then
begin
minaddress := mi.baseaddress;
maxaddress := mi.baseaddress + mi.basesize;
end else
begin
minaddress := 0;
{$ifdef cpu64}
if processhandler.is64Bit then
maxaddress := qword($7fffffffffffffff)
else
{$endif}
begin
if Is64bitOS then
maxaddress := $ffffffff
else
maxaddress := $7fffffff;
end;
end;
ms.OnlyOne := false;
fl := TFoundlist.create(nil, ms, '');
ms.FirstScan(soExactValue, vtByteArray, rtTruncated, aob, '', minaddress, maxaddress, true, false, false, true, fsmAligned, '1');
ms.WaitTillReallyDone; //wait till it's finished scanning
foundCount := fl.Initialize(vtByteArray, nil);
// if there's only one result, the code's AOB is fine
if foundCount = 1 then
begin
resultOffset := 0;
result := aob;
fl.free;
ms.free;
exit;
end;
// now we need to narrow it down. start by disassembling around the injection
// point and creating flags on which bytes need to be masked because they are
// probably pointers to code or data that may frequently change
dline.Init(address - 128, mi);
// 0 to 19: address - 20 to address - 1: before
// 20 to 20 + codesize - 1): original code
// 20 + codesize to 39 + codesize: after
while (dline.Address <= (address + 20)) do
begin
// if we overran injection address, shorten to 'db X X X' statement
if (dline.Address < address) and ((dline.Address + dline.Size) > address) then dline.Shorten(address - dline.Address);
j := (dline.Address + 20) - address;
k := j + dline.Size - 1;
if (k >= 0) and (j <= (codesize + 39)) then
begin
// we're in range, get mask flags
flags := dline.GetMaskFlags();
for i := j to k do
begin
instructionOffset := i - j;
if (i >= 0) and (i <= 39 + codesize) and (instructionOffset >= 0) then
begin
if (i < 20) or (i >= (20 + codesize)) then
maskFlags[i] := flags[instructionOffset]
else
maskFlags[i] := false;
end;
end;
end;
dline.Init(dline.Address + dline.Size, mi); // next instruction
end;
// prep 'finds' array to read memory and make searching easier
SetLength(finds, foundCount);
for i := 0 to foundCount - 1 do
begin
finds[i].Init(fl.GetAddress(i), codesize);
end;
//not needed anymore
fl.free;
ms.free;
// find shortest way to get a single match starting at original code
shortestAfter := 100;
shortestBeforeIndex := 19;
shortestBeforeLength := 100;
for i := codesize + 1 to codesize + 20 do
begin
if CountMatches(20, i) = 1 then
begin
shortestAfter := i;
break;
end;
end;
// now for before, we step back one at a time and loop up to shortestAfter bytes
for i := 19 downto 0 do
begin
// i is index, j is length (checking indices i to i+j-1
for j := codesize + (20 - i) to Min(shortestBeforeLength - 1, Min(shortestAfter - 6, (40 + codesize) - i)) do // first round, 6 to 26
begin
if CountMatches(i, j) = 1 then
begin
shortestBeforeIndex := i;
shortestBeforeLength := j;
break;
end;
end;
end;
if shortestAfter < shortestBeforeLength then
begin
shortestBeforeLength := shortestAfter;
shortestBeforeIndex := 20;
end;
// if we can't find unique AOB, return earlier aob with error
if shortestBeforeLength >= 100 then begin
result := 'ERROR: Could not find unique AOB, tried code "' + aob + '"';
exit;
end;
// create AOB using masking
aob := '';
for i := 0 to shortestBeforeLength - 1 do
begin
if i <> 0 then aob := aob + ' ';
if maskFlags[i + shortestBeforeIndex] then
aob := aob + '*'
else
aob := aob + IntToHex(maskBytes[i + shortestBeforeIndex], 2);
end;
dline.free;
resultOffset := 20 - shortestBeforeIndex;
result := aob;
end;
procedure TDisassemblyLine.Init(_address: ptrUint; _mi: TModuleInfo);
var x:string;
pos1:integer;
pos2:integer;
i:integer;
original: string;
begin
Address := _address;
Original := disassembler.disassemble(_address, Comment);
Size := _address - Address;
OriginalHexBytes := disassembler.getLastBytestring;
Code:=disassembler.LastDisassembleData.prefix+' '+Disassembler.LastDisassembleData.opcode+' '+disassembler.LastDisassembleData.parameters;
if (_mi.basesize = 0) or (_address < _mi.baseaddress) or (_address > (_mi.baseaddress + _mi.basesize)) then
AddressString := inttohex(Address, 8)
else
AddressString := '"' + _mi.modulename + '"+' + inttohex(Address - _mi.baseaddress, 1);
end;
function TDisassemblyLine.GetHexBytes : String;
var i: Integer;
begin
result:='';
if length(Disassembler.LastDisassembleData.Bytes)>=size then
begin
for i:=0 to size-1 do
result:=result+inttohex(Disassembler.LastDisassembleData.Bytes[i],2)+' ';
end;
end;
// true if it is an instruction that probably starts a procedure so we can
// start our commented code here
function TDisassemblyLine.IsStarter : Boolean;
begin
result:=code = 'push ebp';
end;
// true if it is an instruction that probably ends a procedure so we can end
// our commented code here
function TDisassemblyLine.IsEnder : Boolean;
begin
result := Disassembler.LastDisassembleData.isret;
end;
// true if it not an instruction (int3, or add [eax],al : 00 00) that probably is not meant to be
// executed, so we know if we are outside a group of code
function TDisassemblyLine.IsValid : Boolean;
begin
result:=true;
if size>0 then //always true (if init is called once)
begin
if Disassembler.LastDisassembleData.Bytes[0]=$cc then
result := false
else
if size>1 then
begin
if (Disassembler.LastDisassembleData.Bytes[0]=0) and (Disassembler.LastDisassembleData.Bytes[1]=0) then
result:=false;
end;
end;
end;
// array with a boolean for each byte telling if it should be masked or not
function TDisassemblyLine.GetMaskFlags : TBooleanArray;
var
masked : TBooleanArray;
index : Integer;
i, pos1, pos2 : Integer;
part : String;
mask : Boolean;
count : Integer;
begin
setlength(result, size);
pos1:=0;
for i:=0 to Disassembler.LastDisassembleData.SeperatorCount-1 do
begin
pos2:=Disassembler.LastDisassembleData.Seperators[i];
mask:=(pos2<=size) and (pos2-pos1=4) and (abs(pinteger(@Disassembler.LastDisassembleData.Bytes[pos1])^)>=$10000); //value is bigger than 65535 (positive and negative)
for index := pos1 to pos2-1 do
result[index] := mask;
pos1:=pos2;
end;
for index := pos1 to size-1 do
result[index]:=false;
end;
procedure TDisassemblyLine.Shorten(_newSize: Integer);
var
i, j: Integer;
hexbytes: String;
begin
// GetHexBytes() gives us the bytes split out with spaces between
// all, this way we can write our 'db' statement and all bytes will
// be unmasked
Size := _newSize;
OriginalHexBytes := GetHexBytes;
Code := 'db ' + OriginalHexBytes + ' // SHORTENED TO HIT INJECTION FROM: ' + Code;
end;
constructor TDisassemblyLine.create;
begin
disassembler:=TDisassembler.Create;
Disassembler.showsymbols:=false; //seeing that mi is given explicitly to init() I assume that modules are prefered over exports
Disassembler.showmodules:=true;
Disassembler.dataOnly:=false;
end;
destructor TDisassemblyLine.destroy;
begin
if assigned(Disassembler) then
Disassembler.free;
inherited destroy;
end;
procedure TAOBFind.Init(_address: ptrUint; _codesize: Integer);
var
i: integer;
br: ptruint; // bytes actually read
begin
Address := _address;
Size := _codeSize + 40;
SetLength(Bytes, Size);
ReadProcessMemory(processhandle, pointer(Address - 20), @Bytes[0], Size, br);
end;
function TAOBFind.IsMatch(var maskBytes: Array Of Byte; var maskFlags : TBooleanArray; startIndex, endIndex: Integer): Boolean;
var
i: Integer;
mf: Boolean;
mb: Byte;
b: Byte;
begin
for i := startIndex to endIndex do
begin
if (i > 0) and (i < Length(Bytes)) then
begin
mf := maskFlags[i];
mb := maskBytes[i];
b := Bytes[i];
if not maskFlags[i] then
begin
if maskBytes[i] <> Bytes[i] then
begin
result := false;
exit;
end;
end;
end;
end;
result := true;
end;
// /\ http://forum.cheatengine.org/viewtopic.php?t=566415 (jgoemat and some mods by db)
initialization
{$i frmautoinjectunit.lrs}
end.