cheat-engine/Cheat Engine/addressparser.pas
cheat-engine 29d3a02b29 make some error messages more clear and fix an issue with symbol lookup when not debugging
also add in a check when assembling silently fails
2022-12-27 05:37:06 +00:00

566 lines
13 KiB
ObjectPascal
Executable file

unit addressparser;
{$MODE Delphi}
interface
uses
{$ifdef darwin}macport,{$endif}
LCLIntf,SysUtils,dialogs,symbolhandler, NewKernelHandler;
resourcestring
rsAPThisIsNotAValidAddress = 'This is not a valid address';
type
TSeperator = (plus,minus,multiply); //the things the disassembler shows (NO DIVIDE)
TAddressParser=class
private
STR: string;
blurb: string;
ch: integer;
address: ptruint;
increase: boolean;
total: array of byte; //0=seperator 1=value
values: array of int64;
seperators: array of TSeperator;
SpecialContext: PContext;
procedure seperator;
procedure value;
procedure x86register;
procedure e;
procedure arm_F;
procedure armregister;
procedure arm64register;
function simplify3: boolean;
function simplify2: boolean;
function simplify1: boolean;
public
procedure setSpecialContext(c: PContext);
function getaddress(S: string;skipsymhandler: boolean=false):ptrUint;
function getBaseAddress(s: string):ptruint;
end;
var mainthreadAddressParser: TAddressParser;
function getaddress(S: string):ptrUint; //for old code
implementation
uses memorybrowserformunit, StrUtils, ProcessHandlerUnit;
procedure TAddressParser.seperator;
//sets the seperator
begin
setlength(total,length(total)+1);
total[length(total)-1]:=0;
setlength(seperators,length(seperators)+1);
case str[ch] of
'+' : seperators[length(seperators)-1]:=plus;
'-' : seperators[length(seperators)-1]:=minus;
'*' : seperators[length(seperators)-1]:=multiply;
end;
inc(ch);
end;
procedure TAddressParser.value;
//copy all characters of 0 to 9 in tempstr
var tmp: ptruint;
strp: pchar;
count: integer;
begin
strp:=@str[ch];
val('$'+string(strp),tmp,count);
setlength(total,length(total)+1);
total[length(total)-1]:=1; //value
setlength(values,length(values)+1);
values[length(values)-1]:=tmp;
inc(ch,count-2);
if count=0 then ch:=length(str)+1; //it went to the end
end;
procedure TAddressParser.armregister;
var
tmp: ptruint;
tmps: string;
c: PARMCONTEXT;
regnr: integer;
regnrstr: string;
begin
if SpecialContext<>nil then
c:=PARMCONTEXT(SpecialContext)
else
c:=memorybrowser.context;
regnr:=-1;
if str[ch]='R' then
begin
regnrstr:='';
inc(ch);
while str[ch] in ['0'..'9'] do
begin
regnrstr:=regnrstr+str[ch];
inc(ch);
end;
regnr:=regnrstr.ToInteger;
end
else
if (str[ch]='F') and (str[ch+1]='P') then begin regnr:=11; inc(ch,2); end else
if (str[ch]='I') and (str[ch+1]='P') then begin regnr:=12; inc(ch,2); end else
if (str[ch]='S') and (str[ch+1]='P') then begin regnr:=13; inc(ch,2); end else
if (str[ch]='L') and (str[ch+1]='R') then begin regnr:=14; inc(ch,2); end else
if (str[ch]='P') and (str[ch+1]='C') then begin regnr:=15; inc(ch,2); end;
if regnr<>-1 then
begin
setlength(total,length(total)+1);
total[length(total)-1]:=1; //value
setlength(values,length(values)+1);
values[length(values)-1]:=ptruint(@c^.R0)+sizeof(dword)*regnr;
end;
end;
procedure TAddressParser.arm64register;
var
tmp: ptruint;
tmps: string;
c: PARMCONTEXT;
regnr: integer;
regnrstr: string;
begin
if SpecialContext<>nil then
c:=PARMCONTEXT(SpecialContext)
else
c:=memorybrowser.context;
regnr:=-1;
if str[ch]='X' then
begin
regnrstr:='';
inc(ch);
while str[ch] in ['0'..'9'] do
begin
regnrstr:=regnrstr+str[ch];
inc(ch);
end;
regnr:=regnrstr.ToInteger;
end
else
if (str[ch]='S') and (str[ch+1]='P') then begin regnr:=31; inc(ch,2); end;
if regnr<>-1 then
begin
setlength(total,length(total)+1);
total[length(total)-1]:=1; //value
setlength(values,length(values)+1);
values[length(values)-1]:=ptruint(@c^.R0)+sizeof(dword)*regnr;
end;
end;
procedure TAddressParser.x86register;
var tmp: ptruint;
tmps: string;
c: Pcontext;
xregnrstr: string;
xregnr: integer;
begin
tmp:=0;
tmps:=copy(str,ch,3);
if SpecialContext<>nil then
c:=SpecialContext
else
c:=memorybrowser.context;
if c=nil then raise exception.Create(rsAPThisIsNotAValidAddress);
if tmps[1] in ['X','Y'] then
begin
if tmps.Substring(1,2)='MM' then
begin
inc(ch,3);
xregnrstr:='';
while str[ch] in ['0'..'9'] do
begin
xregnrstr:=xregnrstr+str[ch];
inc(ch);
end;
xregnr:=xregnrstr.ToInteger;
setlength(total,length(total)+1);
total[length(total)-1]:=1; //value
setlength(values,length(values)+1);
values[length(values)-1]:=pptruint(@c^.{$ifdef cpu64}FltSave.XmmRegisters[xregnr]{$else}ext.XMMRegisters[xregnr]{$endif})^;
exit;
end
else
raise exception.Create(rsAPThisIsNotAValidAddress);
end
else
begin
if tmps='EAX' then tmp:=c^.{$ifdef cpu64}rax{$else}eax{$endif} else
if tmps='EBX' then tmp:=c^.{$ifdef cpu64}rbx{$else}ebx{$endif} else
if tmps='ECX' then tmp:=c^.{$ifdef cpu64}rcx{$else}ecx{$endif} else
if tmps='EDX' then tmp:=c^.{$ifdef cpu64}rdx{$else}edx{$endif} else
if tmps='ESI' then tmp:=c^.{$ifdef cpu64}rsi{$else}esi{$endif} else
if tmps='EDI' then tmp:=c^.{$ifdef cpu64}rdi{$else}edi{$endif} else
if tmps='EBP' then tmp:=c^.{$ifdef cpu64}rbp{$else}ebp{$endif} else
if tmps='ESP' then tmp:=c^.{$ifdef cpu64}rsp{$else}esp{$endif} else
if tmps='EIP' then tmp:=c^.{$ifdef cpu64}rip{$else}eip{$endif}
{$ifdef cpu64}
else
if tmps='RAX' then tmp:=c^.rax else
if tmps='RBX' then tmp:=c^.rbx else
if tmps='RCX' then tmp:=c^.rcx else
if tmps='RDX' then tmp:=c^.rdx else
if tmps='RSI' then tmp:=c^.rsi else
if tmps='RDI' then tmp:=c^.rdi else
if tmps='RBP' then tmp:=c^.rbp else
if tmps='RSP' then tmp:=c^.rsp else
if tmps='RIP' then tmp:=c^.rip else
if tmps='R10' then tmp:=c^.r10 else
if tmps='R11' then tmp:=c^.r11 else
if tmps='R12' then tmp:=c^.r12 else
if tmps='R13' then tmp:=c^.r13 else
if tmps='R14' then tmp:=c^.r14 else
if tmps='R15' then tmp:=c^.r15 else
begin
tmps:=copy(str,ch,2);
if tmps='R8' then tmp:=c^.r8 else
if tmps='R9' then tmp:=c^.r9;
end
{$endif}
;
end;
setlength(total,length(total)+1);
total[length(total)-1]:=1; //value
setlength(values,length(values)+1);
values[length(values)-1]:=tmp;
inc(ch,length(tmps));
end;
procedure TAddressParser.arm_F;
begin
if (ch+1<=length(str)) then //could be FP
begin
case str[ch+1] of
'P' :armregister; //FP
else value;
end;
end
else value;
end;
procedure TAddressParser.E;
//find out if this is a register or a value
begin
if (ch+2<=length(str)) then //the 3th char of a reg is a X,I or P , so no A to F
begin
case str[ch+2] of
'X','I','P' :x86register;
else value;
end;
end
else value;
end;
function TAddressParser.simplify3: boolean;
var scount,vcount: integer;
i,j: integer;
begin
//calculate the *'s
result:=false;
scount:=0;
vcount:=0;
for i:=0 to length(total)-1 do
begin
if total[i]=0 then
begin //at a * multiplie the value before and after. (start from left to right)
if seperators[scount]=multiply then
begin
for j:=scount to length(seperators)-2 do
seperators[j]:=seperators[j+1];
setlength(seperators,length(seperators)-1);
values[vcount-1]:=values[vcount-1]*values[vcount];
for j:=vcount to length(values)-2 do
values[j]:=values[j+1];
setlength(values,length(values)-1);
//now remove the seperator and value after it
for j:=i to length(total)-4 do //DUH!!!!!!! 0101010101 (i could just cut the last 2 of...)
begin
total[j]:=total[j+2];
total[j+1]:=total[j+3];
end;
setlength(total,length(total)-2);
result:=true;
exit;
end;
inc(scount);
end else inc(vcount);
end;
end;
function TAddressParser.simplify2: boolean;
var scount: integer;
i,j: integer;
begin
//find the spots that have 2 seperators next to eachother and remove the + from it
scount:=0;
result:=false;
for i:=0 to length(total)-2 do
begin
if total[i]=0 then
begin
if total[i+1]=0 then //2 separators next to eachother
begin
if seperators[scount]=plus then
begin
for j:=scount to length(seperators)-2 do
seperators[j]:=seperators[j+1];
setlength(seperators,length(seperators)-1);
for j:=i to length(total)-2 do
total[j]:=total[j+1];
setlength(total,length(total)-1);
result:=true;
exit;
end else
begin //this'll take care of ** too
for j:=scount+1 to length(seperators)-2 do
seperators[j]:=seperators[j+1];
setlength(seperators,length(seperators)-1);
for j:=i+1 to length(total)-2 do
total[j]:=total[j+1];
setlength(total,length(total)-1);
result:=true;
exit;
end;
end;
inc(scount);
end;
end;
end;
function TAddressParser.simplify1: boolean;
var scount,vcount: integer;
i,j: integer;
begin
//find a - and remove it
//then do valueafter:=-valueafter
scount:=0;
vcount:=0;
for i:=0 to length(total)-1 do
begin
if total[i]=0 then
begin
if seperators[scount]=minus then
begin
values[vcount]:=-values[vcount];
seperators[scount]:=plus;
result:=true;
exit;
end;
inc(scount);
end else inc(vcount);
end;
result:=false;
end;
function TAddressParser.getaddress(s: string;skipsymhandler: boolean=false): ptrUint;
var i,j: integer;
scount,vcount: integer;
tempstr: string;
haserror: boolean;
c: Pcontext;
begin
if skipsymhandler=false then
begin
if SpecialContext<>nil then
c:=SpecialContext
else
c:=memorybrowser.context;
result:=symhandler.getaddressfromname(s,false,haserror, c);
if (result<>0) and (not haserror) then exit;
end;
if s='' then s:='0';
setlength(total,2);
setlength(values,1);
setlength(seperators,1);
values[0]:=0; //0+
seperators[0]:=plus;
total[0]:=1; //value
total[1]:=0; //seperator
ch:=1;
address:=0;
increase:=true;
str:=uppercase(s);
if processhandler.SystemArchitecture=archX86 then
begin
while ch<=length(s) do
begin
case str[ch] of
'$' : begin
inc(ch);
value;
end;
'0'..'9' : value;
'A'..'D' : value;
'E' : E;
'F' : value;
'R','X','Y': x86register;
'+','-','*' : seperator;
else raise exception.Create(rsAPThisIsNotAValidAddress);
end;
end;
end
else
begin
//ARM
if processhandler.is64Bit then
begin
//arm64
while ch<=length(s) do
begin
case str[ch] of
'$' : begin
inc(ch);
value;
end;
'0'..'9' : value;
'A'..'F' : value;
'X','S' : arm64register; //X##, SP
'+','-','*' : seperator;
else raise exception.Create(rsAPThisIsNotAValidAddress);
end;
end;
end
else
begin
//arm32
while ch<=length(s) do
begin
case str[ch] of
'$' : begin
inc(ch);
value;
end;
'0'..'9' : value;
'A'..'E' : value;
'F': arm_F;
'R','S','L', 'P': armregister; //R##, SP, LR, PC
'+','-','*' : seperator;
else raise exception.Create(rsAPThisIsNotAValidAddress);
end;
end;
end;
end;
//total should now be filled with a string consisting of 0 and 1
while simplify1 do ; //remove the - seperators
while simplify2 do ; //remove double seperators
while simplify3 do ; //remove the * by replacing the values with the multiplied value
for i:=0 to length(values)-1 do inc(address,values[i]);
result:=address;
end;
function TAddressParser.getBaseAddress(s: string):ptruint;
var maxvalue: ptruint;
i: integer;
part: string;
begin
//strip of the +XXXX part if XXXX is a hexadecimal value
i:=RPos('+',s);
if i>0 then
begin
part:=copy(s,i+1);
try
strtoint('$'+part);
//still here so yes
s:=copy(s,1,i-1);
exit(getAddress(s,true));
except
end;
end;
//still here, use the 'old' method
getaddress(s, true);
maxvalue:=0;
for i:=0 to length(values)-1 do
begin
if values[i]>0 then
begin
if maxvalue<ptruint(values[i]) then
maxvalue:=ptruint(values[i]);
end;
end;
result:=maxvalue;
end;
procedure TAddressParser.setSpecialContext(c: PContext);
begin
SpecialContext:=c;
end;
function getaddress(S: string):ptrUint;
begin
result:=mainthreadAddressParser.getaddress(s);
end;
initialization
mainthreadAddressParser:=TAddressParser.create;
finalization
mainthreadAddressParser.free;
end.