cheat-engine/Cheat Engine/autoassembler.pas
cheat-engine 4e95f565bf Mac fixes
fix compilation issues for mac (windows will come another day) and performance enhancements for hooking while symbols are still not loaded
2023-11-21 16:26:09 +01:00

4685 lines
151 KiB
ObjectPascal
Executable file

// Copyright Cheat Engine. All Rights Reserved.
unit autoassembler;
{$MODE Delphi}
interface
{$ifdef jni}
uses unixporthelper, Assemblerunit, classes, symbolhandler, sysutils,
NewKernelHandler, ProcessHandlerUnit, commonTypeDefs;
{$else}
uses
{$ifdef darwin}
macport, math,
{$endif}
{$ifdef windows}
jwawindows, windows,
{$endif}
Assemblerunit, classes, LCLIntf,symbolhandler, symbolhandlerstructs,
sysutils,dialogs,controls, CEFuncProc, NewKernelHandler ,plugin,
ProcessHandlerUnit, lua, lualib, lauxlib, LuaClass, commonTypeDefs, OpenSave,
SymbolListHandler, tcclib,
betterControls;
{$endif}
type
TDisableInfo=class
private
public
allocs: TCEAllocArray;
exceptions:TCEExceptionListArray;
registeredsymbols: tstringlist;
ccodesymbols: TSymbolListHandler;
sourcecodeinfo: TSourceCodeInfo;
donotfreeccodedata: boolean;
allsymbols: tstringlist; //filled at the end with all known symbols (allocs, labels, kallocs, aobscan results, defines that are addresses, etc...)
constructor create;
destructor destroy; override;
end;
TMemoryrecord=pointer;
function getenableanddisablepos(code:tstrings;var enablepos,disablepos: integer): boolean;
procedure getEnableOrDisableScript(code: TStrings; newscript: tstrings; enablescript: boolean);
function autoassemble(code: tstrings;popupmessages: boolean):boolean; overload;
function autoassemble(code: Tstrings; popupmessages,enable,syntaxcheckonly, targetself: boolean; disableinfo: TDisableInfo=nil; memrec: TMemoryrecord=nil): boolean; overload;
type TAutoAssemblerPrologue=procedure(code: TStrings; syntaxcheckonly: boolean) of object;
type TAutoAssemblerCallback=function(parameters: string; syntaxcheckonly: boolean): string of object;
type TRegisteredAutoAssemblerCommand=class
command: string;
callback: TAutoAssemblerCallback;
end;
type EAutoAssembler=class(exception);
procedure RegisterAutoAssemblerCommand(command: string; callback: TAutoAssemblerCallback);
procedure UnregisterAutoAssemblerCommand(command: string);
function registerAutoAssemblerPrologue(m: TAutoAssemblerPrologue; postAOBSCAN: boolean=false): integer;
procedure unregisterAutoAssemblerPrologue(id: integer);
var oldaamessage: boolean;
function autoassemble2(code: tstrings;popupmessages: boolean;syntaxcheckonly:boolean; targetself: boolean; disableinfo: TDisableInfo=nil; memrec: TMemoryRecord=nil):boolean;
implementation
{$ifdef jni}
uses strutils, memscan, disassembler, networkInterface, networkInterfaceApi,
Parsers, Globals, memoryQuery,types;
{$else}
uses simpleaobscanner, StrUtils, LuaHandler, memscan, disassembler, networkInterface,
networkInterfaceApi, LuaCaller, SynHighlighterAA, Parsers, Globals, memoryQuery,
MemoryBrowserFormUnit, MemoryRecordUnit{$ifdef windows}, vmxfunctions{$endif}, autoassemblerexeptionhandler,
UnexpectedExceptionsHelper, types, autoassemblercode, System.UITypes,
frmautoinjectunit, DebuggerInterfaceAPIWrapper, GDBServerDebuggerInterface;
{$endif}
resourcestring
rsForwardJumpWithNoLabelDefined = 'Forward jump with no label defined';
rsThereIsCodeDefinedWithoutSpecifyingTheAddressItBel = 'There is code defined without specifying the address it belongs to';
rsIsNotAValidBytestring = '%s is not a valid bytestring';
rsTheBytesAtAreNotWhatWasExpected = 'The bytes at %s are not what was expected';
rsTheMemoryAtCanNotBeRead = 'The memory at +%s can not be read';
rsWrongSyntaxASSERTAddress1122335566 = 'Wrong syntax. ASSERT(address,11 22 33 ** 55 66)';
rsIsNotAValidSize = '%s is not a valid size';
rsWrongSyntaxGLOBALALLOCNameSize = 'Wrong syntax. GLOBALALLOC(name,size)';
rsCouldNotBeFound = '%s could not be found';
rsWrongSyntaxIncludeFilenameCea = 'Wrong syntax. Include(filename.cea)';
rsWrongSyntaxCreateThreadAddress = 'Wrong syntax. CreateThread(address)';
rsCouldNotBeInjected = '%s could not be injected';
rsWrongSyntaxLoadLibraryFilename = 'Wrong syntax. LoadLibrary(filename)';
rsWrongSyntaxLuaCall = 'Wrong Syntax. LuaCall(luacommand)';
rsInvalidAddressForReadMem = 'Invalid address for ReadMem';
rsInvalidSizeForReadMem = 'Invalid size for ReadMem';
rsTheMemoryAtCouldNotBeFullyRead = 'The memory at %s could not be fully read';
rsWrongSyntaxReadMemAddressSize = 'Wrong syntax. ReadMem(address,size)';
rsTheFileDoesNotExist = 'The file %s does not exist';
rsWrongSyntaxLoadBinaryAddressFilename = 'Wrong syntax. LoadBinary(address,filename)';
rsWrongSyntaxReAssemble = 'Wrong syntax. Reassemble(address)';
rsSyntaxError = 'Syntax error';
rsTheArrayOfByteCouldNotBeFound = 'The array of byte ''%s'' could not be found';
rsWrongSyntaxAOBSCANName11223355 = 'Wrong syntax. AOBSCAN(name,11 22 33 ** 55)';
rsWrongSyntaxAOBSCANMODULEName11223355 = 'Wrong syntax. AOBSCANMODULE(name, module, 11 22 33 ** 55)';
rsWrongSyntaxAOBSCANREGION = 'Wrong syntax. AOBSCANREGION(name, startaddress, stopaddress, 11 22 33 ** 55)';
rsDefineAlreadyDefined = 'Define %s already defined';
rsWrongSyntaxDEFINENameWhatever = 'Wrong syntax. DEFINE(name,whatever)';
rsSyntaxErrorFullAccessAddressSize = 'Syntax error. FullAccess(address,size)';
rsIsNotAValidIdentifier = '%s is not a valid identifier';
rsIsBeingRedeclared = '%s is being redeclared';
rsLabelIsBeingDefinedMoreThanOnce = 'label %s is being defined more than once';
rsLabelIsNotDefinedInTheScript = 'label %s is not defined in the script';
rsTheIdentifierHasAlreadyBeenDeclared = 'The identifier %s has already been declared';
rsWrongSyntaxALLOCIdentifierSizeinbytes = 'Wrong syntax. ALLOC(identifier,sizeinbytes)';
rsNeedToUseKernelmodeReadWriteprocessmemory = 'You need to use kernelmode read/writeprocessmemory if you want to use KALLOC';
rsSorryButWithoutTheDriverKALLOCWillNotFunction = 'Sorry, but without the driver KALLOC will not function';
rsWrongSyntaxKallocIdentifierSizeinbytes = 'Wrong syntax. kalloc(identifier,sizeinbytes)';
rsThisAddressSpecifierIsNotValid = 'This address specifier is not valid';
rsThisInstructionCanTBeCompiled = 'This instruction can''t be compiled';
rsErrorInLine = 'Error in line %s (%s) :%s';
rsWasSupposedToBeAddedToTheSymbollistButItIsnTDeclar = '%s was supposed to be added to the symbollist, but it isn''t declared';
rsTheAddressInCreatethreadIsNotValid = 'The address in createthread(%s) is not valid';
rsTheAddressInCreatethreadAndWaitIsNotValid = 'The address in createthreadandwait(%s) is not valid';
rsTheAddressInLoadbinaryIsNotValid = 'The address in loadbinary(%s,%s) is not valid';
rsThisCodeCanBeInjectedAreYouSure = 'This code can be injected. Are you sure?';
rsFailureToAllocateMemory = 'Failure to allocate memory';
rsNotAllInstructionsCouldBeInjected = 'Not all instructions could be injected';
rsTheFollowingKernelAddressesWhereAllocated = 'The following kernel addresses where allocated';
rsTheCodeInjectionWasSuccessfull = 'The code injection was successfull';
rsYouCanOnlyHaveOneEnableSection = 'You can only have one enable section';
rsYouCanOnlyHaveOneDisableSection = 'You can only have one disable section';
rsYouHavnTSpecifiedAEnableSection = 'You havn''t specified a enable section';
rsYouHavnTSpecifiedADisableSection = 'You havn''t specified a disable section';
rsWrongSyntaxSHAREDALLOCNameSize = 'Wrong syntax. SHAREDALLOC(name,size)';
rsAAErrorInTheStructureDefinitionOf = 'Error in the structure definition of %s at line %d';
rsAAIsAReservedWord = '%s is a reserved word';
rsAANoIdeaWhatXis = 'No idea what %s is';
rsAANoEndFound = 'No end found';
rsAATheArrayOfByteNamed = 'The array of byte named %s could not be found';
rsXCouldNotBeFound = '%s could not be found';
rsAAErrorWhileSacnningForAobs = 'Error while scanning for AOB''s : ';
rsAAError = 'Error: ';
rsAAModuleNotFound = 'module not found:';
rsAALuaErrorInTheScriptAtLine = 'Lua error in the script at line ';
rsGoTo = 'Go to ';
rsMissingExcept = 'The {$TRY} at line %d has no matching {$EXCEPT}';
rsNoPreferedRangeAllocWarning = 'None of the ALLOC statements specify a '
+'prefered address. Did you take into account that the JMP instruction is'
+' going to be 14 bytes long?';
rsFailureAlloc = 'Failure allocating memory near %.8x for variable named %s';
rsFailureGettingOriginalInstruction = 'Failure getting the instruction at %x';
rsNearbyAllocationError = 'Nearby allocation error';
rsNearbyAllocationErrorMessageQuestion = 'This script uses nearby allocation'
+' but it is impossible to allocate nearby %x. Please rewrite the script '
+'to function without nearby allocation. Try executing the script anyhow '
+'and allocate on a region outside reach of 2GB? (The target will crash if'
+' the script was not designed with this failure in mind)';
rsFailureAssembling = 'Failure assembling %s at %.8x';
//type
// TregisteredAutoAssemblerCommands = TFPGList<TRegisteredAutoAssemblerCommand>;
type
TAutoAssemblerPrologues=array of TAutoAssemblerPrologue;
PAutoAssemblerPrologues=^TAutoAssemblerPrologues;
var
registeredAutoAssemblerCommands: TList;
AutoAssemblerPrologues: TAutoAssemblerPrologues;
AutoAssemblerProloguesPostAOBSCAN: TAutoAssemblerPrologues;
type
TAllocWarn=class
public
preferedaddress: ptruint;
procedure AllocFailedQuestion;
procedure warn;
end;
procedure TAllocWarn.AllocFailedQuestion;
var r:TModalResult;
begin
r:=MessageDlg(rsNearbyAllocationError, Format(
rsNearbyAllocationErrorMessageQuestion, [preferedaddress]),
mtError, [mbyes, mbno, mbYesToAll, mbNoToAll], 0);
NearbyAllocationFailureFatal:=r in [mrNo,mrNoToAll];
if r in [mrYesToAll, mrNoToAll] then
WarnOnNearbyAllocationFailure:=false;
end;
procedure TAllocWarn.warn;
begin
if MainThreadID=GetCurrentThreadId then
AllocFailedQuestion
else
tthread.Synchronize(nil, AllocFailedQuestion);
end;
function registerAutoAssemblerPrologue(m: TAutoAssemblerPrologue; postAOBSCAN: boolean=false): integer;
var i: integer;
prologues: PAutoAssemblerPrologues;
begin
if postAOBSCAN then prologues:=@AutoAssemblerProloguesPostAOBSCAN
else prologues:=@AutoAssemblerPrologues;
for i:=0 to length(prologues^)-1 do
begin
if assigned(prologues^[i])=false then
begin
prologues^[i]:=m;
result:=i+1;
if postAOBSCAN then
result:=-result;
exit;
end
end;
result:=length(prologues^)+1; //first one is id 1
setlength(prologues^, result);
prologues^[result-1]:=m;
if postAOBSCAN then
result:=-result; //first one is id -1
end;
procedure unregisterAutoAssemblerPrologue(id: integer);
var
prologues: PAutoAssemblerPrologues;
begin
if id=0 then exit; //does not exist
if id<0 then
begin
prologues:=@AutoAssemblerProloguesPostAOBSCAN;
id:=-id;
end
else
prologues:=@AutoAssemblerPrologues;
if id<=length(prologues^) then
begin
{$ifndef jni}
CleanupLuaCall(TMethod(prologues^[id-1]));
{$endif}
prologues^[id-1]:=nil;
end;
end;
procedure RegisterAutoAssemblerCommand(command: string; callback: TAutoAssemblerCallback);
var i: integer;
c:TRegisteredAutoAssemblerCommand;
begin
{$ifndef jni}
if registeredAutoAssemblerCommands=nil then
registeredAutoAssemblerCommands:=TList.Create;
UnregisterAutoAssemblerCommand(command);
command:=uppercase(command);
c:=TRegisteredAutoAssemblerCommand.create;
c.command:=command;
c.callback:=callback;
registeredAutoAssemblerCommands.Add(c);
aa_AddExtraCommand(pchar(command));
{$endif}
end;
procedure UnregisterAutoAssemblerCommand(command: string);
var i,j: integer;
c:TRegisteredAutoAssemblerCommand;
begin
{$ifndef jni}
command:=uppercase(command);
i:=0;
while i<registeredAutoAssemblerCommands.count do
begin
if TRegisteredAutoAssemblerCommand(registeredAutoAssemblerCommands[i]).command=command then
begin
c:=registeredAutoAssemblerCommands[i];
CleanupLuaCall(tmethod(c.callback));
registeredAutoAssemblerCommands.Delete(i);
c.free;
end
else
inc(i);
end;
aa_RemoveExtraCommand(pchar(command));
{$endif jni}
end;
//----------------------------
destructor TDisableInfo.destroy;
begin
setlength(Allocs,0);
setlength(exceptions,0);
freeandnil(registeredsymbols);
if (ccodesymbols<>nil) and (donotfreeccodedata=false) then
freeandnil(ccodesymbols);
if (sourcecodeinfo<>nil) and (donotfreeccodedata=false) then
freeandnil(sourcecodeinfo);
end;
constructor TDisableInfo.create;
begin
setlength(Allocs,0);
setlength(exceptions,0);
registeredsymbols:=tstringlist.create;
registeredsymbols.CaseSensitive:=false;
registeredsymbols.Duplicates:=dupIgnore;
ccodesymbols:=TSymbolListHandler.create;
ccodesymbols.PID:=processid;
allsymbols:=TStringList.create;
allsymbols.CaseSensitive:=false;
allsymbols.Duplicates:=dupIgnore;
end;
function lastChanceAllocPrefered(prefered: ptruint; size: integer; protection:dword): ptruint;
var
starttime: ptruint;
distance: qword;
address: ptruint;
count: integer;
begin
if SystemSupportsWritableExecutableMemory=false then
protection:=PAGE_READWRITE;
starttime:=gettickcount64;
address:=0;
distance:=0;
count:=0;
if prefered mod systeminfo.dwAllocationGranularity>0 then
prefered:=prefered-(prefered mod systeminfo.dwAllocationGranularity);
while (address=0) and ((count<10) or (gettickcount64<starttime+10000)) and (distance<$80000000) do
begin
address:=ptrUint(virtualallocex(processhandle,pointer(prefered+distance),size, MEM_RESERVE or MEM_COMMIT,protection));
if (address=0) and (distance>0) then
address:=ptrUint(virtualallocex(processhandle,pointer(prefered-distance),size, MEM_RESERVE or MEM_COMMIT,protection));
if address=0 then
inc(distance, systeminfo.dwAllocationGranularity);
inc(count);
end;
result:=address;
end;
procedure tokenize(input: string; tokens: tstringlist);
var i: integer;
a: integer;
inquote: boolean=false;
inquote2: boolean=false;
begin
tokens.clear;
a:=-1;
for i:=1 to length(input) do
begin
if inquote and (input[i]<>'''') then continue;
if inquote2 and (input[i]<>'"') then continue;
case input[i] of
'a'..'z','A'..'Z','0'..'9','.', '_','#','@', #128..#255: if a=-1 then a:=i;
else
begin
if (input[i]='''') then
begin
if inquote then
begin
if a<>-1 then
tokens.AddObject(copy(input,a,i-a),tobject(a));
a:=-1;
inquote:=false;
end
else
begin
inquote:=true;
a:=i;
end;
continue;
end;
if (input[i]='"') then
begin
if inquote2 then
begin
if a<>-1 then
tokens.AddObject(copy(input,a,i-a),tobject(a));
a:=-1;
inquote2:=false;
end
else
begin
inquote2:=true;
a:=i;
end;
continue;
end;
if a<>-1 then
tokens.AddObject(copy(input,a,i-a),tobject(a));
a:=-1;
end;
end;
end;
if a<>-1 then
tokens.AddObject(copy(input,a,length(input)),tobject(a));
end;
function tokencheck(input,token:string):boolean;
var tokens: tstringlist;
i: integer;
begin
tokens:=tstringlist.Create;
try
tokenize(input,tokens);
result:=false;
for i:=0 to tokens.Count-1 do
if tokens[i]=token then
begin
result:=true;
break;
end;
finally
tokens.free;
end;
end;
function replacetoken(input: string;token:string;replacewith:string):string;
var tokens: tstringlist;
i,j: integer;
begin
result:=input;
tokens:=tstringlist.Create;
try
tokenize(result,tokens);
for i:=tokens.Count-1 downto 0 do
if tokens[i]=token then
begin
j:=integer(tokens.Objects[i]);
result:=copy(result,1,j-1)+replacewith+copy(result,j+length(token),length(result));
end;
finally
tokens.free;
end;
end;
procedure tokenizeStruct(input: string; tokens: tstringlist);
//a version of tokenize using strutils to split up the strings. (I don't want to replace it yet since it is slightly different, probably better, but let's be safe for now till it's fully tested)
var delims: TSysCharSet;
i: integer;
begin
delims:=[' '];
tokens.clear;
for i:=1 to WordCount(input, delims) do
tokens.add(ExtractWord(i, input, delims));
end;
procedure replaceStructWithDefines(code: Tstrings; linenr: integer);
{
parses the structure starting at the given line number.
removes the structure definition from the code
writes a define(xxxx,xxxxx) inplace starting from the linenumber
precondition: linenr is valid and code[linenr] is indeed a STRUCT name line
Structure format:
struct name
db ? db ? db ? db ?
db ? ? ?
dw ?
dd ?
dq ?
resb 4 resb 4
resw 4
resd 2
resq 1
elementname1:
db ?
resw 1
dw ?
elementname2: dw ?
endstruct
Res is an alternate to the usual "d* ?" but is specifically for no fill (Res stands for reserve)
Usage res* # where # is the number of copies. This is better suited for strings which can not be defined with db ?
when done the name.elementname1 and just elementname1 will be defined at the spot the structure was placed. Subsequent structure definitions can override these defines. Elements may NOT have reserved words as they will get replaced. (like assembler commands)
}
var
currentOffset: integer;
structname: string;
elementname: string;
i,j,k: integer;
tokens: Tstringlist;
elements: tstringlist;
starttoken: integer;
bytesize: integer;
endfound: boolean;
lastlinenr: integer;
procedure structError(reason: string='');
var error: string;
begin
error:=format(rsAAErrorInTheStructureDefinitionOf, [structname, lastlinenr+1]);
if reason<>'' then
error:=error+' :'+reason
else
error:=error+'.';
raise exception.create(error);
end;
begin
lastlinenr:=linenr;
endfound:=false;
structname:=trim(copy(code[linenr], 7, length(code[linenr])));
currentOffset:=0;
tokens:=tstringlist.create;
elements:=tstringlist.create;
for i:=linenr+1 to code.count-1 do
begin
lastlinenr:=i;
tokenizeStruct(code[i], tokens);
j:=0;
if tokens.count>0 then
begin
//first check if it's a label definition
if tokens[0][length(tokens[0])]=':' then
begin
elementname:=copy(tokens[0], 1, Length(tokens[0])-1);
if GetOpcodesIndex(elementname)<>-1 then
structError(format(rsAAIsAReservedWord, [elementname]));
elements.AddObject(elementname, tobject(currentOffset));
j:=1;
end;
//then check if it's the end of the struct
if (uppercase(tokens[0])='ENDSTRUCT') or (uppercase(tokens[0])='ENDS') then
begin
endfound:=true;
break; //all done
end;
end;
//if it's neither a label or structure end then it's a size defining token
while j < tokens.count do
begin
tokens[j]:=uppercase(tokens[j]);
case tokens[j][1] of
'R':
begin
//could be res*
if (length(tokens[j])=4) and (copy(tokens[j],1,3)='RES') then
begin
case tokens[j][4] of
'B': bytesize:=1;
'W': bytesize:=2;
'D': bytesize:=4;
'Q': bytesize:=8;
else
StructError;
end;
//now get the count
inc(j);
if j>=tokens.count then
structError;
try
inc(currentOffset, bytesize*StrToInt(tokens[j]));
except
structerror;
end;
end else structerror;
end;
'D':
begin
//could be d* ?
if length(tokens[j])=2 then
begin
case tokens[j][2] of
'B': bytesize:=1;
'W': bytesize:=2;
'D': bytesize:=4;
'Q': bytesize:=8;
else
StructError;
end;
inc(j);
if j>=tokens.count then
structError;
inc(currentOffset, bytesize);
//check if there are more ?'s after this (in case of dw ? ? ?)
while j<tokens.count-1 do
begin
if tokens[j+1]='?' then //check from the spot in front
begin
inc(currentOffset, bytesize);
inc(j);
end
else
break; //nope
end;
end else structerror;
end;
else
structError(format(rsAANoIdeaWhatXis, [tokens[j]])); //we already dealth with labels, so this is wrong
end;
inc(j); //next token
end;
end;
if endfound=false then
structerror(rsAANoEndFound);
//the elements have been filled in, delete the structure (between linenr and lastlinenr) and inject define(element,offset) and define(structname.element,offset)
for i:=lastlinenr downto linenr do
code.Delete(i);
for i:=0 to elements.count-1 do
begin
code.Insert(linenr,'define('+elements[i]+','+inttohex(ptruint(elements.objects[i]),1)+')');
code.Insert(linenr,'define('+structname+'.'+elements[i]+','+inttohex(ptruint(elements.objects[i]),1)+')');
end;
code.insert(linenr, 'define('+structname+'_size,'+inttohex(currentOffset,1)+')');
tokens.Free;
elements.free;
end;
procedure getPotentialLabels(code: Tstrings; labels: TStrings);
//parse the script for xxxx: lines and store them in labels
//this list gets used when it's about to error out on an undefined symbol, and if found in here, add it as a label
//pre: called after comments are removed
var
i: integer;
currentline: string;
a: int64;
begin
for i:=0 to code.count-1 do
begin
currentline:=trim(code[i]);
if (currentline<>'') and (currentline[length(currentline)]=':') then
begin
if (pos('+', currentline)=0) and (pos('.', currentline)=0) then
begin
currentline:=copy(currentline,1,length(currentline)-1);
if trystrtoint64('$'+currentline, a)=false then
labels.add(currentline);
end;
end;
end;
end;
procedure removecomments(code: tstrings);
var i,j: integer;
currentline: string;
instring: boolean;
incomment: boolean;
bracecomment: boolean;
begin
//remove comments
instring:=false;
incomment:=false;
bracecomment:=false;
for i:=0 to code.count-1 do
begin
currentline:=code[i];
instring:=false; //ce doesn't do multiline strings
for j:=1 to length(currentline) do
begin
if incomment then
begin
//inside a comment, remove everything till a } is encountered
if (bracecomment and ((currentline[j]='}') and (processhandler.SystemArchitecture<>archArm))) or
((not bracecomment) and (currentline[j]='*') and (j<length(currentline)) and (currentline[j+1]='/')) then
begin
incomment:=false; //and continue parsing the code...
if (not bracecomment) then
currentline[j+1]:=' ';
end;
currentline[j]:=' ';
end
else
begin
if (currentline[j]='''') then instring:=not instring;
if currentline[j]=#9 then currentline[j]:=' '; //tabs are basicly comments
if not instring then
begin
//not inside a string, so comment markers need to be dealt with
if (currentline[j]='/') and (j<length(currentline)) and (currentline[j+1]='/') then //- comment (only the rest of the line)
begin
//cut off till the end of the line (and might as well jump out now)
currentline:=copy(currentline,1,j-1);
break;
end;
if ((currentline[j]='{') and (processhandler.SystemArchitecture<>archArm)) or
((currentline[j]='/') and (j<length(currentline)) and (currentline[j+1]='*')) then
begin
incomment:=true;
bracecomment:=currentline[j]='{';
currentline[j]:=' '; //replace from here till the first } with spaces, this goes on for multiple lines
end;
end;
end;
end;
code[i]:=trim(currentline);
end;
end;
procedure unlabeledlabels(code: tstrings);
var i,j: integer;
lastseenlabel: integer;
labels: array of string; //sorted in order of definition
currentline: string;
begin
//unlabeled label support
//For those reading the source, PLEASE , try not to code scripts like that
//the scripts you make look like crap, and are hard to read. (like using goto in a c app)
//this is just to make one guy happy
setlength(labels,0);
i:=0;
while i<code.count do
begin
currentline:=code[i];
if length(currentline)>1 then
begin
if currentline='@@:' then
begin
currentline:='RandomLabel'+chr(ord('A')+random(26))+
chr(ord('A')+random(26))+
chr(ord('A')+random(26))+
chr(ord('A')+random(26))+
chr(ord('A')+random(26))+
chr(ord('A')+random(26))+
chr(ord('A')+random(26))+
chr(ord('A')+random(26))+
chr(ord('A')+random(26))+
':';
code[i]:=currentline;
code.Insert(0,'label('+copy(currentline,1,length(currentline)-1)+')');
inc(i);
setlength(labels,length(labels)+1);
labels[length(labels)-1]:=copy(currentline,1,length(currentline)-1);
end else
if currentline[length(currentline)]=':' then
begin
setlength(labels,length(labels)+1);
labels[length(labels)-1]:=copy(currentline,1,length(currentline)-1);
end;
end;
inc(i);
end;
//all label definitions have been filled in
//now change @F (forward) and @B (back) to the labels in front and behind
lastseenlabel:=-1;
for i:=0 to code.Count-1 do
begin
currentline:=code[i];
if length(currentline)>1 then
begin
if currentline[length(currentline)]=':' then
begin
//find this in the array
currentline:=copy(currentline,1,length(currentline)-1);
for j:=(lastseenlabel+1) to length(labels)-1 do //lastseenlabel+1 since it is ordered in definition
begin
if uppercase(currentline)=uppercase(labels[j]) then
begin
lastseenlabel:=j;
break;
end;
end;
currentline:=currentline+':';
//lastseenlabel is now updated to the current pos
end else
if pos('@f',lowercase(currentline))>0 then //forward
begin
//forward label, so labels[lastseenlabel+1]
if lastseenlabel+1 >= length(labels) then
raise exception.Create(rsForwardJumpWithNoLabelDefined);
currentline:=replacetoken(currentline,'@f',labels[lastseenlabel+1]);
currentline:=replacetoken(currentline,'@F',labels[lastseenlabel+1]);
end else
if pos('@b',lowercase(currentline))>0 then //back
begin
//forward label, so labels[lastseenlabel]
if lastseenlabel=-1 then
raise exception.Create(rsThereIsCodeDefinedWithoutSpecifyingTheAddressItBel);
currentline:=replacetoken(currentline,'@b',labels[lastseenlabel]);
currentline:=replacetoken(currentline,'@B',labels[lastseenlabel]);
end;
end;
code[i]:=currentline;
end;
end;
function aobscans(code: tstrings; syntaxcheckonly: boolean): boolean;
//Replaces all AOBSCAN lines with DEFINE(NAME,ADDRESS)
type
TAOBEntry = record
name: string;
aobstring: string;
linenumber: integer;
end;
PAOBEntry=^TAOBEntry;
var i,j,k, m: integer;
aobscanmodules: array of record
name: string;
entries: array of TAOBEntry;
minaddress, maxaddress: ptruint;
protection: string;
memscan: TMemScan;
end;
a,b,c,d,e: integer;
currentline: string;
s1,s2, s3,s4: string;
testptr: ptruint;
cpucount: integer;
threads: integer;
mi: TModuleInfo;
aobstrings: string;
error: boolean;
errorstring: string;
aob1, aob2: dword;
startaddress, stopaddress: ptruint;
procedure finished(f: integer);
//cleanup a memscan and fill in the results
var i,j: integer;
results: TAddresses;
aoblist: string;
begin
setlength(results,0);
if length(aobscanmodules[f].entries)=1 then
begin
//if only 1 entry a normal aobscan was done, so use GetOnlyOneResult instead
if aobscanmodules[f].memscan.GetOnlyOneResult(testptr) then
begin
setlength(results,1);
results[0]:=testptr;
if not InRangeX(results[0], aobscanmodules[f].minaddress, aobscanmodules[f].maxaddress) then
begin
raise EAutoAssembler.Create('Invalid result for aob region scan');
end;
end;
end
else
aobscanmodules[f].memscan.GetOnlyOneResults(results);
if length(results)=length(aobscanmodules[f].entries) then
begin
for i:=0 to length(aobscanmodules[f].entries)-1 do
begin
if results[i]=0 then
begin
error:=true;
errorstring:=format(rsAATheArrayOfByteNamed, [aobscanmodules[f].entries[i].name]);
end
else
code[aobscanmodules[f].entries[i].linenumber]:='DEFINE('+aobscanmodules[f].entries[i].name+', '+inttohex(results[i],8)+')';
end;
end
else
begin
error:=true;
aoblist:='';
for i:=0 to length(aobscanmodules[f].entries)-1 do
aoblist:=aoblist+aobscanmodules[f].entries[i].name+' ';
if aobscanmodules[f].memscan.GetErrorString<>'' then
errorstring:=rsAAErrorWhileSacnningForAobs+aoblist+#13#10#13#10+rsAAError+aobscanmodules[f].memscan.GetErrorString
else
errorstring:=rsAAErrorWhileSacnningForAobs+aoblist+#13#10#13#10+rsAAError+'Not all results found';
end;
aobscanmodules[f].memscan.Free;
aobscanmodules[f].memscan:=nil;
dec(threads);
end;
begin
result:=false;
error:=false;
setlength(aobscanmodules,0);
cpucount:=GetCPUCount;
threads:=0;
for i:=0 to code.Count-1 do
begin
//AOBSCAN(variable,aobtring) (works like define)
currentline:=code[i];
if uppercase(copy(currentline,1,10))='AOBSCANEX(' then
begin
//convert this line from AOBSCANEX(varname,bytestring) to DEFINE(varname,address)
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=pos(')',currentline);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
//s1=varname
//s2=AOBstring
testPtr:=0;
if (not syntaxcheckonly) then
begin
//find the ' ' module (single space)
m:=-1;
for j:=0 to length(aobscanmodules)-1 do
if aobscanmodules[j].name=' ' then
begin
m:=j;
break;
end;
if m=-1 then
begin
setlength(aobscanmodules, length(aobscanmodules)+1);
m:=length(aobscanmodules)-1;
aobscanmodules[m].name:=' ';
aobscanmodules[m].minaddress:=0;
aobscanmodules[m].protection:='*C*W+X'; //don't care about copy on write or writable, but executable must be set
{$ifdef cpu64}
if processhandler.is64Bit then
aobscanmodules[m].maxaddress:=qword($7fffffffffffffff)
else
{$endif}
begin
if Is64bitOS then
aobscanmodules[m].maxaddress:=$ffffffff
else
aobscanmodules[m].maxaddress:=$7fffffff;
end;
aobscanmodules[m].maxaddress:=qword($ffffffffffffffff);
setlength(aobscanmodules[m].entries,0); //shouldn't be needed, but do it anyhow
end;
j:=length(aobscanmodules[m].entries);
setlength(aobscanmodules[m].entries, j+1);
aobscanmodules[m].entries[j].name:=s1;
aobscanmodules[m].entries[j].aobstring:=s2;
aobscanmodules[m].entries[j].linenumber:=i;
end
else
code[i]:='DEFINE('+s1+', 00000000)';
end else raise exception.Create(rsWrongSyntaxAOBSCANName11223355);
end;
if uppercase(copy(currentline,1,8))='AOBSCAN(' then
begin
//convert this line from AOBSCAN(varname,bytestring) to DEFINE(varname,address)
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=pos(')',currentline);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
//s1=varname
//s2=AOBstring
testPtr:=0;
if (not syntaxcheckonly) then
begin
//find the '' module
m:=-1;
for j:=0 to length(aobscanmodules)-1 do
if aobscanmodules[j].name='' then
begin
m:=j;
break;
end;
if m=-1 then
begin
setlength(aobscanmodules, length(aobscanmodules)+1);
m:=length(aobscanmodules)-1;
aobscanmodules[m].name:='';
aobscanmodules[m].minaddress:=0;
{$ifdef cpu64}
if processhandler.is64Bit then
aobscanmodules[m].maxaddress:=qword($7fffffffffffffff)
else
{$endif}
begin
if Is64bitOS then
aobscanmodules[m].maxaddress:=$ffffffff
else
aobscanmodules[m].maxaddress:=$7fffffff;
end;
aobscanmodules[m].maxaddress:=qword($ffffffffffffffff);
setlength(aobscanmodules[m].entries,0); //shouldn't be needed, but do it anyhow
end;
j:=length(aobscanmodules[m].entries);
setlength(aobscanmodules[m].entries, j+1);
aobscanmodules[m].entries[j].name:=s1;
aobscanmodules[m].entries[j].aobstring:=s2;
aobscanmodules[m].entries[j].linenumber:=i;
end
else
code[i]:='DEFINE('+s1+', 00000000)';
end else raise exception.Create(rsWrongSyntaxAOBSCANName11223355);
end;
if uppercase(copy(currentline,1,14))='AOBSCANMODULE(' then //AOBSCANMODULE(varname, modulename, bytestring)
begin
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=PosEx(',',currentline,b+1);
d:=pos(')',currentline);
if d<=a then raise exception.create(rsWrongSyntaxAOBSCANMODULEName11223355);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
s3:=trim(copy(currentline,c+1,d-c-1));
//s1=varname
//s2=MODULE
//s3=aob
testPtr:=0;
if (not syntaxcheckonly) then
begin
//find the s2 module
m:=-1;
for j:=0 to length(aobscanmodules)-1 do
if aobscanmodules[j].name=uppercase(s2) then
begin
m:=j;
break;
end;
if m=-1 then
begin
setlength(aobscanmodules, length(aobscanmodules)+1);
m:=length(aobscanmodules)-1;
aobscanmodules[m].name:=uppercase(s2);
if symhandler.getmodulebyname(s2, mi) then
begin
aobscanmodules[m].minaddress:=mi.baseaddress;
aobscanmodules[m].maxaddress:=mi.baseaddress+mi.basesize;
end
else
begin
//modulename not found. Perhaps a symbol was used
try
testptr:=symhandler.getAddressFromName(s2);
if symhandler.getmodulebyaddress(testptr, mi) then
begin
aobscanmodules[m].minaddress:=mi.baseaddress;
aobscanmodules[m].maxaddress:=mi.baseaddress+mi.basesize;
end;
except
raise exception.create(rsAAModuleNotFound+s2);
end;
end;
setlength(aobscanmodules[m].entries,0);
end;
j:=length(aobscanmodules[m].entries);
setlength(aobscanmodules[m].entries, j+1);
aobscanmodules[m].entries[j].name:=s1;
aobscanmodules[m].entries[j].aobstring:=s3;
aobscanmodules[m].entries[j].linenumber:=i;
end
else
code[i]:='DEFINE('+s1+', 00000000)';
end else raise exception.Create(rsWrongSyntaxAOBSCANMODULEName11223355);
end;
if uppercase(copy(currentline,1,14))='AOBSCANREGION(' then //AOBSCANREGION(varname, startaddress, stopaddress, bytestring)
begin
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=PosEx(',',currentline,b+1);
d:=PosEx(',',currentline,c+1);
e:=pos(')',currentline);
if (d<=a) or (b<=a) or (c<=a) or (d<=a) or (e<=a) then raise exception.create(rsWrongSyntaxAOBSCANREGION);
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
s3:=trim(copy(currentline,c+1,d-c-1));
s4:=trim(copy(currentline,d+1,e-d-1));
if (not syntaxcheckonly) then
begin
startaddress:=symhandler.getAddressFromName(s2);
stopaddress:=symhandler.getAddressFromName(s3);
//see if this region is already being scanned
m:=-1;
for j:=0 to length(aobscanmodules)-1 do
begin
//exact address only. No widening/appending of the groups is possible (Users may want to scan for 00 00 in a 16 byte region)
if (startaddress=aobscanmodules[j].minaddress) and (stopaddress=aobscanmodules[j].maxaddress) then
begin
m:=j;
break;
end;
end;
if m=-1 then
begin
setlength(aobscanmodules, length(aobscanmodules)+1);
m:=length(aobscanmodules)-1;
aobscanmodules[m].name:='<REGION>';
aobscanmodules[m].minaddress:=startaddress;
aobscanmodules[m].maxaddress:=stopaddress;
setlength(aobscanmodules[m].entries,0);
end;
j:=length(aobscanmodules[m].entries);
setlength(aobscanmodules[m].entries, j+1);
aobscanmodules[m].entries[j].name:=s1;
aobscanmodules[m].entries[j].aobstring:=s4;
aobscanmodules[m].entries[j].linenumber:=i;
end
else
code[i]:='DEFINE('+s1+', 00000000)';
end;
end;
//do simultaneous scans for the selected modules
if length(aobscanmodules)>0 then
result:=true; //script uses aobscan
for i:=0 to length(aobscanmodules)-1 do
begin
//wait for one to finish
j:=0;
while threads>=cpucount do
begin
if (aobscanmodules[j].memscan<>nil) and (aobscanmodules[j].memscan.waittilldone(50)) then
begin
finished(j);
break;
end;
j:=(j+1) mod i;
end;
inc(threads);
aobscanmodules[i].memscan:=TMemScan.create(nil);
aobscanmodules[i].memscan.OnlyOne:=true;
aobstrings:='';
for j:=0 to length(aobscanmodules[i].entries)-1 do
aobstrings:=aobstrings+'('+aobscanmodules[i].entries[j].aobstring+')';
if length(aobscanmodules[i].entries)=1 then //bytearrays is slightly slower, so only use it if more than one entry is to be scanned
aobscanmodules[i].memscan.firstscan(soExactValue, vtByteArray, rtRounded, aobscanmodules[i].entries[0].aobstring, aobscanmodules[i].protection, aobscanmodules[i].minaddress, aobscanmodules[i].maxaddress, true, false, false, false, fsmNotAligned)
else
aobscanmodules[i].memscan.firstscan(soExactValue, vtByteArrays, rtRounded, aobstrings, aobscanmodules[i].protection, aobscanmodules[i].minaddress, aobscanmodules[i].maxaddress, true, false, false, false, fsmNotAligned);
end;
//now wait till all are finished
for i:=0 to length(aobscanmodules)-1 do
if aobscanmodules[i].memscan<>nil then
begin
aobscanmodules[i].memscan.waittilldone;
finished(i);
end;
if error then raise EAutoAssembler.create(errorstring);
end;
procedure parseTryExcept(code: tstrings; var exceptionlist: TAAExceptionInfoList);
//Find and replace {$TRY} , {$EXCEPT} with labels
var
i,j: integer;
trynr: integer;
trylist: array of record
linenr: integer;
trynr: integer;
hasexcept: boolean;
trylabel, exceptlabel: string;
end;
found: boolean;
begin
trynr:=0;
setlength(trylist,0);
for i:=0 to code.Count-1 do
begin
if uppercase(code[i])='{$TRY}' then
begin
inc(trynr);
j:=length(trylist);
setlength(trylist,j+1);
trylist[j].trynr:=trynr;
trylist[j].hasexcept:=false;
trylist[j].linenr:=integer(code.Objects[i]);
trylist[j].trylabel:='tryoperation_'+inttostr(trynr);
code[i]:=trylist[j].trylabel+':';
end;
if uppercase(code[i])='{$EXCEPT}' then
begin
//find the last try that doesn't have an except filled in
found:=false;
for j:=length(trylist)-1 downto 0 do
begin
if trylist[j].hasexcept=false then
begin
trylist[j].hasexcept:=true;
trylist[j].exceptlabel:='tryoperation'+inttostr(trylist[j].trynr)+'_except';
code[i]:=trylist[j].exceptlabel+':';
found:=true;
break;
end;
end;
if not found then
raise exception.create(format('Found an {$EXCEPT} at line %d with no matching {$TRY}',[integer(code.Objects[i])]));
end;
end;
setlength(exceptionlist, length(trylist));
for i:=0 to length(trylist)-1 do
begin
code.Insert(0,'label('+trylist[i].trylabel+')');
code.Insert(0,'label('+trylist[i].exceptlabel+')');
exceptionlist[i].trylabel:=trylist[i].trylabel;
exceptionlist[i].exceptlabel:=trylist[i].exceptlabel;
if trylist[i].hasexcept=false then raise exception.create(format(rsMissingExcept, [trylist[i].linenr]));
end;
end;
procedure luacode(code: TStrings; syntaxcheckonly: boolean; memrec: TMemoryRecord=nil);
{
Find and execute the LUA parts:
function (syntaxcheck)
<code>
end
If the functions return a string, substitute the code with the given string
}
var
i,j,k: integer;
s: tstringlist;
stack: integer;
str: string;
error: boolean;
L: Plua_State;
begin
i:=0;
while i<code.Count do
begin
//search for {$LUA}
str:=uppercase(TrimRight(code[i]));
if str='{$LUA}' then
begin
//search for {$ASM} or the end
j:=i+1;
while j<=code.count do
begin
if (j=code.count) or (uppercase(TrimRight(code[j]))='{$ASM}') then
begin
s:=TStringList.create;
s.add('local syntaxcheck,memrec=...');
code[i]:='';
for k:=i+1 to j-1 do
begin
s.Add(code[k]);
code[k]:=''; //empty
end;
if j<>code.count then
code[j]:='';
{$ifndef NOLUA}
L:=GetLuaState;
try
stack:=lua_Gettop(L);
error:=false;
luaL_loadstring(L, pchar(s.text));
if lua_isfunction(L, -1) then
begin
lua_pushboolean(L, syntaxcheckonly);
luaclass_newClass(L, memrec);
if lua.lua_pcall(L, 2, 1, 0)=0 then
begin
if lua_isstring(L,-1) then
begin
str:=Lua_ToString(L, -1);
s:=tstringlist.create;
s.text:=str;
for k:=0 to s.count-1 do
code.Insert(i+k, s[k]);
s.free;
end;
end
else
error:=true;
end
else
error:=true;
if error then
begin
if lua_isstring(L, -1) then
raise exception.create(rsAALuaErrorInTheScriptAtLine+inttostr(integer(code.Objects[i]))+':'+lua_tostring(L, -1))
else
raise exception.create(rsAALuaErrorInTheScriptAtLine+inttostr(integer(code.Objects[i])));
end;
finally
lua_settop(L, stack);
end;
{$endif}
break;
end;
inc(j);
end;
end
else
inc(i);
end;
end;
var nextaaid: longint;
function autoassemble2(code: tstrings;popupmessages: boolean;syntaxcheckonly:boolean; targetself: boolean; disableinfo: TDisableInfo=nil; memrec: TMemoryRecord=nil):boolean;
{
registeredsymbols is a stringlist that is initialized by the caller as case insensitive and no duplicates
}
type tassembled=record
address: ptrUint;
bytes: TAssemblerbytes;
createthreadandwait: integer;
end;
type tlabel=record
defined: boolean;
afterccode: boolean;
insideAllocatedMemory: boolean;
address:ptrUint;
labelname: string;
assemblerline: integer;
references: array of integer; //index of assembled array
references2: array of integer; //index of assemblerlines array
end;
type tfullaccess=record
address: ptrUint;
size: dword;
end;
type tdefine=record
name: string;
whatever: string;
end;
var i: integer=0;
j: integer=0;
k: integer=0;
l: integer=0;
e: integer=0;
currentline: string='';
currentline2: string='';
currentlinenr: integer=0;
currentlinep: pchar=nil;
currentaddress: ptrUint=0;
assembled: array of tassembled=[];
x: ptruint=0;
y:dword=0;
op: dword=0;
op2:dword=0;
ok1:boolean=false;
ok2:boolean=false;
loadbinary: array of record
address: string; //string since it might be a label/alloc/define
filename: string;
end=[];
readmems: array of record
bytelength: integer;
bytes: PByteArray;
end=[];
//allocs:
globalallocs: array of tcealloc=[];
allocs: array of tcealloc=[];
kallocs: array of tcealloc=[];
sallocs: array of tcealloc=[];
tempalloc: tcealloc;
labels: array of tlabel=[];
defines: array of tdefine=[];
fullaccess: array of tfullaccess=[];
dealloc: array of PtrUInt=[];
addsymbollist: array of string=[];
deletesymbollist: array of string=[];
createthread: array of string=[];
createthreadandwait: array of record
name: string;
position: integer; //after what position should the call happen (This is so that the exception handlers can be registered before the final hookcode is written)
timeout: integer;
end;
a,b,c,d: integer;
s1,s2,s3: string;
slist: TStringDynArray=[];
sli: integer=0; //slist iterator
diff: ptruint=0;
assemblerlines: array of record
linenr: integer;
line: string;
end=[];
exceptionlist: TAAExceptionInfoList=[];
{$ifdef onebytejumps}
onebytejumps: TAAExceptionRIPChangeInfoList=[];
{$endif}
varsize: integer=0;
tokens: tstringlist=nil;
baseaddress: ptrUint=0;
multilineinjection: tstringlist=nil;
include: tstringlist=nil;
testdword,bw: dword;
testPtr: ptrUint=0;
binaryfile: tmemorystream=nil;
incomment: boolean=false;
bytebuf: PByteArray=nil;
processhandle: THandle=0;
ProcessID: DWORD=0;
bytes: tbytes=[];
oldprefered: ptrUint=0;
prefered: ptrUint=0;
protection: dword=0;
oldprocessid: dword;
oldhandle: thandle=0;
oldsymhandler: TSymHandler=nil;
disassembler: TDisassembler=nil;
threadhandle: THandle=0;
potentiallabels: TStringlist=nil;
connection: TCEConnection=nil;
mi: TModuleInfo;
aaid: longint=0;
strictmode: boolean=false;
hastryexcept: boolean=false;
//aggressiveAlloc: boolean;
createthreadandwaitid: integer=0;
vpe: boolean=false;
nops: Tassemblerbytes=[];
mustbefar: boolean=false;
usesaobscan: boolean=false;
dataForAACodePass2: TAutoAssemblerCodePass2Data;
debug_getAddressFromScript: boolean=false;
function getAddressFromScript(name: string; alternate: boolean=false): ptruint;
var
found: boolean;
j,k: integer;
temps: string;
oldname: string;
begin
if name='' then
begin
OutputDebugString('getAddressFromScript with an empty name');
exit(0);
end;
result:=0;
found:=false;
if debug_getAddressFromScript then OutputDebugString('getAddressFromScript');
oldname:=name;
name:=uppercase(name);
if debug_getAddressFromScript then OutputDebugString('looking for '+name);
if debug_getAddressFromScript then OutputDebugString('allocs...');
for j:=0 to length(allocs)-1 do
if uppercase(allocs[j].varname)=name then
exit(allocs[j].address);
if debug_getAddressFromScript then OutputDebugString('kallocs...');
for j:=0 to length(kallocs)-1 do
if uppercase(kallocs[j].varname)=name then
exit(kallocs[j].address);
if debug_getAddressFromScript then OutputDebugString('labels...');
for j:=0 to length(labels)-1 do
if uppercase(labels[j].labelname)=name then
begin
if labels[j].defined then
exit(labels[j].address);
end;
if debug_getAddressFromScript then OutputDebugString('defines...');
for j:=0 to length(defines)-1 do
if uppercase(defines[j].name)=name then
begin
try
result:=symhandler.getAddressFromName(defines[j].whatever);
exit;
except
end;
end;
if debug_getAddressFromScript then OutputDebugString('symbols original case...');
try
if targetself then
result:=selfsymhandler.getAddressFromName(oldname)
else
result:=symhandler.getAddressFromName(oldname);
if result<>0 then exit;
if debug_getAddressFromScript then OutputDebugString('result=0 and no exception....');
except
end;
if debug_getAddressFromScript then OutputDebugString('symbols uppercase...');
try
if targetself then
result:=selfsymhandler.getAddressFromName(name)
else
result:=symhandler.getAddressFromName(name);
if result<>0 then exit;
if debug_getAddressFromScript then OutputDebugString('result=0 and no exception....');
except
end;
if debug_getAddressFromScript then OutputDebugString('not a registered symbol');
if (not alternate) and
(not processhandler.is64Bit) and
(name[1]='_') and
(name[length(name)] in ['0'..'9']) and
name.Contains('@')
then
begin
//it could be a _symbolname@###
j:=name.IndexOf('@');
temps:=name.Substring(j+1);
if TryStrToInt(temps,k) then
begin
//it is a _symbolname@###
temps:=name.Substring(1,j-1);
exit(getAddressFromScript(temps,true));
end;
end;
if debug_getAddressFromScript then OutputDebugString('not found');
end;
procedure handleCreateThreadAndWait(ctawi: integer);
begin
//create the thread and wait for it's result
testptr:=getAddressFromScript(createthreadandwait[ctawi].name);
threadhandle:=createremotethread(processhandle,nil,0,pointer(testptr),nil,0,bw);
ok2:=threadhandle>0;
if ok2 then
begin
{$ifdef windows}
try
k:=createthreadandwait[ctawi].timeout;
if k<=0 then y:=INFINITE else y:=k;
if WaitForSingleObject(threadhandle, y)<>WAIT_OBJECT_0 then
raise EAssemblerException.create('createthreadandwait did not execute properly');
finally
closehandle(threadhandle);
end;
{$else}
sleep(5000); //todo: implement proper wait
{$endif}
end;
createthreadandwait[ctawi].position:=-1; //mark it as handled
end;
begin
setlength(readmems,0);
setlength(allocs,0);
setlength(kallocs,0);
setlength(globalallocs,0);
setlength(sallocs,0);
setlength(createthread,0);
setlength(createthreadandwait,0);
setlength(defines,0);
setlength(labels,0);
FillChar(dataForAACodePass2, sizeof(dataForAACodePass2),0);
connection:=nil;
i:=0;
j:=0;
k:=0;
l:=0;
e:=0;
currentaddress:=0;
//add all symbols as predefined labels
if disableinfo<>nil then
begin
setlength(labels, disableinfo.allsymbols.count);
for i:=0 to disableinfo.allsymbols.count-1 do
begin
FillMemory(@labels[length(labels)-1],sizeof(labels[0]),0);
labels[i].defined:=true;
labels[i].address:=ptruint(disableinfo.allsymbols.Objects[i]);
labels[i].labelname:=disableinfo.allsymbols[i];
labels[i].assemblerline:=-2; //undo symbol label
end;
end;
{$ifndef jni}
if targetself then
begin
//get this function to use the symbolhandler that's pointing to CE itself and the self processid/handle
oldhandle:=processhandlerunit.ProcessHandle;
oldprocessid:=processid;
processid:=getcurrentprocessid;
processhandle:=getcurrentprocess;
oldsymhandler:=symhandler;
symhandler:=selfsymhandler;
processhandler.processid:=getcurrentprocessid;
processhandler.processhandle:=processhandle;
end
else
{$endif}
begin
processid:=processhandlerunit.ProcessID;
processhandle:=processhandlerunit.ProcessHandle;
end;
{$ifndef darwin}
symhandler.waitforsymbolsloaded(true);
{$endif}
{$ifndef jni}
if pluginhandler=nil then exit; //Error. Cheat Engine is not properly configured
aaid:=InterLockedIncrement(nextaaid);
pluginhandler.handleAutoAssemblerPlugin(@currentlinep, 0, aaid); //tell the plugins that an autoassembler script is about to get executed
{$endif}
potentiallabels:=tstringlist.create;
potentiallabels.CaseSensitive:=false;
//2 pass scanner
try
setlength(assembled,1);
setlength(kallocs,0);
setlength(allocs,0);
setlength(dealloc,0);
setlength(assemblerlines,0);
setlength(fullaccess,0);
setlength(addsymbollist,0);
setlength(deletesymbollist,0);
setlength(loadbinary,0);
setlength(exceptionlist,0);
tokens:=tstringlist.Create;
incomment:=false;
if not targetself then
for i:=0 to length(AutoAssemblerPrologues)-1 do
if assigned(AutoAssemblerPrologues[i]) then
AutoAssemblerPrologues[i](code, syntaxcheckonly);
luacode(code, syntaxcheckonly, memrec); //replaces {$lua}/{$asm} blocks with the output of those functions
AutoAssemblerCodePass1(code,dataForAACodePass2, syntaxcheckonly, targetself); //replaces the {$luacode} and {$ccode} blocks with a call to extra routines added to the script
//still here
//c-symbol addition
for i:=0 to length(dataForAACodePass2.cdata.symbols)-1 do
begin
//define the c-code symbol as an undefined labels
j:=length(labels);
setlength(labels, j+1);
ZeroMemory(@labels[j],sizeof(labels[j]));
labels[j].labelname:=dataForAACodePass2.cdata.symbols[i].name;
labels[j].defined:=false;
labels[j].afterccode:=true;
labels[j].assemblerline:=-1;
setlength(labels[j].references,0);
setlength(labels[j].references2,0);
end;
//c-symbol addition^
//one more time getting rid of {$ASM} lines that have been added while they shouldn't be required
for i:=0 to code.count-1 do
if uppercase(TrimRight(code[i]))='{$ASM}' then
code[i]:='';
strictmode:=false;
hastryexcept:=false;
for i:=0 to code.count-1 do
begin
currentline:=uppercase(TrimRight(code[i]));
if currentline='{$STRICT}' then
strictmode:=true;
if currentline='{$TRY}' then
hastryexcept:=true;
//if currentline='{$AGGRESSIVEALLOC}' then
// aggressiveAlloc:=true; //1: pause game, find mem_private block even close to here with some non-allocated space free, resize resume
// //2: allocate in reserved memory
end;
if hastryexcept then
parseTryExcept(code, exceptionlist);
removecomments(code); //also trims each line
unlabeledlabels(code);
if not strictmode then
getPotentialLabels(code, potentiallabels);
//6.3: do the aobscans first
//this will break scripts that use define(state,33) aobscan(name, 11 22 state 44 55), but really, live with it
usesaobscan:=aobscans(code, syntaxcheckonly);
if not targetself then
for i:=0 to length(AutoAssemblerProloguesPostAOBSCAN)-1 do
if assigned(AutoAssemblerProloguesPostAOBSCAN[i]) then
AutoAssemblerProloguesPostAOBSCAN[i](code, syntaxcheckonly);
//first pass
i:=0;
while i<code.Count do
begin
try
try
currentline:=code[i];
currentlinenr:=ptrUint(code.Objects[i]);
//check if useless
if length(currentline)=0 then continue;
if copy(currentline,1,2)='//' then continue; //skip
//do this first. Do not touch registersymbol with any kind of define/label/whatsoever
if uppercase(copy(currentline,1,15))='REGISTERSYMBOL(' then
begin
//add this symbol to the register symbollist
a:=pos('(',currentline);
b:=pos(')',currentline);
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
slist:=s1.Split([',',' ']);
for sli:=0 to length(slist)-1 do
begin
s1:=slist[sli];
setlength(addsymbollist,length(addsymbollist)+1);
addsymbollist[length(addsymbollist)-1]:=s1;
if disableinfo<>nil then
disableinfo.registeredsymbols.Add(s1);
end;
end
else raise exception.Create(rsSyntaxError);
continue;
end;
//apply defines (before DEFINE since define(bla, 123) and define(xxx, bla+123) should work
//also, do not touch define with any previous define
if uppercase(copy(currentline,1,7))='DEFINE(' then
begin
//syntax: define(x,whatever) x=variable name size=bytes
//allocate memory
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=rpos(')',currentline);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=copy(currentline,b+1,c-b-1);
//apply earlier defines to the second part
for j:=0 to length(defines)-1 do
s2:=replacetoken(s2,defines[j].name,defines[j].whatever);
ok1:=true;
for j:=0 to length(defines)-1 do
if uppercase(defines[j].name)=uppercase(s1) then
begin
//redefined from here on
ok1:=false;
defines[length(defines)-1].whatever:=s2
end;
if ok1 then //not duplicate, create it
begin
setlength(defines,length(defines)+1);
defines[length(defines)-1].name:=s1;
defines[length(defines)-1].whatever:=s2;
end;
continue;
end else raise exception.Create(rsWrongSyntaxDEFINENameWhatever+' Got '+currentline);
end;
//normal loop code
for j:=0 to length(defines)-1 do
currentline:=replacetoken(currentline,defines[j].name,defines[j].whatever);
setlength(assemblerlines,length(assemblerlines)+1);
assemblerlines[length(assemblerlines)-1].linenr:=currentlinenr;
assemblerlines[length(assemblerlines)-1].line:=currentline;
//plugins
currentlinep:=@currentline[1];
{$ifndef jni}
pluginhandler.handleAutoAssemblerPlugin(@currentlinep, 1,aaid);
{$endif}
currentline:=currentlinep;
//lua extensions
if registeredAutoAssemblerCommands<>nil then
begin
j:=pos('(', currentline);
if j>0 then
begin
s1:=uppercase(copy(currentline, 1, j-1));
for j:=0 to registeredAutoAssemblerCommands.count-1 do
begin
if TRegisteredAutoAssemblerCommand(registeredAutoAssemblerCommands[j]).command=s1 then
begin
a:=pos('(',currentline);
b:=RPos(')',currentline);
s1:=copy(currentline, a+1, b-a-1);
currentline:=TRegisteredAutoAssemblerCommand(registeredAutoAssemblerCommands[j]).callback(s1, syntaxcheckonly);
//insert the current text as lines into the codelist
multilineinjection:=TStringList.create;
try
multilineinjection.Text:=currentline;
for k:=0 to multilineinjection.Count-1 do
code.InsertObject(i+1+k, multilineinjection[k], pointer(ptruint(currentlinenr)));
finally
multilineinjection.Free;
end;
//showmessage(code.text);
currentline:='';
break;
end;
end;
end;
end;
//if the newline is empty then it has been handled and the plugin doesn't want it to be added for phase2
if length(currentline)=0 then
begin
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end;
//otherwise it hasn't been handled, or it has been handled and the string is a compatible string that passes the phase1 tests (so variablenames converted to 00000000 and whatever else is needed)
//plugins^^^
if uppercase(copy(currentline,1,7))='ASSERT(' then //assert(address,aob)
begin
if not syntaxcheckonly then
begin
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=pos(')',currentline);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
testPtr:= symhandler.getAddressFromName(s1,false);
setlength(bytes,0);
try
ConvertStringToBytes(s2, true, bytes);
except
raise exception.create(Format(rsIsNotAValidBytestring, [s2]));
end;
if length(bytes)>0 then
begin
getmem(bytebuf,length(bytes));
try
if ReadProcessMemory(processhandle, pointer(testPtr), bytebuf, length(bytes),x) then
begin
for j:=0 to length(bytes)-1 do
begin
if bytes[j]>=0 then
if byte(bytes[j])<>bytebuf[j] then
raise exception.Create(Format(rsTheBytesAtAreNotWhatWasExpected, [s1]));
end;
end else raise exception.Create(Format(rsTheMemoryAtCanNotBeRead, [s1]));
finally
freememandnil(bytebuf);
end;
end
else raise exception.Create(Format(rsIsNotAValidBytestring, [s2]));
end
else
raise exception.Create(rsWrongSyntaxASSERTAddress1122335566);
end;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end;
{
if uppercase(copy(currentline,1,12))='SHAREDALLOC(' then
begin
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=pos(')',currentline);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
try
x:=strtoint(s2);
except
raise exception.Create(Format(rsIsNotAValidSize, [s2]));
end;
setlength(sallocs,length(sallocs)+1);
sallocs[length(sallocs)-1].address:=allocateSharedMemoryIntoTargetProcess(s1,x);
sallocs[length(sallocs)-1].varname:=s1;
sallocs[length(sallocs)-1].size:=x;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end
else raise exception.Create(rsWrongSyntaxSHAREDALLOCNameSize);
end; }
if uppercase(copy(currentline,1,12))='GLOBALALLOC(' then
begin
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=PosEx(',',currentline,b+1);
d:=pos(')',currentline);
if (a>0) and (b>0) and (d>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
if c>0 then
begin
s2:=trim(copy(currentline,b+1,c-b-1));
s3:=trim(copy(currentline,c+1,d-c-1));
end
else
begin
s2:=trim(copy(currentline,b+1,d-b-1));
s3:='';
end;
try
x:=strtoint(s2);
except
raise exception.Create(Format(rsIsNotAValidSize, [s2]));
end;
//define it here already
if s3<>'' then
symhandler.SetUserdefinedSymbolAllocSize(s1,x, symhandler.getAddressFromName(s3))
else
symhandler.SetUserdefinedSymbolAllocSize(s1,x);
setlength(globalallocs,length(globalallocs)+1);
globalallocs[length(globalallocs)-1].address:=symhandler.GetUserdefinedSymbolByName(s1);
globalallocs[length(globalallocs)-1].varname:=s1;
globalallocs[length(globalallocs)-1].size:=x;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end
else raise exception.Create(rsWrongSyntaxGLOBALALLOCNameSize);
end;
if uppercase(copy(currentline,1,8))='INCLUDE(' then
begin
a:=pos('(',currentline);
b:=pos(')',currentline);
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
if ExtractFileExt(uppercase(s1))='.' then
s1:=s1+'CEA';
if ExtractFileExt(uppercase(s1))='' then
s1:=s1+'.CEA';
if not fileexists(s1) then //check if it's inside the current location
begin
//if not, check the default paths
s2:=cheatenginedir+'includes'+pathdelim+'s1';
if fileexists(s2) then s1:=s2 else
begin
s2:=cheatenginedir+s1;
if fileexists(s2) then s1:=s2
else
begin
s2:=tablesdir+s1;
if fileexists(s2) then s1:=s2;
end;
end;
if not fileexists(s1) then
raise exception.Create(Format(rsCouldNotBeFound, [s1]));
end;
include:=tstringlist.Create;
try
include.LoadFromFile(s1{$if FPC_FULLVERSION >= 030200}, true{$endif});
removecomments(include);
unlabeledlabels(include);
for j:=i+1 to (i+1)+(include.Count-1) do
code.Insert(j,include[j-(i+1)]);
finally
include.Free;
end;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end
else raise exception.Create(rsWrongSyntaxIncludeFilenameCea);
end;
if uppercase(copy(currentline,1,12))='CREATETHREAD' then
begin
if currentline[13]='(' then //CREATETHREAD(
begin
//create a thread
a:=pos('(',currentline);
b:=pos(')',currentline);
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
setlength(createthread,length(createthread)+1);
createthread[length(createthread)-1]:=s1;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end else raise exception.Create(rsWrongSyntaxCreateThreadAddress);
end
else
begin
//could be createthreadandwait
if uppercase(copy(currentline,13,8))='ANDWAIT(' then //CREATETHREADANDWAIT(
begin
a:=pos('(',currentline);
b:=pos(')',currentline);
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
slist:=s1.Split([',']);
if length(slist)=2 then
begin
try
j:=strtoint(trim(slist[1]));
except
raise exception.Create('Invalid timeout for createthreadandwait');
end;
end
else
begin
if (lastLoadedTableVersion>0) and (lastLoadedTableVersion<=30) then //when the current table was made with an older CE build and it uses CREATETHREADANDWAIT
j:=5000
else
j:=0;
end;
setlength(createthreadandwait,length(createthreadandwait)+1);
createthreadandwait[length(createthreadandwait)-1].name:=slist[0];
createthreadandwait[length(createthreadandwait)-1].position:=length(assemblerlines)-1;
createthreadandwait[length(createthreadandwait)-1].timeout:=j;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end else raise exception.Create(rsWrongSyntaxCreateThreadAddress);
end;
end;
end;
{$ifndef jni}
{$ifdef ONEBYTEJUMPS}
if uppercase(copy(currentline,1,5))='JMP1 ' then
begin
s1:=copy(currentline, 6);
assemblerlines[length(assemblerlines)-1].linenr:=currentlinenr;
assemblerlines[length(assemblerlines)-1].line:='<JMP1 '+s1+'>';
continue;
end;
{$endif}
if uppercase(copy(currentline,1,12))='LOADLIBRARY(' then
begin
//load a library into memory , this one already executes BEFORE the 2nd pass to get addressnames correct
a:=pos('(',currentline);
b:=pos(')',currentline);
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
if (length(s1)>1) and ((s1[1]='''') or (s1[1]='"')) then
s1:=AnsiDequotedStr(s1,s1[1]);
if pos(':',s1)=0 then
begin
s2:=extractfilename(s1);
{$ifdef windows}
if getConnection=nil then //no connection, so local. Check if the file can be found locally and if so, set the specific path
begin
{$endif}
if fileexists(cheatenginedir+s2) then s1:=cheatenginedir+s2 else
if fileexists(getcurrentdir+ PathDelim+s2) then s1:=getcurrentdir+PathDelim+s2 else
if fileexists(cheatenginedir+s1) then s1:=cheatenginedir+s1;
{$ifdef windows}
end;
{$endif}
//else just hope it's in the dll searchpath
end; //else direct file path
try
if symhandler.getmodulebyname(extractfilename(s1), mi)=false then //check if it's already injected
begin
InjectDll(s1,'');
symhandler.reinitialize;
end;
symhandler.waitforsymbolsloaded;
symhandler.waitForExports;
except
raise exception.create(Format(rsCouldNotBeInjected, [s1]));
end;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end else raise exception.Create(rsWrongSyntaxLoadLibraryFilename);
end;
if uppercase(copy(currentline,1,8))='LUACALL(' then
begin
//execute a given lua command
a:=pos('(',currentline);
b:=length(currentline);
if currentline[b]<>')' then b:=-1;
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
LUA_DoScript(s1); //raises an exception on error, which is exactly what we want here
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end else raise exception.Create(rsWrongSyntaxLuaCall);
end;
{$endif}
if uppercase(copy(currentline,1,8))='READMEM(' then
begin
//read memory and place it here (readmem(address,size) )
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=pos(')',currentline);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
//read memory and replace with lines of DB xx xx xx xx xx xx xx xx
try
testptr:=symhandler.getAddressFromName(s1);
except
if not syntaxcheckonly then
raise exception.Create(rsInvalidAddressForReadMem)
else
testptr:=0;
end;
try
a:=strtoint(s2);
except
raise exception.Create(rsInvalidSizeForReadMem);
end;
if a=0 then
raise exception.Create(rsInvalidSizeForReadMem);
getmem(bytebuf,a);
try
if not syntaxcheckonly then
begin
if (not ReadProcessMemory(processhandle, pointer(testptr),bytebuf,a,x)) or (x<a) then
raise exception.Create(Format(rsTheMemoryAtCouldNotBeFullyRead, [s1]));
end;
except
on e:exception do
begin
if bytebuf<>nil then
begin
freememandnil(bytebuf);
end;
raise exception.create(e.Message);
end;
end;
//still here so everything ok
assemblerlines[length(assemblerlines)-1].linenr:=currentlinenr;
assemblerlines[length(assemblerlines)-1].line:='<READMEM'+IntToStr(length(readmems))+'>';
setlength(readmems, length(readmems)+1);
readmems[length(readmems)-1].bytelength:=a;
readmems[length(readmems)-1].bytes:=bytebuf;
bytebuf:=nil;
continue;
end else raise exception.Create(rsWrongSyntaxReadMemAddressSize);
continue;
end;
if uppercase(copy(currentline,1,11))='REASSEMBLE(' then
begin
a:=pos('(',currentline);
b:=pos(')',currentline);
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
try
testptr:=getAddressFromScript(s1);
except
raise exception.Create(format(rsXCouldNotBeFound, [s1]));
end;
if testptr=0 then
raise exception.Create(format(rsXCouldNotBeFound, [s1]));
multilineinjection:=TStringList.create;
GetOriginalInstruction(testptr, multilineinjection, processhandler.is64Bit,true); //don't take on the symbol. (module and section are ok, not symbols)
if multilineinjection.count=0 then
raise exception.create(format(rsFailureGettingOriginalInstruction, [testptr]));
if trim(multilineinjection[0]).StartsWith('//') then //skip comments
setlength(assemblerlines, length(assemblerlines)-1)
else
begin
assemblerlines[length(assemblerlines)-1].linenr:=currentlinenr;
assemblerlines[length(assemblerlines)-1].line:=multilineinjection[0];
end;
for j:=1 to multilineinjection.Count-1 do
begin
setlength(assemblerlines, length(assemblerlines)+1);
assemblerlines[length(assemblerlines)-1].linenr:=currentlinenr;
assemblerlines[length(assemblerlines)-1].line:=multilineinjection[j];
end;
freeandnil(multilineinjection);
continue;
{
disassembler:=TDisassembler.create;
disassembler.dataOnly:=true;
disassembler.disassemble(testptr, s1);
if syntaxcheckonly then currentline:='nop' else
currentline:=disassembler.LastDisassembleData.prefix+' '+Disassembler.LastDisassembleData.opcode+' '+disassembler.LastDisassembleData.parameters;;
assemblerlines[length(assemblerlines)-1].linenr:=currentlinenr;
assemblerlines[length(assemblerlines)-1].line:=currentline;
disassembler.free; }
end else raise exception.Create(rsWrongSyntaxReAssemble);
end;
if uppercase(copy(currentline,1,11))='LOADBINARY(' then
begin
//load a binary file into memory
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=pos(')',currentline);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
if not fileexists(s2) then raise exception.Create(Format(rsTheFileDoesNotExist, [s2]));
setlength(loadbinary,length(loadbinary)+1);
loadbinary[length(loadbinary)-1].address:=s1;
loadbinary[length(loadbinary)-1].filename:=s2;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end else raise exception.Create(rsWrongSyntaxLoadBinaryAddressFilename);
end;
if uppercase(copy(currentline,1,17))='UNREGISTERSYMBOL(' then
begin
//add this symbol to the register symbollist
a:=pos('(',currentline);
b:=pos(')',currentline);
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
if (disableinfo<>nil) and (s1='*') then
begin
j:=length(deletesymbollist);
setlength(deletesymbollist, j+ disableinfo.registeredsymbols.Count);
for k:=0 to disableinfo.registeredsymbols.Count-1 do
deletesymbollist[j+k]:=disableinfo.registeredsymbols[k];
end
else
begin
slist:=s1.Split([',',' ']);
for sli:=0 to length(slist)-1 do
begin
s1:=slist[sli];
setlength(deletesymbollist,length(deletesymbollist)+1);
deletesymbollist[length(deletesymbollist)-1]:=s1;
end;
end;
end
else raise exception.Create(rsSyntaxError);
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end;
//AOBSCAN used to live here, but he moved up
//define
if uppercase(copy(currentline,1,7))='STRUCT ' then
begin
replaceStructWithDefines(code, i);
setlength(assemblerlines,length(assemblerlines)-1);
dec(i); //repeat from this line
continue;
end;
if uppercase(copy(currentline,1,11))='FULLACCESS(' then
begin
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=pos(')',currentline);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
setlength(fullaccess,length(fullaccess)+1);
fullaccess[length(fullaccess)-1].address:=symhandler.getAddressFromName(s1);
fullaccess[length(fullaccess)-1].size:=strtoint(s2);
end else raise exception.Create(rsSyntaxErrorFullAccessAddressSize);
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end;
if uppercase(copy(currentline,1,6))='LABEL(' then
begin
//syntax: label(x) x=name of the label
//later on in the code there has to be a line with "labelname:"
a:=pos('(',currentline);
b:=pos(')',currentline);
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
slist:=s1.Split([',',' ']);
for sli:=0 to length(slist)-1 do
begin
s1:=slist[sli];
val('$'+s1,j,a);
if a=0 then raise exception.Create(Format(rsIsNotAValidIdentifier, [s1]));
varsize:=length(s1);
j:=0;
while (j<length(labels)) and (length(labels[j].labelname)>=varsize) do
begin
//if labels[j].labelname=s1 then
// raise exception.Create(Format(rsIsBeingRedeclared, [s1]));
inc(j);
end;
j:=length(labels);//quickfix
l:=j;
//check for the line "labelname:"
ok1:=false;
for j:=0 to code.Count-1 do
if trim(code[j])=s1+':' then
begin
if ok1 then raise exception.Create(Format(rsLabelIsBeingDefinedMoreThanOnce, [s1]));
ok1:=true;
end;
if not ok1 then raise exception.Create(Format(rsLabelIsNotDefinedInTheScript, [s1]));
//still here so ok
//insert it
setlength(labels,length(labels)+1);
for k:=length(labels)-1 downto j+1 do
labels[k]:=labels[k-1];
ZeroMemory(@labels[l], sizeof(labels[l]));
labels[l].labelname:=s1;
labels[l].defined:=false;
setlength(labels[l].references,0);
setlength(labels[l].references2,0);
end;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end else raise exception.Create(rsSyntaxError);
end;
if (uppercase(copy(currentline,1,8))='DEALLOC(') then
begin
//syntax: dealloc(x) x=name of region to deallocate
//later on in the code there has to be a line with "labelname:"
a:=pos('(',currentline);
b:=pos(')',currentline);
if (a>0) and (b>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
if (s1='*') then
begin
//everything that the script allocated
if (disableinfo<>nil) then
begin
setlength(dealloc, length(disableinfo.allocs));
for j:=0 to length(disableinfo.allocs)-1 do
dealloc[j]:=disableinfo.allocs[j].address;
end;
end
else
begin
slist:=s1.Split([',',' ']);
for sli:=0 to length(slist)-1 do
begin
s1:=slist[sli];
//find s1 in the ceallocarray
for j:=0 to length(disableinfo.allocs)-1 do
begin
if uppercase(disableinfo.allocs[j].varname)=uppercase(s1) then
begin
setlength(dealloc,length(dealloc)+1);
dealloc[length(dealloc)-1]:=disableinfo.allocs[j].address;
end;
end;
end;
end;
end;
setlength(assemblerlines,length(assemblerlines)-1);
continue;
end;
//memory alloc
if (uppercase(copy(currentline,1,5))='ALLOC') and
(
(uppercase(copy(currentline,1,6))='ALLOC(') or
(uppercase(copy(currentline,1,8))='ALLOCNX(') or
(uppercase(copy(currentline,1,8))='ALLOCXO(')
)
then
begin
//syntax: alloc(x,size) x=variable name size=bytes
//or
//syntax: alloc(x,size,prefered region) x=variable name size=bytes
//allocate memory
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=PosEx(',',currentline,b+1);
d:=pos(')',currentline);
if (a>0) and (b>0) and (d>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
if c>0 then
begin
s2:=trim(copy(currentline,b+1,c-b-1));
s3:=trim(copy(currentline,c+1,d-c-1));
end
else
begin
s2:=trim(copy(currentline,b+1,d-b-1));
s3:='';
end;
val('$'+s1,j,a);
if a=0 then raise exception.Create(Format(rsIsNotAValidIdentifier, [s1]));
varsize:=length(s1);
//check for duplicate identifiers
j:=0;
while (j<length(allocs)) and (length(allocs[j].varname)>varsize) do
begin
if allocs[j].varname=s1 then
raise exception.Create(Format(rsTheIdentifierHasAlreadyBeenDeclared, [s1]));
inc(j);
end;
j:=length(allocs);//quickfix
setlength(allocs,length(allocs)+1);
//longest varnames first so the rename of a shorter matching var wont override the longer one
//move up the other allocs so I can inser this element (A linked list might have been better)
for k:=length(allocs)-1 downto j+1 do
allocs[k]:=allocs[k-1];
allocs[j].varname:=s1;
allocs[j].size:=StrToInt(s2);
if s3<>'' then
allocs[j].prefered:=symhandler.getAddressFromName(s3)
else
allocs[j].prefered:=0;
if SystemSupportsWritableExecutableMemory then
allocs[j].protection:=PAGE_EXECUTE_READWRITE
else
allocs[j].protection:=PAGE_EXECUTE_READ;
if uppercase(copy(currentline,1,8))='ALLOCNX(' then
allocs[j].protection:=PAGE_READWRITE
else
if uppercase(copy(currentline,1,8))='ALLOCXO(' then
allocs[j].protection:=PAGE_EXECUTE_READ;
setlength(assemblerlines,length(assemblerlines)-1); //don't bother with this in the 2nd pass
continue;
end else raise exception.Create(rsWrongSyntaxALLOCIdentifierSizeinbytes);
end;
//replace ALLOC identifiers with values so the assemble error check doesnt crash on that
if processhandler.is64bit then
begin
for j:=0 to length(allocs)-1 do
currentline:=replacetoken(currentline,allocs[j].varname,'ffffffffffffffff');
end
else
begin
for j:=0 to length(allocs)-1 do
currentline:=replacetoken(currentline,allocs[j].varname,'00000000');
end;
{$ifndef net}
//memory kalloc
{$ifdef windows}
if uppercase(copy(currentline,1,7))='KALLOC(' then
begin
if not DBKReadWrite then raise exception.Create(rsNeedToUseKernelmodeReadWriteprocessmemory);
if DBKLoaded=false then
raise exception.Create(rsSorryButWithoutTheDriverKALLOCWillNotFunction);
//syntax: kalloc(x,size) x=variable name size=bytes
//kallocate memory
a:=pos('(',currentline);
b:=pos(',',currentline);
c:=pos(')',currentline);
if (a>0) and (b>0) and (c>0) then
begin
s1:=trim(copy(currentline,a+1,b-a-1));
s2:=trim(copy(currentline,b+1,c-b-1));
val('$'+s1,j,a);
if a=0 then raise exception.Create(Format(rsIsNotAValidIdentifier, [s1]));
varsize:=length(s1);
//check for duplicate identifiers
j:=0;
while (j<length(kallocs)) and (length(kallocs[j].varname)>varsize) do
begin
if kallocs[j].varname=s1 then
raise exception.Create(Format(rsTheIdentifierHasAlreadyBeenDeclared, [s1]));
inc(j);
end;
j:=length(kallocs);//quickfix
setlength(kallocs,length(kallocs)+1);
//longest varnames first so the rename of a shorter matching var wont override the longer one
//move up the other kallocs so I can inser this element (A linked list might have been better)
for k:=length(kallocs)-1 downto j+1 do
kallocs[k]:=kallocs[k-1];
kallocs[j].varname:=s1;
kallocs[j].size:=StrToInt(s2);
setlength(assemblerlines,length(assemblerlines)-1); //don't bother with this in the 2nd pass
continue;
end else raise exception.Create(rsWrongSyntaxKallocIdentifierSizeinbytes);
end;
{$endif}
{$endif}
//replace KALLOC identifiers with values so the assemble error check doesnt crash on that
{$ifdef windows}
if processhandler.is64bit then
begin
for j:=0 to length(kallocs)-1 do
currentline:=replacetoken(currentline,kallocs[j].varname,'ffffffffffffffff');
end
else
begin
for j:=0 to length(kallocs)-1 do
currentline:=replacetoken(currentline,kallocs[j].varname,'00000000');
end;
{$endif}
//check for assembler errors
//address
if currentline[length(currentline)]=':' then
begin
try
ok1:=false;
for j:=0 to length(labels)-1 do
if currentline=labels[j].labelname+':' then
begin
labels[j].assemblerline:=length(assemblerlines)-1;
ok1:=true;
continue;
end;
if ok1 then continue; //no check
//still here, so more complex
if syntaxcheckonly and (disableinfo<>nil) then
begin
//replace tokens with registered symbols from the enable part
for j:=0 to disableinfo.registeredsymbols.count-1 do
currentline:=replacetoken(currentline, disableinfo.registeredsymbols[j], '00000000');
end;
try
s1:=copy(currentline,1,length(currentline)-1);
if s1<>'' then
testPtr:=symhandler.getAddressFromName(s1);
except
currentline:=inttohex(symhandler.getaddressfromname(copy(currentline,1,length(currentline)-1)),8)+':';
assemblerlines[length(assemblerlines)-1].linenr:=currentlinenr;
assemblerlines[length(assemblerlines)-1].line:=currentline;
end;
except
//add this as a label if a potential label
if potentiallabels.IndexOf(copy(currentline,1,length(currentline)-1))=-1 then
raise symexception.Create(rsThisAddressSpecifierIsNotValid);
j:=length(labels);
setlength(labels,j+1);
ZeroMemory(@labels[j],sizeof(labels[j]));
labels[j].labelname:=copy(currentline,1,length(currentline)-1);
labels[j].assemblerline:=length(assemblerlines)-1;
labels[j].defined:=false;
setlength(labels[j].references,0);
setlength(labels[j].references2,0);
//setlength(assemblerlines, length(assemblerlines)-1);
// assemblerlines[length(assemblerlines)-1]:='';
// continue;
//raise exception.Create(rsThisAddressSpecifierIsNotValid);
end;
continue; //next line
end;
//replace label references with 00000000 so the assembler check doesn't complain about it
if processhandler.is64bit then
begin
for j:=0 to length(labels)-1 do
currentline:=replacetoken(currentline,labels[j].labelname,'ffffffffffffffff');
end
else
begin
for j:=0 to length(labels)-1 do
currentline:=replacetoken(currentline,labels[j].labelname,'00000000');
end;
try
//replace identifiers in the line with their address
ok1:=false;
try
ok1:=assemble(currentline,currentaddress,assembled[0].bytes, apNone, true);
except
end;
if not ok1 then //the instruction could not be assembled as it is right now
begin
//try potential labels
ok1:=false;
for j:=0 to potentiallabels.count-1 do
begin
if processhandler.is64bit then
currentline:=replacetoken(currentline,potentiallabels[j],'ffffffffffffffff')
else
currentline:=replacetoken(currentline,potentiallabels[j],'00000000');
try
ok1:=assemble(currentline,currentaddress,assembled[0].bytes, apNone, true);
if ok1 then
begin
//define this potential label as a full label
k:=length(labels);
setlength(labels, k+1);
ZeroMemory(@labels[k],sizeof(labels[k]));
labels[k].labelname:=potentiallabels[j];
labels[k].defined:=false;
labels[k].afterccode:=false;
setlength(labels[k].references,0);
setlength(labels[k].references2,0);
break;
end;
except
//don't quit yet
on e: exception do
begin
OutputDebugString('Potential labeling error:'+e.message);
end
end;
end;
end;
if not ok1 then
raise EAutoAssembler.Create('bla');
except
//ShowMessage(code.text);
raise EAutoAssembler.Create(rsThisInstructionCanTBeCompiled);
end;
finally
inc(i);
end;
except
on E:exception do
raise EAutoAssembler.Create(Format(rsErrorInLine, [IntToStr(currentlinenr), currentline, e.Message]));
end;
end;
if length(addsymbollist)>0 then
begin
//now scan the addsymbollist entries for allocs and labels and see if they exist
for i:=0 to length(addsymbollist)-1 do
begin
ok1:=false;
for j:=0 to length(allocs)-1 do //scan allocs
if uppercase(addsymbollist[i])=uppercase(allocs[j].varname) then
begin
ok1:=true;
break;
end;
if not ok1 then //scan labels
for j:=0 to length(labels)-1 do
if uppercase(addsymbollist[i])=uppercase(labels[j].labelname) then
begin
ok1:=true;
break;
end;
if not ok1 then //scan defines
for j:=0 to length(defines)-1 do
if uppercase(addsymbollist[i])=uppercase(defines[j].name) then
begin
ok1:=true;
break;
end;
if not ok1 then raise EAssemblerException.create(Format(rsWasSupposedToBeAddedToTheSymbollistButItIsnTDeclar, [addsymbollist[i]]));
end;
end;
//check to see if the addresses are valid (label, alloc, define)
if length(createthread)>0 then
for i:=0 to length(createthread)-1 do
begin
ok1:=true;
try
testptr:=symhandler.getAddressFromName(createthread[i]);
except
ok1:=false;
end;
if not ok1 then
for j:=0 to length(labels)-1 do
if uppercase(labels[j].labelname)=uppercase(createthread[i]) then
begin
ok1:=true;
break;
end;
if not ok1 then
for j:=0 to length(allocs)-1 do
if uppercase(allocs[j].varname)=uppercase(createthread[i]) then
begin
ok1:=true;
break;
end;
{$ifndef net}
if not ok1 then
for j:=0 to length(kallocs)-1 do
if uppercase(kallocs[j].varname)=uppercase(createthread[i]) then
begin
ok1:=true;
break;
end;
{$endif}
if not ok1 then
for j:=0 to length(defines)-1 do
if uppercase(defines[j].name)=uppercase(createthread[i]) then
begin
try
testptr:=symhandler.getAddressFromName(defines[j].whatever);
ok1:=true;
except
end;
break;
end;
if not ok1 then raise EAssemblerException.create(Format(rsTheAddressInCreatethreadIsNotValid, [createthread[i]]));
end;
if length(createthreadandwait)>0 then
for i:=0 to length(createthreadandwait)-1 do
begin
ok1:=true;
try
testptr:=symhandler.getAddressFromName(createthreadandwait[i].name);
except
ok1:=false;
end;
if not ok1 then
for j:=0 to length(labels)-1 do
if uppercase(labels[j].labelname)=uppercase(createthreadandwait[i].name) then
begin
ok1:=true;
break;
end;
if not ok1 then
for j:=0 to length(allocs)-1 do
if uppercase(allocs[j].varname)=uppercase(createthreadandwait[i].name) then
begin
ok1:=true;
break;
end;
{$ifndef net}
if not ok1 then
for j:=0 to length(kallocs)-1 do
if uppercase(kallocs[j].varname)=uppercase(createthreadandwait[i].name) then
begin
ok1:=true;
break;
end;
{$endif}
if not ok1 then
for j:=0 to length(defines)-1 do
if uppercase(defines[j].name)=uppercase(createthreadandwait[i].name) then
begin
try
testptr:=symhandler.getAddressFromName(defines[j].whatever);
ok1:=true;
except
end;
break;
end;
if not ok1 then
begin
raise EAssemblerException.create(Format(rsTheAddressInCreatethreadAndWaitIsNotValid, [createthreadandwait[i].name]));
end;
end;
if length(loadbinary)>0 then
for i:=0 to length(loadbinary)-1 do
begin
ok1:=true;
try
testptr:=symhandler.getAddressFromName(loadbinary[i].address);
except
ok1:=false;
end;
if not ok1 then
for j:=0 to length(labels)-1 do
if uppercase(labels[j].labelname)=uppercase(loadbinary[i].address) then
begin
ok1:=true;
break;
end;
if not ok1 then
for j:=0 to length(allocs)-1 do
if uppercase(allocs[j].varname)=uppercase(loadbinary[i].address) then
begin
ok1:=true;
break;
end;
{$ifndef net}
if not ok1 then
for j:=0 to length(kallocs)-1 do
if uppercase(kallocs[j].varname)=uppercase(loadbinary[i].address) then
begin
ok1:=true;
break;
end;
{$endif}
if not ok1 then
for j:=0 to length(defines)-1 do
if uppercase(defines[j].name)=uppercase(loadbinary[i].address) then
begin
try
testptr:=symhandler.getAddressFromName(defines[j].whatever);
ok1:=true;
except
end;
break;
end;
if not ok1 then raise EAssemblerException.create(Format(rsTheAddressInLoadbinaryIsNotValid, [loadbinary[i].address, loadbinary[i].filename]));
end;
{
//check for the 3th alloc parameter when testing the validity of the script, and ask if the user understands what will happen
if popupmessages and processhandler.is64Bit and usesaobscan and (length(allocs)>0) then
begin
//check if a prefered address is used
prefered:=0;
for i:=0 to length(allocs)-1 do
if allocs[i].prefered<>0 then
begin
prefered:=allocs[i].prefered;
break;
end;
if (prefered=0) and (MessageDlg(rsNoPreferedRangeAllocWarning, mtWarning, [mbyes, mbno], 0)<>mryes) then
exit(false);
end;}
if syntaxcheckonly then
exit(true);
{$ifndef jni}
if popupmessages and (messagedlg(rsThisCodeCanBeInjectedAreYouSure, mtConfirmation, [mbyes, mbno], 0)<>mryes) then exit;
{$endif}
//allocate the memory
if length(allocs)>0 then
begin
//move ceinternal_autofree allocs to the end
k:=length(allocs);
i:=0;
while i<k do
begin
if allocs[i].varname.StartsWith('ceinternal_autofree') then
begin
//move it to the back
tempalloc:=allocs[i];
for j:=i to length(allocs)-2 do
allocs[j]:=allocs[j+1];
allocs[length(allocs)-1]:=tempalloc;
dec(k);
end
else inc(i);
end;
j:=0; //entry to go from
prefered:=allocs[0].prefered;
protection:=allocs[0].protection;
x:=allocs[0].size;
for i:=1 to length(allocs)-1 do
begin
//does this entry have a prefered location or a non default protection
if allocs[i].protection<>protection then
begin
//increment x to the next pagebase
{$ifdef windows}
if (x and $fff>0) then
begin
y:=$1000- (x and $fff);
inc(x,y);
inc(allocs[i-1].size,y); //adjust the previous entry's size
end;
{$else}
if (x and (getPageSize-1)>0) then
begin
y:=getPageSize- (x and (getPageSize-1));
inc(x,y);
inc(allocs[i-1].size,y); //adjust the previous entry's size
end;
{$endif}
protection:=allocs[i].protection;
end;
if (allocs[i].prefered<>0) then
begin
//if yes, is it the same as the previous entry? (or was the previous one that doesn't care?)
if prefered=0 then
prefered:=allocs[i].prefered;
if (prefered<>allocs[i].prefered) then
begin
//different prefered address
if x>0 then //it has some previous entries with compatible locations
begin
k:=10;
allocs[j].address:=0;
while (k>0) and (allocs[j].address=0) do
begin
//try allocating until a memory region has been found (e.g due to quick allocating by the game)
if (prefered=0) and (j>0) then //if not a prefered address but there is a previous alloc, allocate near there
prefered:=allocs[j-1].address;
oldprefered:=prefered;
prefered:=ptrUint(FindFreeBlockForRegion(prefered,x));
if (prefered=0) and (oldprefered<>0) then
prefered:=oldprefered;
if SystemSupportsWritableExecutableMemory then
allocs[j].address:=ptrUint(virtualallocex(processhandle,pointer(prefered),x, MEM_RESERVE or MEM_COMMIT,PAGE_EXECUTE_READWRITE))
else
allocs[j].address:=ptrUint(virtualallocex(processhandle,pointer(prefered),x, MEM_RESERVE or MEM_COMMIT,PAGE_READWRITE));
if allocs[j].address=0 then
begin
OutputDebugString(rsFailureToAllocateMemory+' 1');
inc(prefered,65536);
end;
dec(k);
end;
if allocs[j].address=0 then
allocs[j].address:=lastChanceAllocPrefered(prefered,x, protection);
if allocs[j].address=0 then
begin
if WarnOnNearbyAllocationFailure then
begin
with TAllocWarn.create do
begin
if allocs[j].prefered<>0 then
preferedaddress:=allocs[j].prefered
else
preferedaddress:=prefered;
warn;
free;
end;
end;
if NearbyAllocationFailureFatal then
raise EAssemblerException.create(format(rsFailureAlloc, [prefered,allocs[j].varname]))
else
begin
if SystemSupportsWritableExecutableMemory then
allocs[j].address:=ptrUint(virtualallocex(processhandle,nil,x, MEM_RESERVE or MEM_COMMIT,PAGE_EXECUTE_READWRITE))
else
allocs[j].address:=ptrUint(virtualallocex(processhandle,nil,x, MEM_RESERVE or MEM_COMMIT,PAGE_READWRITE));
end;
end;
if allocs[j].address=0 then raise EAssemblerException.create(rsFailureToAllocateMemory);
//adjust the addresses of entries that are part of this block
for k:=j+1 to i-1 do
allocs[k].address:=allocs[k-1].address+allocs[k-1].size;
x:=0;
end;
//new prefered address
j:=i;
prefered:=allocs[i].prefered;
protection:=allocs[i].protection;
end;
end;
//no prefered location specified, OR same prefered location
inc(x,allocs[i].size);
end; //after the loop
if x>0 then
begin
//adjust the address of entries that are part of this final block
k:=10;
allocs[j].address:=0;
while (k>0) and (allocs[j].address=0) do
begin
i:=0;
if (prefered=0) and (j>0) then //if not a prefered address but there is a previous alloc, allocate near there
prefered:=allocs[j-1].address;
oldprefered:=prefered;
prefered:=ptrUint(FindFreeBlockForRegion(prefered,x));
if (prefered=0) and (oldprefered<>0) then
prefered:=oldprefered;
if SystemSupportsWritableExecutableMemory then
allocs[j].address:=ptrUint(virtualallocex(processhandle,pointer(prefered),x, MEM_RESERVE or MEM_COMMIT,PAGE_EXECUTE_READWRITE))
else
allocs[j].address:=ptrUint(virtualallocex(processhandle,pointer(prefered),x, MEM_RESERVE or MEM_COMMIT,PAGE_READWRITE));
if allocs[j].address=0 then
begin
OutputDebugString(rsFailureToAllocateMemory+' 3 (prefered='+inttohex(prefered,8)+')');
inc(prefered, 65536);
end;
dec(k);
end;
if allocs[j].address=0 then
allocs[j].address:=lastChanceAllocPrefered(prefered,x, protection);
if allocs[j].address=0 then
begin
if WarnOnNearbyAllocationFailure then
begin
with TAllocWarn.create do
begin
if allocs[j].prefered<>0 then
preferedaddress:=allocs[j].prefered
else
preferedaddress:=prefered;
warn;
free;
end;
end;
if NearbyAllocationFailureFatal then
raise EAssemblerException.create(format(rsFailureAlloc, [prefered,allocs[j].varname]))
else
begin
if SystemSupportsWritableExecutableMemory then
allocs[j].address:=ptrUint(virtualallocex(processhandle,nil,x, MEM_RESERVE or MEM_COMMIT,PAGE_EXECUTE_READWRITE))
else
allocs[j].address:=ptrUint(virtualallocex(processhandle,nil,x, MEM_RESERVE or MEM_COMMIT,PAGE_READWRITE));
end;
end;
// allocs[j].address:=ptrUint(virtualallocex(processhandle,nil,x, MEM_RESERVE or MEM_COMMIT,protection));
if allocs[j].address=0 then raise EAssemblerException.create(rsFailureToAllocateMemory);
for i:=j+1 to length(allocs)-1 do
allocs[i].address:=allocs[i-1].address+allocs[i-1].size;
end;
//apply protections:
for i:=0 to length(allocs)-1 do
begin
if (not SystemSupportsWritableExecutableMemory) or (allocs[i].protection<>PAGE_EXECUTE_READWRITE) then
VirtualProtectEx(processhandle, pointer(allocs[i].address), allocs[i].size, allocs[i].protection,protection);
end;
end;
{$ifdef windows}
{$ifndef net}
//kernel alloc
if length(kallocs)>0 then
begin
x:=0;
for i:=0 to length(kallocs)-1 do
inc(x,kallocs[i].size);
kallocs[0].address:=ptrUint(KernelAlloc(x));
for i:=1 to length(kallocs)-1 do
kallocs[i].address:=kallocs[i-1].address+kallocs[i-1].size;
end;
{$endif}
{$endif}
//-----------------------2nd pass------------------------
//assemblerlines only contains label specifiers and assembler instructions
setlength(assembled,0);
currentlinenr:=0;
try
for i:=0 to length(assemblerlines)-1 do
begin
currentline:=assemblerlines[i].line;
currentlinenr:=assemblerlines[i].linenr;
createthreadandwaitid:=-1;
for j:=0 to length(createthreadandwait)-1 do //there can be multiple at the time of assembly. All entries up to the higest value will be picked at a blockwrite (and made 0 so next blockwrite won't do them)
begin
if (i>createthreadandwait[j].position) or (i=length(Assemblerlines)-1) then //if it's the last line, then do all remaining
createthreadandwaitid:=j;
end;
//plugin
{$ifndef jni}
if length(currentline)>0 then
begin
currentlinep:=@currentline[1];
pluginhandler.handleAutoAssemblerPlugin(@currentlinep, 2,aaid);
currentline:=currentlinep;
//if handled currentline will have it's identifiers regarding the plugin's previously registered stuff replaced
//note that this can be called in a multithreaded situation, so the plugin must hld storage containers on a threadid base and handle the locking itself
end;
{$endif}
//plugin
tokenize(currentline,tokens);
//if alloc then replace with the address
for j:=0 to length(allocs)-1 do
currentline:=replacetoken(currentline,allocs[j].varname,IntToHex(allocs[j].address,8));
//if kalloc then replace with the address
for j:=0 to length(kallocs)-1 do
currentline:=replacetoken(currentline,kallocs[j].varname,IntToHex(kallocs[j].address,8));
for j:=0 to length(defines)-1 do
currentline:=replacetoken(currentline,defines[j].name,defines[j].whatever);
ok1:=false;
if currentline[length(currentline)]<>':' then //if it's not a definition then
begin
for j:=0 to length(labels)-1 do
begin
if (tokencheck(currentline,labels[j].labelname)) then
begin
//this instruction references a label
if not labels[j].defined then
begin
//the address hasn't been found yet
//this is the part that causes those nops after a short jump below the current instruction
//problem: The size of these instructions determine where this label will be defined
//close
s1:=replacetoken(currentline,labels[j].labelname,IntToHex(currentaddress,8));
//far and big
if processhandler.SystemArchitecture=archarm then
begin
currentline:=replacetoken(currentline,labels[j].labelname,IntToHex(currentaddress+$4FFFFF8,8));
end
else
begin
if (processhandler.is64Bit) then //and not in region
begin
//check if between here and the definition of labels[j].labelname is an write pointer change specifier to a region too far away from currentaddress, if not, LONG will suffice
//tip: you 'could' disassemble everything inbetween and see if a small jmp is possible as well (just a lot slower)
mustbefar:=false;
for l:=i+1 to length(assemblerlines)-1 do
begin
currentline2:=assemblerlines[l].line;
if currentline2=labels[j].labelname+':' then break; //reached the label
if currentline2[length(currentline2)]=':' then
begin
//check if it's just a label or alloc in the same group
for k:=0 to length(defines)-1 do
currentline2:=replacetoken(currentline2,defines[k].name,defines[k].whatever);
s2:=copy(currentline2,1,length(currentline2)-1);
for k:=0 to length(allocs)-1 do
begin
if allocs[k].varname=s2 then
begin
if currentaddress>allocs[k].address then
diff:=currentaddress-allocs[k].address
else
diff:=allocs[k].address-currentaddress;
if diff>=$80000000 then
begin
mustbefar:=true;
break;
end;
end;
end;
if mustbefar then break;
for k:=0 to length(kallocs)-1 do
begin
if kallocs[k].varname=s2 then
begin
if currentaddress>kallocs[k].address then
diff:=currentaddress-kallocs[k].address
else
diff:=kallocs[k].address-currentaddress;
if diff>=$80000000 then
begin
mustbefar:=true;
break;
end;
end;
end;
if mustbefar then break;
//if it's a label it's ok
ok1:=false;
for k:=0 to length(labels)-1 do
begin
if labels[k].labelname=s2 then
begin
ok1:=true;
break;
end;
end;
if ok1 then continue; //it's a label, no need to do a heavy symbol lookup
//not an alloc or kalloc
try
testptr:=getAddressFromScript(s2);
if testptr=0 then
testptr:=symhandler.getAddressFromName(s2);
if currentaddress>testptr then
diff:=currentaddress-testptr
else
diff:=testptr-currentaddress;
if diff>=$80000000 then
begin
mustbefar:=true;
break;
end;
except
mustbefar:=true;
end;
if mustbefar then break;
end;
end;
if mustbefar=false then
begin
if dataForAACodePass2.cdata.cscript<>nil then
begin
for k:=0 to length(dataForAACodePass2.cdata.symbols)-1 do
begin
if lowercase(labels[j].labelname)=lowercase(dataForAACodePass2.cdata.symbols[k].name) then
begin
//the c code could be outside reach from the current point
testptr:=getAddressFromScript('ceinternal_autofree_ccode');
if currentaddress>testptr then
diff:=currentaddress-testptr
else
diff:=testptr-currentaddress;
if diff>=$80000000 then
mustbefar:=true;
break;
end;
end;
end;
end;
if mustbefar then
currentline:=replacetoken(currentline,labels[j].labelname,IntToHex(currentaddress+$2000FFFFF,8))
else
currentline:=replacetoken(currentline,labels[j].labelname,IntToHex(currentaddress+$FFFFF,8));
end
else
currentline:=replacetoken(currentline,labels[j].labelname,IntToHex(currentaddress+$FFFFF,8));
end;
setlength(assembled,length(assembled)+1);
assembled[length(assembled)-1].createthreadandwait:=createthreadandwaitid;
assembled[length(assembled)-1].address:=currentaddress;
ok1:=assemble(currentline,currentaddress,assembled[length(assembled)-1].bytes, apnone, true); //far
a:=length(assembled[length(assembled)-1].bytes);
ok2:=assemble(s1,currentaddress,assembled[length(assembled)-1].bytes, apnone, true); //close
b:=length(assembled[length(assembled)-1].bytes);
if not (ok1 or ok2) then
raise exception.create(assemblerlines[l].line+' can not be assembled');
if a>b then //pick the biggest one
assemble(currentline,currentaddress,assembled[length(assembled)-1].bytes);
//add this instruction to the list of lines that reference this label
setlength(labels[j].references,length(labels[j].references)+1);
labels[j].references[length(labels[j].references)-1]:=length(assembled)-1;
setlength(labels[j].references2,length(labels[j].references2)+1);
labels[j].references2[length(labels[j].references2)-1]:=i;
inc(currentaddress,length(assembled[length(assembled)-1].bytes));
ok1:=true;
end else currentline:=replacetoken(currentline,labels[j].labelname,IntToHex(labels[j].address,8));
//break;
end;
end;
end;
if ok1 then continue;
if currentline[length(currentline)]=':' then //address setter/assigner
begin
ok1:=false;
for j:=0 to length(labels)-1 do
begin
if i=labels[j].assemblerline then
begin
if labels[j].defined=true then
begin
currentaddress:=labels[j].address
end
else
begin
labels[j].address:=currentaddress;
labels[j].defined:=true;
end;
ok1:=true;
//reassemble the instructions that had no target
for k:=0 to length(labels[j].references)-1 do
begin
a:=length(assembled[labels[j].references[k]].bytes); //original size of the assembled code
s1:=replacetoken(assemblerlines[labels[j].references2[k]].line,labels[j].labelname,IntToHex(labels[j].address,8));
{$ifdef cpu64}
if processhandler.is64Bit then
ok1:=assemble(s1,assembled[labels[j].references[k]].address,assembled[labels[j].references[k]].bytes)
else
{$endif}
ok1:=assemble(s1,assembled[labels[j].references[k]].address,assembled[labels[j].references[k]].bytes, apLong);
if not ok1 then raise exception.create(format(rsFailureAssembling,[s1, assembled[labels[j].references[k]].address]));
b:=length(assembled[labels[j].references[k]].bytes); //new size
setlength(assembled[labels[j].references[k]].bytes,a); //original size (original size is always bigger or equal than newsize)
if b>a then
begin
raise exception.create('Assembler error. The generated instruction referencing a label ended up bigger than expected. Try using the far indicator');
end;
if (b<a) and (a<12) then //try to grow the instruction as some people cry about nops (unless it was a megajmp/call as those are less efficient)
begin
//try a bigger one
assemble(s1,assembled[labels[j].references[k]].address,nops, apLong);
if length(nops)=a then //found a match size
begin
copymemory(@assembled[labels[j].references[k]].bytes[0], @nops[0], a);
b:=a;
end;
end;
//fill the difference with nops (not the most efficient approach, but it should work)
if processhandler.SystemArchitecture=archarm then
begin
for l:=0 to ((a-b+3) div 4)-1 do
pdword(@assembled[labels[j].references[k]].bytes[b+l*4])^:=$e1a00000; //<mov r0,r0: (nop equivalent)
end
else
begin
// todo: if a-b>8 then replace with the far version
assemble('nop '+inttohex(a-b,1),0,nops);
for l:=b to a-1 do
assembled[labels[j].references[k]].bytes[l]:=nops[l-b];
// for l:=b to a-1 do
// assembled[labels[j].references[k]].bytes[l]:=$90; //nop
end;
end;
break;
end;
end;
if ok1 then continue;
try
currentaddress:=symhandler.getAddressFromName(copy(currentline,1,length(currentline)-1));
continue; //next line
except
raise EAssemblerException.create(rsThisAddressSpecifierIsNotValid);
end;
end;
setlength(assembled,length(assembled)+1);
assembled[length(assembled)-1].address:=currentaddress;
assembled[length(assembled)-1].createthreadandwait:=createthreadandwaitid;
if (currentline<>'') and (currentline[1]='<') then //special assembler instruction
begin
if copy(currentline,1,8)='<READMEM' then
begin
//lets try this for once
sscanf(currentline, '<READMEM%d>', [@l]);
setlength(assembled[length(assembled)-1].bytes, readmems[l].bytelength);
CopyMemory(@assembled[length(assembled)-1].bytes[0], readmems[l].bytes, readmems[l].bytelength);
end
{$ifdef onebytejumps}
else
if copy(currentline,1,6)='<JMP1 ' then
begin
s1:=copy(currentline,7);
s1:=copy(s1,1,length(s1)-1);
setlength(onebytejumps, length(onebytejumps)+1);
onebytejumps[length(onebytejumps)-1].destinationlabel:=s1;
onebytejumps[length(onebytejumps)-1].originaddress:=currentaddress;
setlength(assembled[length(assembled)-1].bytes,1);
assembled[length(assembled)-1].bytes[0]:=$cc;
end
{$endif}
else
begin
assemble(currentline,currentaddress,assembled[length(assembled)-1].bytes);
end;
end
else
begin
if assemble(currentline,currentaddress,assembled[length(assembled)-1].bytes) =false then
raise exception.create(Format(rsFailureAssembling, [currentline, currentaddress]));
end;
inc(currentaddress,length(assembled[length(assembled)-1].bytes));
end;
except
on e:exception do
raise EAssemblerException.create(inttostr(currentlinenr)+':'+e.message);
end;
//end of loop
ok2:=true;
//unprotectmemory
if SystemSupportsWritableExecutableMemory then
begin
for i:=0 to length(fullaccess)-1 do
begin
virtualprotectex(processhandle,pointer(fullaccess[i].address),fullaccess[i].size,PAGE_EXECUTE_READWRITE,op);
{$ifdef windows}
if (fullaccess[i].address>$80000000) and (DBKLoaded) then
MakeWritable(fullaccess[i].address,(fullaccess[i].size div 4096)*4096,false);
{$endif}
end;
end;
//load binaries
if length(loadbinary)>0 then
begin
for i:=0 to length(loadbinary)-1 do
begin
testptr:=getAddressFromScript(loadbinary[i].address);
if testptr<>0 then
begin
binaryfile:=tmemorystream.Create;
try
binaryfile.LoadFromFile(loadbinary[i].filename);
if (CurrentDebuggerInterface is TGDBServerDebuggerInterface) and GDBWriteProcessMemoryCodeOnly then
ok2:=TGDBServerDebuggerInterface(CurrentDebuggerInterface).writeBytes(testptr, binaryfile.Memory, binaryfile.size)
else
ok2:=writeprocessmemory(processhandle,pointer(testptr),binaryfile.Memory,binaryfile.Size,x);
finally
binaryfile.free;
end;
end
else
raise exception.create('Failure ');
end;
end;
//fill in the addresses requested by dataForAACodePass2 and finish the compilation
if dataForAACodePass2.cdata.cscript<>nil then
begin
dataForAACodePass2.cdata.address:=getAddressFromScript('ceinternal_autofree_ccode'); //warning: do not step over this with the debugger
for i:=0 to length(dataForAACodePass2.cdata.references)-1 do
begin
dataForAACodePass2.cdata.references[i].address:=getAddressFromScript(dataForAACodePass2.cdata.references[i].name);
if dataForAACodePass2.cdata.references[i].address=0 then
begin
OutputDebugString('Failure getting reference for '+dataForAACodePass2.cdata.references[i].name);
end;
end;
if disableinfo<>nil then
AutoassemblerCodePass2(dataForAACodePass2, disableinfo.ccodesymbols)
else
AutoassemblerCodePass2(dataForAACodePass2, nil);
if disableinfo<>nil then
begin
if targetself then
selfsymhandler.AddSymbolList(disableinfo.ccodesymbols)
else
symhandler.AddSymbolList(disableinfo.ccodesymbols);
disableinfo.sourcecodeinfo:=dataForAACodePass2.cdata.sourceCodeInfo;
if disableinfo.sourcecodeinfo<>nil then
disableinfo.sourcecodeinfo.register;
end
else
begin
//else do not register the symbols (You can't disable them otherwise)
freeandnil(dataForAACodePass2.cdata.sourceCodeInfo);
end;
//reassemble c-code reference
for j:=0 to length(labels)-1 do
begin
if labels[j].afterccode then
begin
ok1:=false;
for k:=0 to length(dataForAACodePass2.cdata.symbols)-1 do
begin
if labels[j].labelname=dataForAACodePass2.cdata.symbols[k].name then
begin
labels[j].address:=dataForAACodePass2.cdata.symbols[k].address;
labels[j].defined:=true;
ok1:=labels[j].address<>0;
break;
end;
end;
if not ok1 then raise exception.create('Failure getting the address for c-symbol '+labels[j].labelname);
//todo: change to a function so both originallabel and this can use it
for k:=0 to length(labels[j].references)-1 do
begin
a:=length(assembled[labels[j].references[k]].bytes); //original size of the assembled code
s1:=replacetoken(assemblerlines[labels[j].references2[k]].line,labels[j].labelname,IntToHex(labels[j].address,8));
{$ifdef cpu64}
if processhandler.is64Bit then
assemble(s1,assembled[labels[j].references[k]].address,assembled[labels[j].references[k]].bytes)
else
{$endif}
assemble(s1,assembled[labels[j].references[k]].address,assembled[labels[j].references[k]].bytes, apLong);
b:=length(assembled[labels[j].references[k]].bytes); //new size
setlength(assembled[labels[j].references[k]].bytes,a); //original size (original size is always bigger or equal than newsize)
if (b<a) and (a<12) then //try to grow the instruction as some people cry about nops (unless it was a megajmp/call as those are less efficient)
begin
//try a bigger one
assemble(s1,assembled[labels[j].references[k]].address,nops, apLong);
if length(nops)=a then //found a match size
begin
copymemory(@assembled[labels[j].references[k]].bytes[0], @nops[0], a);
b:=a;
end;
end;
//fill the difference with nops (not the most efficient approach, but it should work)
if processhandler.SystemArchitecture=archarm then
begin
for l:=0 to ((a-b+3) div 4)-1 do
pdword(@assembled[labels[j].references[k]].bytes[b+l*4])^:=$e1a00000; //<mov r0,r0: (nop equivalent)
end
else
begin
assemble('nop '+inttohex(a-b,1),0,nops);
for l:=b to a-1 do
assembled[labels[j].references[k]].bytes[l]:=nops[l-b];
end;
end;
end;
end;
end;
//we're still here so inject the rest of it
//addresses are known here, so parse the exception list if there is one
if (length(exceptionlist)>0) {$ifdef onebytejumps}or (length(onebytejumps)>0){$endif} then
InitializeAutoAssemblerExceptionHandler;
if length(exceptionlist)>0 then
for i:=length(exceptionlist)-1 downto 0 do //add it in the reverse order so the nested try/excepts come first
AutoAssemblerExceptionHandlerAddExceptionRange(getAddressFromScript(exceptionlist[i].trylabel), getAddressFromScript(exceptionlist[i].exceptlabel));
{$ifdef onebytejumps}
if length(onebytejumps)>0 then
for i:=0 to length(onebytejumps)-1 do
AutoAssemblerExceptionHandlerAddChangeRIPEntry(onebytejumps[i].originaddress, getAddressFromScript(onebytejumps[i].destinationlabel));
{$endif}
if (length(exceptionlist)>0) {$ifdef onebytejumps}or (length(onebytejumps)>0){$endif} then
AutoAssemblerExceptionHandlerApplyChanges;
{$ifdef windows}
connection:=getconnection;
if connection<>nil then
connection.beginWriteProcessMemory; //group all writes
{$endif}
//combine assembly lines
j:=0;
for i:=1 to length(assembled)-1 do
begin
if assembled[i].address=assembled[j].address+length(assembled[j].bytes) then //matches the previous entry
begin
//group
k:=length(assembled[j].bytes);
setlength(assembled[j].bytes, k+length(assembled[i].bytes));
copymemory(@assembled[j].bytes[k], @assembled[i].bytes[0], length(assembled[i].bytes));
assembled[j].createthreadandwait:=max(assembled[j].createthreadandwait, assembled[i].createthreadandwait); //should always pick i
//mark it as empty
setlength(assembled[i].bytes,0);
assembled[i].address:=0;
assembled[i].createthreadandwait:=-1;
end
else
begin
j:=i; //new block
end;
end;
if (not SystemSupportsWritableExecutableMemory) and (not SkipVirtualProtectEx) and (ProcessID<>GetCurrentProcessId) then
begin
if (CurrentDebuggerInterface is TGDBServerDebuggerInterface) then
TGDBServerDebuggerInterface(CurrentDebuggerInterface).suspendProcess
else
ntsuspendProcess(processhandle);
end;
for i:=0 to length(assembled)-1 do
begin
if length(assembled[i].bytes)=0 then continue;
testptr:=assembled[i].address;
op:=0;
if SystemSupportsWritableExecutableMemory or SkipVirtualProtectEx then
vpe:=(SkipVirtualProtectEx=false) and virtualprotectex(processhandle,pointer(testptr),length(assembled[i].bytes),PAGE_EXECUTE_READWRITE,op)
else
vpe:=(SkipVirtualProtectEx=false) and virtualprotectex(processhandle,pointer(testptr),length(assembled[i].bytes),PAGE_READWRITE,op);
if vpe then
outputdebugstring('autoassemble: original protection was '+op.ToString);
if (CurrentDebuggerInterface is TGDBServerDebuggerInterface) and GDBWriteProcessMemoryCodeOnly then
ok1:=TGDBServerDebuggerInterface(CurrentDebuggerInterface).writeBytes(testptr, @assembled[i].bytes[0], length(assembled[i].bytes))
else
ok1:={$ifdef windows}WriteProcessMemoryWithCloakSupport{$else}WriteProcessMemory{$endif}(processhandle, pointer(testptr),@assembled[i].bytes[0],length(assembled[i].bytes),x);
if vpe then
virtualprotectex(processhandle,pointer(testptr),length(assembled[i].bytes),op,op2);
if not ok1 then ok2:=false;
if ok2 and (assembled[i].createthreadandwait<>-1) then
begin
//create threads
for j:=0 to assembled[i].createthreadandwait do
begin
if createthreadandwait[j].position<>-1 then
HandleCreateThreadAndWait(j);
end;
end;
end;
if (not SystemSupportsWritableExecutableMemory) and (not SkipVirtualProtectEx) and (ProcessID<>GetCurrentProcessId) then
begin
if (CurrentDebuggerInterface is TGDBServerDebuggerInterface) then
TGDBServerDebuggerInterface(CurrentDebuggerInterface).resumeProcess
else
ntresumeProcess(processhandle);
end;
{$ifdef windows}
if connection<>nil then //group all writes
begin
if connection.endWriteProcessMemory=false then
ok2:=false;
end;
{$endif}
//handle the unhandled createthreadandwait blocks
for i:=0 to length(createthreadandwait)-1 do
begin
if createthreadandwait[i].position<>-1 then
HandleCreateThreadAndWait(i);
end;
if not ok2 then
begin
{$ifndef jni}
if popupmessages then showmessage(rsNotAllInstructionsCouldBeInjected)
else
begin
if memrec<>nil then //there is an memrec provided, so also an ewxception handler
raise exception.create(rsNotAllInstructionsCouldBeInjected);
end;
{$endif}
end
else
begin
if disableinfo<>nil then
begin
//see if all allocs are deallocated
for i:=0 to length(disableinfo.allocs)-1 do
begin
//free the ceinternal_autofree entries (if they aren't already marked)
if disableinfo.allocs[i].varname.StartsWith('ceinternal_autofree') then
begin
ok1:=false;
for j:=0 to length(dealloc)-1 do
begin
if dealloc[j]=disableinfo.allocs[i].address then
begin
ok1:=true;
break;
end;
end;
if ok1=false then //not in the list yet, add it
begin
j:=length(dealloc);
setlength(dealloc, j+1);
dealloc[j]:=disableinfo.allocs[i].address;
end;
end;
end;
if (length(dealloc)>0) and (length(dealloc)=length(disableinfo.allocs)) then //free everything
begin
{$ifdef cpu64}
baseaddress:=ptrUint($FFFFFFFFFFFFFFFF);
{$else}
baseaddress:=$FFFFFFFF;
{$endif}
for i:=0 to length(disableinfo.allocs)-1 do
begin
virtualfreeex(processhandle,pointer(dealloc[i]),0,MEM_RELEASE);
if (targetself=false) and allocsAddToUnexpectedExceptionList then
RemoveUnexpectedExceptionRegion(dealloc[i],0);
{ if ceallocarray[i].address<baseaddress then
baseaddress:=ceallocarray[i].address;}
end;
//virtualfreeex(processhandle,pointer(baseaddress),0,MEM_RELEASE);
disableinfo.ccodesymbols.clear;
disableinfo.ccodesymbols.unregisterList;
freeandnil(disableinfo.sourcecodeinfo);
end;
setlength(disableinfo.allocs,length(allocs));
for i:=0 to length(allocs)-1 do
disableinfo.allocs[i]:=allocs[i];
if (length(disableinfo.exceptions)>0) and (AutoAssemblerExceptionHandlerHasEntries) then
begin
for i:=0 to length(disableinfo.exceptions)-1 do
AutoAssemblerExceptionHandlerRemoveExceptionRange(disableinfo.exceptions[i]);
AutoAssemblerExceptionHandlerApplyChanges;
end;
setlength(disableinfo.exceptions, length(exceptionlist));
for i:=0 to length(disableinfo.exceptions)-1 do
disableinfo.exceptions[i]:=getAddressFromScript(exceptionlist[i].trylabel);
end;
//check the addsymbollist array and deletesymbollist array
//first delete
for i:=0 to length(deletesymbollist)-1 do
symhandler.DeleteUserdefinedSymbol(deletesymbollist[i]);
//now scan the addsymbollist array and add them to the userdefined list
for i:=0 to length(addsymbollist)-1 do
begin
ok1:=false;
for j:=0 to length(allocs)-1 do
if uppercase(addsymbollist[i])=uppercase(allocs[j].varname) then
begin
try
symhandler.DeleteUserdefinedSymbol(addsymbollist[i]); //delete old one so you can add the new one
symhandler.AddUserdefinedSymbol(inttohex(allocs[j].address,8),addsymbollist[i], true);
ok1:=true;
except
//don't crash when it's already defined or address=0
end;
break;
end;
if not ok1 then
for j:=0 to length(labels)-1 do
if uppercase(addsymbollist[i])=uppercase(labels[j].labelname) then
begin
try
symhandler.DeleteUserdefinedSymbol(addsymbollist[i]); //delete old one so you can add the new one
symhandler.AddUserdefinedSymbol(inttohex(labels[j].address,8),addsymbollist[i], true);
ok1:=true;
except
//don't crash when it's already defined or address=0
end;
end;
if not ok1 then
for j:=0 to length(defines)-1 do
if uppercase(addsymbollist[i])=uppercase(defines[j].name) then
begin
try
symhandler.DeleteUserdefinedSymbol(addsymbollist[i]); //delete old one so you can add the new one
symhandler.AddUserdefinedSymbol(defines[j].whatever, addsymbollist[i], true);
ok1:=true;
except
end;
end;
end;
//still here, so create threads if needed
if length(createthread)>0 then
begin
for i:=0 to length(createthread)-1 do
begin
testptr:=getAddressFromScript(createthread[i]);
ok1:=testptr<>0;
if ok1 then //address found
begin
try
threadhandle:=createremotethread(processhandle,nil,0,pointer(testptr),nil,0,bw);
ok2:=threadhandle>0;
if ok2 then
closehandle(threadhandle);
finally
end;
end;
end;
end; //^ thread creation
//fill "allSymbols"
if disableinfo<>nil then
begin
for i:=0 to length(labels)-1 do
disableinfo.allsymbols.AddObject(labels[i].labelname, tobject(labels[i].address));
for i:=0 to length(allocs)-1 do
disableinfo.allsymbols.AddObject(allocs[i].varname, tobject(allocs[i].address));
for i:=0 to length(kallocs)-1 do
disableinfo.allsymbols.AddObject(kallocs[i].varname, tobject(kallocs[i].address));
for i:=0 to length(defines)-1 do
begin
testptr:=symhandler.getAddressFromName(defines[i].whatever,false,ok1);
if ok1=false then
disableinfo.allsymbols.AddObject(defines[i].name, tobject(testptr));
end;
end;
{$IFNDEF jni}
if popupmessages then
begin
testPtr:=0;
s1:='';
for i:=0 to length(globalallocs)-1 do
begin
if testPtr=0 then testPtr:=globalallocs[i].address;
s1:=s1+#13#10+globalallocs[i].varname+'='+IntToHex(globalallocs[i].address,8);
end;
for i:=0 to length(allocs)-1 do
begin
if allocs[i].varname.StartsWith('ceinternal_') then continue; //don't show these
if testPtr=0 then testPtr:=allocs[i].address;
s1:=s1+#13#10+allocs[i].varname+'='+IntToHex(allocs[i].address,8);
end;
if length(kallocs)>0 then
begin
if testPtr=0 then testPtr:=kallocs[i].address;
s1:=#13#10+rsTheFollowingKernelAddressesWhereAllocated+':';
for i:=0 to length(kallocs)-1 do
s1:=s1+#13#10+kallocs[i].varname+'='+IntToHex(kallocs[i].address,8);
end;
// if messagedl
if (testPtr=0) or (oldaamessage) then
showmessage(rsTheCodeInjectionWasSuccessfull+s1)
else
begin
if MessageDlg(rsTheCodeInjectionWasSuccessfull+s1+#13#10+rsGoTo+inttohex(testptr,8)+'?', mtInformation,[mbYes, mbNo], 0, mbno)=mrYes then
begin
memorybrowser.disassemblerview.selectedaddress:=testptr;
memorybrowser.show;
end;
end;
end;
{$ENDIF}
end;
result:=ok2;
if result and allocsAddToUnexpectedExceptionList and (not targetself) then
begin
for i:=0 to length(allocs)-1 do
AddUnexpectedExceptionRegion(allocs[i].address,allocs[i].size);
end;
finally
if dataForAACodePass2.cdata.cscript<>nil then
freeandnil(dataForAACodePass2.cdata.cscript);
for i:=0 to length(assembled)-1 do
setlength(assembled[i].bytes,0);
setlength(assembled,0);
for i:=0 to length(readmems)-1 do
if readmems[i].bytes<>nil then
begin
freememandnil(readmems[i].bytes);
end;
setlength(readmems,0);
if tokens<>nil then
freeandnil(tokens);
{$IFNDEF jni}
if pluginhandler<>nil then
pluginhandler.handleAutoAssemblerPlugin(@currentlinep, 3,aaid); //tell the plugins to free their data
if targetself then
begin
processhandler.processid:=oldprocessid;
processhandler.processhandle:=oldhandle;
symhandler:=oldsymhandler;
end;
{$ENDIF}
if potentiallabels<>nil then
freeandnil(potentiallabels);
end;
end;
function getenableanddisablepos(code:tstrings;var enablepos,disablepos: integer): boolean;
var i,j: integer;
currentline: string;
begin
result:=false;
enablepos:=-1;
disablepos:=-1;
for i:=0 to code.Count-1 do
begin
currentline:=code[i];
j:=pos('//',currentline);
if j>0 then
currentline:=copy(currentline,1,j-1);
while (length(currentline)>0) and (currentline[1]=' ') do currentline:=copy(currentline,2,length(currentline)-1);
while (length(currentline)>0) and (currentline[length(currentline)]=' ') do currentline:=copy(currentline,1,length(currentline)-1);
if length(currentline)=0 then continue;
if copy(currentline,1,2)='//' then continue; //skip
if (uppercase(currentline))='[ENABLE]' then
begin
result:=true; //there's at least a enable section, so it's ok
if enablepos<>-1 then
begin
enablepos:=-2;
exit;
end;
enablepos:=i;
end;
if (uppercase(currentline))='[DISABLE]' then
begin
if disablepos<>-1 then
begin
disablepos:=-2;
exit;
end;
disablepos:=i;
end;
end;
end;
procedure getEnableOrDisableScript(code: TStrings; newscript: tstrings; enablescript: boolean);
{
removes the enable or disable section from a script leaving only the outer code and the selected script routine
}
var
i: integer;
insideenable: boolean;
insidedisable: boolean;
begin
insideenable:=false;
insidedisable:=false;
for i:=0 to code.Count-1 do
begin
if (uppercase(trim(code[i])))='[ENABLE]' then
begin
insideenable:=true;
insidedisable:=false;
continue;
end;
if (uppercase(trim(code[i])))='[DISABLE]' then
begin
insideenable:=false;
insidedisable:=true;
continue;
end;
//
if ((not insideenable) and (not insidedisable)) or
(insideenable and enablescript) or
(insidedisable and not enablescript) then newscript.AddObject(code[i], code.Objects[i]);
end;
end;
procedure stripCPUspecificCode(code: tstrings; strip32bit: boolean);
var i: integer;
s: string;
inexcludedbitblock: boolean;
begin
inexcludedbitblock:=false;
for i:=0 to code.Count-1 do
begin
s:=uppercase(Trim(code[i]));
if s='[32-BIT]' then
begin
if strip32bit then
inexcludedbitblock:=true;
code[i]:=' ';
end;
if s='[/32-BIT]' then
begin
if strip32bit then
inexcludedbitblock:=false;
code[i]:=' ';
end;
if s='[64-BIT]' then
begin
if not strip32bit then
inexcludedbitblock:=true;
code[i]:=' ';
end;
if s='[/64-BIT]' then
begin
if not strip32bit then
inexcludedbitblock:=false;
code[i]:=' ';
end;
if inexcludedbitblock then
code[i]:=' ';
end;
end;
function autoassemble(code: Tstrings; popupmessages,enable,syntaxcheckonly, targetself: boolean; disableinfo: TDisableinfo=nil; memrec: TMemoryRecord=nil): boolean; overload;
{
targetself defines if the process that gets injected to is CE itself or the target process
}
var tempstrings: tstringlist;
i,j: integer;
currentline: string;
enablepos,disablepos: integer;
strip32bitcode: boolean;
begin
//add line numbers to the code
for i:=0 to code.Count-1 do
code.Objects[i]:=pointer(i+1);
getenableanddisablepos(code,enablepos,disablepos);
result:=false;
if enablepos=-2 then
begin
raise EAssemblerException.create(rsYouCanOnlyHaveOneEnableSection);
end;
if disablepos=-2 then
begin
raise EAssemblerException.create(rsYouCanOnlyHaveOneDisableSection);
end;
tempstrings:=tstringlist.create;
try
if (enablepos=-1) and (disablepos=-1) then
begin
//everything
tempstrings.AddStrings(code);
end
else
begin
if (enablepos=-1) then
begin
if not popupmessages then exit;
raise EAssemblerException.create(rsYouHavnTSpecifiedAEnableSection);
end;
if (disablepos=-1) then
begin
if not popupmessages then exit;
raise EAssemblerException.create(rsYouHavnTSpecifiedADisableSection);
end;
if enable then
begin
getEnableOrDisableScript(code, tempstrings, true);
end
else
begin
getEnableOrDisableScript(code, tempstrings,false);
end;
end;
strip32bitcode:=processhandler.is64Bit;
if targetself then
strip32bitcode:={$ifdef cpu64}true{$else}false{$endif};
Stripcpuspecificcode(tempstrings, strip32bitcode); //todo: change to set for other types like arm
result:=autoassemble2(tempstrings,popupmessages,syntaxcheckonly,targetself, disableinfo, memrec);
finally
tempstrings.Free;
end;
end;
function autoassemble(code: tstrings;popupmessages: boolean):boolean; overload;
begin
result:=autoassemble(code,popupmessages,true,false,false);
end;
end.