mirror of
https://github.com/cheat-engine/cheat-engine
synced 2026-08-15 02:26:08 -04:00
2717 lines
95 KiB
ObjectPascal
Executable file
2717 lines
95 KiB
ObjectPascal
Executable file
unit olddebugger;
|
|
|
|
{$mode delphi}
|
|
|
|
interface
|
|
|
|
uses windows, Classes,LCLIntf,sysutils,CEFuncProc,Messages,forms,SyncObjs,
|
|
dialogs,controls,Graphics,NewKernelHandler,symbolhandler,StrUtils,
|
|
ComCtrls ,Assemblerunit,addressparser, debughelper;
|
|
|
|
|
|
type TReadonly = record
|
|
pagebase: uint_ptr;
|
|
pagesize: uint_ptr;
|
|
Address: uint_ptr;
|
|
size: integer;
|
|
originalprotection: dword;
|
|
end;
|
|
|
|
type tThreadEntry=record
|
|
threadHandle: thandle;
|
|
address: uint_ptr;
|
|
end;
|
|
|
|
|
|
type Process=record
|
|
ProcessID: dword;
|
|
running: boolean;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type TDebugger = class(TThread)
|
|
private
|
|
originalbyte: byte;
|
|
filename: String;
|
|
parameters: string;
|
|
CreateAProcess: boolean;
|
|
OpenProcessID: Thandle;
|
|
|
|
AddressFound: Dword;
|
|
HideDebugger: boolean;
|
|
HandleBreakpoints: boolean;
|
|
|
|
CanUseDebugRegs: boolean;
|
|
createdusingprocesswindow:boolean;
|
|
|
|
CurrentProcess: integer;
|
|
|
|
|
|
{$ifdef netserver}
|
|
coderecords: array of dword;
|
|
{$endif}
|
|
|
|
procedure addtochangeslist;
|
|
|
|
|
|
procedure RestoreNTOpenProcess;
|
|
|
|
procedure ResetBreakpoint;
|
|
procedure SetSingleStepping(Threadid: dword);
|
|
procedure AddDebugString;
|
|
function injectcode(AddressOfEntryPoint:dword;processhandle:thandle):dword;
|
|
function handledebuggerplugins(devent:PDebugEvent):integer;
|
|
|
|
procedure tracersync;
|
|
function tracer(devent: _Debug_EVENT):boolean;
|
|
|
|
public
|
|
//semaphore: THandle;
|
|
Processes: array of process;
|
|
Newprocesses: array of TNewProcedureData;
|
|
|
|
|
|
pausedthreadhandle: thandle;
|
|
|
|
running: boolean;
|
|
continueprocess: boolean;
|
|
continuehow: integer;
|
|
|
|
DRRegs: _CONTEXT;
|
|
|
|
FindWriter2: boolean;
|
|
|
|
|
|
(* attaching: boolean;*)
|
|
attached: boolean;
|
|
attachedevent: Tevent;
|
|
|
|
context: _CONTEXT;
|
|
debugging: boolean;
|
|
stepping: boolean;
|
|
HowToContinue: integer;
|
|
removed: boolean;
|
|
|
|
readonly: TReadonly;
|
|
readonlyset: boolean;
|
|
readonlyremoved: boolean;
|
|
|
|
findreaderset: boolean;
|
|
findreader: TReadOnly;
|
|
findreaderremoved:boolean;
|
|
|
|
alsowrites: boolean;
|
|
|
|
breakpointset: boolean;
|
|
breakpointaddress: dword;
|
|
lastbreakpoint: dword;
|
|
temps: string;
|
|
|
|
whattodo: integer; //ignore, continue, showwindow etc....
|
|
|
|
userisdebugging: boolean;
|
|
userbreakpoints: array of dword;
|
|
registermodificationBPs: array of tregistermodificationBP;
|
|
|
|
int3userbreakpoints: array of tbreakpoint;
|
|
int3CEBreakpoint: TBreakpoint;
|
|
|
|
threadlist: array of array [0..4] of dword;
|
|
threadinfo: array of dword;
|
|
|
|
|
|
testje: string;
|
|
|
|
breakpointlistsection: TCriticalSection;
|
|
threadentrys: array of tThreadEntry;
|
|
|
|
traceaddress: dword;
|
|
tracecount: integer;
|
|
Constructor MyCreate(filenm: String);
|
|
Constructor MyCreate2(processID: THandle);
|
|
destructor Destroy; override;
|
|
procedure Execute; override;
|
|
procedure Debuginfo;
|
|
procedure waitforattach;
|
|
|
|
procedure Updateregisters;
|
|
procedure FoundOne;
|
|
procedure RemoveBreakpoint;
|
|
|
|
procedure suspendallthreads;
|
|
procedure resumeallthreads;
|
|
|
|
procedure ProcessCreated;
|
|
|
|
end;
|
|
|
|
|
|
type TDbgUIDebugActiveProcess = function(processhandle:THandle):boolean; stdcall;
|
|
type TDebugBreakProcess = function(processhandle:THandle):boolean; stdcall;
|
|
type TDebugActiveProcessStop= function(pid: dword):boolean; stdcall;
|
|
type TDebugSetProcessKillOnExit=function(KillOnExit: boolean):boolean; stdcall;
|
|
type TIsDebuggerPresent=function:boolean; stdcall;
|
|
type TntSuspendProcess=function(ProcessID:Dword):DWORD; stdcall;
|
|
type TntResumeProcess=function(ProcessID:Dword):DWORD; stdcall;
|
|
|
|
|
|
|
|
|
|
type
|
|
TProcessBasicInformation = record
|
|
ExitStatus : Longint;
|
|
PebBaseAddress : Pointer;
|
|
AffinityMask : DWORD;
|
|
BasePriority : Longint;
|
|
UniqueProcessId : DWORD;
|
|
InheritedFromUniqueProcessId : DWORD
|
|
end;
|
|
|
|
|
|
TProcessInfoClass=(
|
|
ProcessBasicInformation,ProcessQuotaLimits,ProcessIoCounters,ProcessVmCounters,ProcessTimes,
|
|
ProcessBasePriority,ProcessRaisePriority,ProcessDebugPort,ProcessExceptionPort,ProcessAccessToken,
|
|
ProcessLdtInformation,ProcessLdtSize,ProcessDefaultHardErrorMode,ProcessIoPortHandlers,
|
|
ProcessPooledUsageAndLimits,ProcessWorkingSetWatch,ProcessUserModeIOPL,ProcessEnableAlignmentFaultFixup,
|
|
ProcessPriorityClass,ProcessWx86Information,ProcessHandleCount,ProcessAffinityMask,ProcessPriorityBoost,
|
|
ProcessDeviceMap,ProcessSessionInformation,ProcessForegroundInformation,ProcessWow64Information,
|
|
MaxProcessInfoClass);
|
|
type TNtQueryInformationProcess=function(
|
|
Handle : THandle;
|
|
infoClass : TProcessInfoClass;
|
|
processInformation : Pointer;
|
|
processInformationLength : ULONG;
|
|
returnLength : PULONG
|
|
) : DWORD; stdcall;
|
|
|
|
var //DebuggerThread: TDebugger;
|
|
|
|
DbgUIDebugActiveProcess:TDbgUIDebugActiveProcess;
|
|
DebugBreakProcess:TDebugBreakProcess;
|
|
DebugActiveProcessStop:TDebugActiveProcessStop;
|
|
DebugSetProcessKillOnExit:TDebugSetProcessKillOnExit;
|
|
IsDebuggerPresent:TIsDebuggerPresent;
|
|
ntSuspendProcess: TntSuspendProcess;
|
|
ntResumeProcess: tntResumeProcess;
|
|
|
|
NtQueryInformationProcess: TNtQueryInformationProcess;
|
|
IsDebuggerPresentLocation:dword;
|
|
DbgBreakPointLocation:dword;
|
|
|
|
|
|
krn: thandle;
|
|
ntdlllib: thandle;
|
|
|
|
CRDebugging: TCriticalSection;
|
|
|
|
|
|
function startdebuggerifneeded: boolean; overload;
|
|
function startdebuggerifneeded(ask:boolean): boolean; overload;
|
|
function breakthread(threadhandle: thandle):boolean;
|
|
function DebugActiveProcessStopProstitute(x: dword): boolean;
|
|
function ToggleBreakpoint(address:dword):boolean;
|
|
|
|
implementation
|
|
|
|
uses debugeventhandler, MainUnit,frmFloatingPointPanelUnit,Memorybrowserformunit,disassembler,frmTracerUnit,foundcodeunit,debugger2,advancedoptionsunit,formChangedAddresses,frmstacktraceunit,frmThreadlistunit,formdebugstringsunit,formsettingsunit,processwindowunit,plugin,frmCreatedProcessListUnit;
|
|
|
|
function ToggleBreakpoint(address:dword):boolean;
|
|
|
|
procedure setbreakpoints;
|
|
var i: integer;
|
|
begin
|
|
(*
|
|
with debuggerthread do
|
|
begin
|
|
//set the debug breakpoint
|
|
suspend;
|
|
DRRegs.ContextFlags:=CONTEXT_DEBUG_REGISTERS;
|
|
DRRegs.Dr7:=reg0set or reg1set or reg2set or reg3set;
|
|
|
|
DRRegs.Dr0:=0;
|
|
drregs.dr1:=0;
|
|
drregs.dr2:=0;
|
|
|
|
for i:=0 to length(userbreakpoints)-1 do
|
|
begin
|
|
if i=0 then DRRegs.Dr0:=userbreakpoints[0] else
|
|
if i=1 then DRRegs.Dr1:=userbreakpoints[1] else
|
|
if i=2 then DRRegs.Dr2:=userbreakpoints[2];
|
|
end;
|
|
|
|
for i:=0 to length(threadlist)-1 do
|
|
begin
|
|
suspendthread(debuggerthread.threadlist[i][1]);
|
|
setthreadcontext(debuggerthread.threadlist[i][1],debuggerthread.DRRegs);
|
|
resumethread(debuggerthread.threadlist[i][1]);
|
|
end;
|
|
|
|
Resume;
|
|
end;
|
|
*)
|
|
end;
|
|
|
|
var i,j,k: integer;
|
|
a,original,written: dword;
|
|
|
|
int3: byte;
|
|
|
|
|
|
begin
|
|
|
|
result:=false;
|
|
|
|
(*
|
|
|
|
if foundcodedialog<>nil then
|
|
raise exception.Create('I can''t do that! You are currently using one of the code finder options, please, stop it first');
|
|
*)
|
|
|
|
if debuggerthread2<>nil then
|
|
begin
|
|
for i:=0 to 3 do
|
|
begin
|
|
if debuggerthread2.breakpoints[i]=address then
|
|
begin
|
|
outputdebugstring('Going to disable this changereg breakpoint');
|
|
|
|
if debuggerthread2.breakpointchanges[i].address<>0 then
|
|
begin
|
|
StopRegisterChange(i);
|
|
debuggerthread2.breakpointchanges[i].address:=0;
|
|
end;
|
|
|
|
debuggerthread2.breakpoints[i]:=0;
|
|
debuggerthread2.setbreakpoints;
|
|
memorybrowser.updatebplist;
|
|
|
|
result:=false;
|
|
for j:=0 to 3 do
|
|
if debuggerthread2.breakpoints[j]<>0 then
|
|
begin
|
|
result:=true;
|
|
break;
|
|
end;
|
|
|
|
if not result then freeandnil(debuggerthread2);
|
|
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
if not startdebuggerifneeded then exit;
|
|
int3:=$cc;
|
|
|
|
|
|
//still here so the debugger has been attached to the process or i've fucked up again
|
|
|
|
(*
|
|
with debuggerthread do
|
|
begin
|
|
|
|
try
|
|
WaitForSingleObject(semaphore,infinite);
|
|
userisdebugging:=true;
|
|
|
|
if formsettings.rbDebugAsBreakpoint.checked then
|
|
begin
|
|
//try to see if it already is in the list
|
|
for i:=0 to length(userbreakpoints)-1 do
|
|
if userbreakpoints[i]=address then
|
|
begin
|
|
//if it is then remove the breakpoint
|
|
for j:=i to length(userbreakpoints)-2 do
|
|
userbreakpoints[j]:=userbreakpoints[j+1];
|
|
setlength(userbreakpoints,length(userbreakpoints)-1);
|
|
|
|
//also remove the registermodificationBP
|
|
for j:=0 to length(debuggerthread.registermodificationBPs)-1 do
|
|
if registermodificationBPs[j].address=address then
|
|
begin
|
|
for k:=i to length(debuggerthread.registermodificationBPs)-2 do
|
|
registermodificationBPs[k]:=registermodificationBPs[k+1];
|
|
|
|
setlength(registermodificationBPs,length(registermodificationBPs)-1);
|
|
break;
|
|
end;
|
|
|
|
|
|
setbreakpoints;
|
|
releasesemaphore(semaphore,1,nil);
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
|
|
|
|
if length(userbreakpoints)=3 then
|
|
raise exception.Create('The current implementation doesn''t support more than 3 breakpoints. (4 debug registers, but 1 is needed for stepping-over code)');
|
|
|
|
setlength(userbreakpoints,length(userbreakpoints)+1);
|
|
userbreakpoints[length(userbreakpoints)-1]:=address;
|
|
|
|
setbreakpoints;
|
|
result:=true;
|
|
end
|
|
else
|
|
begin
|
|
//int3 breakpoints
|
|
//find it in the breakpointlist
|
|
for i:=0 to length(int3userbreakpoints)-1 do
|
|
begin
|
|
if int3userbreakpoints[i].address=address then
|
|
begin
|
|
//remove it
|
|
RewriteCode(processhandle,address,@int3userbreakpoints[i].originalbyte,1);
|
|
|
|
for j:=i to length(int3userbreakpoints)-2 do
|
|
begin
|
|
int3userbreakpoints[j].address:=int3userbreakpoints[j+1].address;
|
|
int3userbreakpoints[j].originalbyte:=int3userbreakpoints[j+1].originalbyte;
|
|
end;
|
|
|
|
setlength(int3userbreakpoints,length(int3userbreakpoints)-1);
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
//no, it's not in the list, so ADD:
|
|
setlength(int3userbreakpoints,length(int3userbreakpoints)+1);
|
|
int3userbreakpoints[length(int3userbreakpoints)-1].address:=address;
|
|
|
|
readprocessmemory(processhandle,pointer(address),@int3userbreakpoints[length(int3userbreakpoints)-1].originalbyte,1,a);
|
|
if a=1 then
|
|
RewriteCode(processhandle,address,@int3,1)
|
|
else
|
|
setlength(int3userbreakpoints,length(int3userbreakpoints)-1);
|
|
|
|
result:=true;
|
|
end;
|
|
finally
|
|
memorybrowser.updatebplist;
|
|
releasesemaphore(semaphore,1,nil);
|
|
end;
|
|
end;
|
|
*)
|
|
|
|
end;
|
|
|
|
procedure RemoveDebuggerDetection(processhandle: thandle);
|
|
var Newfunction: array [0..2] of byte;
|
|
original,written: dword;
|
|
begin
|
|
if IsDebuggerPresentLocation=0 then exit;
|
|
{
|
|
xor eax,eax
|
|
ret
|
|
}
|
|
Newfunction[0]:=$31;
|
|
NewFunction[1]:=$c0;
|
|
NewFunction[2]:=$c3;
|
|
|
|
RewriteCode(processhandle,IsDebuggerPresentLocation,@newfunction[0],3);
|
|
end;
|
|
|
|
function DebugBreakProstitute(x: Thandle):boolean;
|
|
begin
|
|
result:=false;
|
|
end;
|
|
|
|
function DebugSetProcessKillOnExitProtitute(x: boolean):boolean;
|
|
begin
|
|
result:=false;
|
|
end;
|
|
|
|
function DebugActiveProcessStopProstitute(x: dword): boolean;
|
|
begin
|
|
result:=false;
|
|
end;
|
|
|
|
function startdebuggerifneeded(ask:boolean): boolean; overload;
|
|
var mes: string;
|
|
reS:boolean;
|
|
i: integer;
|
|
begin
|
|
result:=false;
|
|
if processhandle=0 then raise exception.create('You must first open a process');
|
|
|
|
|
|
|
|
|
|
if debuggerthread2<>nil then
|
|
begin
|
|
if messagedlg('The kerneldebugger is currently active. Enabling the default windows debugger will cause the kernel debugger to terminate itself. Continue?',mtwarning,[mbyes,mbno],0)=mrno then exit;
|
|
freeandnil(debuggerthread2);
|
|
end;
|
|
|
|
|
|
|
|
if (debuggerthread=nil) then
|
|
begin
|
|
if @DebugActiveProcessStop=@DebugActiveProcessStopProstitute then
|
|
mes:='This will attach the debugger of Cheat Engine to the current process. If you close Cheat Engine while the game is running, the game will close too. Are you sure you want to do this?'
|
|
else
|
|
mes:='This will attach the debugger of Cheat Engine to the current process. Continue?';
|
|
|
|
{$ifndef net}
|
|
if ask then
|
|
res:=Messagedlg(mes,mtConfirmation,[mbYes, mbNo],0)=mrYes
|
|
else
|
|
res:=true;
|
|
|
|
if res then
|
|
{$endif}
|
|
begin
|
|
{$ifndef net}
|
|
if not advancedoptions.Pausebutton.Down then
|
|
@ntsuspendprocess:=nil; //lets use the debugger for this
|
|
{$endif}
|
|
|
|
//start the debugger on the current process
|
|
//check for a debugger
|
|
try
|
|
Debuggerthread:=TDebuggerThread.MyCreate2(processid);
|
|
except
|
|
raise exception.Create('I couldn''t attach the debugger to this process! You could try to open the process using the processpicker and try that! If that also doesn''t work check if you have debugging rights.');
|
|
end;
|
|
|
|
{$ifndef netserver}
|
|
//Enable the debugger screen for the memorybrowser
|
|
memorybrowser.splitter1.Visible:=true;
|
|
memorybrowser.panel1.Visible:=true;
|
|
|
|
memorybrowser.view1.Visible:=true;
|
|
memorybrowser.Debug1.Visible:=true;
|
|
memorybrowser.Splitter2.Visible:=true;
|
|
memorybrowser.RegisterView.Visible:=true;
|
|
|
|
Memorybrowser.UpdateRegisterview;
|
|
{$endif}
|
|
|
|
result:=true;
|
|
exit;
|
|
end
|
|
{$ifndef net}
|
|
else
|
|
begin
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
{$endif}
|
|
end;
|
|
result:=true;
|
|
|
|
end;
|
|
|
|
function startdebuggerifneeded: boolean; overload;
|
|
begin
|
|
result:=startdebuggerifneeded(true);
|
|
end;
|
|
|
|
function Breakthread(threadhandle:thandle):boolean;
|
|
var c: _context;
|
|
begin
|
|
if GetSystemType<3 then raise exception.Create('You can''t break on this OS. Windows ME or later is required. (You CAN set breakpoints at key locations)');
|
|
|
|
zeromemory(@c,sizeof(c));
|
|
|
|
c.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_DEBUG_REGISTERS;
|
|
|
|
debuggerthread.suspend;
|
|
suspendthread(threadhandle);
|
|
if not getthreadcontext(threadhandle,@c) then exit;
|
|
|
|
(*
|
|
debuggerthread.userisdebugging:=true;
|
|
|
|
|
|
with debuggerthread do
|
|
begin
|
|
DRRegs.ContextFlags:=CONTEXT_DEBUG_REGISTERS;
|
|
DRRegs.Dr7:=reg0set or reg1set or reg2set or reg3set;
|
|
DRRegs.Dr0:=0;
|
|
drregs.dr1:=0;
|
|
drregs.dr2:=0;
|
|
DRRegs.Dr3:=c.{$ifdef CPU64}Rip{$else}Eip{$endif};
|
|
if setthreadcontext(threadhandle,drregs) then result:=true;
|
|
end;
|
|
*)
|
|
|
|
|
|
resumethread(threadhandle);
|
|
debuggerthread.Resume;
|
|
end;
|
|
|
|
procedure TDebugger.waitforattach;
|
|
var i: integer;
|
|
wr: TWaitresult;
|
|
begin
|
|
i:=0;
|
|
while i<=90 do //90 do
|
|
begin
|
|
wr:=attachedevent.WaitFor(100);
|
|
if wr=wrSignaled then break;
|
|
if wr<>wrTimeout then exit;
|
|
|
|
if i=100 then exit;
|
|
inc(i);
|
|
end;
|
|
if not attached then terminate;
|
|
WaitFor;
|
|
end;
|
|
|
|
|
|
Constructor TDebugger.MyCreate(filenm: String);
|
|
begin
|
|
(*
|
|
debuggerthread:=self;
|
|
*)
|
|
attachedevent:=tevent.Create(nil, true,false,'');
|
|
|
|
{$ifndef netserver}
|
|
if debuggerthread2<>nil then raise exception.Create('Please stop the kernelmode debugging routines and breakpoints before starting this debugger');
|
|
|
|
if formsettings.cbUndoMemoryChanges.checked then CheckForChanges; //place this line at important places
|
|
|
|
if formsettings.cbBreakOnAttach.checked and (ProcessWindow<>nil) then //created using the process list
|
|
createdusingprocesswindow:=true;
|
|
{$endif}
|
|
|
|
userisdebugging:=false;
|
|
{$ifndef netserver}
|
|
handlebreakpoints:=formsettings.CheckBox1.Checked;
|
|
hidedebugger:=formsettings.checkbox1.checked;
|
|
canusedebugregs:=formsettings.rbDebugAsBreakpoint.checked;
|
|
formdebugstrings.listbox1.clear;
|
|
{$endif}
|
|
|
|
(* attaching:=true;*)
|
|
filename:=filenm;
|
|
Inputquery(filename,'Parameters:',parameters);
|
|
|
|
debugging:=true;
|
|
ProcessHandler.ProcessHandle:=2;
|
|
howtocontinue:=0;
|
|
createAprocess:=true;
|
|
|
|
inherited Create(false); //I know, I know, these initalizes could also be done in the execute part.
|
|
|
|
waitforattach;
|
|
end;
|
|
|
|
Constructor TDebugger.MyCreate2(processID: THandle);
|
|
var
|
|
debugportoffset:dword;
|
|
debugport:dword;
|
|
ar:dword;
|
|
peprocess: dword;
|
|
begin
|
|
(* debuggerthread:=self;*)
|
|
attachedevent:=tevent.Create(nil, true,false,'');
|
|
|
|
{$ifndef netserver}
|
|
if debuggerthread2<>nil then
|
|
begin
|
|
if messagedlg('The kerneldebugger is currently active. Enabling the default windows debugger will cause the kernel debugger to terminate itself. Continue?',mtwarning,[mbyes,mbno],0)=mrno then exit;
|
|
freeandnil(debuggerthread2);
|
|
end;
|
|
|
|
if formsettings.cbUndoMemoryChanges.checked then CheckForChanges; //place this line at important places
|
|
|
|
if formsettings.cbBreakOnAttach.checked and (ProcessWindow<>nil) then //created using the process list
|
|
createdusingprocesswindow:=true;
|
|
{$endif}
|
|
|
|
userisdebugging:=false;
|
|
|
|
{$ifndef netserver}
|
|
handlebreakpoints:=formsettings.cbHandleBreakpoints.checked;
|
|
hidedebugger:=formsettings.checkbox1.checked;
|
|
canusedebugregs:=formsettings.rbDebugAsBreakpoint.checked;
|
|
formdebugstrings.listbox1.clear;
|
|
{$endif}
|
|
|
|
(* attaching:=true;*)
|
|
debugging:=true;
|
|
howtocontinue:=0;
|
|
createAprocess:=false;
|
|
OpenprocessID:=processid;
|
|
|
|
ProcessHandler.ProcessHandle:=OpenProcess(process_all_access,false,processid);
|
|
|
|
{$ifndef net}
|
|
//check for a debugger
|
|
|
|
if (getsystemtype>5) and (formsettings.cbKernelOpenProcess.Checked) then
|
|
begin
|
|
debugportoffset:=GetDebugportOffset;
|
|
|
|
if debugportoffset<>0 then
|
|
begin
|
|
peprocess:=getpeprocess(processid);
|
|
if peprocess<>0 then
|
|
begin
|
|
debugport:=0;
|
|
if KernelReadProcessMemory(processhandle,pointer(peprocess+debugportoffset),@debugport,4,ar) then
|
|
begin
|
|
//readable
|
|
if (debugport<>0) and
|
|
(messagedlg('There is already a debugger attached to this process. Do you want to override it ? (Only works in winxp+)',mtconfirmation,[mbyes,mbno],0)=mryes) then
|
|
begin
|
|
debugport:=0;
|
|
KernelWriteProcessMemory(processhandle,pointer(peprocess+debugportoffset),@debugport,4,ar);
|
|
//debugger gone
|
|
end;
|
|
end;
|
|
end;
|
|
end; //else no way to check or fix it
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
inherited Create(false); //I know, I know, these initalizes could also be done in the execute part.
|
|
waitforattach;
|
|
end;
|
|
|
|
destructor TDebugger.Destroy;
|
|
begin
|
|
//clear all breakpoints
|
|
RemoveBreakpoint;
|
|
attachedevent.free;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TDebugger.tracersync;
|
|
{$ifndef net}
|
|
var s: string;
|
|
i: integer;
|
|
d: TTraceDebugInfo;
|
|
|
|
{$endif}
|
|
begin
|
|
{$ifndef net}
|
|
if frmtracer<>nil then
|
|
begin
|
|
s:=disassemble(context.{$ifdef CPU64}rip{$else}eip{$endif});
|
|
i:=posex('-',s);
|
|
i:=posex('-',s,i+1);
|
|
s:=copy(s,i+2,length(s));
|
|
s:=inttohex(context.{$ifdef CPU64}rip{$else}eip{$endif},8)+' - '+s;
|
|
|
|
d:=TTraceDebugInfo.Create;
|
|
d.c:=context;
|
|
frmtracer.ListBox1.Items.AddObject(s,d);
|
|
end;
|
|
{$endif}
|
|
end;
|
|
|
|
function TDebugger.tracer(devent: _Debug_EVENT):boolean;
|
|
var i,c: integer;
|
|
b: boolean;
|
|
h: thandle;
|
|
hid: dword;
|
|
begin
|
|
c:=0;
|
|
b:=true;
|
|
result:=false;
|
|
|
|
hid:=devent.dwThreadId;
|
|
for i:=0 to length(threadlist)-1 do
|
|
begin
|
|
if threadlist[i,0]=devent.dwThreadId then
|
|
begin
|
|
h:=threadlist[i,1];
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
|
|
while b and (c<tracecount) do
|
|
begin
|
|
for i:=0 to length(threadlist)-1 do
|
|
begin
|
|
if threadlist[i,0]=devent.dwThreadId then
|
|
begin
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
|
|
getthreadcontext(threadlist[i,1],@context);
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
if devent.dwThreadId=hid then
|
|
synchronize(tracersync);
|
|
|
|
inc(c);
|
|
if c<tracecount then
|
|
begin
|
|
if devent.dwThreadId=hid then
|
|
SetSingleStepping(hid);
|
|
|
|
b:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
if b then b:=waitfordebugevent(devent,4000);
|
|
end;
|
|
end;
|
|
|
|
if b then
|
|
result:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
end;
|
|
|
|
procedure TDebugger.RestoreNTOpenProcess;
|
|
var NTOpenProcessPos: dword;
|
|
MyNTOpenProcessPos: dword;
|
|
x: dword;
|
|
y: array [0..4] of byte;
|
|
mykernel: thandle;
|
|
begin
|
|
NTOpenProcessPos:=dword(getprocaddress(ntdlllib,'NtOpenProcess'));
|
|
|
|
LoadDbk32;
|
|
if GetLoadedState then
|
|
begin
|
|
MyNTOpenProcessPos:=dword(getprocaddress(DarkByteKernel,'NOP'));
|
|
|
|
If MyNTOpenProcessPos<>0 then
|
|
begin
|
|
if VirtualProtect(pointer(NTOpenProcessPos-50),55,PAGE_EXECUTE_READWRITE,x) then
|
|
begin
|
|
y[0]:=$e9;
|
|
pdword(@y[1])^:=MyNTOpenProcessPos-NTOpenProcessPos-5;
|
|
|
|
try
|
|
asm
|
|
push edi
|
|
push esi
|
|
lea esi,y[0]
|
|
mov edi,NTOpenProcessPos
|
|
movsd
|
|
movsb
|
|
pop esi
|
|
pop edi
|
|
end;
|
|
except
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TDebugger.AddDebugString;
|
|
begin
|
|
{$ifndef netserver}
|
|
formdebugstrings.listbox1.Items.add(temps);
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TDebugger.addtochangeslist;
|
|
var i: integer;
|
|
lbs: string;
|
|
newitem: TListItem;
|
|
x: pointer;
|
|
begin
|
|
{$ifndef netserver}
|
|
|
|
|
|
with memorybrowser do
|
|
begin
|
|
EAXv:=context.{$ifdef CPU64}Rax{$else}Eax{$endif};
|
|
EBXv:=context.{$ifdef CPU64}Rbp{$else}Ebx{$endif};
|
|
ECXv:=context.{$ifdef CPU64}Rcx{$else}Ecx{$endif};
|
|
EDXv:=context.{$ifdef CPU64}Rdx{$else}Edx{$endif};
|
|
ESIv:=context.{$ifdef CPU64}Rsi{$else}Esi{$endif};
|
|
EDIv:=context.{$ifdef CPU64}Rdi{$else}Edi{$endif};
|
|
EBPv:=context.{$ifdef CPU64}Rbp{$else}Ebp{$endif};
|
|
ESPv:=context.{$ifdef CPU64}Rsp{$else}Esp{$endif};
|
|
EIPv:=context.{$ifdef CPU64}Rip{$else}Eip{$endif};
|
|
end;
|
|
|
|
try
|
|
lastbreakpoint:=getaddress(temps);
|
|
except
|
|
//
|
|
end;
|
|
|
|
|
|
lbs:=inttohex(lastbreakpoint,8);
|
|
for i:=0 to frmchangedaddresses.Changedlist.Items.Count-1 do
|
|
if frmchangedaddresses.Changedlist.Items[i].Caption=lbs then exit; //already in the list
|
|
|
|
newitem:=frmchangedaddresses.Changedlist.Items.Add;
|
|
getmem(x,sizeof(_CONTEXT));
|
|
CopyMemory(x,@context,sizeof(_CONTEXT));
|
|
newitem.Data:=x;
|
|
newitem.Caption:=lbs;
|
|
|
|
//enable the timer if needed, there's data to be handled
|
|
if not frmchangedaddresses.Timer1.Enabled then
|
|
frmchangedaddresses.Timer1.Enabled:=true;
|
|
|
|
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TDebugger.debuginfo;
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TDebugger.ProcessCreated;
|
|
begin
|
|
{$ifndef netserver}
|
|
|
|
if frmCreatedProcessList=nil then
|
|
begin
|
|
frmCreatedProcesslist:=tfrmcreatedprocesslist.create(nil);
|
|
frmCreatedProcesslist.show;
|
|
end;
|
|
|
|
frmcreatedprocesslist.ListBox1.Items.Add(IntToHex(newprocesses[currentprocess].processid,8));
|
|
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
procedure TDebugger.FoundOne;
|
|
var desc,opcode: string;
|
|
address: uint_ptr;
|
|
|
|
{$ifdef netserver}
|
|
i: integer;
|
|
threadlist: tlist;
|
|
output: array [0..41] of byte;
|
|
{$endif}
|
|
begin
|
|
{$ifndef netserver}
|
|
|
|
(*
|
|
if foundcodedialog<>nil then
|
|
with foundcodedialog do
|
|
begin
|
|
address:=addressfound;
|
|
opcode:=disassemble(address,desc);
|
|
|
|
setlength(coderecords,length(coderecords)+1);
|
|
coderecords[length(coderecords)-1].address:=addressfound;
|
|
coderecords[length(coderecords)-1].size:=address-addressfound;
|
|
coderecords[length(coderecords)-1].opcode:=opcode;
|
|
coderecords[length(coderecords)-1].desciption:=desc;
|
|
|
|
|
|
coderecords[length(coderecords)-1].eax:=context.{$ifdef CPU64}rax{$else}Eax{$endif};
|
|
coderecords[length(coderecords)-1].ebx:=context.{$ifdef CPU64}rbx{$else}Ebx{$endif};
|
|
coderecords[length(coderecords)-1].ecx:=context.{$ifdef CPU64}rcx{$else}Ecx{$endif};
|
|
coderecords[length(coderecords)-1].edx:=context.{$ifdef CPU64}rdx{$else}Edx{$endif};
|
|
coderecords[length(coderecords)-1].esi:=context.{$ifdef CPU64}rsi{$else}Esi{$endif};
|
|
coderecords[length(coderecords)-1].edi:=context.{$ifdef CPU64}rdi{$else}Edi{$endif};
|
|
coderecords[length(coderecords)-1].ebp:=context.{$ifdef CPU64}rbp{$else}Ebp{$endif};
|
|
coderecords[length(coderecords)-1].esp:=context.{$ifdef CPU64}rsp{$else}Esp{$endif};
|
|
coderecords[length(coderecords)-1].eip:=context.{$ifdef CPU64}rip{$else}Eip{$endif};
|
|
Foundcodelist.Items.Add(opcode);
|
|
end;
|
|
{$else}
|
|
//send the state information to the client(s)
|
|
//FoundCode(Address: dword;eax:dword; ebx:dword; ecx:dword; edx:dword;esi:dword;edi:dword;ebp:dword;esp:dword;eip:dword)
|
|
|
|
setlength(coderecords,length(coderecords)+1);
|
|
coderecords[length(coderecords)-1]:=addressfound;
|
|
output[0]:=SC_FoundCode;
|
|
pdword(@output[1])^:=addressfound;
|
|
pdword(@output[5])^:=context.{$ifdef CPU64}rax{$else}Eax{$endif};
|
|
pdword(@output[9])^:=context.{$ifdef CPU64}rbx{$else}Ebx{$endif};
|
|
pdword(@output[13])^:=context.{$ifdef CPU64}rcx{$else}Ecx{$endif};
|
|
pdword(@output[17])^:=context.{$ifdef CPU64}rdx{$else}Edx{$endif};
|
|
pdword(@output[21])^:=context.{$ifdef CPU64}rsi{$else}Esi{$endif};
|
|
pdword(@output[25])^:=context.{$ifdef CPU64}rdi{$else}Edi{$endif};
|
|
pdword(@output[29])^:=context.{$ifdef CPU64}rbp{$else}Ebp{$endif};
|
|
pdword(@output[33])^:=context.{$ifdef CPU64}rsp{$else}Esp{$endif};
|
|
pdword(@output[37])^:=context.{$ifdef CPU64}rip{$else}Eip{$endif};
|
|
|
|
|
|
threadlist:=form1.idtcpserver1.Threads.LockList;
|
|
try
|
|
for i:=0 to threadlist.count-1 do
|
|
TIdPeerThread(threadlist[i]).Connection.WriteBuffer(output[0],41);
|
|
finally
|
|
form1.idtcpserver1.Threads.UnlockList;
|
|
end;
|
|
*)
|
|
|
|
{$endif}
|
|
end;
|
|
|
|
procedure tdebugger.suspendallthreads;
|
|
var i: integer;
|
|
begin
|
|
for i:=0 to length(threadlist)-1 do
|
|
suspendthread(threadlist[i,1]);
|
|
end;
|
|
|
|
procedure tdebugger.resumeallthreads;
|
|
var i: integer;
|
|
begin
|
|
for i:=0 to length(threadlist)-1 do
|
|
resumethread(threadlist[i,1]);
|
|
end;
|
|
|
|
|
|
procedure TDebugger.UpdateRegisters;
|
|
var temp: string;
|
|
i: integer;
|
|
begin
|
|
//removed for a while
|
|
{$ifndef netserver}
|
|
|
|
with memorybrowser do
|
|
begin
|
|
//enable debug mode
|
|
run1.Enabled:=true;
|
|
step1.Enabled:=true;
|
|
stepover1.Enabled:=true;
|
|
runtill1.Enabled:=true;
|
|
stacktrace1.Enabled:=true;
|
|
|
|
for i:=0 to length(threadlist)-1 do
|
|
begin
|
|
if threadlist[i,1]=pausedthreadhandle then
|
|
begin
|
|
caption:='Memory Viewer - Currently debugging thread '+inttohex(threadlist[i,0],8);
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
if frmstacktrace<>nil then
|
|
begin
|
|
(* frmstacktrace.stacktrace(debuggerthread.pausedthreadhandle,debuggerthread.context);*)
|
|
end;
|
|
|
|
Disassembleraddress:=context.{$ifdef CPU64}rip{$else}eip{$endif};
|
|
dselected:=context.{$ifdef CPU64}rip{$else}eip{$endif};
|
|
|
|
temp:='EAX '+IntToHex(context.{$ifdef CPU64}rax{$else}eax{$endif},8);
|
|
if temp<>eaxlabel.Caption then
|
|
begin
|
|
eaxlabel.Font.Color:=clred;
|
|
eaxlabel.Caption:=temp;
|
|
end else eaxlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EBX '+IntToHex(context.{$ifdef CPU64}rbx{$else}ebx{$endif},8);
|
|
if temp<>ebxlabel.Caption then
|
|
begin
|
|
ebxlabel.Font.Color:=clred;
|
|
ebxlabel.Caption:=temp;
|
|
end else ebxlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='ECX '+IntToHex(context.{$ifdef CPU64}rcx{$else}ecx{$endif},8);
|
|
if temp<>eCxlabel.Caption then
|
|
begin
|
|
eCXlabel.Font.Color:=clred;
|
|
eCXlabel.Caption:=temp;
|
|
end else eCXlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EDX '+IntToHex(context.{$ifdef CPU64}rdx{$else}edx{$endif},8);
|
|
if temp<>eDxlabel.Caption then
|
|
begin
|
|
eDxlabel.Font.Color:=clred;
|
|
eDxlabel.Caption:=temp;
|
|
end else eDxlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='ESI '+IntToHex(context.{$ifdef CPU64}rsi{$else}esi{$endif},8);
|
|
if temp<>eSIlabel.Caption then
|
|
begin
|
|
eSIlabel.Font.Color:=clred;
|
|
eSIlabel.Caption:=temp;
|
|
end else eSIlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EDI '+IntToHex(context.{$ifdef CPU64}rdi{$else}edi{$endif},8);
|
|
if temp<>eDIlabel.Caption then
|
|
begin
|
|
eDIlabel.Font.Color:=clred;
|
|
eDIlabel.Caption:=temp;
|
|
end else eDIlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EBP '+IntToHex(context.{$ifdef CPU64}rbp{$else}ebp{$endif},8);
|
|
if temp<>eBPlabel.Caption then
|
|
begin
|
|
eBPlabel.Font.Color:=clred;
|
|
eBPlabel.Caption:=temp;
|
|
end else eBPlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='ESP '+IntToHex(context.{$ifdef CPU64}rsp{$else}esp{$endif},8);
|
|
if temp<>eSPlabel.Caption then
|
|
begin
|
|
eSPlabel.Font.Color:=clred;
|
|
eSPlabel.Caption:=temp;
|
|
end else eSPlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EIP '+IntToHex(context.{$ifdef CPU64}rip{$else}eip{$endif},8);
|
|
if temp<>eIPlabel.Caption then
|
|
begin
|
|
eIPlabel.Font.Color:=clred;
|
|
eIPlabel.Caption:=temp;
|
|
end else eIPlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='CS '+IntToHex(context.SEGCS,4);
|
|
if temp<>CSlabel.Caption then
|
|
begin
|
|
CSlabel.Font.Color:=clred;
|
|
CSlabel.Caption:=temp;
|
|
end else CSlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='DS '+IntToHex(context.SEGDS,4);
|
|
if temp<>DSlabel.Caption then
|
|
begin
|
|
DSlabel.Font.Color:=clred;
|
|
DSlabel.Caption:=temp;
|
|
end else DSLabel.Font.Color:=clWindowText;
|
|
|
|
temp:='SS '+IntToHex(context.SEGSS,4);
|
|
if temp<>SSlabel.Caption then
|
|
begin
|
|
SSlabel.Font.Color:=clred;
|
|
SSlabel.Caption:=temp;
|
|
end else SSlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='ES '+IntToHex(context.SEGES,4);
|
|
if temp<>ESlabel.Caption then
|
|
begin
|
|
ESlabel.Font.Color:=clred;
|
|
ESlabel.Caption:=temp;
|
|
end else ESlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='FS '+IntToHex(context.SEGFS,4);
|
|
if temp<>FSlabel.Caption then
|
|
begin
|
|
FSlabel.Font.Color:=clred;
|
|
FSlabel.Caption:=temp;
|
|
end else FSlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='GS '+IntToHex(context.SEGGS,4);
|
|
if temp<>GSlabel.Caption then
|
|
begin
|
|
GSlabel.Font.Color:=clred;
|
|
GSlabel.Caption:=temp;
|
|
end else GSlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='CF '+IntToStr(GetBitOf(context.EFLAgs,0));
|
|
if temp<>cflabel.Caption then
|
|
begin
|
|
CFlabel.Font.Color:=clred;
|
|
CFlabel.caption:=temp;
|
|
end else cflabel.Font.Color:=clWindowText;
|
|
|
|
temp:='PF '+IntToStr(GetBitOf(context.EFlags,2));
|
|
if temp<>Pflabel.Caption then
|
|
begin
|
|
Pflabel.Font.Color:=clred;
|
|
Pflabel.caption:=temp;
|
|
end else Pflabel.Font.Color:=clWindowText;
|
|
|
|
temp:='AF '+IntToStr(GetBitOf(context.EFlags,4));
|
|
if temp<>Aflabel.Caption then
|
|
begin
|
|
Aflabel.Font.Color:=clred;
|
|
Aflabel.caption:=temp;
|
|
end else Aflabel.Font.Color:=clWindowText;
|
|
|
|
temp:='ZF '+IntToStr(GetBitOf(context.EFlags,6));
|
|
if temp<>Zflabel.Caption then
|
|
begin
|
|
Zflabel.Font.Color:=clred;
|
|
Zflabel.caption:=temp;
|
|
end else Zflabel.Font.Color:=clWindowText;
|
|
|
|
temp:='SF '+IntToStr(GetBitOf(context.EFlags,7));
|
|
if temp<>Sflabel.Caption then
|
|
begin
|
|
Sflabel.Font.Color:=clred;
|
|
Sflabel.caption:=temp;
|
|
end else Sflabel.Font.Color:=clWindowText;
|
|
|
|
temp:='DF '+IntToStr(GetBitOf(context.EFlags,10));
|
|
if temp<>Dflabel.Caption then
|
|
begin
|
|
Dflabel.Font.Color:=clred;
|
|
Dflabel.caption:=temp;
|
|
end else Dflabel.Font.Color:=clWindowText;
|
|
|
|
temp:='OF '+IntToStr(GetBitOf(context.EFlags,11));
|
|
if temp<>Oflabel.Caption then
|
|
begin
|
|
Oflabel.Font.Color:=clred;
|
|
Oflabel.caption:=temp;
|
|
end else Oflabel.Font.Color:=clWindowText;
|
|
|
|
|
|
EAXv:=context.{$ifdef CPU64}rax{$else}eax{$endif};
|
|
EBXv:=context.{$ifdef CPU64}rbx{$else}ebx{$endif};
|
|
ECXv:=context.{$ifdef CPU64}rcx{$else}ecx{$endif};
|
|
EDXv:=context.{$ifdef CPU64}rdx{$else}edx{$endif};
|
|
ESIv:=context.{$ifdef CPU64}rsi{$else}esi{$endif};
|
|
EDIv:=context.{$ifdef CPU64}rdi{$else}edi{$endif};
|
|
EBPv:=context.{$ifdef CPU64}rbp{$else}ebp{$endif};
|
|
ESPv:=context.{$ifdef CPU64}rsp{$else}esp{$endif};
|
|
EIPv:=context.{$ifdef CPU64}rip{$else}eip{$endif};
|
|
|
|
if frmFloatingpointpanel<>nil then
|
|
begin
|
|
frmFloatingpointpanel.SetContextPointer(@context);
|
|
frmFloatingpointpanel.UpdatedContext;
|
|
end;
|
|
|
|
|
|
updatedisassemblerview;
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
procedure TDebugger.SetSingleStepping(Threadid: dword);
|
|
var i: integer;
|
|
begin
|
|
for i:=0 to length(threadlist)-1 do
|
|
if threadlist[i,0]=ThreadId then
|
|
begin
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
|
|
getthreadcontext(threadlist[i,1],@context);
|
|
|
|
context.ContextFlags:=CONTEXT_FULL; //CONTEXT_CONTROL;
|
|
context.EFlags:=context.EFlags or $100;
|
|
setthreadcontext(threadlist[i,1],@context);
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure TDebugger.ResetBreakpoint;
|
|
var zerobreak: _context;
|
|
i: integer;
|
|
int3: byte;
|
|
original,a,written:dword;
|
|
begin
|
|
if canusedebugregs then
|
|
for i:=0 to length(threadlist)-1 do
|
|
begin
|
|
suspendthread(threadlist[i,1]);
|
|
setthreadcontext(threadlist[i,1],@drregs);
|
|
resumethread(threadlist[i,1]);
|
|
end
|
|
else
|
|
begin
|
|
int3:=$cc;
|
|
for i:=0 to length(int3userbreakpoints)-1 do
|
|
RewriteCode(processhandle,int3userbreakpoints[i].address,@int3,1);
|
|
|
|
if int3CEBreakpoint.address>0 then
|
|
RewriteCode(processhandle,int3CEBreakpoint.address,@int3,1);
|
|
|
|
end;
|
|
end;
|
|
|
|
procedure TDebugger.RemoveBreakpoint;
|
|
var zerobreak: _context;
|
|
i: integer;
|
|
original,written,a:dword;
|
|
begin
|
|
(*
|
|
if canusedebugregs then
|
|
begin
|
|
zeromemory(@zerobreak,sizeof(zerobreak));
|
|
zerobreak.ContextFlags:=CONTEXT_DEBUG_REGISTERS;
|
|
zerobreak.Dr7:=reg0set or reg1set or reg2set or reg3set;
|
|
|
|
for i:=0 to length(threadlist)-1 do
|
|
begin
|
|
suspendthread(threadlist[i,1]);
|
|
setthreadcontext(threadlist[i,1],@zerobreak);
|
|
resumethread(threadlist[i,1]);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
//temporarily disable all breakpoints
|
|
for i:=0 to length(int3userbreakpoints)-1 do
|
|
RewriteCode(processhandle,int3userbreakpoints[i].address,@int3userbreakpoints[i].originalbyte,1);
|
|
|
|
if int3CEBreakpoint.address>0 then
|
|
begin
|
|
RewriteCode(processhandle,int3CEbreakpoint.address,@int3cebreakpoint.originalbyte,1);
|
|
int3CEBreakpoint.address:=0;
|
|
end;
|
|
end;
|
|
*)
|
|
end;
|
|
|
|
|
|
function TDebugger.handledebuggerplugins(devent:PDebugEvent):integer;
|
|
begin
|
|
{$ifndef net}
|
|
//go through the list of debugger plugins
|
|
result:=pluginhandler.handledebuggerplugins(devent);
|
|
{$endif}
|
|
end;
|
|
|
|
//I always knew viruses are usefull
|
|
function TDebugger.injectcode(AddressOfEntryPoint:dword;processhandle:thandle):dword;
|
|
{$ifndef net}
|
|
var LoadLibraryPtr: pointer;
|
|
GetProcAddressPtr: Pointer;
|
|
|
|
injectedlocation: pointer;
|
|
h: Thandle;
|
|
|
|
inject: array [0..255] of byte;
|
|
x:dword;
|
|
|
|
outp:TAssemblerBytes;
|
|
position,position2: dword;
|
|
|
|
dllLocation: string;
|
|
startaddresS: dword;
|
|
IHWCIloc: dword;
|
|
{$endif}
|
|
begin
|
|
{$ifndef net}
|
|
h:=LoadLibrary('Kernel32.dll');
|
|
if h=0 then raise exception.Create('No kernel32.dll loaded');
|
|
|
|
try
|
|
GetProcAddressPtr:=GetProcAddress(h,'GetProcAddress');
|
|
if getprocaddressptr=nil then raise exception.Create('GetProcAddress not found');
|
|
|
|
LoadLibraryPtr:=GetProcAddress(h,'LoadLibraryA');
|
|
if LoadLibraryptr=nil then raise exception.Create('LoadLibraryA not found');
|
|
|
|
injectedlocation:=VirtualAllocEx(processhandle,nil,4096,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
|
|
|
|
if injectedlocation=nil then raise exception.Create('Failed to allocate memory');
|
|
|
|
dlllocation:=extractfilepath(application.exename)+'CEHOOK.DLL';
|
|
|
|
position:=dword(injectedlocation);
|
|
position2:=0;
|
|
copymemory(@inject[0],pchar(dllLocation+#0),length(dllLocation)+1);
|
|
inc(position,length(dllLocation)+1);
|
|
inc(position2,length(dlllocation)+1);
|
|
|
|
ihwciloc:=position;
|
|
copymemory(@inject[position2],pchar('IHWCI'#0),6);
|
|
inc(position,6);
|
|
inc(position2,6);
|
|
startaddress:=position;
|
|
|
|
assemble('PUSHAD',position,outp);
|
|
copymemory(@inject[position2],outp,length(outp));
|
|
inc(position,length(outp));
|
|
inc(position2,length(outp));
|
|
|
|
//loadlibrary(cehook);
|
|
assemble('PUSH '+IntToHex(dword(injectedlocation),8),position,outp);
|
|
copymemory(@inject[position2],outp,length(outp));
|
|
inc(position,length(outp));
|
|
inc(position2,length(outp));
|
|
|
|
assemble('CALL '+IntToHex(dword(LoadLibraryPtr),8),position,outp);
|
|
copymemory(@inject[position2],outp,length(outp));
|
|
inc(position,length(outp));
|
|
inc(position2,length(outp));
|
|
|
|
//getprocaddress
|
|
assemble('PUSH '+IntToHex(ihwciloc,8),position,outp);
|
|
copymemory(@inject[position2],outp,length(outp));
|
|
inc(position,length(outp));
|
|
inc(position2,length(outp));
|
|
|
|
assemble('PUSH EAX',position,outp);
|
|
copymemory(@inject[position2],outp,length(outp));
|
|
inc(position,length(outp));
|
|
inc(position2,length(outp));
|
|
|
|
assemble('CALL '+IntToHex(dword(GetProcAddressPtr),8),position,outp);
|
|
copymemory(@inject[position2],outp,length(outp));
|
|
inc(position,length(outp));
|
|
inc(position2,length(outp));
|
|
|
|
assemble('CALL EAX',position,outp);
|
|
copymemory(@inject[position2],outp,length(outp));
|
|
inc(position,length(outp));
|
|
inc(position2,length(outp));
|
|
|
|
assemble('POPAD',position,outp);
|
|
copymemory(@inject[position2],outp,length(outp));
|
|
inc(position,length(outp));
|
|
inc(position2,length(outp));
|
|
|
|
|
|
//jump to original start address
|
|
assemble('JMP '+IntToHex(AddressOfEntryPoint,8),position,outp);
|
|
copymemory(@inject[position2],outp,length(outp));
|
|
inc(position,length(outp));
|
|
inc(position2,length(outp));
|
|
|
|
|
|
//call the routine
|
|
|
|
writeprocessmemory(processhandle,injectedlocation,@inject[0],position2,x);
|
|
result:=startaddress;
|
|
//showmessage('Injected code at: '+IntToHex(dword(injectedlocation),8)+' startaddress='+IntToHex(startaddress,8));
|
|
finally
|
|
FreeLibrary(h);
|
|
end;
|
|
|
|
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
procedure TDebugger.Execute;
|
|
//todo: splitup in seperate sections
|
|
var startupinfo: TSTARTUPINFO;
|
|
int3: byte;
|
|
devent: _Debug_EVENT;
|
|
processinfo:_Process_information; //make an array of this
|
|
i,j: integer;
|
|
original: dword;
|
|
tobefrozen: integer; //-1 is none
|
|
notinlist: boolean;
|
|
tid,thnd: dword;
|
|
|
|
//----
|
|
temp: dword;
|
|
desc,opcode: string;
|
|
|
|
seperator: integer;
|
|
fb: integer;
|
|
nb: integer;
|
|
address: string;
|
|
offset:uint_ptr;
|
|
|
|
errorlog: textfile;
|
|
times: integer;
|
|
debugpatch: integer;
|
|
previousexception: dword;
|
|
mbi : _MEMORY_BASIC_INFORMATION;
|
|
|
|
hthr: thandle;
|
|
|
|
asciistring: pansichar;
|
|
unicodestring: pwidechar;
|
|
bytesread: dword;
|
|
err:string;
|
|
|
|
found: boolean;
|
|
|
|
a,b,c: dword;
|
|
ab: byte;
|
|
SelectorEntry:_LDT_ENTRY;
|
|
|
|
oldcontext: _CONTEXT;
|
|
|
|
exefile: File;
|
|
dosheader: ^IMAGE_DOS_HEADER;
|
|
Optionalheader: ^IMAGE_OPTIONAL_HEADER;
|
|
ExeHeader: array [0..4095] of byte;
|
|
AddressOfEntryPoint:dword;
|
|
|
|
dotnet: boolean;
|
|
firstbreak: boolean;
|
|
creationhandleD: boolean;
|
|
|
|
|
|
begin
|
|
|
|
AddressOfEntryPoint:=0;
|
|
setlength(processes,0);
|
|
creationhandleD:=false;
|
|
|
|
zeromemory(@drregs,sizeof(drregs));
|
|
drregs.ContextFlags:=CONTEXT_DEBUG_REGISTERS;
|
|
|
|
previousexception:=0;
|
|
times:=0;
|
|
debugpatch:=0;
|
|
|
|
int3:=$cc;
|
|
breakpointset:=false;
|
|
(* attached:=false;*)
|
|
readonlyset:=false;
|
|
|
|
ProcessHandler.ProcessHandle:=0;
|
|
|
|
tobefrozen:=-1;
|
|
//GetStartUpInfo(STARTUPINFO);
|
|
FillMemory(@STARTUPINFO,sizeof(STARTUPINFO),0);
|
|
STARTUPINFO.cb :=sizeof(STARTUPINFO);
|
|
|
|
try
|
|
|
|
if createaprocess then
|
|
begin
|
|
//create a process
|
|
filemode:=0;
|
|
assignfile(exefile,filename);
|
|
try
|
|
reset(exefile,1);
|
|
except
|
|
(* attaching:=false;*)
|
|
attachedevent.SetEvent;
|
|
err:='The file can''t be opened!';
|
|
messagebox(0,pchar(err),'Create failure',mb_ok);
|
|
exit;
|
|
end;
|
|
|
|
try
|
|
temp:=0;
|
|
blockread(exefile,exeheader[0],4096,temp);
|
|
if temp>0 then
|
|
begin
|
|
dosheader:=@exeheader[0];
|
|
optionalheader:=pointer(dword(dosheader)+dosheader._lfanew+24);
|
|
|
|
AddressOfEntryPoint:=optionalheader.ImageBase+optionalheader.AddressOfEntryPoint;
|
|
end;
|
|
finally
|
|
closefile(exefile);
|
|
end;
|
|
|
|
if not Createprocess(nil,pchar(filename+' '+parameters),nil,nil,false,(CREATE_SUSPENDED or DEBUG_PROCESS or NORMAL_PRIORITY_CLASS),nil,nil,Startupinfo,processinfo) then
|
|
begin
|
|
// attaching:=false;
|
|
attachedevent.SetEvent;
|
|
|
|
|
|
err:='Cheat Engine failed to create the process. (Error='+pchar(IntToStr(GetLastError))+')';
|
|
messagebox(0,pchar(err),'Create failure',mb_ok);
|
|
|
|
{$ifndef net}
|
|
postmessage(mainform.Handle,WM_USER+1,0,0);
|
|
{$endif}
|
|
exit;
|
|
end;
|
|
|
|
readprocessmemory(processinfo.hProcess,pointer(addressofentrypoint),@originalbyte,1,temp);
|
|
|
|
{ dotneT:=false;
|
|
firstbreak:=true;
|
|
if originalbyte=$ff then
|
|
begin
|
|
readprocessmemory(processinfo.hProcess,pointer(addressofentrypoint+1),@ab,1,temp);
|
|
if ab=$25 then dotnet:=true;
|
|
end;
|
|
|
|
if not dotnet then writeprocessmemory(processinfo.hProcess,pointeR(AddressOfEntryPoint),@int3,1,temp);
|
|
}
|
|
// messagebox(0,pchar(IntToHex(context.{$ifdef CPU64}rax{$else}eax{$endif},8)),pchar(IntTOHex(context.{$ifdef CPU64}rax{$else}eax{$endif},8)),MB_OK );
|
|
resumethread(processinfo.hThread);
|
|
end
|
|
else
|
|
begin
|
|
|
|
|
|
// BOOL DebugActiveProcess(DWORD dwProcessId)=
|
|
{
|
|
if(DbgUiConnectToDbg())
|
|
{
|
|
HANDLE hProcess = ProcessIdToHandle(dwProcessId);
|
|
|
|
if(hProcess)
|
|
{
|
|
DbgUiDebugActiveProcess(hProcess);
|
|
NtClose(hProcess);
|
|
}
|
|
// }
|
|
// return FALSE;
|
|
// }
|
|
|
|
//attach to a running process
|
|
//rewrite nt
|
|
|
|
{$ifdef netserver}
|
|
RestoreNTOpenProcess;
|
|
{$else}
|
|
if formsettings.cbKernelOpenProcess.checked then
|
|
RestoreNTOpenProcess;
|
|
{$endif}
|
|
|
|
|
|
if not DebugActiveProcess(Openprocessid) then
|
|
begin
|
|
//attaching:=false;
|
|
attachedevent.SetEvent;
|
|
|
|
{$ifndef net}
|
|
err:='Cheat Engine failed to attach to the process. (Error='+IntToStr(GetLastError)+')';
|
|
messagebox(0,pchar(err),'Attach failure',mb_ok);
|
|
postmessage(mainform.Handle,WM_USER+1,0,0); //set debuggerthread to nil
|
|
{$endif}
|
|
exit;
|
|
end;
|
|
|
|
end;
|
|
|
|
(*InitializeThreadHandlersList;*)
|
|
DebugSetProcessKillOnExit(true);
|
|
running:=true;
|
|
|
|
|
|
|
|
|
|
while not terminated and debugging do
|
|
begin
|
|
|
|
if WaitForDebugEvent(devent,4000) then //4 seconds wont cause a hog I hope
|
|
begin
|
|
{$ifndef net}
|
|
if handledebuggerplugins(@devent)=1 then
|
|
continue;
|
|
{$endif}
|
|
|
|
//working on a debug thing
|
|
(*
|
|
debugging:=HandleDebugEvent(devent);
|
|
*)
|
|
continue;
|
|
|
|
(*
|
|
currentprocess:=-1;
|
|
for i:=0 to length(newprocesses)-1 do
|
|
begin
|
|
if newprocesses[i].processid=devent.dwProcessId then
|
|
begin
|
|
currentprocess:=i;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
if length(newprocesses)>0 then
|
|
ProcessHandler.ProcessHandle:=newprocesses[length(newprocesses)-1].processhandle;
|
|
|
|
case devent.dwDebugEventCode of
|
|
EXCEPTION_DEBUG_EVENT:
|
|
begin
|
|
|
|
|
|
if newprocesses[currentprocess].firstbreak then
|
|
begin
|
|
if newprocesses[currentprocess].dotnet then
|
|
begin
|
|
//jump near (I assume the next byte is a $25
|
|
if readprocessmemory(newprocesses[currentprocess].processhandle,pointer(addressofentrypoint+2),@a,4,temp) then
|
|
begin
|
|
readprocessmemory(newprocesses[currentprocess].processhandle,pointer(a),@b,4,temp);
|
|
newprocesses[currentprocess].EntryPoint:=b;
|
|
|
|
readprocessmemory(newprocesses[currentprocess].processhandle,pointer(b),@newprocesses[currentprocess].OriginalEntryByte,1,temp);
|
|
writeprocessmemory(newprocesses[currentprocess].processhandle,pointeR(b),@int3,1,temp);
|
|
end;
|
|
end;
|
|
|
|
if hidedebugger then RemoveDebuggerDetection(newprocesses[currentprocess].processhandle);
|
|
newprocesses[currentprocess].firstbreak:=false;
|
|
end;
|
|
|
|
// if devent.Excdeeption.dwFirstChance=0 then MessageBox(0,'seconde time','second time',MB_OK);
|
|
//this is called after the threadlist has been initialised
|
|
|
|
for i:=0 to length(threadlist)-1 do
|
|
if threadlist[i,0]=devent.dwThreadId then
|
|
begin
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
|
|
getthreadcontext(threadlist[i,1],@context);
|
|
pausedthreadhandle:=threadlist[i,1];
|
|
break;
|
|
end;
|
|
|
|
|
|
if createaprocess and (context.{$ifdef CPU64}rip{$else}eip{$endif}=newprocesses[currentprocess].EntryPoint+1) then
|
|
begin
|
|
creationHandled:=true;
|
|
|
|
writeprocessmemory(newprocesses[currentprocess].processhandle,pointeR(newprocesses[currentprocess].EntryPoint),@newprocesses[currentprocess].OriginalEntryByte ,1,temp);
|
|
context.ContextFlags:=context_full;
|
|
outputdebugstring('Injecting dll');
|
|
context.{$ifdef CPU64}rip{$else}eip{$endif}:=injectcode(newprocesses[currentprocess].EntryPoint,newprocesses[currentprocess].processhandle)+1;
|
|
SetThreadcontext(pausedthreadhandle,@context);
|
|
|
|
|
|
if createdusingprocesswindow and (currentprocess=0) then
|
|
begin
|
|
//set a breakpoint at the AddressOfEntryPoint
|
|
if not attached then
|
|
begin
|
|
attached:=true;
|
|
attachedevent.SetEvent;
|
|
|
|
|
|
synchronize(ProcessCreated);
|
|
end;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
continue;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
if attached and {hidedebugger and} (not userisdebugging) then
|
|
if readprocessmemory(newprocesses[currentprocess].processhandle,pointeR(context.{$ifdef CPU64}rip{$else}eip{$endif}-1),@ab,1,a) then
|
|
begin
|
|
if ab=int3 then
|
|
begin
|
|
if (not createaprocess) or (creationHandled) then
|
|
begin
|
|
if (context.{$ifdef CPU64}rip{$else}eip{$endif}-1)<>DbgBreakPointLocation then
|
|
begin
|
|
if handlebreakpoints then
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE)
|
|
else
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
|
|
outputdebugstring('err1');
|
|
continue;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if not attached then
|
|
begin
|
|
attached:=true;
|
|
attachedevent.SetEvent;
|
|
synchronize(ProcessCreated);
|
|
end;
|
|
|
|
|
|
i:=0;
|
|
|
|
//find out what exception it is
|
|
if devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_BREAKPOINT then
|
|
begin
|
|
if userisdebugging then
|
|
begin
|
|
//check if this is a exception breakpoint that was made by cheat engine
|
|
addressfound:=dword(devent.Exception.ExceptionRecord.ExceptionAddress);
|
|
|
|
found:=(int3CEBreakpoint.address=addressfound);
|
|
|
|
crdebugging.enter;
|
|
try
|
|
for i:=0 to length(int3userbreakpoints)-1 do
|
|
if (int3userbreakpoints[i].address=addressfound) then found:=true;
|
|
finally
|
|
crdebugging.Leave;
|
|
end;
|
|
|
|
if found or createdusingprocesswindow then
|
|
begin
|
|
//we have a confirmation....
|
|
//set the byte back
|
|
if not createdusingprocesswindow then
|
|
removebreakpoint;
|
|
|
|
createdusingprocesswindow:=false;
|
|
if int3CEBreakpoint.address=addressfound then
|
|
begin
|
|
int3CEBreakpoint.address:=0;
|
|
int3CEBreakpoint.originalbyte:=0;
|
|
end;
|
|
|
|
//set eip back
|
|
dec(context.{$ifdef CPU64}rip{$else}eip{$endif});
|
|
|
|
notinlist:=true;
|
|
crdebugging.enter;
|
|
|
|
|
|
try
|
|
|
|
for i:=0 to length(registermodificationBPs)-1 do
|
|
begin
|
|
if registermodificationBPs[i].address=context.{$ifdef CPU64}rip{$else}eip{$endif} then
|
|
begin
|
|
notinlist:=false;
|
|
|
|
//modify the context of this thread according to the data in registermodificationBPs[i]
|
|
if registermodificationBPs[i].change_eax then context.{$ifdef CPU64}rax{$else}eax{$endif}:=registermodificationBPs[i].new_eax;
|
|
if registermodificationBPs[i].change_ebx then context.{$ifdef CPU64}rbx{$else}ebx{$endif}:=registermodificationBPs[i].new_ebx;
|
|
if registermodificationBPs[i].change_ecx then context.{$ifdef CPU64}rcx{$else}ecx{$endif}:=registermodificationBPs[i].new_ecx;
|
|
if registermodificationBPs[i].change_edx then context.{$ifdef CPU64}rdx{$else}edx{$endif}:=registermodificationBPs[i].new_edx;
|
|
if registermodificationBPs[i].change_esi then context.{$ifdef CPU64}rsi{$else}esi{$endif}:=registermodificationBPs[i].new_esi;
|
|
if registermodificationBPs[i].change_edi then context.{$ifdef CPU64}rdi{$else}edi{$endif}:=registermodificationBPs[i].new_edi;
|
|
if registermodificationBPs[i].change_ebp then context.{$ifdef CPU64}rbp{$else}ebp{$endif}:=registermodificationBPs[i].new_ebp;
|
|
if registermodificationBPs[i].change_esp then context.{$ifdef CPU64}rsp{$else}esp{$endif}:=registermodificationBPs[i].new_esp;
|
|
if registermodificationBPs[i].change_eip then context.{$ifdef CPU64}rip{$else}eip{$endif}:=registermodificationBPs[i].new_eip;
|
|
|
|
if registermodificationBPs[i].change_cf then
|
|
if registermodificationBPs[i].new_cf then
|
|
context.EFlags:=context.EFlags or $1 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $1);
|
|
|
|
if registermodificationBPs[i].change_pf then
|
|
if registermodificationBPs[i].new_pf then
|
|
context.EFlags:=context.EFlags or $4 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $4);
|
|
|
|
if registermodificationBPs[i].change_af then
|
|
if registermodificationBPs[i].new_af then
|
|
context.EFlags:=context.EFlags or $10 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $10);
|
|
|
|
if registermodificationBPs[i].change_zf then
|
|
if registermodificationBPs[i].new_zf then
|
|
context.EFlags:=context.EFlags or $40 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $40);
|
|
|
|
if registermodificationBPs[i].change_sf then
|
|
if registermodificationBPs[i].new_sf then
|
|
context.EFlags:=context.EFlags or $80 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $80);
|
|
|
|
if registermodificationBPs[i].change_of then
|
|
if registermodificationBPs[i].new_of then
|
|
context.EFlags:=context.EFlags or $800 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $800);
|
|
|
|
context.ContextFlags:=CONTEXT_FULL;
|
|
setthreadcontext(pausedthreadhandle,@context);
|
|
|
|
SetSingleStepping(devent.dwThreadId);
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
|
|
j:=devent.dwThreadId;
|
|
if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
|
|
|
|
while j<>devent.dwthreadid do
|
|
begin
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
|
|
end;
|
|
resetbreakpoint;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
break;
|
|
end;
|
|
end; //registermodificiation loop
|
|
|
|
finally
|
|
crdebugging.Leave;
|
|
end;
|
|
|
|
if (traceaddress<>0) and (self.traceaddress=dword(devent.Exception.ExceptionRecord.ExceptionAddress)) then //do tracecount steps
|
|
begin
|
|
context.ContextFlags:=CONTEXT_FULL;
|
|
setthreadcontext(pausedthreadhandle,@context);
|
|
|
|
if tracecount>0 then
|
|
begin
|
|
debugging:=tracer(devent);
|
|
tracecount:=0;
|
|
end else debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
resetbreakpoint;
|
|
|
|
continue;
|
|
end;
|
|
|
|
if not notinlist then continue; //exit, we've done what was needed
|
|
|
|
|
|
context.ContextFlags:=CONTEXT_FULL;
|
|
setthreadcontext(pausedthreadhandle,@context);
|
|
|
|
|
|
continueprocess:=false;
|
|
continuehow:=0;
|
|
suspendallthreads;
|
|
|
|
synchronize(updateregisters);
|
|
|
|
running:=false;
|
|
while not continueprocess do sleep(10);
|
|
|
|
WaitForSingleObject(semaphore,infinite);
|
|
running:=true; //meaning, keep your fucking hands of the registers
|
|
|
|
resumeallthreads;
|
|
|
|
//make a step
|
|
SetSingleStepping(devent.dwThreadId);
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
|
|
j:=devent.dwThreadId;
|
|
if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
|
|
|
|
while j<>devent.dwthreadid do
|
|
begin
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
|
|
end;
|
|
|
|
//set the breakpoint back if needed
|
|
resetbreakpoint;
|
|
releasesemaphore(semaphore,1,nil);
|
|
|
|
if continuehow=1 then //it was a step, so
|
|
begin
|
|
continueprocess:=false;
|
|
continuehow:=0;
|
|
suspendallthreads;
|
|
|
|
for j:=0 to length(threadlist)-1 do
|
|
if threadlist[j,0]=devent.dwThreadId then
|
|
begin
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
|
|
getthreadcontext(threadlist[j,1],@context);
|
|
pausedthreadhandle:=threadlist[j,1];
|
|
break;
|
|
end;
|
|
|
|
|
|
synchronize(updateregisters);
|
|
|
|
running:=true; //meaning, keep your fucking hands of the registers
|
|
while not continueprocess do sleep(10);
|
|
running:=true; //meaning, keep your fucking hands of the registers
|
|
|
|
resumeallthreads;
|
|
|
|
//make a step
|
|
if continuehow=1 then SetSingleStepping(devent.dwThreadId);
|
|
end;
|
|
end;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
continue;
|
|
end;
|
|
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
Outputdebugstring(pchar('Breakpoint:'+IntToHex(dword(devent.Exception.ExceptionRecord.ExceptionAddress),8)));
|
|
continue;
|
|
end;
|
|
|
|
//check if it is because the single stepping mode is enabled
|
|
if devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_SINGLE_STEP then
|
|
begin //it is in single step mode
|
|
//--added
|
|
if (traceaddress<>0) and (dword(devent.Exception.ExceptionRecord.ExceptionAddress)=traceaddress) then
|
|
begin
|
|
RemoveBreakpoint;
|
|
if tracecount>0 then
|
|
debugging:=tracer(devent)
|
|
else
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
|
|
tracecount:=0;
|
|
ResetBreakpoint;
|
|
continue;
|
|
end;
|
|
|
|
if findwriter2 then
|
|
begin
|
|
addressfound:=dword(devent.Exception.ExceptionRecord.ExceptionAddress);
|
|
|
|
offset:=addressfound;
|
|
opcode:=disassemble(offset,desc);
|
|
|
|
|
|
if pos('REP',opcode)>0 then
|
|
begin
|
|
if context.{$ifdef CPU64}rcx{$else}ecx{$endif}=0 then addressfound:=previousopcode(addressfound);
|
|
|
|
end else
|
|
addressfound:=previousopcode(addressfound);
|
|
|
|
//check if the address is in the list
|
|
notinlist:=true;
|
|
try
|
|
{$ifndef net}
|
|
|
|
for i:=0 to length(foundcodedialog.coderecords)-1 do
|
|
if foundcodedialog.coderecords[i].address=addressfound then //if it is in the list then set notinlist to false and go out of the loop
|
|
begin
|
|
notinlist:=false;
|
|
break;
|
|
end;
|
|
|
|
{$else}
|
|
//check if it is in the list
|
|
for i:=0 to length(coderecords)-1 do
|
|
if coderecords[i]=addressfound then
|
|
begin
|
|
notinlist:=false;
|
|
break;
|
|
end;
|
|
{$endif}
|
|
|
|
except
|
|
//list got shortened or invalid
|
|
end;
|
|
|
|
if notinlist then synchronize(foundone); //add this memory address to the foundcode window.
|
|
end;
|
|
|
|
if findreaderset then
|
|
begin
|
|
WaitForSingleObject(semaphore,infinite);
|
|
if findreaderset then VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,PAGE_NOACCESS,original);
|
|
releasesemaphore(semaphore,1,nil);
|
|
end;
|
|
|
|
if breakpointset then
|
|
begin
|
|
if breakpointaddress=dword(devent.Exception.ExceptionRecord.ExceptionAddress) then
|
|
begin
|
|
//find out what the address points to
|
|
offset:=breakpointaddress;
|
|
opcode:=disassemble(offset,desc);
|
|
|
|
fb:=pos('[',opcode);
|
|
if fb>0 then
|
|
begin
|
|
nb:=pos(']',opcode);
|
|
|
|
if nb>fb then //just a simple check to verify the opcode is ok
|
|
begin
|
|
temps:=copy(opcode,fb+1,nb-fb-1);
|
|
//lastbreakpoint:=address;
|
|
synchronize(addtochangeslist);
|
|
//and add that address to a list
|
|
|
|
removebreakpoint;
|
|
SetSingleStepping(devent.dwThreadId);
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
|
|
if WaitForDebugEvent(devent,10000)=false then application.MessageBox('error2-3','error2-3',0);
|
|
while devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP do
|
|
begin
|
|
if devent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT then
|
|
begin
|
|
//I hate it when this happens
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
outputdebugstring('err2');
|
|
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
if breakpointset then resetbreakpoint;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if userisdebugging then
|
|
begin
|
|
//got a breakpoint
|
|
//set the breakpoint
|
|
if not canusedebugregs then
|
|
removebreakpoint;
|
|
|
|
|
|
if (canusedebugregs) and (drregs.dr3=context.{$ifdef CPU64}rip{$else}eip{$endif}) then
|
|
begin
|
|
drregs.Dr3:=0;
|
|
resetbreakpoint;
|
|
end;
|
|
|
|
//check if it is a regmodification bp
|
|
crdebugging.Enter;
|
|
try
|
|
notinlist:=true;
|
|
for i:=0 to length(registermodificationBPs)-1 do
|
|
begin
|
|
if registermodificationBPs[i].address=context.{$ifdef CPU64}rip{$else}eip{$endif} then
|
|
begin
|
|
notinlist:=false;
|
|
|
|
//modify the context of this thread according to the data in registermodificationBPs[i]
|
|
if registermodificationBPs[i].change_eax then context.{$ifdef CPU64}rax{$else}eax{$endif}:=registermodificationBPs[i].new_eax;
|
|
if registermodificationBPs[i].change_ebx then context.{$ifdef CPU64}rbx{$else}ebx{$endif}:=registermodificationBPs[i].new_ebx;
|
|
if registermodificationBPs[i].change_ecx then context.{$ifdef CPU64}rcx{$else}ecx{$endif}:=registermodificationBPs[i].new_ecx;
|
|
if registermodificationBPs[i].change_edx then context.{$ifdef CPU64}rdx{$else}edx{$endif}:=registermodificationBPs[i].new_edx;
|
|
if registermodificationBPs[i].change_esi then context.{$ifdef CPU64}rsi{$else}esi{$endif}:=registermodificationBPs[i].new_esi;
|
|
if registermodificationBPs[i].change_edi then context.{$ifdef CPU64}rdi{$else}edi{$endif}:=registermodificationBPs[i].new_edi;
|
|
if registermodificationBPs[i].change_ebp then context.{$ifdef CPU64}rbp{$else}ebp{$endif}:=registermodificationBPs[i].new_ebp;
|
|
if registermodificationBPs[i].change_esp then context.{$ifdef CPU64}rsp{$else}esp{$endif}:=registermodificationBPs[i].new_esp;
|
|
if registermodificationBPs[i].change_eip then context.{$ifdef CPU64}rip{$else}eip{$endif}:=registermodificationBPs[i].new_eip;
|
|
|
|
if registermodificationBPs[i].change_cf then
|
|
if registermodificationBPs[i].new_cf then
|
|
context.EFlags:=context.EFlags or $1 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $1);
|
|
|
|
if registermodificationBPs[i].change_pf then
|
|
if registermodificationBPs[i].new_pf then
|
|
context.EFlags:=context.EFlags or $4 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $4);
|
|
|
|
if registermodificationBPs[i].change_af then
|
|
if registermodificationBPs[i].new_af then
|
|
context.EFlags:=context.EFlags or $10 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $10);
|
|
|
|
if registermodificationBPs[i].change_zf then
|
|
if registermodificationBPs[i].new_zf then
|
|
context.EFlags:=context.EFlags or $40 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $40);
|
|
|
|
if registermodificationBPs[i].change_sf then
|
|
if registermodificationBPs[i].new_sf then
|
|
context.EFlags:=context.EFlags or $80 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $80);
|
|
|
|
if registermodificationBPs[i].change_of then
|
|
if registermodificationBPs[i].new_of then
|
|
context.EFlags:=context.EFlags or $800 //enable the bit
|
|
else
|
|
context.EFlags:=context.EFlags and (not $800);
|
|
|
|
context.ContextFlags:=CONTEXT_FULL;
|
|
setthreadcontext(pausedthreadhandle,@context);
|
|
|
|
removebreakpoint;
|
|
SetSingleStepping(devent.dwThreadId);
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
|
|
j:=devent.dwThreadId;
|
|
if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
|
|
|
|
while j<>devent.dwthreadid do
|
|
begin
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
|
|
end;
|
|
resetbreakpoint;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
finally
|
|
crdebugging.Leave;
|
|
end;
|
|
|
|
if not notinlist then continue;
|
|
|
|
//-----------------------------------
|
|
|
|
|
|
continueprocess:=false;
|
|
continuehow:=0;
|
|
suspendallthreads;
|
|
|
|
synchronize(updateregisters);
|
|
|
|
running:=false;
|
|
while not continueprocess do sleep(10);
|
|
running:=true; //meaning, keep your fucking hands of the registers
|
|
|
|
resumeallthreads;
|
|
|
|
removebreakpoint;
|
|
SetSingleStepping(devent.dwThreadId);
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
|
|
i:=devent.dwThreadId;
|
|
if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
|
|
|
|
while i<>devent.dwthreadid do
|
|
begin
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
|
|
|
|
end;
|
|
|
|
resetbreakpoint;
|
|
|
|
//get new context info
|
|
for i:=0 to length(threadlist)-1 do
|
|
if threadlist[i,0]=devent.dwThreadId then
|
|
begin
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
|
|
getthreadcontext(threadlist[i,1],@context);
|
|
pausedthreadhandle:=threadlist[i,1];
|
|
break;
|
|
end;
|
|
|
|
if ((canusedebugregs) and (drregs.Dr3=context.{$ifdef CPU64}rip{$else}eip{$endif})) or (continuehow=1) then
|
|
begin
|
|
|
|
if (canusedebugregs) and (drregs.dr3=context.{$ifdef CPU64}rip{$else}eip{$endif}) then
|
|
begin
|
|
drregs.Dr3:=0;
|
|
resetbreakpoint;
|
|
end;
|
|
|
|
synchronize(updateregisters);
|
|
continueprocess:=false;
|
|
continuehow:=0;
|
|
running:=false;
|
|
while not continueprocess do;
|
|
running:=true;
|
|
|
|
if continuehow=1 then SetSingleStepping(devent.dwThreadId);
|
|
end;
|
|
|
|
end;
|
|
|
|
if not canusedebugregs then
|
|
resetbreakpoint;
|
|
|
|
|
|
//it's a single step exception (most likely made by me, so I'd better fix it)
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
continue;
|
|
end;
|
|
|
|
//if it was a access violation, and it was a WRITE access violation then
|
|
if readonlyset and
|
|
(devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_ACCESS_VIOLATION) and
|
|
(devent.Exception.ExceptionRecord.ExceptionInformation[0]=1)
|
|
then
|
|
begin
|
|
//now check if this address is in our locked array?
|
|
if (devent.Exception.ExceptionRecord.ExceptionInformation[1]>=readonly.pagebase) and
|
|
(devent.Exception.ExceptionRecord.ExceptionInformation[1]<=readonly.pagebase+readonly.pagesize) then
|
|
begin
|
|
//the address that created an exception is in a page I locked
|
|
|
|
//check if the exception is at the same spot as the entry in the read-only list
|
|
if (devent.Exception.ExceptionRecord.ExceptionInformation[1]>=readonly.Address-readonly.size+1) and
|
|
(devent.Exception.ExceptionRecord.ExceptionInformation[1]<=readonly.Address+readonly.size-1) then
|
|
begin
|
|
//it is!!!
|
|
addressfound:=dword(devent.Exception.ExceptionRecord.ExceptionAddress);
|
|
|
|
//check if the address is in the list
|
|
notinlist:=true;
|
|
//go through the list
|
|
|
|
try
|
|
{$ifndef net}
|
|
|
|
for i:=0 to length(foundcodedialog.coderecords)-1 do
|
|
if foundcodedialog.coderecords[i].address=addressfound then //if it is in the list then set notinlist to false and go out of the loop
|
|
begin
|
|
notinlist:=false;
|
|
break;
|
|
end;
|
|
|
|
{$else}
|
|
//check if it is in the list
|
|
for i:=0 to length(coderecords)-1 do
|
|
if coderecords[i]=addressfound then
|
|
begin
|
|
notinlist:=false;
|
|
break;
|
|
end;
|
|
{$endif}
|
|
except
|
|
|
|
end;
|
|
|
|
|
|
if notinlist then synchronize(foundone); //add this memory address to the foundcode window.
|
|
|
|
end;
|
|
|
|
//set the protection back to the original, in single stepping mode
|
|
VirtualProtectEx(processhandle,pointer(readonly.Address),readonly.size,readonly.originalprotection,original);
|
|
|
|
//set the process to single stepping
|
|
//find the threadhandle
|
|
|
|
SetSingleStepping(devent.dwThreadId);
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
|
|
if WaitForDebugEvent(devent,30000) then
|
|
while debugging and readonlyset and (devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP) do
|
|
begin
|
|
inc(debugpatch);
|
|
//This SHOULD never come up...
|
|
if devent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT then //in case of a thread that does something
|
|
begin
|
|
for i:=0 to length(threadlist)-1 do
|
|
if threadlist[i,0]=devent.dwThreadId then
|
|
begin
|
|
hthr:=threadlist[i,1];
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
|
|
getthreadcontext(hthr,@context);
|
|
|
|
context.ContextFlags:=CONTEXT_FULL; //CONTEXT_CONTROL;
|
|
context.EFlags:=context.EFlags or $100;
|
|
setthreadcontext(hthr,@context);
|
|
break;
|
|
end;
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
outputdebugstring('err3');
|
|
|
|
break;
|
|
end;
|
|
|
|
for i:=0 to length(threadlist)-1 do
|
|
if threadlist[i,0]=devent.dwThreadId then
|
|
begin
|
|
hthr:=threadlist[i,1];
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
|
|
getthreadcontext(hthr,@context);
|
|
|
|
context.ContextFlags:=CONTEXT_FULL; //CONTEXT_CONTROL;
|
|
context.EFlags:=context.EFlags or $100;
|
|
setthreadcontext(hthr,@context);
|
|
break;
|
|
end;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
if WaitForDebugEvent(devent,30000)=false then application.MessageBox('Write Exception Error. (2)','Debugger error',0)
|
|
end; {while}
|
|
|
|
//set the protection back to read-only if the readonly flag is still needed
|
|
WaitForSingleObject(semaphore,infinite);
|
|
if readonlyset then VirtualProtectEx(processhandle,pointer(readonly.Address),readonly.size,PAGE_EXECUTE_READ,original);
|
|
releasesemaphore(semaphore,1,nil);
|
|
|
|
if devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP then continue;
|
|
|
|
//debugloghelp
|
|
inc(times);
|
|
previousexception:=devent.Exception.ExceptionRecord.ExceptionInformation[1];
|
|
end;
|
|
|
|
//release the semaphore so other threads that want to use the rea-only list can access this
|
|
|
|
//the access violation was created by me, so handle it myself, and not the app
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
continue;
|
|
end; {access violation}
|
|
|
|
|
|
//read access violations
|
|
if findreaderset then
|
|
begin
|
|
if (devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_ACCESS_VIOLATION) and
|
|
((devent.Exception.ExceptionRecord.ExceptionInformation[0]=0) or (alsowrites)) //0=read
|
|
then //0=read
|
|
begin
|
|
//check if the exception is at the same spot as the entry in the read-only list
|
|
if ((devent.Exception.ExceptionRecord.ExceptionInformation[1]>=findreader.Address) and
|
|
(devent.Exception.ExceptionRecord.ExceptionInformation[1]<findreader.Address+findreader.size))
|
|
or
|
|
((devent.Exception.ExceptionRecord.ExceptionInformation[1]<=findreader.Address) and
|
|
(devent.Exception.ExceptionRecord.ExceptionInformation[1]>findreader.Address-findreader.size))
|
|
then
|
|
begin
|
|
//it is!!!
|
|
addressfound:=dword(devent.Exception.ExceptionRecord.ExceptionAddress);
|
|
|
|
//check if the address is in the list
|
|
notinlist:=true;
|
|
//go through the list
|
|
|
|
try
|
|
{$ifndef net}
|
|
|
|
for i:=0 to length(foundcodedialog.coderecords)-1 do
|
|
if foundcodedialog.coderecords[i].address=addressfound then //if it is in the list then set notinlist to false and go out of the loop
|
|
begin
|
|
notinlist:=false;
|
|
break;
|
|
end;
|
|
|
|
{$else}
|
|
//check if it is in the list
|
|
for i:=0 to length(coderecords)-1 do
|
|
if coderecords[i]=addressfound then
|
|
begin
|
|
notinlist:=false;
|
|
break;
|
|
end;
|
|
{$endif}
|
|
except
|
|
|
|
end;
|
|
|
|
|
|
if notinlist then synchronize(foundone); //add this memory address to the foundcode window.
|
|
|
|
end;
|
|
|
|
//set the protection back to the original, in single stepping mode
|
|
VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,findreader.originalprotection,original);
|
|
|
|
//set the process to single stepping
|
|
SetSingleStepping(devent.dwThreadId);
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
|
|
if WaitForDebugEvent(devent,10000)=false then application.MessageBox('error1-3','error1-3',0);
|
|
while devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP do
|
|
begin
|
|
if devent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT then
|
|
begin
|
|
//I hate it when this happens
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
outputdebugstring('err4');
|
|
|
|
break;
|
|
end;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
if WaitForDebugEvent(devent,30000)=false then application.MessageBox('error2','error2',0)
|
|
end;
|
|
|
|
|
|
//set the protection back to read-only if the readonly flag is still needed
|
|
WaitForSingleObject(semaphore,infinite);
|
|
if findreaderset then VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,original,original);
|
|
releasesemaphore(semaphore,1,nil);
|
|
|
|
if devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP then continue;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
continue;
|
|
end; {read access violation}
|
|
|
|
//check if it was a write access violation
|
|
if (devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_ACCESS_VIOLATION) and
|
|
(devent.Exception.ExceptionRecord.ExceptionInformation[0]=1) then //1=write
|
|
begin
|
|
VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,findreader.originalprotection,original);
|
|
|
|
//set the process to single stepping
|
|
SetSingleStepping(devent.dwThreadId);
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
|
|
if WaitForDebugEvent(devent,10000)=false then application.MessageBox('error1-1','error1-1',0);
|
|
while devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP do
|
|
begin
|
|
if devent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT then
|
|
begin
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
outputdebugstring('err5');
|
|
continue;
|
|
end;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
if WaitForDebugEvent(devent,30000)=false then application.MessageBox('error2','error2',0)
|
|
end;
|
|
|
|
//set the protection back to no access if the readonly flag is still needed
|
|
WaitForSingleObject(semaphore,infinite);
|
|
if findreaderset then VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,original,original);
|
|
releasesemaphore(semaphore,1,nil);
|
|
|
|
if devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP then continue;
|
|
|
|
end;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
continue;
|
|
|
|
end {if findreaderset}
|
|
else
|
|
begin
|
|
if (devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_ACCESS_VIOLATION) then
|
|
begin
|
|
if findreader.Address<>0 then
|
|
begin
|
|
//check if the exception is at the same spot as the entry in the read-only list
|
|
if ((devent.Exception.ExceptionRecord.ExceptionInformation[1]>=(findreader.Address div $1000) * $1000) and
|
|
(devent.Exception.ExceptionRecord.ExceptionInformation[1]<((findreader.Address+$1000 div $1000) * $1000)+findreader.size)) then
|
|
begin
|
|
//perhaps a leftover or not, lets handle it anyhow
|
|
VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,findreader.originalprotection,original);
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
continue;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
//not captured
|
|
|
|
if findreaderremoved or readonlyremoved then
|
|
begin
|
|
findreaderremoved:=false;
|
|
readonlyremoved:=false;
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
end
|
|
else
|
|
begin
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
outputdebugstring(pchar('err6:'+IntToStr(currentprocess)+ '/'+IntToStr(length(newprocesses)-1)+ ' eip='+IntToHex(context.{$ifdef CPU64}rip{$else}eip{$endif},8)+' esi='+IntToHex(context.{$ifdef CPU64}rsi{$else}esi{$endif},8)+' edi='+IntToHex(context.{$ifdef CPU64}rdi{$else}edi{$endif},8) ));
|
|
end;
|
|
|
|
|
|
continue;
|
|
end;
|
|
|
|
|
|
CREATE_THREAD_DEBUG_EVENT:
|
|
begin
|
|
|
|
//seems this is a way of unhooking the debugger
|
|
ProcessHandler.ProcessHandle:=newprocesses[currentprocess].processhandle;
|
|
|
|
setlength(threadlist,length(threadlist)+1);
|
|
threadlist[length(threadlist)-1,0]:=devent.dwThreadId;
|
|
threadlist[length(threadlist)-1,1]:=devent.CreateThread.hThread;
|
|
|
|
threadlist[length(threadlist)-1,2]:=dword(devent.CreateThread.lpStartAddress);
|
|
threadlist[length(threadlist)-1,3]:=dword(devent.CreateThread.lpThreadLocalBase);
|
|
|
|
|
|
if canusedebugregs and (findwriter2 or breakpointset or userisdebugging) then //set the debug registers here too
|
|
setthreadcontext(devent.CreateThread.hThread,@drregs);
|
|
|
|
{$ifndef net}
|
|
if frmthreadlist<>nil then synchronize(frmthreadlist.fillthreadlist);
|
|
{$endif}
|
|
|
|
//let the application/OS handle this debug event
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
end;
|
|
|
|
EXIT_THREAD_DEBUG_EVENT:
|
|
begin
|
|
//search the threadid in the threadid list
|
|
tid:=devent.dwThreadId;
|
|
for i:=0 to length(threadlist)-1 do
|
|
if threadlist[i,0]=tid then
|
|
begin
|
|
for j:=i to length(threadlist)-2 do threadlist[j]:=threadlist[j+1];
|
|
setlength(threadlist,length(threadlist)-1);
|
|
break;
|
|
end;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
end;
|
|
|
|
CREATE_PROCESS_DEBUG_EVENT:
|
|
begin
|
|
currentprocess:=length(newprocesses);
|
|
setlength(newprocesses,length(newprocesses)+1);
|
|
newprocesses[currentprocess].processid:=devent.dwProcessId;
|
|
newprocesses[currentprocess].processhandle:=devent.CreateProcessInfo.hProcess;
|
|
newprocesses[currentprocess].filehandle:=devent.CreateProcessInfo.hFile;
|
|
newprocesses[currentprocess].EntryPoint:=dword(devent.CreateProcessInfo.lpStartAddress);
|
|
newprocesses[currentprocess].FirstBreak:=true;
|
|
|
|
attached:=true;
|
|
attachedevent.SetEvent;
|
|
|
|
if attached then synchronize(ProcessCreated);
|
|
|
|
readprocessmemory(newprocesses[currentprocess].processhandle,pointer(newprocesses[currentprocess].EntryPoint),@newprocesses[currentprocess].OriginalEntryByte,1,temp);
|
|
|
|
newprocesses[currentprocess].dotnet:=false;
|
|
if newprocesses[currentprocess].OriginalEntryByte=$ff then
|
|
begin
|
|
readprocessmemory(newprocesses[currentprocess].processhandle,pointer(newprocesses[currentprocess].EntryPoint),@ab,1,temp);
|
|
if ab=$25 then newprocesses[currentprocess].dotnet:=true;
|
|
end;
|
|
|
|
if not newprocesses[currentprocess].dotnet then if not writeprocessmemory(newprocesses[currentprocess].processhandle,pointeR(newprocesses[currentprocess].EntryPoint),@int3,1,temp) then outputdebugstring('failed to set break');
|
|
|
|
|
|
|
|
|
|
setlength(threadlist,length(threadlist)+1);
|
|
threadlist[length(threadlist)-1,0]:=devent.dwThreadId;
|
|
threadlist[length(threadlist)-1,1]:=devent.CreateProcessInfo.hThread;
|
|
|
|
setlength(Processes,length(processes)+1);
|
|
processes[length(processes)-1].ProcessID:=devent.dwProcessId;
|
|
processes[length(processes)-1].running:=true;
|
|
|
|
if processhandle=0 then
|
|
begin
|
|
ProcessHandler.ProcessHandle:=devent.CreateProcessInfo.hProcess;
|
|
ProcessHandler.processid:=devent.dwProcessId;
|
|
processinfo.hProcess:=processhandle;
|
|
processinfo.hThread:=devent.CreateProcessInfo.hThread;
|
|
if devent.CreateProcessInfo.hProcess<>processhandle then BEEP;
|
|
end;
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
{$ifndef net}
|
|
memorybrowser.thhandle:=devent.CreateProcessInfo.hThread;
|
|
{$endif}
|
|
|
|
symhandler.reinitialize;
|
|
end;
|
|
|
|
|
|
EXIT_PROCESS_DEBUG_EVENT:
|
|
begin
|
|
//a process stopped
|
|
//find the process in the process list
|
|
for i:=0 to length(processes)-1 do
|
|
begin
|
|
if devent.dwProcessId=Processes[i].ProcessID then
|
|
begin
|
|
processes[i].running:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
for i:=0 to length(newprocesses)-1 do
|
|
if devent.dwProcessId=newprocesses[i].processid then
|
|
begin
|
|
for j:=i to length(newprocesses)-2 do
|
|
newprocesses[i]:=newprocesses[i+1];
|
|
break;
|
|
end;
|
|
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
|
|
//check if there are processes left
|
|
debugging:=false;
|
|
for i:=0 to length(processes)-1 do
|
|
if processes[i].running then debugging:=true;
|
|
end;
|
|
|
|
OUTPUT_DEBUG_STRING_EVENT:
|
|
begin
|
|
if devent.DebugString.fUnicode=0 then
|
|
begin
|
|
//8 bit ascii
|
|
try
|
|
getmem(asciistring,devent.DebugString.nDebugStringLength+1);
|
|
try
|
|
ReadProcessMemory(newprocesses[currentprocess].processhandle,devent.DebugString.lpDebugStringData,asciistring,devent.DebugString.nDebugStringLength,bytesread);
|
|
asciistring[devent.DebugString.nDebugStringLength]:=#0;
|
|
temps:=asciistring;
|
|
finally
|
|
freemem(asciistring);
|
|
end;
|
|
except
|
|
;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
//16 bit unicode
|
|
try
|
|
getmem(unicodestring,devent.DebugString.nDebugStringLength+1);
|
|
try
|
|
ReadProcessMemory(newprocesses[currentprocess].processhandle,devent.DebugString.lpDebugStringData,unicodestring,devent.DebugString.nDebugStringLength,bytesread);
|
|
unicodestring[devent.DebugString.nDebugStringLength div 2]:=#0;
|
|
temps:=unicodestring;
|
|
finally
|
|
freemem(unicodestring);
|
|
end;
|
|
except
|
|
|
|
end;
|
|
end;
|
|
synchronize(AddDebugString);
|
|
|
|
if hidedebugger then //double bevcause of the way it is implementedin the os
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED)
|
|
else
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE)
|
|
|
|
end;
|
|
|
|
else debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
|
|
|
|
end; //case
|
|
*)
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
except
|
|
messagebox(0,'Debugger crash','CE Debugger',mb_ok);
|
|
end;
|
|
|
|
OutputDebugString('End of debuggerthread');
|
|
|
|
|
|
setlength(userbreakpoints,0);
|
|
setlength(int3userbreakpoints,0);
|
|
|
|
|
|
for i:=0 to length(processes)-1 do
|
|
if processes[i].running then DebugActiveProcessStop(processes[i].ProcessID); //this sometimes works
|
|
|
|
attached:=false;
|
|
attachedevent.SetEvent;
|
|
|
|
setlength(threadlist,0);
|
|
{$ifndef net}
|
|
postmessage(mainform.Handle,WM_USER+1,0,0);
|
|
{$endif}
|
|
end;
|
|
|
|
initialization
|
|
CRDebugging:=TCriticalSection.Create;
|
|
|
|
krn := LoadLibrary('Kernel32.dll');
|
|
if krn <> 0 then
|
|
begin
|
|
|
|
@DebugBreakProcess := GetProcAddress(krn, 'DebugBreakProcess');
|
|
@DebugSetProcessKillOnExit :=GetProcAddress(krn,'DebugSetProcessKillOnExit');
|
|
@DebugActiveProcessStop :=GetProcAddress(krn,'DebugActiveProcessStop');
|
|
@IsDebuggerPresent:=GetProcAddress(krn,'IsDebuggerPresent');
|
|
IsDebuggerPresentLocation:=DworD(GetProcAddress(krn,'IsDebuggerPresent'));
|
|
|
|
if @DebugBreakProcess=nil then
|
|
@DebugBreakProcess:=@DebugBreakProstitute;
|
|
|
|
if @DebugSetProcessKillOnExit=nil then
|
|
@DebugSetProcessKillOnExit:=@DebugSetProcessKillOnExitProtitute;
|
|
|
|
if @DebugActiveProcessStop=nil then
|
|
@DebugActiveProcessStop:=@DebugActiveProcessStopProstitute;
|
|
|
|
end;
|
|
|
|
ntdlllib:=LoadLibrary('ntdll.dll');
|
|
if ntdlllib<>0 then
|
|
begin
|
|
DbgUIDebugActiveProcess:=getprocaddress(ntdlllib,'DbgUiDebugActiveProcess');
|
|
DbgBreakPointLocation:=dword(getprocaddress(ntdlllib,'DbgBreakPoint'));
|
|
|
|
NtQueryInformationProcess:=nil;
|
|
NtQueryInformationProcess:=GetProcAddress(ntdlllib,'NtQueryInformationProcess');
|
|
|
|
ntsuspendprocess:=nil;
|
|
ntsuspendprocess:=GetProcAddress(ntdlllib,'NtSuspendProcess');
|
|
ntresumeprocess:=GetProcAddress(ntdlllib,'NtResumeProcess');
|
|
freelibrary(ntdlllib);
|
|
end;
|
|
|
|
|
|
finalization
|
|
if CRDebugging<>nil then
|
|
CRDebugging.Free;
|
|
|
|
end.
|
|
|