2996 lines
93 KiB
ObjectPascal
2996 lines
93 KiB
ObjectPascal
unit autoassembler;
|
|
|
|
{$MODE Delphi}
|
|
|
|
interface
|
|
|
|
uses jwawindows, windows, Assemblerunit, classes, LCLIntf,symbolhandler,
|
|
sysutils,dialogs,controls, CEFuncProc, NewKernelHandler ,plugin,
|
|
ProcessHandlerUnit, lua, lualib, lauxlib, commonTypeDefs;
|
|
|
|
|
|
|
|
function getenableanddisablepos(code:tstrings;var enablepos,disablepos: integer): boolean;
|
|
function autoassemble(code: tstrings;popupmessages: boolean):boolean; overload;
|
|
function autoassemble(code: Tstrings; popupmessages,enable,syntaxcheckonly, targetself: boolean):boolean; overload;
|
|
function autoassemble(code: Tstrings; popupmessages,enable,syntaxcheckonly, targetself: boolean;var CEAllocarray: TCEAllocArray; registeredsymbols: tstringlist=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;
|
|
|
|
procedure RegisterAutoAssemblerCommand(command: string; callback: TAutoAssemblerCallback);
|
|
procedure UnregisterAutoAssemblerCommand(command: string);
|
|
function registerAutoAssemblerPrologue(m: TAutoAssemblerPrologue): integer;
|
|
procedure unregisterAutoAssemblerPrologue(id: integer);
|
|
|
|
|
|
implementation
|
|
|
|
uses simpleaobscanner, StrUtils, LuaHandler, memscan, disassembler, networkInterface,
|
|
networkInterfaceApi, fgl, LuaCaller, SynHighlighterAA;
|
|
|
|
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)';
|
|
|
|
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';
|
|
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)';
|
|
|
|
type
|
|
TregisteredAutoAssemblerCommands = TFPGList<TRegisteredAutoAssemblerCommand>;
|
|
|
|
var
|
|
registeredAutoAssemblerCommands: TregisteredAutoAssemblerCommands;
|
|
|
|
AutoAssemblerPrologues: array of TAutoAssemblerPrologue;
|
|
|
|
function registerAutoAssemblerPrologue(m: TAutoAssemblerPrologue): integer;
|
|
var i: integer;
|
|
begin
|
|
for i:=0 to length(AutoAssemblerPrologues)-1 do
|
|
begin
|
|
if assigned(AutoAssemblerPrologues[i])=false then
|
|
begin
|
|
AutoAssemblerPrologues[i]:=m;
|
|
result:=i;
|
|
exit;
|
|
end
|
|
end;
|
|
|
|
result:=length(AutoAssemblerPrologues);
|
|
setlength(AutoAssemblerPrologues, result+1);
|
|
AutoAssemblerPrologues[result]:=m;
|
|
end;
|
|
|
|
procedure unregisterAutoAssemblerPrologue(id: integer);
|
|
begin
|
|
if id<length(AutoAssemblerPrologues) then
|
|
AutoAssemblerPrologues[id]:=nil;
|
|
end;
|
|
|
|
procedure RegisterAutoAssemblerCommand(command: string; callback: TAutoAssemblerCallback);
|
|
var i: integer;
|
|
c:TRegisteredAutoAssemblerCommand;
|
|
begin
|
|
if registeredAutoAssemblerCommands=nil then
|
|
registeredAutoAssemblerCommands:=TregisteredAutoAssemblerCommands.Create;
|
|
|
|
command:=uppercase(command);
|
|
for i:=0 to registeredAutoAssemblerCommands.Count-1 do
|
|
if registeredAutoAssemblerCommands[i].command=command then
|
|
begin
|
|
//update
|
|
CleanupLuaCall(tmethod(registeredAutoAssemblerCommands[i].callback));
|
|
registeredAutoAssemblerCommands[i].callback:=nil;//TAutoAssemblerCallback(callback);
|
|
exit;
|
|
end;
|
|
|
|
c:=TRegisteredAutoAssemblerCommand.create;
|
|
c.command:=command;
|
|
c.callback:=callback;
|
|
registeredAutoAssemblerCommands.Add(c);
|
|
|
|
aa_AddExtraCommand(pchar(command));
|
|
end;
|
|
|
|
procedure UnregisterAutoAssemblerCommand(command: string);
|
|
var i,j: integer;
|
|
c:TRegisteredAutoAssemblerCommand;
|
|
begin
|
|
command:=uppercase(command);
|
|
i:=0;
|
|
while i<registeredAutoAssemblerCommands.count do
|
|
begin
|
|
if 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));
|
|
end;
|
|
|
|
procedure tokenize(input: string; tokens: tstringlist);
|
|
var i: integer;
|
|
a: integer;
|
|
begin
|
|
|
|
tokens.clear;
|
|
a:=-1;
|
|
for i:=1 to length(input) do
|
|
begin
|
|
case input[i] of
|
|
'a'..'z','A'..'Z','0'..'9','.', '_','#','@': if a=-1 then a:=i;
|
|
else
|
|
begin
|
|
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(input,tokens);
|
|
for i:=0 to tokens.Count-1 do
|
|
if tokens[i]=token then
|
|
begin
|
|
j:=integer(tokens.Objects[i]);
|
|
result:=copy(input,1,j-1)+replacewith+copy(input,j+length(token),length(input));
|
|
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:='Error in the structure definition of '+structname+' at line '+inttostr(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(elementname+' is a reserved word');
|
|
|
|
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('No idea what '+tokens[j]+' is'); //we already dealth with labels, so this is wrong
|
|
end;
|
|
|
|
|
|
inc(j); //next token
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
if endfound=false then
|
|
structerror('No end found');
|
|
|
|
//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 removecomments(code: tstrings);
|
|
var i,j: integer;
|
|
currentline: string;
|
|
instring: boolean;
|
|
incomment: boolean;
|
|
begin
|
|
//remove comments
|
|
instring:=false;
|
|
incomment:=false;
|
|
for i:=0 to code.count-1 do
|
|
begin
|
|
currentline:=code[i];
|
|
|
|
for j:=1 to length(currentline) do
|
|
begin
|
|
if incomment then
|
|
begin
|
|
//inside a comment, remove everything till a } is encountered
|
|
if ((currentline[j]='}') and (processhandler.SystemArchitecture<>archArm)) or
|
|
((currentline[j]='*') and (j<length(currentline)) and (currentline[j+1]='/')) then
|
|
begin
|
|
incomment:=false; //and continue parsing the code...
|
|
|
|
if ((currentline[j]='*') and (j<length(currentline)) and (currentline[j+1]='/')) 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;
|
|
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;
|
|
|
|
procedure aobscans(code: tstrings; syntaxcheckonly: 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;
|
|
memscan: TMemScan;
|
|
end;
|
|
|
|
a,b,c,d: integer;
|
|
currentline: string;
|
|
s1,s2, s3: string;
|
|
testptr: ptruint;
|
|
|
|
cpucount: integer;
|
|
threads: integer;
|
|
mi: TModuleInfo;
|
|
aobstrings: string;
|
|
|
|
error: boolean;
|
|
errorstring: string;
|
|
|
|
aob1, aob2: dword;
|
|
|
|
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;
|
|
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:='The array of byte named '+aobscanmodules[f].entries[i].name+' could not be found';
|
|
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+' ';
|
|
|
|
errorstring:='Error while scanning for AOB''s : '+aoblist+#13#10#13#10+'Error: '+aobscanmodules[f].memscan.GetErrorString;
|
|
|
|
|
|
end;
|
|
|
|
aobscanmodules[f].memscan.Free;
|
|
aobscanmodules[f].memscan:=nil;
|
|
|
|
dec(threads);
|
|
end;
|
|
|
|
begin
|
|
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,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:=$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:=uppercase(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=s2 then
|
|
begin
|
|
m:=j;
|
|
break;
|
|
end;
|
|
|
|
if m=-1 then
|
|
begin
|
|
setlength(aobscanmodules, length(aobscanmodules)+1);
|
|
m:=length(aobscanmodules)-1;
|
|
|
|
aobscanmodules[m].name:=s2;
|
|
if symhandler.getmodulebyname(s2, mi) then
|
|
begin
|
|
aobscanmodules[m].minaddress:=mi.baseaddress;
|
|
aobscanmodules[m].maxaddress:=mi.baseaddress+mi.basesize;
|
|
end
|
|
else
|
|
raise exception.create('module not found:'+s2);
|
|
|
|
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;
|
|
end;
|
|
|
|
//do simultaneous scans for the selected modules
|
|
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].minaddress, aobscanmodules[i].maxaddress, true, false, false, false, fsmNotAligned)
|
|
else
|
|
aobscanmodules[i].memscan.firstscan(soExactValue, vtByteArrays, rtRounded, aobstrings, '', 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 exception.create(errorstring);
|
|
|
|
|
|
end;
|
|
|
|
procedure luacode(code: TStrings; syntaxcheckonly: boolean);
|
|
{
|
|
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;
|
|
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=...');
|
|
|
|
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]:='';
|
|
|
|
|
|
|
|
LUACS.Enter;
|
|
try
|
|
stack:=lua_Gettop(luavm);
|
|
|
|
error:=false;
|
|
|
|
luaL_loadstring(luavm, pchar(s.text));
|
|
if lua_isfunction(luavm, -1) then
|
|
begin
|
|
lua_pushboolean(luavm, syntaxcheckonly);
|
|
if lua.lua_pcall(luavm, 1, 1, 0)=0 then
|
|
begin
|
|
if lua_isstring(luavm,-1) then
|
|
begin
|
|
str:=Lua_ToString(luavm, -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(luavm, -1) then
|
|
raise exception.create('Lua error in the script at line '+inttostr(integer(code.Objects[i]))+':'+lua_tostring(luavm, -1))
|
|
else
|
|
raise exception.create('Lua error in the script at line '+inttostr(integer(code.Objects[i])));
|
|
|
|
end;
|
|
|
|
finally
|
|
lua_settop(Luavm, stack);
|
|
LUACS.Leave;
|
|
end;
|
|
|
|
break;
|
|
|
|
end;
|
|
inc(j);
|
|
end;
|
|
|
|
|
|
end
|
|
else
|
|
inc(i);
|
|
end;
|
|
|
|
//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]:='';
|
|
|
|
end;
|
|
|
|
function autoassemble2(code: tstrings;popupmessages: boolean;syntaxcheckonly:boolean; targetself: boolean ;var ceallocarray:TCEAllocArray; registeredsymbols: tstringlist=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;
|
|
end;
|
|
|
|
|
|
type tlabel=record
|
|
defined: 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,j,k,l,e: integer;
|
|
currentline: string;
|
|
currentlinenr: integer;
|
|
currentlinep: pchar;
|
|
|
|
currentaddress: ptrUint;
|
|
assembled: array of tassembled;
|
|
x: ptruint;
|
|
y,op,op2:dword;
|
|
ok1,ok2:boolean;
|
|
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;
|
|
|
|
|
|
globalallocs, allocs, kallocs, sallocs: array of 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;
|
|
|
|
// aoblist: array of TAOBEntry;
|
|
|
|
a,b,c,d: integer;
|
|
s1,s2,s3: string;
|
|
|
|
assemblerlines: array of string;
|
|
|
|
varsize: integer;
|
|
tokens: tstringlist;
|
|
baseaddress: ptrUint;
|
|
|
|
multilineinjection: tstringlist;
|
|
include: tstringlist;
|
|
testdword,bw: dword;
|
|
testPtr: ptrUint;
|
|
binaryfile: tmemorystream;
|
|
|
|
incomment: boolean;
|
|
|
|
bytebuf: PByteArray;
|
|
|
|
processhandle: THandle;
|
|
ProcessID: DWORD;
|
|
|
|
bytes: tbytes;
|
|
prefered: ptrUint;
|
|
|
|
oldhandle: thandle;
|
|
oldsymhandler: TSymHandler;
|
|
|
|
|
|
disassembler: TDisassembler;
|
|
|
|
threadhandle: THandle;
|
|
|
|
connection: TCEConnection;
|
|
begin
|
|
setlength(readmems,0);
|
|
setlength(allocs,0);
|
|
setlength(kallocs,0);
|
|
setlength(globalallocs,0);
|
|
setlength(sallocs,0);
|
|
setlength(createthread,0);
|
|
|
|
currentaddress:=0;
|
|
|
|
|
|
if syntaxcheckonly and (registeredsymbols<>nil) then
|
|
begin
|
|
//add the symbols as defined labels
|
|
setlength(labels,registeredsymbols.count);
|
|
for i:=0 to registeredsymbols.count-1 do
|
|
begin
|
|
labels[i].labelname:=registeredsymbols[i];
|
|
labels[i].defined:=true;
|
|
labels[i].address:=0;
|
|
labels[i].assemblerline:=0;
|
|
setlength(labels[i].references,0);
|
|
setlength(labels[i].references2,0);
|
|
end;
|
|
end;
|
|
|
|
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;
|
|
processid:=getcurrentprocessid;
|
|
processhandle:=getcurrentprocess;
|
|
oldsymhandler:=symhandler;
|
|
symhandler:=selfsymhandler;
|
|
processhandler.processhandle:=processhandle;
|
|
end
|
|
else
|
|
begin
|
|
processid:=processhandlerunit.ProcessID;
|
|
processhandle:=processhandlerunit.ProcessHandle;
|
|
end;
|
|
|
|
symhandler.waitforsymbolsloaded(true);
|
|
|
|
{$ifndef standalonetrainer}
|
|
if pluginhandler=nil then exit; //Error. Cheat Engine is not properly configured
|
|
|
|
pluginhandler.handleAutoAssemblerPlugin(@currentlinep, 0); //tell the plugins that an autoassembler script is about to get executed
|
|
{$endif}
|
|
|
|
//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(defines,0);
|
|
setlength(loadbinary,0);
|
|
// setlength(aoblist,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);
|
|
|
|
removecomments(code); //also trims each line
|
|
unlabeledlabels(code);
|
|
|
|
//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
|
|
|
|
aobscans(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));
|
|
|
|
setlength(addsymbollist,length(addsymbollist)+1);
|
|
addsymbollist[length(addsymbollist)-1]:=s1;
|
|
|
|
if registeredsymbols<>nil then
|
|
registeredsymbols.Add(s1);
|
|
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: alloc(x,size) 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]:=currentline;
|
|
|
|
//plugins
|
|
currentlinep:=@currentline[1];
|
|
pluginhandler.handleAutoAssemblerPlugin(@currentlinep, 1);
|
|
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 registeredAutoAssemblerCommands[j].command=s1 then
|
|
begin
|
|
a:=pos('(',currentline);
|
|
b:=RPos(')',currentline);
|
|
s1:=copy(currentline, a+1, b-a-1);
|
|
|
|
currentline:=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(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
|
|
freemem(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:=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;
|
|
|
|
//define it here already
|
|
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);
|
|
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,13))='CREATETHREAD(' then
|
|
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;
|
|
|
|
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 pos(':',s1)=0 then
|
|
begin
|
|
s2:=extractfilename(s1);
|
|
|
|
if getConnection=nil then //no connection, so local. Check if the file can be found locally and if so, set the specific path
|
|
begin
|
|
if fileexists(cheatenginedir+s2) then s1:=cheatenginedir+s2 else
|
|
if fileexists(getcurrentdir+'\'+s2) then s1:=getcurrentdir+'\'+s2 else
|
|
if fileexists(cheatenginedir+s1) then s1:=cheatenginedir+s1;
|
|
end;
|
|
|
|
//else just hope it's in the dll searchpath
|
|
end; //else direct file path
|
|
|
|
try
|
|
InjectDll(s1,'');
|
|
symhandler.reinitialize;
|
|
symhandler.waitforsymbolsloaded
|
|
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;
|
|
|
|
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
|
|
freemem(bytebuf);
|
|
|
|
raise exception.create(e.Message);
|
|
end;
|
|
end;
|
|
|
|
|
|
//still here so everything ok
|
|
assemblerlines[length(assemblerlines)-1]:='<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:=symhandler.getAddressFromName(s1);
|
|
except
|
|
raise exception.Create(s1+' could not be found');
|
|
end;
|
|
|
|
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]:=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));
|
|
|
|
setlength(deletesymbollist,length(deletesymbollist)+1);
|
|
deletesymbollist[length(deletesymbollist)-1]:=s1;
|
|
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));
|
|
|
|
|
|
val('$'+s1,j,a);
|
|
if a=0 then raise exception.Create(Format(rsIsNotAValidIdentifier, [s1]));
|
|
|
|
varsize:=length(s1);
|
|
|
|
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];
|
|
|
|
|
|
labels[l].labelname:=s1;
|
|
labels[l].defined:=false;
|
|
setlength(assemblerlines,length(assemblerlines)-1);
|
|
setlength(labels[l].references,0);
|
|
setlength(labels[l].references2,0);
|
|
|
|
continue;
|
|
end else raise exception.Create(rsSyntaxError);
|
|
end;
|
|
|
|
if (uppercase(copy(currentline,1,8))='DEALLOC(') then
|
|
begin
|
|
if (ceallocarray<>nil) then//memory dealloc=possible
|
|
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));
|
|
|
|
//find s1 in the ceallocarray
|
|
for j:=0 to length(ceallocarray)-1 do
|
|
begin
|
|
if uppercase(ceallocarray[j].varname)=uppercase(s1) then
|
|
begin
|
|
setlength(dealloc,length(dealloc)+1);
|
|
dealloc[length(dealloc)-1]:=ceallocarray[j].address;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
setlength(assemblerlines,length(assemblerlines)-1);
|
|
continue;
|
|
end;
|
|
|
|
//memory alloc
|
|
if uppercase(copy(currentline,1,6))='ALLOC(' 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
|
|
begin
|
|
|
|
allocs[j].prefered:=symhandler.getAddressFromName(s3);
|
|
end
|
|
else
|
|
allocs[j].prefered:=0;
|
|
|
|
|
|
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
|
|
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}
|
|
|
|
//replace KALLOC identifiers with values so the assemble error check doesnt crash on that
|
|
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;
|
|
|
|
|
|
|
|
//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 (registeredsymbols<>nil) then
|
|
begin
|
|
//replace tokens with registered symbols from the enable part
|
|
for j:=0 to registeredsymbols.count-1 do
|
|
currentline:=replacetoken(currentline, registeredsymbols[j], '00000000');
|
|
end;
|
|
|
|
try
|
|
j:=symhandler.getAddressFromName(copy(currentline,1,length(currentline)-1));
|
|
except
|
|
currentline:=inttohex(symhandler.getaddressfromname(copy(currentline,1,length(currentline)-1)),8)+':';
|
|
assemblerlines[length(assemblerlines)-1]:=currentline;
|
|
end;
|
|
|
|
continue; //next line
|
|
except
|
|
raise exception.Create(rsThisAddressSpecifierIsNotValid);
|
|
end;
|
|
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
|
|
if not assemble(currentline,currentaddress,assembled[0].bytes, apNone, true) then raise exception.Create('bla');
|
|
except
|
|
raise exception.Create(rsThisInstructionCanTBeCompiled);
|
|
end;
|
|
|
|
finally
|
|
inc(i);
|
|
end;
|
|
|
|
except
|
|
on E:exception do
|
|
raise exception.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 exception.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 exception.Create(Format(rsTheAddressInCreatethreadIsNotValid, [createthread[i]]));
|
|
|
|
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 exception.Create(Format(rsTheAddressInLoadbinaryIsNotValid, [loadbinary[i].address, loadbinary[i].filename]));
|
|
|
|
end;
|
|
|
|
|
|
if syntaxcheckonly then
|
|
begin
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
|
|
if popupmessages and (messagedlg(rsThisCodeCanBeInjectedAreYouSure, mtConfirmation , [mbyes, mbno], 0)<>mryes) then exit;
|
|
|
|
//allocate the memory
|
|
|
|
if length(allocs)>0 then
|
|
begin
|
|
|
|
j:=0; //entry to go from
|
|
prefered:=allocs[0].prefered;
|
|
x:=allocs[0].size;
|
|
|
|
for i:=1 to length(allocs)-1 do
|
|
begin
|
|
//does this entry have a prefered location?
|
|
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<>allocs[i].prefered) and (prefered<>0) 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)
|
|
allocs[j].address:=ptrUint(virtualallocex(processhandle,FindFreeBlockForRegion(prefered,x),x, MEM_RESERVE or MEM_COMMIT,page_execute_readwrite));
|
|
if allocs[j].address=0 then OutputDebugString(rsFailureToAllocateMemory+' 1');
|
|
|
|
dec(k);
|
|
end;
|
|
|
|
if allocs[j].address=0 then
|
|
allocs[j].address:=ptrUint(virtualallocex(processhandle,nil,x, MEM_RESERVE or MEM_COMMIT,page_execute_readwrite));
|
|
|
|
if allocs[j].address=0 then OutputDebugString(rsFailureToAllocateMemory+' 2');
|
|
|
|
//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;
|
|
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;
|
|
prefered:=ptrUint(FindFreeBlockForRegion(prefered,x));
|
|
|
|
|
|
allocs[j].address:=ptrUint(virtualallocex(processhandle,pointer(prefered),x, MEM_RESERVE or MEM_COMMIT,page_execute_readwrite));
|
|
if allocs[j].address=0 then
|
|
begin
|
|
OutputDebugString(rsFailureToAllocateMemory+' 3');
|
|
inc(prefered, 65536);
|
|
end;
|
|
dec(k);
|
|
end;
|
|
|
|
if allocs[j].address=0 then
|
|
allocs[j].address:=ptrUint(virtualallocex(processhandle,nil,x, MEM_RESERVE or MEM_COMMIT,page_execute_readwrite));
|
|
|
|
if allocs[j].address=0 then raise exception.create(rsFailureToAllocateMemory+' 4');
|
|
|
|
for i:=j+1 to length(allocs)-1 do
|
|
allocs[i].address:=allocs[i-1].address+allocs[i-1].size;
|
|
|
|
|
|
end;
|
|
end;
|
|
|
|
{$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}
|
|
|
|
//-----------------------2nd pass------------------------
|
|
//assemblerlines only contains label specifiers and assembler instructions
|
|
|
|
setlength(assembled,0);
|
|
for i:=0 to length(assemblerlines)-1 do
|
|
begin
|
|
currentline:=assemblerlines[i];
|
|
|
|
|
|
//plugin
|
|
if length(currentline)>0 then
|
|
begin
|
|
currentlinep:=@currentline[1];
|
|
pluginhandler.handleAutoAssemblerPlugin(@currentlinep, 2);
|
|
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;
|
|
//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
|
|
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
|
|
currentline:=replacetoken(currentline,labels[j].labelname,IntToHex(currentaddress+$1000FFFFF,8))
|
|
else
|
|
currentline:=replacetoken(currentline,labels[j].labelname,IntToHex(currentaddress+$FFFFF,8));
|
|
end;
|
|
|
|
|
|
|
|
setlength(assembled,length(assembled)+1);
|
|
assembled[length(assembled)-1].address:=currentaddress;
|
|
assemble(currentline,currentaddress,assembled[length(assembled)-1].bytes, apnone, true);
|
|
a:=length(assembled[length(assembled)-1].bytes);
|
|
|
|
assemble(s1,currentaddress,assembled[length(assembled)-1].bytes, apnone, true);
|
|
b:=length(assembled[length(assembled)-1].bytes);
|
|
|
|
if a>b then //pick the biggest one
|
|
assemble(currentline,currentaddress,assembled[length(assembled)-1].bytes);
|
|
|
|
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
|
|
begin
|
|
ok1:=false;
|
|
for j:=0 to length(labels)-1 do
|
|
begin
|
|
if i=labels[j].assemblerline then
|
|
begin
|
|
labels[j].address:=currentaddress;
|
|
labels[j].defined:=true;
|
|
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]],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)
|
|
//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
|
|
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 exception.Create(rsThisAddressSpecifierIsNotValid);
|
|
end;
|
|
end;
|
|
|
|
|
|
setlength(assembled,length(assembled)+1);
|
|
assembled[length(assembled)-1].address:=currentaddress;
|
|
|
|
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
|
|
else
|
|
assemble(currentline,currentaddress,assembled[length(assembled)-1].bytes);
|
|
end
|
|
else
|
|
assemble(currentline,currentaddress,assembled[length(assembled)-1].bytes);
|
|
|
|
inc(currentaddress,length(assembled[length(assembled)-1].bytes));
|
|
end;
|
|
//end of loop
|
|
|
|
ok2:=true;
|
|
|
|
//unprotectmemory
|
|
for i:=0 to length(fullaccess)-1 do
|
|
begin
|
|
virtualprotectex(processhandle,pointer(fullaccess[i].address),fullaccess[i].size,PAGE_EXECUTE_READWRITE,op);
|
|
|
|
if (fullaccess[i].address>$80000000) and (DBKLoaded) then
|
|
MakeWritable(fullaccess[i].address,(fullaccess[i].size div 4096)*4096,false);
|
|
end;
|
|
|
|
//load binaries
|
|
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;
|
|
testptr:=labels[j].address;
|
|
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;
|
|
testptr:=allocs[j].address;
|
|
break;
|
|
end;
|
|
|
|
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;
|
|
testptr:=kallocs[j].address;
|
|
break;
|
|
end;
|
|
|
|
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 ok1 then
|
|
begin
|
|
binaryfile:=tmemorystream.Create;
|
|
try
|
|
binaryfile.LoadFromFile(loadbinary[i].filename);
|
|
ok2:=writeprocessmemory(processhandle,pointer(testptr),binaryfile.Memory,binaryfile.Size,x);
|
|
finally
|
|
binaryfile.free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//we're still here so, inject it
|
|
|
|
|
|
connection:=getconnection;
|
|
if connection<>nil then
|
|
connection.beginWriteProcessMemory; //group all writes
|
|
|
|
for i:=0 to length(assembled)-1 do
|
|
begin
|
|
testptr:=assembled[i].address;
|
|
ok1:=virtualprotectex(processhandle,pointer(testptr),length(assembled[i].bytes),PAGE_EXECUTE_READWRITE,op);
|
|
ok1:=WriteProcessMemory(processhandle,pointeR(testptr),@assembled[i].bytes[0],length(assembled[i].bytes),x);
|
|
virtualprotectex(processhandle,pointer(testptr),length(assembled[i].bytes),op,op2);
|
|
|
|
if not ok1 then ok2:=false;
|
|
end;
|
|
|
|
if connection<>nil then //group all writes
|
|
begin
|
|
if connection.endWriteProcessMemory=false then
|
|
ok2:=false;
|
|
end;
|
|
|
|
|
|
|
|
|
|
if not ok2 then
|
|
begin
|
|
if popupmessages then showmessage(rsNotAllInstructionsCouldBeInjected)
|
|
end
|
|
else
|
|
begin
|
|
//if ceallocarray<>nil then
|
|
begin
|
|
//see if all allocs are deallocated
|
|
if (length(dealloc)>0) and (length(dealloc)=length(ceallocarray)) then //free everything
|
|
begin
|
|
{$ifdef cpu64}
|
|
baseaddress:=ptrUint($FFFFFFFFFFFFFFFF);
|
|
{$else}
|
|
baseaddress:=$FFFFFFFF;
|
|
{$endif}
|
|
|
|
for i:=0 to length(ceallocarray)-1 do
|
|
begin
|
|
if ceallocarray[i].address<baseaddress then
|
|
baseaddress:=ceallocarray[i].address;
|
|
end;
|
|
|
|
virtualfreeex(processhandle,pointer(baseaddress),0,MEM_RELEASE);
|
|
end;
|
|
|
|
setlength(ceallocarray,length(allocs));
|
|
for i:=0 to length(allocs)-1 do
|
|
ceallocarray[i]:=allocs[i];
|
|
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]);
|
|
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]);
|
|
ok1:=true;
|
|
except
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//still here, so create threads if needed
|
|
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;
|
|
testptr:=labels[j].address;
|
|
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;
|
|
testptr:=allocs[j].address;
|
|
break;
|
|
end;
|
|
|
|
if not ok1 then
|
|
for j:=0 to length(kallocs)-1 do
|
|
if uppercase(kallocs[j].varname)=uppercase(createthread[i]) then
|
|
begin
|
|
ok1:=true;
|
|
testptr:=kallocs[j].address;
|
|
break;
|
|
end;
|
|
|
|
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 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;
|
|
|
|
if popupmessages then
|
|
begin
|
|
s1:='';
|
|
for i:=0 to length(globalallocs)-1 do
|
|
s1:=s1+#13#10+globalallocs[i].varname+'='+IntToHex(globalallocs[i].address,8);
|
|
|
|
|
|
for i:=0 to length(allocs)-1 do
|
|
s1:=s1+#13#10+allocs[i].varname+'='+IntToHex(allocs[i].address,8);
|
|
|
|
if length(kallocs)>0 then
|
|
begin
|
|
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;
|
|
|
|
showmessage(rsTheCodeInjectionWasSuccessfull+s1);
|
|
end;
|
|
end;
|
|
|
|
result:=ok2;
|
|
|
|
finally
|
|
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
|
|
freemem(readmems[i].bytes);
|
|
|
|
setlength(readmems,0);
|
|
|
|
|
|
|
|
tokens.free;
|
|
|
|
pluginhandler.handleAutoAssemblerPlugin(@currentlinep, 3); //tell the plugins to free their data
|
|
|
|
if targetself then
|
|
begin
|
|
processhandler.processhandle:=oldhandle;
|
|
symhandler:=oldsymhandler;
|
|
end;
|
|
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 getScript(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(code[i]))='[ENABLE]' then
|
|
begin
|
|
insideenable:=true;
|
|
insidedisable:=false;
|
|
continue;
|
|
end;
|
|
|
|
if (uppercase(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);
|
|
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
|
|
{$ifdef cpu64}
|
|
inexcludedbitblock:=true;
|
|
{$endif}
|
|
code[i]:=' ';
|
|
end;
|
|
|
|
if s='[/32-BIT]' then
|
|
begin
|
|
{$ifdef cpu64}
|
|
inexcludedbitblock:=false;
|
|
{$endif}
|
|
code[i]:=' ';
|
|
end;
|
|
|
|
if s='[64-BIT]' then
|
|
begin
|
|
{$ifdef cpu32}
|
|
inexcludedbitblock:=true;
|
|
{$endif}
|
|
code[i]:=' ';
|
|
end;
|
|
|
|
if s='[/64-BIT]' then
|
|
begin
|
|
{$ifdef cpu32}
|
|
inexcludedbitblock:=false;
|
|
{$endif}
|
|
code[i]:=' ';
|
|
end;
|
|
|
|
if inexcludedbitblock then
|
|
code[i]:=' ';
|
|
|
|
|
|
|
|
end;
|
|
end;
|
|
|
|
function autoassemble(code: Tstrings; popupmessages,enable,syntaxcheckonly, targetself: boolean;var CEAllocarray: TCEAllocArray; registeredsymbols: tstringlist=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;
|
|
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
|
|
if not popupmessages then exit;
|
|
raise exception.Create(rsYouCanOnlyHaveOneEnableSection);
|
|
end;
|
|
|
|
if disablepos=-2 then
|
|
begin
|
|
if not popupmessages then exit;
|
|
raise exception.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 exception.Create(rsYouHavnTSpecifiedAEnableSection);
|
|
|
|
end;
|
|
|
|
if (disablepos=-1) then
|
|
begin
|
|
if not popupmessages then exit;
|
|
raise exception.Create(rsYouHavnTSpecifiedADisableSection);
|
|
|
|
end;
|
|
|
|
if enable then
|
|
begin
|
|
getscript(code, tempstrings, true);
|
|
end
|
|
else
|
|
begin
|
|
getscript(code, tempstrings,false);
|
|
end;
|
|
end;
|
|
|
|
if targetself then
|
|
Stripcpuspecificcode(tempstrings);
|
|
|
|
result:=autoassemble2(tempstrings,popupmessages,syntaxcheckonly,targetself,ceallocarray, registeredsymbols);
|
|
finally
|
|
tempstrings.Free;
|
|
end;
|
|
end;
|
|
|
|
function autoassemble(code: Tstrings; popupmessages,enable,syntaxcheckonly, targetself: boolean):boolean; overload;
|
|
var aa: TCEAllocArray;
|
|
begin
|
|
setlength(aa,0);
|
|
result:=autoassemble(code,popupmessages,enable,syntaxcheckonly,targetself,aa,nil);
|
|
end;
|
|
|
|
function autoassemble(code: tstrings;popupmessages: boolean):boolean; overload;
|
|
var aa: TCEAllocArray;
|
|
begin
|
|
setlength(aa,0);
|
|
result:=autoassemble(code,popupmessages,true,false,false,aa,nil);
|
|
end;
|
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|