mirror of
https://github.com/cheat-engine/cheat-engine
synced 2026-08-15 02:26:08 -04:00
2731 lines
94 KiB
ObjectPascal
2731 lines
94 KiB
ObjectPascal
unit Debugger;
|
|
//just my place to play with all kinds of threading methods
|
|
//when i find out the best suited method I'll use that....
|
|
|
|
interface
|
|
|
|
uses Classes,windows,sysutils,cefuncproc,Messages,forms,SyncObjs,
|
|
dialogs,controls,Graphics,NewKernelHandler,symbolhandler,StrUtils,
|
|
ComCtrls, assemblerunit, addressparser;
|
|
|
|
const reg0set= $3; //(00000000000000000000000000000011)
|
|
reg1set= $C; //(00000000000000000000000000001100)
|
|
reg2set= $30; //(00000000000000000000000000110000)
|
|
reg3set= $c0; //(00000000000000000000000011000000)
|
|
|
|
|
|
|
|
debugexact= $300;
|
|
gd_flag= $2000; //(00000000000000000010000000000000)
|
|
reg0w= $10000; //(00000000000000010000000000000000)
|
|
reg0rw= $30000; //(00000000000000110000000000000000)
|
|
reg0len2=$40000; //(00000000000001000000000000000000)
|
|
reg0len4=$c0000; //(00000000000011000000000000000000)
|
|
reg1w= $100000; //(00000000000100000000000000000000)
|
|
reg1rw= $300000; //(00000000001100000000000000000000)
|
|
reg1len2=$400000; //(00000000010000000000000000000000)
|
|
reg1len4=$c00000; //(00000000110000000000000000000000)
|
|
reg2w= $1000000; //(00000001000000000000000000000000)
|
|
reg2rw= $3000000; //(00000011000000000000000000000000)
|
|
reg2len2=$4000000; //(00000100000000000000000000000000)
|
|
reg2len4=$c000000; //(00001100000000000000000000000000)
|
|
reg3w= $10000000; //(00010000000000000000000000000000)
|
|
reg3rw= $30000000; //(00110000000000000000000000000000)
|
|
reg3len2=$40000000; //(01000000000000000000000000000000)
|
|
reg3len4=$c0000000; //(11000000000000000000000000000000)
|
|
reg0len0=0;
|
|
reg1len0=0;
|
|
reg2len0=0;
|
|
reg3len0=0;
|
|
|
|
|
|
type TWindowsDebuggerContinueOption = (wdco_run=0, wdco_stepinto, wdco_stepover, wdco_runtill);
|
|
|
|
type TReadonly = record
|
|
pagebase: dword;
|
|
pagesize: dword;
|
|
Address: dword;
|
|
size: integer;
|
|
originalprotection: dword;
|
|
end;
|
|
|
|
type tThreadEntry=record
|
|
threadHandle: thandle;
|
|
address: dword;
|
|
end;
|
|
|
|
|
|
type Process=record
|
|
ProcessID: dword;
|
|
running: boolean;
|
|
end;
|
|
|
|
type tbreakpoint = record
|
|
address:dword;
|
|
originalbyte: byte;
|
|
end;
|
|
|
|
type TRegistermodificationBP=record
|
|
address:dword; //addres to break on
|
|
change_eax:BOOL;
|
|
change_ebx:BOOL;
|
|
change_ecx:BOOL;
|
|
change_edx:BOOL;
|
|
change_esi:BOOL;
|
|
change_edi:BOOL;
|
|
change_ebp:BOOL;
|
|
change_esp:BOOL;
|
|
change_eip:BOOL;
|
|
change_cf:BOOL;
|
|
change_pf:BOOL;
|
|
change_af:BOOL;
|
|
change_zf:BOOL;
|
|
change_sf:BOOL;
|
|
change_of:BOOL;
|
|
new_eax:dword;
|
|
new_ebx:dword;
|
|
new_ecx:dword;
|
|
new_edx:dword;
|
|
new_esi:dword;
|
|
new_edi:dword;
|
|
new_ebp:dword;
|
|
new_esp:dword;
|
|
new_eip:dword;
|
|
new_cf:BOOL;
|
|
new_pf:BOOL;
|
|
new_af:BOOL;
|
|
new_zf:BOOL;
|
|
new_sf:BOOL;
|
|
new_of:BOOL;
|
|
end;
|
|
type PRegisterModificationBP=^TRegisterModificationBP;
|
|
|
|
type TNewProcedureData=record
|
|
processid: dword;
|
|
processhandle: dword;
|
|
filehandle: dword;
|
|
EntryPoint: dword;
|
|
OriginalEntryByte: byte;
|
|
DotNet: boolean;
|
|
FirstBreak: 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;
|
|
|
|
|
|
procedure addtochangeslist;
|
|
procedure ProcessCreated;
|
|
|
|
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;
|
|
|
|
function WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL;
|
|
function continueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus: DWORD): BOOL;
|
|
public
|
|
//semaphore: THandle;
|
|
Processes: array of process;
|
|
Newprocesses: array of TNewProcedureData;
|
|
|
|
|
|
pausedthreadhandle: thandle;
|
|
|
|
running: boolean;
|
|
continueprocess: boolean;
|
|
continuehow: TWindowsDebuggerContinueOption;
|
|
|
|
DRRegs: _CONTEXT;
|
|
|
|
FindWriter2: boolean;
|
|
|
|
|
|
attaching: boolean;
|
|
attached: boolean;
|
|
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 Updateregisters;
|
|
procedure FoundOne;
|
|
procedure RemoveBreakpoint;
|
|
|
|
procedure suspendallthreads;
|
|
procedure resumeallthreads;
|
|
|
|
function isBreakpoint(address:dword): boolean;
|
|
|
|
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;
|
|
Semaphore: Thandle;
|
|
|
|
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 Mainunit,frmFloatingPointPanelUnit,Memorybrowserformunit,disassembler,frmTracerUnit,foundcodeunit,kerneldebugger,advancedoptionsunit,formChangedAddresses,frmstacktraceunit,frmThreadlistunit,formdebugstringsunit,formsettingsunit,processwindowunit,plugin,frmCreatedProcessListUnit;
|
|
|
|
function ToggleBreakpoint(address:dword):boolean;
|
|
{$ifndef net}
|
|
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;
|
|
{$endif}
|
|
|
|
begin
|
|
{$ifndef net}
|
|
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 kdebugger.isactive 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;
|
|
|
|
{$endif}
|
|
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');
|
|
|
|
{$ifndef netserver}
|
|
if kdebugger.isactive 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;
|
|
kdebugger.StopDebugger;
|
|
end;
|
|
{$endif}
|
|
|
|
if (debuggerthread=nil) or (not debuggerthread.attached) 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
|
|
Debuggerthread:=TDebugger.MyCreate2(processid);
|
|
while (debuggerthread<>nil) and DebuggerThread.attaching do sleep(10); //give him some time
|
|
if not debuggerthread.attached then
|
|
begin
|
|
debuggerthread.Free;
|
|
debuggerthread:=nil;
|
|
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 or CONTEXT_EXTENDED_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.Eip;
|
|
if setthreadcontext(threadhandle,drregs) then result:=true;
|
|
end;
|
|
|
|
|
|
resumethread(threadhandle);
|
|
debuggerthread.Resume;
|
|
end;
|
|
|
|
|
|
Constructor TDebugger.MyCreate(filenm: String);
|
|
begin
|
|
{$ifndef netserver}
|
|
{
|
|
if debuggerthread3<>nil then raise exception.Create('Please stop the kernelmode debugging routines and breakpoints before starting this debugger');
|
|
}
|
|
|
|
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.
|
|
end;
|
|
|
|
Constructor TDebugger.MyCreate2(processID: THandle);
|
|
var
|
|
debugportoffset:dword;
|
|
debugport:dword;
|
|
ar:dword;
|
|
peprocess: dword;
|
|
begin
|
|
|
|
{$ifndef netserver}
|
|
{
|
|
if debuggerthread3<>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.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.
|
|
end;
|
|
|
|
destructor TDebugger.Destroy;
|
|
begin
|
|
//clear all breakpoints
|
|
RemoveBreakpoint;
|
|
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TDebugger.tracersync;
|
|
{$ifndef net}
|
|
var s: string;
|
|
i: integer;
|
|
d: TTraceDebugInfo;
|
|
temp: dword;
|
|
{$endif}
|
|
begin
|
|
{$ifndef net}
|
|
if frmtracer<>nil then
|
|
begin
|
|
temp:=context.eip;
|
|
s:=disassemble(temp);
|
|
i:=posex('-',s);
|
|
i:=posex('-',s,i+1);
|
|
s:=copy(s,i+2,length(s));
|
|
s:=inttohex(context.eip,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
|
|
ZeroMemory(@context,sizeof(context));
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS;
|
|
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.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.Eax;
|
|
EBXv:=context.Ebx;
|
|
ECXv:=context.Ecx;
|
|
EDXv:=context.Edx;
|
|
ESIv:=context.Esi;
|
|
EDIv:=context.Edi;
|
|
EBPv:=context.Ebp;
|
|
ESPv:=context.Esp;
|
|
EIPv:=context.Eip;}
|
|
lastdebugcontext:=context;
|
|
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: dword;
|
|
|
|
{$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].description:=desc;
|
|
|
|
|
|
coderecords[length(coderecords)-1].eax:=context.Eax;
|
|
coderecords[length(coderecords)-1].ebx:=context.Ebx;
|
|
coderecords[length(coderecords)-1].ecx:=context.Ecx;
|
|
coderecords[length(coderecords)-1].edx:=context.Edx;
|
|
coderecords[length(coderecords)-1].esi:=context.Esi;
|
|
coderecords[length(coderecords)-1].edi:=context.Edi;
|
|
coderecords[length(coderecords)-1].ebp:=context.Ebp;
|
|
coderecords[length(coderecords)-1].esp:=context.Esp;
|
|
coderecords[length(coderecords)-1].eip:=context.Eip;
|
|
coderecords[length(coderecords)-1].context:=context;
|
|
|
|
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.Eax;
|
|
pdword(@output[9])^:=context.Ebx;
|
|
pdword(@output[13])^:=context.Ecx;
|
|
pdword(@output[17])^:=context.Edx;
|
|
pdword(@output[21])^:=context.Esi;
|
|
pdword(@output[25])^:=context.Edi;
|
|
pdword(@output[29])^:=context.Ebp;
|
|
pdword(@output[33])^:=context.Esp;
|
|
pdword(@output[37])^:=context.Eip;
|
|
|
|
|
|
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;
|
|
|
|
function TDebugger.isBreakpoint(address: dword): boolean;
|
|
var j: integer;
|
|
begin
|
|
result:=false;
|
|
for j:=0 to length(debuggerthread.userbreakpoints)-1 do
|
|
if address=userbreakpoints[j] then
|
|
begin
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
|
|
for j:=0 to length(debuggerthread.int3userbreakpoints)-1 do
|
|
if address=int3userbreakpoints[j].address then
|
|
begin
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TDebugger.UpdateRegisters;
|
|
var temp: string;
|
|
i: integer;
|
|
begin
|
|
{$ifndef netserver}
|
|
with memorybrowser do
|
|
begin
|
|
//enable debug mode
|
|
run1.Enabled:=true;
|
|
step1.Enabled:=true;
|
|
stepover1.Enabled:=true;
|
|
runtill1.Enabled:=true;
|
|
stacktrace1.Enabled:=true;
|
|
Executetillreturn1.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;
|
|
|
|
disassemblerview.SelectedAddress:=context.Eip;
|
|
|
|
temp:='EAX '+IntToHex(context.Eax,8);
|
|
if temp<>eaxlabel.Caption then
|
|
begin
|
|
eaxlabel.Font.Color:=clred;
|
|
eaxlabel.Caption:=temp;
|
|
end else eaxlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EBX '+IntToHex(context.Ebx,8);
|
|
if temp<>ebxlabel.Caption then
|
|
begin
|
|
ebxlabel.Font.Color:=clred;
|
|
ebxlabel.Caption:=temp;
|
|
end else ebxlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='ECX '+IntToHex(context.ECx,8);
|
|
if temp<>eCxlabel.Caption then
|
|
begin
|
|
eCXlabel.Font.Color:=clred;
|
|
eCXlabel.Caption:=temp;
|
|
end else eCXlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EDX '+IntToHex(context.EDx,8);
|
|
if temp<>eDxlabel.Caption then
|
|
begin
|
|
eDxlabel.Font.Color:=clred;
|
|
eDxlabel.Caption:=temp;
|
|
end else eDxlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='ESI '+IntToHex(context.ESI,8);
|
|
if temp<>eSIlabel.Caption then
|
|
begin
|
|
eSIlabel.Font.Color:=clred;
|
|
eSIlabel.Caption:=temp;
|
|
end else eSIlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EDI '+IntToHex(context.EDI,8);
|
|
if temp<>eDIlabel.Caption then
|
|
begin
|
|
eDIlabel.Font.Color:=clred;
|
|
eDIlabel.Caption:=temp;
|
|
end else eDIlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EBP '+IntToHex(context.EBP,8);
|
|
if temp<>eBPlabel.Caption then
|
|
begin
|
|
eBPlabel.Font.Color:=clred;
|
|
eBPlabel.Caption:=temp;
|
|
end else eBPlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='ESP '+IntToHex(context.ESP,8);
|
|
if temp<>eSPlabel.Caption then
|
|
begin
|
|
eSPlabel.Font.Color:=clred;
|
|
eSPlabel.Caption:=temp;
|
|
end else eSPlabel.Font.Color:=clWindowText;
|
|
|
|
temp:='EIP '+IntToHex(context.EIP,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;
|
|
|
|
|
|
lastdebugcontext:=context;
|
|
{
|
|
EAXv:=context.Eax;
|
|
EBXv:=context.Ebx;
|
|
ECXv:=context.Ecx;
|
|
EDXv:=context.Edx;
|
|
ESIv:=context.ESi;
|
|
EDIv:=context.Edi;
|
|
EBPv:=context.Ebp;
|
|
ESPv:=context.Esp;
|
|
EIPv:=context.Eip;
|
|
}
|
|
if frmFloatingpointpanel<>nil then
|
|
begin
|
|
frmFloatingpointpanel.SetContextPointer(@context);
|
|
frmFloatingpointpanel.UpdatedContext;
|
|
end;
|
|
|
|
|
|
disassemblerview.Update;
|
|
|
|
showDebugPanels:=true;
|
|
reloadStacktrace;
|
|
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
|
|
ZeroMemory(@context,sizeof(context));
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS;
|
|
getthreadcontext(threadlist[i,1],context);
|
|
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS; //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;
|
|
|
|
|
|
function TDebugger.WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL;
|
|
begin
|
|
if not terminated then
|
|
result:=newkernelhandler.WaitForDebugEvent(lpDebugEvent, dwMilliseconds)
|
|
else
|
|
result:=false;
|
|
end;
|
|
|
|
function TDebugger.continueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus: DWORD): BOOL;
|
|
var i: integer;
|
|
begin
|
|
if terminated then
|
|
begin
|
|
RemoveBreakpoint;
|
|
|
|
ZeroMemory(@context,sizeof(context));
|
|
context.ContextFlags:=CONTEXT_FULL;
|
|
getthreadcontext(pausedthreadhandle,context);
|
|
|
|
context.ContextFlags:=CONTEXT_FULL; //CONTEXT_CONTROL;
|
|
context.EFlags:=context.EFlags and (not $100);
|
|
setthreadcontext(pausedthreadhandle,context);
|
|
|
|
result:=newkernelhandler.ContinueDebugEvent(dwProcessID, dwThreadID, dwContinueStatus);
|
|
|
|
for i:=0 to length(processes)-1 do
|
|
if processes[i].running then DebugActiveProcessStop(processes[i].ProcessID); //this sometimes works
|
|
end
|
|
else
|
|
result:=newkernelhandler.ContinueDebugEvent(dwProcessID, dwThreadID, dwContinueStatus);
|
|
|
|
|
|
|
|
end;
|
|
|
|
procedure TDebugger.Execute;
|
|
var startupinfo:_STARTUPINFOA;
|
|
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:dword;
|
|
|
|
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;
|
|
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;
|
|
|
|
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.Eax,8)),pchar(IntTOHex(Context.Eax,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
|
|
|
|
if not DebugActiveProcess(Openprocessid) then
|
|
begin
|
|
attaching:=false;
|
|
|
|
{$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;
|
|
|
|
DebugSetProcessKillOnExit(true); //WHY DOESN'T IT WORK LIKE I WANT TO?
|
|
running:=true;
|
|
|
|
|
|
continuehow:=wdco_run;
|
|
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
|
|
|
|
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
|
|
ZeroMemory(@context,sizeof(context));
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS;
|
|
getthreadcontext(threadlist[i,1],context);
|
|
pausedthreadhandle:=threadlist[i,1];
|
|
break;
|
|
end;
|
|
|
|
|
|
|
|
if createaprocess and (context.eip=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.Eip:=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;
|
|
attaching:=false;
|
|
|
|
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.eip-1),@ab,1,a) then
|
|
begin
|
|
if ab=int3 then
|
|
begin
|
|
if (not createaprocess) or (creationHandled) then
|
|
begin
|
|
if (context.Eip-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;
|
|
attaching:=false;
|
|
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.Eip);
|
|
|
|
notinlist:=true;
|
|
crdebugging.enter;
|
|
try
|
|
|
|
for i:=0 to length(registermodificationBPs)-1 do
|
|
begin
|
|
if registermodificationBPs[i].address=context.eip then
|
|
begin
|
|
notinlist:=false;
|
|
|
|
//modify the context of this thread according to the data in registermodificationBPs[i]
|
|
if registermodificationBPs[i].change_eax then context.Eax:=registermodificationBPs[i].new_eax;
|
|
if registermodificationBPs[i].change_ebx then context.Ebx:=registermodificationBPs[i].new_ebx;
|
|
if registermodificationBPs[i].change_ecx then context.Ecx:=registermodificationBPs[i].new_ecx;
|
|
if registermodificationBPs[i].change_edx then context.Edx:=registermodificationBPs[i].new_edx;
|
|
if registermodificationBPs[i].change_esi then context.Esi:=registermodificationBPs[i].new_esi;
|
|
if registermodificationBPs[i].change_edi then context.Edi:=registermodificationBPs[i].new_edi;
|
|
if registermodificationBPs[i].change_ebp then context.Ebp:=registermodificationBPs[i].new_ebp;
|
|
if registermodificationBPs[i].change_esp then context.Esp:=registermodificationBPs[i].new_esp;
|
|
if registermodificationBPs[i].change_eip then context.Eip:=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:=wdco_run;
|
|
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;
|
|
WaitForDebugEvent(devent,10000);
|
|
|
|
while j<>devent.dwthreadid do
|
|
begin
|
|
debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
|
|
WaitForDebugEvent(devent,10000);
|
|
end;
|
|
|
|
//set the breakpoint back if needed
|
|
resetbreakpoint;
|
|
releasesemaphore(semaphore,1,nil);
|
|
|
|
if continuehow=wdco_stepinto then //it was a step, so
|
|
begin
|
|
continueprocess:=false;
|
|
continuehow:=wdco_run;
|
|
suspendallthreads;
|
|
|
|
for j:=0 to length(threadlist)-1 do
|
|
if threadlist[j,0]=devent.dwThreadId then
|
|
begin
|
|
ZeroMemory(@context,sizeof(context));
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS;
|
|
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=wdco_stepinto 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.Ecx=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.Eip) 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.eip then
|
|
begin
|
|
notinlist:=false;
|
|
|
|
//modify the context of this thread according to the data in registermodificationBPs[i]
|
|
if registermodificationBPs[i].change_eax then context.Eax:=registermodificationBPs[i].new_eax;
|
|
if registermodificationBPs[i].change_ebx then context.Ebx:=registermodificationBPs[i].new_ebx;
|
|
if registermodificationBPs[i].change_ecx then context.Ecx:=registermodificationBPs[i].new_ecx;
|
|
if registermodificationBPs[i].change_edx then context.Edx:=registermodificationBPs[i].new_edx;
|
|
if registermodificationBPs[i].change_esi then context.Esi:=registermodificationBPs[i].new_esi;
|
|
if registermodificationBPs[i].change_edi then context.Edi:=registermodificationBPs[i].new_edi;
|
|
if registermodificationBPs[i].change_ebp then context.Ebp:=registermodificationBPs[i].new_ebp;
|
|
if registermodificationBPs[i].change_esp then context.Esp:=registermodificationBPs[i].new_esp;
|
|
if registermodificationBPs[i].change_eip then context.Eip:=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:=wdco_run;
|
|
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
|
|
ZeroMemory(@context,sizeof(context));
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS;
|
|
getthreadcontext(threadlist[i,1],context);
|
|
pausedthreadhandle:=threadlist[i,1];
|
|
break;
|
|
end;
|
|
|
|
if ((canusedebugregs) and (drregs.Dr3=context.Eip)) or (continuehow=wdco_stepinto) then
|
|
begin
|
|
|
|
if (canusedebugregs) and (drregs.dr3=context.Eip) then
|
|
begin
|
|
drregs.Dr3:=0;
|
|
resetbreakpoint;
|
|
end;
|
|
|
|
suspendallthreads;
|
|
|
|
synchronize(updateregisters);
|
|
continueprocess:=false;
|
|
continuehow:=wdco_run;
|
|
running:=false;
|
|
while not continueprocess do;
|
|
running:=true;
|
|
|
|
resumeallthreads;
|
|
|
|
|
|
|
|
if continuehow=wdco_stepinto 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];
|
|
ZeroMemory(@context,sizeof(context));
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS;
|
|
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];
|
|
ZeroMemory(@context,sizeof(context));
|
|
context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS;
|
|
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.eip,8)+' esi='+IntToHex(context.esi,8)+' edi='+IntToHex(context.edi,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;
|
|
|
|
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;
|
|
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
except
|
|
messagebox(0,'Debugger crash','CE Debugger',mb_ok);
|
|
end;
|
|
|
|
// application.MessageBox(pchar('devent.dwDebugEventCode='+inttohex(devent.dwDebugEventCode,8)),'error');
|
|
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;
|
|
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.
|