cheat-engine/Cheat Engine/VEHDebugger.pas
cheatengine@gmail.com 0f35a52416 move the processid and handle from cefuncproc to processhandlerunit
move the processlist function from cefuncproc to it's own unit
start work on the jni library for java
2014-09-08 12:00:14 +00:00

474 lines
14 KiB
ObjectPascal

unit VEHDebugger;
{$mode delphi}
interface
uses
jwaNtStatus, Windows, Classes, SysUtils,symbolhandler,VEHDebugSharedMem,cefuncproc,
autoassembler,newkernelhandler,DebuggerInterface, Clipbrd;
type
TVEHDebugInterface=class(TDebuggerInterface)
private
guid: TGUID; //to indentify this specific debugger
guidstring: string;
HasDebugEvent: THandle;
HasHandledDebugEvent: THandle;
ConfigFileMapping: THandle;
VEHDebugView: PVEHDebugSharedMem;
active: boolean;
is64bit: boolean; //stored local so it doesn't have to evaluate the property (saves some time)
hasPausedProcess: 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 DebugActiveProcess(dwProcessId: DWORD): WINBOOL; override;
function DebugActiveProcessStop(dwProcessID: DWORD): WINBOOL; override;
destructor destroy; override;
constructor create;
end;
implementation
uses ProcessHandlerUnit;
resourcestring
rsErrorWhileTryingToCreateTheConfigurationStructure = 'Error while trying '
+'to create the configuration structure! (Which effectively renders this '
+'whole feature useless) Errorcode=%s';
rsCheatEngineFailedToGetIntoTheConfig = 'Cheat Engine failed to get into '
+'the config of the selected program. (Error=%s)';
rsFailureDuplicatingTheEventHandlesToTheOtherProcess = 'Failure duplicating '
+'the event handles to the other process';
rsVEHDebugError = 'VEH Debug error';
constructor TVEHDebugInterface.create;
begin
inherited create;
fDebuggerCapabilities:=[dbcSoftwareBreakpoint,dbcHardwareBreakpoint, dbcExceptionBreakpoint];
name:='VEH Debugger';
fmaxSharedBreakpointCount:=4;
end;
destructor TVEHDebugInterface.destroy;
begin
if HasDebugEvent<>0 then
closehandle(HasDebugEvent);
if HasHandledDebugEvent<>0 then
closehandle(HasHandledDebugEvent);
inherited destroy;
end;
function TVEHDebugInterface.SetThreadContext(hThread: THandle; const lpContext: TContext; isFrozenThread: Boolean=false): BOOL;
var c: PContext;
{$ifdef cpu64}
c32: PContext32 absolute c;
{$endif}
begin
if isFrozenThread then //use the VEHDebugView context
begin
result:=true;
c:=@VEHDebugView.CurrentContext[0];
{$ifdef cpu64}
if not is64bit then
begin
//the given context needs to be converted to 32-bit
//c32.ContextFlags:=lpcontext.ContextFlags;
c32.Dr0:=lpcontext.Dr0;
c32.Dr1:=lpcontext.Dr1;
c32.Dr2:=lpcontext.Dr2;
c32.Dr3:=lpcontext.Dr3;
c32.Dr6:=lpcontext.Dr6;
c32.Dr7:=lpcontext.Dr7;
c32.SegGs:=lpcontext.SegGs;
c32.SegFs:=lpcontext.SegFs;
c32.SegEs:=lpcontext.SegEs;
c32.SegDs:=lpcontext.SegDs;
c32.edi:=lpcontext.rdi;
c32.esi:=lpcontext.rsi;
c32.ebx:=lpcontext.rbx;
c32.edx:=lpcontext.rdx;
c32.ecx:=lpcontext.rcx;
c32.eax:=lpcontext.rax;
c32.ebp:=lpcontext.rbp;
c32.eip:=lpcontext.rip;
c32.SegCs:=lpcontext.SegCs;
c32.EFlags:=lpcontext.EFlags;
c32.esp:=lpcontext.rsp;
c32.SegSs:=lpcontext.SegSs;
CopyMemory(@c32.ext, @lpContext.fltsave,sizeof(c32.ext));
end else c^:=lpContext;
// end;// else lpContext:=c^;
{$else}
c^:=lpContext;
{$endif}
//Interesting effect: result:=NewKernelHandler.SetThreadContext(hThread,lpContext);
end
else
result:=NewKernelHandler.SetThreadContext(hThread,lpContext);
end;
function TVEHDebugInterface.GetThreadContext(hThread: THandle; var lpContext: TContext; isFrozenThread: Boolean=false): BOOL;
var c: PContext;
{$ifdef cpu64}
c32: PContext32 absolute c;
i: integer;
{$endif}
begin
if isFrozenThread then //use the VEHDebugView context
begin
result:=true;
c:=@VEHDebugView.CurrentContext[0];
{$ifdef cpu64}
if not is64bit then
begin
ZeroMemory(@lpContext,sizeof(TContext));
lpContext.ContextFlags:=c32.ContextFlags;
lpContext.Dr0:=c32.Dr0;
lpContext.Dr1:=c32.Dr1;
lpContext.Dr2:=c32.Dr2;
lpContext.Dr3:=c32.Dr3;
lpContext.Dr6:=c32.Dr6;
lpContext.Dr7:=c32.Dr7;
lpContext.SegGs:=c32.SegGs;
lpContext.SegFs:=c32.SegFs;
lpContext.SegEs:=c32.SegEs;
lpContext.SegDs:=c32.SegDs;
lpContext.Rdi:=c32.Edi;
lpContext.Rsi:=c32.Esi;
lpContext.Rbx:=c32.Ebx;
lpContext.Rdx:=c32.Edx;
lpContext.Rcx:=c32.Ecx;
lpContext.Rax:=c32.Eax;
lpContext.Rbp:=c32.Ebp;
lpContext.Rip:=c32.Eip;
lpContext.SegCs:=c32.SegCs;
lpContext.EFlags:=c32.EFlags;
lpContext.Rsp:=c32.Esp;
lpContext.SegSs:=c32.SegSs;
CopyMemory(@lpcontext.FltSave, @c32.ext,sizeof(c32.ext));
end else lpContext:=c^;
{$else}
lpContext:=c^;
{$endif}
end
else
result:=NewKernelHandler.GetThreadContext(hThread,lpContext);
end;
function TVEHDebugInterface.WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL;
var i: integer;
c: PContext;
{$ifdef cpu64}
c32: PContext32 absolute c;
{$endif}
begin
result:=waitforsingleobject(HasDebugEvent, dwMilliseconds)=WAIT_OBJECT_0;
if result then
begin
ZeroMemory(@lpDebugEvent, sizeof(TdebugEvent));
//fetch the data from the debugged app
// lpDebugEvent.dwDebugEventCode:=EXCEPTION_DEBUG_EVENT; //exception
lpDebugEvent.dwProcessId:=VEHDebugView.ProcessID;
lpDebugEvent.dwThreadId:=VEHDebugView.ThreadID;
lpDebugEvent.Exception.dwFirstChance:=1;
case VEHDebugView.Exception64.ExceptionCode of
$ce000000: //create process
begin
lpDebugEvent.dwDebugEventCode:=CREATE_PROCESS_DEBUG_EVENT;
lpDebugEvent.CreateProcessInfo.hFile:=0;
lpDebugEvent.CreateProcessInfo.hProcess:=processhandle;
lpDebugEvent.CreateProcessInfo.hThread:=OpenThread(THREAD_ALL_ACCESS,false, lpDebugEvent.dwThreadId);
end;
$ce000001: //create thread
begin
lpDebugEvent.dwDebugEventCode:=CREATE_THREAD_DEBUG_EVENT;
lpDebugEvent.CreateThread.hThread:=OpenThread(THREAD_ALL_ACCESS,false, lpDebugEvent.dwThreadId);
lpDebugEvent.CreateThread.lpStartAddress:=nil;
lpDebugEvent.CreateThread.lpThreadLocalBase:=nil;
end;
$ce000002: //destroy thread
begin
lpDebugEvent.dwDebugEventCode:=EXIT_THREAD_DEBUG_EVENT;
lpDebugEvent.ExitThread.dwExitCode:=0;
end;
DBG_PRINTEXCEPTION_C:
begin
lpDebugEvent.dwDebugEventCode:=OUTPUT_DEBUG_STRING_EVENT;
lpDebugEvent.DebugString.fUnicode:=0;
if is64bit then
begin
lpDebugEvent.DebugString.nDebugStringLength:=VEHDebugView.Exception64.ExceptionInformation[0];
lpDebugEvent.DebugString.lpDebugStringData:=pointer(ptrUint(VEHDebugView.Exception64.ExceptionInformation[1]));
end
else
begin
lpDebugEvent.DebugString.nDebugStringLength:=VEHDebugView.Exception32.ExceptionInformation[0];
lpDebugEvent.DebugString.lpDebugStringData:=pointer(ptrUint(VEHDebugView.Exception32.ExceptionInformation[1]));
end;
end;
else
begin
//unknown event, standard exception
lpDebugEvent.dwDebugEventCode:=EXCEPTION_DEBUG_EVENT;
lpDebugEvent.Exception.ExceptionRecord.ExceptionRecord:=nil;
{$ifdef cpu64}
if is64bit then
begin
lpDebugEvent.Exception.ExceptionRecord.ExceptionCode:=VEHDebugView.Exception64.ExceptionCode;
lpDebugEvent.Exception.ExceptionRecord.ExceptionFlags:=VEHDebugView.Exception64.ExceptionFlags;
lpDebugEvent.Exception.ExceptionRecord.ExceptionRecord:=pointer(VEHDebugView.Exception64.ExceptionRecord);
lpDebugEvent.Exception.ExceptionRecord.ExceptionAddress:=pointer(VEHDebugView.Exception64.ExceptionAddress);
lpDebugEvent.Exception.ExceptionRecord.NumberParameters:=VEHDebugView.Exception64.NumberParameters;
for i:=0 to VEHDebugView.Exception64.NumberParameters-1 do
lpDebugEvent.Exception.ExceptionRecord.ExceptionInformation[i]:=VEHDebugView.Exception64.ExceptionInformation[i];
end
else
{$endif}
begin
lpDebugEvent.Exception.ExceptionRecord.ExceptionCode:=VEHDebugView.Exception32.ExceptionCode;
lpDebugEvent.Exception.ExceptionRecord.ExceptionFlags:=VEHDebugView.Exception32.ExceptionFlags;
lpDebugEvent.Exception.ExceptionRecord.ExceptionRecord:=pointer(ptrUint(VEHDebugView.Exception32.ExceptionRecord));
lpDebugEvent.Exception.ExceptionRecord.ExceptionAddress:=pointer(ptrUint(VEHDebugView.Exception32.ExceptionAddress));
lpDebugEvent.Exception.ExceptionRecord.NumberParameters:=VEHDebugView.Exception32.NumberParameters;
for i:=0 to VEHDebugView.Exception32.NumberParameters-1 do
lpDebugEvent.Exception.ExceptionRecord.ExceptionInformation[i]:=VEHDebugView.Exception32.ExceptionInformation[i];
end;
if lpdebugEvent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_BREAKPOINT then
begin
c:=@VEHDebugView.CurrentContext[0];
{$ifdef cpu64}
if is64bit then
c.Rip:=c.rip+1
else
c32.Eip:=c32.eip+1;
{$else}
c.eip:=c.eip+1
{$endif}
end;
end;
end;
hasPausedProcess:=true;
end;
end;
function TVEHDebugInterface.ContinueDebugEvent(dwProcessId: DWORD; dwThreadId: DWORD; dwContinueStatus: DWORD): BOOL;
begin
hasPausedProcess:=false;
VEHDebugView.ContinueMethod:=dwContinueStatus;
SetEvent(HasHandledDebugEvent);
result:=true;
end;
function TVEHDebugInterface.DebugActiveProcess(dwProcessId: DWORD): WINBOOL;
var s: tstringlist;
e: integer;
prefix: string;
testptr: ptruint;
mi: tmoduleinfo;
begin
try
processhandler.processid:=dwProcessID;
Open_Process;
symhandler.reinitialize;
is64bit:=processhandler.is64Bit;
if is64bit then
prefix:='-x86_64'
else
prefix:='-i386';
result:=false;
if symhandler.getmodulebyname('vehdebug'+prefix+'.dll',mi) then
exit; //no reattach supported right now
CreateGUID(guid);
guidstring:='"'+GUIDToString(guid)+'"';
//guidstring:='Global\'+GUIDToString(guid);
OutputDebugString('Creating filemap with name '+pchar(guidstring));
ConfigFileMapping:=CreateFileMapping(INVALID_HANDLE_VALUE,nil,PAGE_READWRITE,0,sizeof(TVEHDebugSharedMem),pchar(guidstring));
if ConfigFileMapping=0 then
begin
e:=getlasterror;
OutPutDebugString('Failed:'+inttostr(e));
raise exception.Create(Format(
rsErrorWhileTryingToCreateTheConfigurationStructure, [inttostr(e)]));
end;
OutPutDebugString('Created the filemap');
VEHDebugView:=MapViewOfFile(ConfigFileMapping,FILE_MAP_READ or FILE_MAP_WRITE,0,0,sizeof(TVEHDebugSharedMem));
if (VEHDebugView=nil) then
begin
e:=GetLastError;
closehandle(ConfigFileMapping);
OutputDebugString('MapViewOfFile failed: '+inttostr(e));
raise exception.Create(Format(rsCheatEngineFailedToGetIntoTheConfig, [
inttostr(e)]));
end;
ZeroMemory(VEHDebugView,sizeof(TVEHDebugSharedMem));
VEHDebugView.ThreadWatchMethod:=0; //vehthreadwatchmethod;
if VEHRealContextOnThreadCreation then
VEHDebugView.ThreadWatchMethodConfig:=TPOLL_TCREATEREALCONTEXT
else
VEHDebugView.ThreadWatchMethodConfig:=0;;
HasDebugEvent:=CreateEvent(nil, false, false, nil);
HasHandledDebugEvent:=CreateEvent(nil, false, false, nil);
if not DuplicateHandle(GetCurrentProcess, HasDebugEvent, processhandle, @VEHDebugView^.HasDebugEvent, 0, false, DUPLICATE_SAME_ACCESS ) then
raise exception.Create(
rsFailureDuplicatingTheEventHandlesToTheOtherProcess);
if not DuplicateHandle(GetCurrentProcess, HasHandledDebugEvent, processhandle, @VEHDebugView^.HasHandledDebugEvent, 0, false, DUPLICATE_SAME_ACCESS ) then
raise exception.Create(
rsFailureDuplicatingTheEventHandlesToTheOtherProcess);
symhandler.waitforsymbolsloaded(true,'kernel32.dll');
InjectDll(cheatenginedir+'vehdebug'+prefix+'.dll');
symhandler.reinitialize;
symhandler.waitforsymbolsloaded(true,'vehdebug'+prefix+'.dll');
testptr:=symhandler.getAddressFromName('"vehdebug'+prefix+'.InitializeVEH"');
s:=tstringlist.Create;
try
s.Clear;
s.Add('"vehdebug'+prefix+'.ConfigName":');
s.add('db '''+guidstring+''',0');
s.Add('CreateThread("vehdebug'+prefix+'.InitializeVEH")');
//Clipboard.SetTextBuf(pchar(s.text));
if autoassemble(s,false) then
begin
//debugger is attached and ready to go
active:=true;
end
else
begin
// showmessage(s.text);
end;
result:=true;
finally
s.free;
end;
except
on e: exception do
begin
messagebox(0, pchar(e.message), pchar(rsVEHDebugError), MB_OK or MB_ICONERROR);
result:=false;
end;
end;
end;
function TVEHDebugInterface.DebugActiveProcessStop(dwProcessID: DWORD): WINBOOL;
var prefix: string;
s: Tstringlist;
begin
result:=false;
if active and (processhandler.processid=processhandler.processid) then
begin
try
if is64Bit then
prefix:='-x86_64'
else
prefix:='-i386';
s:=tstringlist.Create;
try
s.Add('CreateThread("vehdebug'+prefix+'.UnloadVEH")');
if autoassemble(s,false) then
begin
active:=false;
result:=true;
end;
finally
s.free;
end;
except
end;
end;
end;
end.