cheat-engine/Cheat Engine/networkdebuggerinterface.pas

665 lines
21 KiB
ObjectPascal
Executable File

unit NetworkDebuggerInterface;
{$mode delphi}
interface
uses
{$ifdef windows}
jwawindows, windows,
{$endif}
Classes, SysUtils,cefuncproc, newkernelhandler,
DebuggerInterface, networkInterface, networkInterfaceApi, contnrs{$ifdef darwin},macport, macportdefines{$endif};
type
TNetworkDebuggerInterface=class(TDebuggerInterface)
private
handle: THandle;
lastevent: TNetworkDebugEvent;
fsinglestepNextContinue: boolean;
public
function WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL; override;
function ContinueDebugEvent(dwProcessId: DWORD; dwThreadId: DWORD; dwContinueStatus: DWORD): BOOL; override;
function SetThreadContext(hThread: THandle; const lpContext: TContext; isFrozenThread: Boolean=false): BOOL; override;
function GetThreadContext(hThread: THandle; var lpContext: TContext; isFrozenThread: Boolean=false): BOOL; override;
function SetThreadContextArm(hThread: THandle; const lpContext: TArmContext; isFrozenThread: Boolean=false): BOOL; override;
function GetThreadContextArm(hThread: THandle; var lpContext: TArmContext; isFrozenThread: Boolean=false): BOOL; override;
function GetThreadContextArm64(hThread: THandle; var lpContext: TArm64Context; isFrozenThread: Boolean=false): BOOL; override;
function SetThreadContextArm64(hThread: THandle; const lpContext: TArm64Context; isFrozenThread: Boolean=false): BOOL; override;
function GetLastBranchRecords(lbr: pointer): integer; override;
function canReportExactDebugRegisterTrigger: boolean; override;
function DebugActiveProcess(dwProcessId: DWORD): WINBOOL; override;
property SingleStepNextContinue: boolean read fSingleStepNextContinue write fSingleStepNextContinue;
destructor destroy; override;
constructor create;
end;
function SetThreadContext(hThread: THandle; const lpContext: TContext): BOOL;
function GetThreadContext(hThread: THandle; var lpContext: TContext): BOOL;
function SetThreadContextArm(hThread: THandle; const lpContext: TArmContext): BOOL;
function GetThreadContextArm(hThread: THandle; var lpContext: TArmContext): BOOL;
function GetThreadContextArm64(hThread: THandle; var lpContext: TArm64Context): BOOL;
function SetThreadContextArm64(hThread: THandle; const lpContext: TArm64Context): BOOL;
implementation
uses debuggertypedefinitions, ProcessHandlerUnit;
const
networkContextType_X86=0;
networkContextType_X86_64=1;
networkContextType_Arm=2;
networkContextType_Arm64=3;
type
TNetworkX86_32Context=packed record
ebx: dword;
ecx: dword;
edx: dword;
esi: dword;
edi: dword;
ebp: dword;
eax: dword;
ds: integer;
es: integer;
fs: integer;
gs: integer;
orig_eax: dword;
eip: dword;
cs: integer;
eflags: dword;
esp: dword;
ss: integer;
fp: TXmmSaveArea;
end;
PNetworkX86_32Context=^TNetworkX86_32Context;
TNetworkX86_64Context=packed record
r15: qword;
r14: qword;
r13: qword;
r12: qword;
rbp: qword;
rbx: qword;
r11: qword;
r10: qword;
r9: qword;
r8: qword;
rax: qword;
rcx: qword;
rdx: qword;
rsi: qword;
rdi: qword;
orig_rax: qword;
rip: qword;
cs: qword;
eflags: qword;
rsp: qword;
ss: qword;
fs_base: qword;
gs_base: qword;
ds: qword;
es: qword;
fs: qword;
gs: qword;
fp: TXmmSaveArea;
end;
PNetworkX86_64Context=^TNetworkX86_64Context;
TNetworkARM_32Context=packed record
R0: DWORD;
R1: DWORD;
R2: DWORD;
R3: DWORD;
R4: DWORD;
R5: DWORD;
R6: DWORD;
R7: DWORD;
R8: DWORD;
R9: DWORD;
R10: DWORD;
FP: DWORD;
IP: DWORD;
SP: DWORD;
LR: DWORD;
PC: DWORD;
CPSR: DWORD;
ORIG_R0: DWORD;
fpu: record
regs: array [0..31] of QWORD;
control: DWORD;
end;
end;
TNetworkARM_64Context=TARM64CONTEXT;
TNetworkContext=packed record
contextsize: uint32;
contexttype: uint32; //0=x86, 1=x86_64, 2=arm, 3=arm64
case integer of //contexttype
0: (contextx86: TNetworkX86_32Context);
1: (contextx86_64: TNetworkX86_64Context);
2: (contextarm32: TNetworkARM_32Context);
3: (contextarm64: TNetworkARM_64Context);
end;
PNetworkContext=^TNetworkContext;
function SetThreadContext(hThread: THandle; const lpContext: TContext): bool;
var
context: TNetworkContext;
c: TCEConnection=nil;
begin
c:=getConnection;
if c<>nil then
begin
{$ifdef cpu64}
if processhandler.is64Bit then
begin
context.contextsize:=sizeof(TNetworkX86_64Context)+8;
context.contexttype:=1; //x86_64
context.contextx86_64.r15:=lpcontext.r15;
context.contextx86_64.r14:=lpcontext.r14;
context.contextx86_64.r13:=lpcontext.r13;
context.contextx86_64.r12:=lpcontext.r12;
context.contextx86_64.rbp:=lpcontext.rbp;
context.contextx86_64.rbx:=lpcontext.Rbx;
context.contextx86_64.r11:=lpcontext.R11;
context.contextx86_64.r10:=lpcontext.R10;
context.contextx86_64.r9:= lpcontext.R9;
context.contextx86_64.r8:= lpcontext.R8;
context.contextx86_64.rax:=lpcontext.Rax;
context.contextx86_64.rcx:=lpcontext.Rcx;
context.contextx86_64.rdx:=lpcontext.Rdx;
context.contextx86_64.rsi:=lpcontext.Rsi;
context.contextx86_64.rdi:=lpcontext.Rdi;
context.contextx86_64.orig_rax:=lpcontext.P1Home;
context.contextx86_64.rip:=lpcontext.rip;
context.contextx86_64.cs:=lpcontext.SegCs;
context.contextx86_64.eflags:=lpcontext.EFlags;
context.contextx86_64.rsp:=lpcontext.rsp;
context.contextx86_64.ss:=lpcontext.Segss;
context.contextx86_64.fs_base:=lpcontext.P2Home;
context.contextx86_64.gs_base:=lpcontext.P3Home;
context.contextx86_64.ds:=lpcontext.Segds;
context.contextx86_64.es:=lpcontext.Seges;
context.contextx86_64.fs:=lpcontext.Segfs;
context.contextx86_64.gs:=lpcontext.Seggs;
context.contextx86_64.fp:=lpcontext.FltSave;
end
else
{$endif}
begin
context.contextsize:=sizeof(TNetworkX86_64Context)+8;
context.contexttype:=0; // x86
context.contextx86.ebx:=lpcontext.{$ifdef cpu64}rbx{$else}ebx{$endif};
context.contextx86.ecx:=lpcontext.{$ifdef cpu64}rcx{$else}ecx{$endif};
context.contextx86.edx:=lpcontext.{$ifdef cpu64}rdx{$else}edx{$endif};
context.contextx86.esi:=lpcontext.{$ifdef cpu64}rsi{$else}esi{$endif};
context.contextx86.edi:=lpcontext.{$ifdef cpu64}rdi{$else}edi{$endif};
context.contextx86.ebp:=lpcontext.{$ifdef cpu64}rbp{$else}ebp{$endif};
context.contextx86.eax:=lpcontext.{$ifdef cpu64}rax{$else}eax{$endif};
context.contextx86.ds:=lpcontext.segds;
context.contextx86.es:=lpcontext.seges;
context.contextx86.fs:=lpcontext.segfs;
context.contextx86.gs:=lpcontext.seggs;
context.contextx86.eip:=lpcontext.{$ifdef cpu64}rip{$else}eip{$endif};
context.contextx86.cs:=lpcontext.segcs;
context.contextx86.eflags:=lpcontext.EFlags;
context.contextx86.esp:=lpcontext.{$ifdef cpu64}rsp{$else}esp{$endif};
context.contextx86.ss:=lpcontext.segss;
context.contextx86.fp:=lpcontext.{$ifdef cpu64}FltSave{$else}ext{$endif};
end;
result:=c.setContext(processhandle, hThread, @context, context.contextsize);
end;
end;
function GetThreadContext(hThread: THandle; var lpContext: TContext): BOOL;
var
context: PNetworkContext=nil;
c: TCEConnection=nil;
begin
result:=false;
zeromemory(@lpContext, sizeof(TContext));
c:=getConnection;
if c<>nil then
begin
context:=c.AllocateAndGetContext(processhandle, hThread);
try
if (context<>nil) and (processhandler.SystemArchitecture=archX86) then
begin
{$ifdef cpu64}
if processhandler.is64Bit then
begin
if context^.contexttype<>networkContextType_X86_64 then
begin
OutputDebugString('Expected X86_64 context, received type '+context^.contexttype.ToString);
exit(false);
end;
lpcontext.r15:=context^.contextx86_64.r15;
lpcontext.r14:=context^.contextx86_64.r14;
lpcontext.r13:=context^.contextx86_64.r13;
lpcontext.r12:=context^.contextx86_64.r12;
lpcontext.rbp:=context^.contextx86_64.rbp;
lpcontext.Rbx:=context^.contextx86_64.rbx;
lpcontext.R11:=context^.contextx86_64.r11;
lpcontext.R10:=context^.contextx86_64.r10;
lpcontext.R9:=context^.contextx86_64.r9;
lpcontext.R8:=context^.contextx86_64.r8;
lpcontext.Rax:=context^.contextx86_64.rax;
lpcontext.Rcx:=context^.contextx86_64.rcx;
lpcontext.Rdx:=context^.contextx86_64.rdx;
lpcontext.Rsi:=context^.contextx86_64.rsi;
lpcontext.Rdi:=context^.contextx86_64.rdi;
lpcontext.P1Home:=context^.contextx86_64.orig_rax;
lpcontext.rip:=context^.contextx86_64.rip;
lpcontext.SegCs:=context^.contextx86_64.cs;
lpcontext.EFlags:=context^.contextx86_64.eflags;
lpcontext.rsp:=context^.contextx86_64.rsp;
lpcontext.Segss:=context^.contextx86_64.ss;
lpcontext.P2Home:=context^.contextx86_64.fs_base;
lpcontext.P3Home:=context^.contextx86_64.gs_base;
lpcontext.Segds:=context^.contextx86_64.ds;
lpcontext.Seges:=context^.contextx86_64.es;
lpcontext.Segfs:=context^.contextx86_64.fs;
lpcontext.Seggs:=context^.contextx86_64.gs;
lpcontext.FltSave:=context^.contextx86_64.fp;
end
else
{$endif}
begin
if context^.contexttype<>networkContextType_X86 then
begin
OutputDebugString('Expected X86_32 context, received type '+context^.contexttype.ToString);
exit(false);
end;
lpcontext.{$ifdef cpu64}rbx{$else}ebx{$endif}:=context^.contextx86.ebx;
lpcontext.{$ifdef cpu64}rcx{$else}ecx{$endif}:=context^.contextx86.ecx;
lpcontext.{$ifdef cpu64}rdx{$else}edx{$endif}:=context^.contextx86.edx;
lpcontext.{$ifdef cpu64}rsi{$else}esi{$endif}:=context^.contextx86.esi;
lpcontext.{$ifdef cpu64}rdi{$else}edi{$endif}:=context^.contextx86.edi;
lpcontext.{$ifdef cpu64}rbp{$else}ebp{$endif}:=context^.contextx86.ebp;
lpcontext.{$ifdef cpu64}rax{$else}eax{$endif}:=context^.contextx86.eax;
lpcontext.segds:=context^.contextx86.ds;
lpcontext.seges:=context^.contextx86.es;
lpcontext.segfs:=context^.contextx86.fs;
lpcontext.seggs:=context^.contextx86.gs;
lpcontext.{$ifdef cpu64}rip{$else}eip{$endif}:=context^.contextx86.eip;
lpcontext.segcs:=context^.contextx86.cs;
lpcontext.EFlags:=context^.contextx86.eflags;
lpcontext.{$ifdef cpu64}rsp{$else}esp{$endif}:=context^.contextx86.esp;
lpcontext.segss:=context^.contextx86.ss;
lpcontext.{$ifdef cpu64}FltSave{$else}ext{$endif}:=context^.contextx86.fp;
end;
end; //you should use GetThreadContextArm
finally
if context<>nil then
FreeMemAndNil(context);
end;
end;
result:=lpContext.{$ifdef cpu64}rip{$else}eip{$endif}<>0;
end;
function SetThreadContextArm(hThread: THandle; const lpContext: TArmContext): BOOL;
var
carm: TNetworkContext;
c: TCEConnection=nil;
begin
c:=getConnection;
if c<>nil then
begin
carm.contextsize:=sizeof(TNetworkARM_32Context)+8;
carm.contexttype:=2; //arm 32
carm.contextarm32.R0:=lpContext.R0;
carm.contextarm32.R1:=lpContext.R1;
carm.contextarm32.R2:=lpContext.R2;
carm.contextarm32.R3:=lpContext.R3;
carm.contextarm32.R4:=lpContext.R4;
carm.contextarm32.R5:=lpContext.R5;
carm.contextarm32.R6:=lpContext.R6;
carm.contextarm32.R7:=lpContext.R7;
carm.contextarm32.R8:=lpContext.R8;
carm.contextarm32.R9:=lpContext.R9;
carm.contextarm32.R10:=lpContext.R10;
carm.contextarm32.FP:=lpContext.FP;
carm.contextarm32.IP:=lpContext.IP;
carm.contextarm32.SP:=lpContext.SP;
carm.contextarm32.LR:=lpContext.LR;
carm.contextarm32.PC:=lpContext.PC and $fffffffe;
carm.contextarm32.CPSR:=lpContext.CPSR;
carm.contextarm32.ORIG_R0:=lpContext.ORIG_R0;
CopyMemory(@carm.contextarm32.fpu.regs[0], @lpContext.fpu[0], 32*sizeof(qword));
carm.contextarm32.fpu.control:=lpContext.fpureg;
result:=c.setContext(processhandle, hThread, @carm, carm.contextsize);
end;
end;
function GetThreadContextArm(hThread: THandle; var lpContext: TArmContext): BOOL;
var
carm: PNetworkContext=nil;
c: TCEConnection=nil;
begin
result:=false;
c:=getConnection;
if c<>nil then
begin
carm:=c.AllocateAndGetContext(processhandle, hThread);
try
if (carm<>nil) and (processhandler.SystemArchitecture=archARM) then
begin
if processhandler.is64Bit then
begin
lpContext.PC:=$64646464; //holder for 64 bit for now
end
else
begin
if carm^.contexttype<>networkContextType_Arm then
begin
OutputDebugString('Expected ARM context, received type '+carm^.contexttype.ToString);
exit(false);
end;
lpContext.R0:=carm^.contextarm32.R0;
lpContext.R1:=carm^.contextarm32.R1;
lpContext.R2:=carm^.contextarm32.R2;
lpContext.R3:=carm^.contextarm32.R3;
lpContext.R4:=carm^.contextarm32.R4;
lpContext.R5:=carm^.contextarm32.R5;
lpContext.R6:=carm^.contextarm32.R6;
lpContext.R7:=carm^.contextarm32.R7;
lpContext.R8:=carm^.contextarm32.R8;
lpContext.R9:=carm^.contextarm32.R9;
lpContext.R10:=carm^.contextarm32.R10;
lpContext.FP:=carm^.contextarm32.FP;
lpContext.IP:=carm^.contextarm32.IP;
lpContext.SP:=carm^.contextarm32.SP;
lpContext.LR:=carm^.contextarm32.LR;
lpContext.PC:=carm^.contextarm32.PC;
lpContext.CPSR:=carm^.contextarm32.CPSR;
lpContext.ORIG_R0:=carm^.contextarm32.ORIG_R0;
if (lpContext.CPSR and (1 shl 5))<>0 then //Thumb bit
lpContext.PC:=lpContext.PC or 1; //quick hack to identify that thumb is used
CopyMemory(@lpContext.fpu[0], @carm^.contextarm32.fpu.regs[0], 32*sizeof(qword) );
lpcontext.fpureg:=carm^.contextarm32.fpu.control;
result:=true;
end;
end; //else use GetThreadContext
finally
if (carm<>nil) then
FreeMemAndNil(carm);
end;
end;
end;
function GetThreadContextArm64(hThread: THandle; var lpContext: TArm64Context): BOOL;
var
carm64: PNetworkContext=nil;
c: TCEConnection=nil;
begin
result:=false;
c:=getConnection;
if c<>nil then
begin
carm64:=c.AllocateAndGetContext(processhandle, hThread);
try
if (carm64<>nil) and (processhandler.SystemArchitecture=archArm) then
begin
if processhandler.is64Bit then
begin
if carm64^.contexttype<>networkContextType_Arm64 then
begin
OutputDebugString('Expected ARM64 context, received type '+carm64^.contexttype.ToString);
exit(false);
end;
lpContext:=carm64^.contextarm64;
result:=true;
end; //else use GetThreadContextArm()
end; //else use GetThreadContext
finally
if (carm64<>nil) then
FreeMemAndNil(carm64);
end;
end;
end;
function SetThreadContextArm64(hThread: THandle; const lpContext: TArm64Context): BOOL;
var
carm64: TNetworkContext;
c: TCEConnection=nil;
begin
c:=getConnection;
if c<>nil then
begin
carm64.contextsize:=sizeof(TNetworkARM_64Context)+8;
carm64.contexttype:=3; //arm64
carm64.contextarm64:=lpContext;
result:=c.setContext(processhandle, hThread, @carm64, carm64.contextsize);
end;
end;
function TNetworkDebuggerInterface.WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL;
var
c: TCEConnection;
begin
result:=false;
c:=getConnection;
if c<>nil then
begin
lastevent.signal:=5;
result:=c.WaitForDebugEvent(handle, dwMilliseconds*5, lastevent);
if result then
begin
//convert it to 'something' useful
lpDebugEvent.dwThreadId:=lastevent.threadid;
lpDebugEvent.dwProcessId:=processid;
case lastevent.signal of
-1 : //create thread
begin
lpDebugEvent.dwDebugEventCode:=CREATE_THREAD_DEBUG_EVENT;
lpDebugEvent.CreateThread.hThread:=lastevent.threadid;
end;
-2 : //create process
begin
lpDebugEvent.dwDebugEventCode:=CREATE_PROCESS_DEBUG_EVENT;
lpDebugEvent.CreateProcessInfo.hProcess:=handle;
lpDebugEvent.CreateProcessInfo.hThread:=lastevent.threadid;
//set the breakpoint capability
fmaxInstructionBreakpointCount:=lastevent.createProcess.maxBreakpointCount;
fmaxWatchpointBreakpointCount:=lastevent.createProcess.maxWatchpointCount;
fmaxSharedBreakpointCount:=lastevent.createProcess.maxSharedBreakpoints;
end;
5: //SIGTRAP
begin
lpDebugEvent.dwDebugEventCode:=EXCEPTION_DEBUG_EVENT;
lpDebugEvent.Exception.dwFirstChance:=1;
lpDebugEvent.Exception.ExceptionRecord.NumberParameters:=0;
lpDebugEvent.Exception.ExceptionRecord.ExceptionCode:=EXCEPTION_SINGLE_STEP;
lpDebugEvent.Exception.ExceptionRecord.ExceptionAddress:=pointer(lastevent.address);
end;
19: //sigstop
begin
//just ignore. continue and return that no stop happened (timeout)
ContinueDebugEvent(handle, lastevent.threadid, DBG_CONTINUE);
result:=false;
end;
else
begin
//no idea
ContinueDebugEvent(handle, lastevent.threadid, DBG_EXCEPTION_NOT_HANDLED);
result:=false;
end;
end;
end;
end;
end;
function TNetworkDebuggerInterface.ContinueDebugEvent(dwProcessId: DWORD; dwThreadId: DWORD; dwContinueStatus: DWORD): BOOL;
var
c: TCEConnection;
begin
result:=false;
c:=getConnection;
if c<>nil then
begin
if dwContinueStatus=DBG_CONTINUE then
begin
if fSingleStepNextContinue then
result:=c.ContinueDebugEvent(handle, dwThreadID, 2) //ignore this signal and enter a single step mode
else
result:=c.ContinueDebugEvent(handle, dwThreadID, 1); //ignore this signal
fSingleStepNextContinue:=false;
end
else
result:=c.ContinueDebugEvent(handle, dwThreadID, 0);
end;
end;
function TNetworkDebuggerInterface.SetThreadContextArm(hThread: THandle; const lpContext: TArmContext; isFrozenThread: Boolean=false): BOOL;
begin
exit(NetworkDebuggerInterface.SetThreadContextArm(hThread, lpContext));
end;
function TNetworkDebuggerInterface.SetThreadContextArm64(hThread: THandle; const lpContext: TArm64Context; isFrozenThread: Boolean=false): BOOL;
begin
exit(NetworkDebuggerInterface.SetThreadContextArm64(hThread, lpContext));
end;
function TNetworkDebuggerInterface.GetThreadContextArm64(hThread: THandle; var lpContext: TArm64Context; isFrozenThread: Boolean=false): BOOL;
begin
exit(NetworkDebuggerInterface.GetThreadContextArm64(hThread, lpContext));
end;
function TNetworkDebuggerInterface.GetThreadContextArm(hThread: THandle; var lpContext: TArmContext; isFrozenThread: Boolean=false): BOOL;
begin
exit(NetworkDebuggerInterface.GetThreadContextArm(hthread, lpcontext));
end;
function TNetworkDebuggerInterface.SetThreadContext(hThread: THandle; const lpContext: TContext; isFrozenThread: Boolean=false): BOOL;
begin
exit(NetworkDebuggerInterface.SetThreadContext(hthread, lpcontext));
end;
function TNetworkDebuggerInterface.GetThreadContext(hThread: THandle; var lpContext: TContext; isFrozenThread: Boolean=false): BOOL;
begin
exit(NetworkDebuggerInterface.GetThreadContext(hthread, lpcontext));
end;
function TNetworkDebuggerInterface.GetLastBranchRecords(lbr: pointer): integer;
begin
result:=0;
end;
function TNetworkDebuggerInterface.DebugActiveProcess(dwProcessId: DWORD): WINBOOL;
var c: TCEConnection;
begin
result:=false;
processhandler.processid:=dwProcessID;
Open_Process;
handle:=ProcessHandle;
if (handle<>0) then
begin
c:=getConnection;
if c<>nil then
result:=c.StartDebug(handle);
end;
end;
function TNetworkDebuggerInterface.canReportExactDebugRegisterTrigger: boolean;
begin
result:=false;
end;
destructor TNetworkDebuggerInterface.destroy;
begin
{
if (handle)
networkStopDebug();
}
inherited destroy;
end;
constructor TNetworkDebuggerInterface.create;
begin
inherited create;
//no software breakpoint for now
fDebuggerCapabilities:=[dbcHardwareBreakpoint];
end;
end.