cheat-engine/Debugger.pas
cheatengine@gmail.com 4d297adfd2
2011-07-04 19:52:19 +00:00

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.