unit olddebugger;

{$mode delphi}

interface

uses windows, Classes,LCLIntf,sysutils,CEFuncProc,Messages,forms,SyncObjs,
     dialogs,controls,Graphics,NewKernelHandler,symbolhandler,StrUtils,
     ComCtrls ,Assemblerunit,addressparser, debughelper;


type TReadonly = record
  pagebase: uint_ptr;
  pagesize: uint_ptr;
  Address: uint_ptr;
  size: integer;
  originalprotection:  dword;
end;

type tThreadEntry=record
  threadHandle: thandle;
  address: uint_ptr;
end;


type Process=record
  ProcessID: dword;
  running: boolean;
end;







type TDebugger = class(TThread)
  private
    originalbyte: byte;
    filename: String;
    parameters: string;
    CreateAProcess: boolean;
    OpenProcessID: Thandle;

    AddressFound: Dword;
    HideDebugger: boolean;
    HandleBreakpoints: boolean;

    CanUseDebugRegs: boolean;
    createdusingprocesswindow:boolean;

    CurrentProcess: integer;


    {$ifdef netserver}
    coderecords: array of dword;
    {$endif}

    procedure addtochangeslist;


    procedure RestoreNTOpenProcess;

    procedure ResetBreakpoint;
    procedure SetSingleStepping(Threadid: dword);
    procedure AddDebugString;
    function injectcode(AddressOfEntryPoint:dword;processhandle:thandle):dword;
    function handledebuggerplugins(devent:PDebugEvent):integer;

    procedure tracersync;
    function tracer(devent: _Debug_EVENT):boolean;

  public
    //semaphore: THandle;
    Processes: array of process;
    Newprocesses: array of TNewProcedureData;


    pausedthreadhandle: thandle;

    running: boolean;
    continueprocess: boolean;
    continuehow: integer;

    DRRegs: _CONTEXT;

    FindWriter2: boolean;


(*    attaching: boolean;*)
    attached: boolean;
    attachedevent: Tevent;

    context: _CONTEXT;
    debugging: boolean;
    stepping: boolean;
    HowToContinue: integer;
    removed: boolean;

    readonly: TReadonly;
    readonlyset: boolean;
    readonlyremoved: boolean;

    findreaderset: boolean;
    findreader: TReadOnly;
    findreaderremoved:boolean;

    alsowrites: boolean;

    breakpointset: boolean;
    breakpointaddress: dword;
    lastbreakpoint: dword;
    temps: string;

    whattodo: integer; //ignore, continue, showwindow etc....

    userisdebugging: boolean;
    userbreakpoints: array of dword;
    registermodificationBPs: array of tregistermodificationBP;

    int3userbreakpoints: array of tbreakpoint;
    int3CEBreakpoint: TBreakpoint;

    threadlist: array of array [0..4] of dword;
    threadinfo: array of dword;


    testje: string;

    breakpointlistsection: TCriticalSection;
    threadentrys: array of tThreadEntry;

    traceaddress: dword;
    tracecount: integer;
    Constructor MyCreate(filenm: String);
    Constructor MyCreate2(processID: THandle);
    destructor Destroy; override;
    procedure Execute; override;
    procedure Debuginfo;
    procedure waitforattach;

    procedure Updateregisters;
    procedure FoundOne;
    procedure RemoveBreakpoint;

    procedure suspendallthreads;
    procedure resumeallthreads;

    procedure ProcessCreated;

  end;


type TDbgUIDebugActiveProcess = function(processhandle:THandle):boolean; stdcall;
type TDebugBreakProcess = function(processhandle:THandle):boolean; stdcall;
type TDebugActiveProcessStop= function(pid: dword):boolean; stdcall;
type TDebugSetProcessKillOnExit=function(KillOnExit: boolean):boolean; stdcall;
type TIsDebuggerPresent=function:boolean; stdcall;
type TntSuspendProcess=function(ProcessID:Dword):DWORD; stdcall;
type TntResumeProcess=function(ProcessID:Dword):DWORD; stdcall;




type
TProcessBasicInformation = record
ExitStatus : Longint;
PebBaseAddress : Pointer;
AffinityMask : DWORD;
BasePriority : Longint;
UniqueProcessId : DWORD;
InheritedFromUniqueProcessId : DWORD
end;


  TProcessInfoClass=(
  ProcessBasicInformation,ProcessQuotaLimits,ProcessIoCounters,ProcessVmCounters,ProcessTimes,
  ProcessBasePriority,ProcessRaisePriority,ProcessDebugPort,ProcessExceptionPort,ProcessAccessToken,
  ProcessLdtInformation,ProcessLdtSize,ProcessDefaultHardErrorMode,ProcessIoPortHandlers,
  ProcessPooledUsageAndLimits,ProcessWorkingSetWatch,ProcessUserModeIOPL,ProcessEnableAlignmentFaultFixup,
  ProcessPriorityClass,ProcessWx86Information,ProcessHandleCount,ProcessAffinityMask,ProcessPriorityBoost,
  ProcessDeviceMap,ProcessSessionInformation,ProcessForegroundInformation,ProcessWow64Information,
  MaxProcessInfoClass);
type TNtQueryInformationProcess=function(
Handle : THandle;
infoClass : TProcessInfoClass;
processInformation : Pointer;
processInformationLength : ULONG;
returnLength : PULONG
) : DWORD; stdcall;

var //DebuggerThread: TDebugger;

    DbgUIDebugActiveProcess:TDbgUIDebugActiveProcess;
    DebugBreakProcess:TDebugBreakProcess;
    DebugActiveProcessStop:TDebugActiveProcessStop;
    DebugSetProcessKillOnExit:TDebugSetProcessKillOnExit;
    IsDebuggerPresent:TIsDebuggerPresent;
    ntSuspendProcess: TntSuspendProcess;
    ntResumeProcess: tntResumeProcess;

    NtQueryInformationProcess: TNtQueryInformationProcess;
    IsDebuggerPresentLocation:dword;
    DbgBreakPointLocation:dword;


    krn: thandle;
    ntdlllib: thandle;

    CRDebugging: TCriticalSection;


  function startdebuggerifneeded: boolean; overload;
  function startdebuggerifneeded(ask:boolean): boolean; overload;
  function breakthread(threadhandle: thandle):boolean;
  function DebugActiveProcessStopProstitute(x: dword): boolean;
  function ToggleBreakpoint(address:dword):boolean;

implementation

uses debugeventhandler, MainUnit,frmFloatingPointPanelUnit,Memorybrowserformunit,disassembler,frmTracerUnit,foundcodeunit,debugger2,advancedoptionsunit,formChangedAddresses,frmstacktraceunit,frmThreadlistunit,formdebugstringsunit,formsettingsunit,processwindowunit,plugin,frmCreatedProcessListUnit;

function ToggleBreakpoint(address:dword):boolean;

procedure setbreakpoints;
var i: integer;
begin
(*
  with debuggerthread do
  begin
    //set the debug breakpoint
    suspend;
    DRRegs.ContextFlags:=CONTEXT_DEBUG_REGISTERS;
    DRRegs.Dr7:=reg0set or reg1set or reg2set or reg3set;

    DRRegs.Dr0:=0;
    drregs.dr1:=0;
    drregs.dr2:=0;

    for i:=0 to length(userbreakpoints)-1 do
    begin
      if i=0 then DRRegs.Dr0:=userbreakpoints[0] else
      if i=1 then DRRegs.Dr1:=userbreakpoints[1] else
      if i=2 then DRRegs.Dr2:=userbreakpoints[2];
    end;

    for i:=0 to length(threadlist)-1 do
    begin
      suspendthread(debuggerthread.threadlist[i][1]);
      setthreadcontext(debuggerthread.threadlist[i][1],debuggerthread.DRRegs);
      resumethread(debuggerthread.threadlist[i][1]);
    end;

    Resume;
  end;
   *)
end;

var i,j,k: integer;
    a,original,written: dword;

    int3: byte;


begin

  result:=false;

  (*

  if foundcodedialog<>nil then
    raise exception.Create('I can''t do that! You are currently using one of the code finder options, please, stop it first');
    *)

  if debuggerthread2<>nil then
  begin
    for i:=0 to 3 do
    begin
      if debuggerthread2.breakpoints[i]=address then
      begin
        outputdebugstring('Going to disable this changereg breakpoint');

        if debuggerthread2.breakpointchanges[i].address<>0 then
        begin
          StopRegisterChange(i);
          debuggerthread2.breakpointchanges[i].address:=0;
        end;

        debuggerthread2.breakpoints[i]:=0;
        debuggerthread2.setbreakpoints;
        memorybrowser.updatebplist;

        result:=false;
        for j:=0 to 3 do
          if debuggerthread2.breakpoints[j]<>0 then
          begin
            result:=true;
            break;
          end;

        if not result then freeandnil(debuggerthread2);

        exit;
      end;
    end;
  end;


  if not startdebuggerifneeded then exit;
  int3:=$cc;


  //still here so the debugger has been attached to the process or i've fucked up again

  (*
  with debuggerthread do
  begin

    try
      WaitForSingleObject(semaphore,infinite);
      userisdebugging:=true;

      if formsettings.rbDebugAsBreakpoint.checked then
      begin
        //try to see if it already is in the list
        for i:=0 to length(userbreakpoints)-1 do
          if userbreakpoints[i]=address then
          begin
            //if it is then remove the breakpoint
            for j:=i to length(userbreakpoints)-2 do
              userbreakpoints[j]:=userbreakpoints[j+1];
            setlength(userbreakpoints,length(userbreakpoints)-1);

            //also remove the registermodificationBP
            for j:=0 to length(debuggerthread.registermodificationBPs)-1 do
              if registermodificationBPs[j].address=address then
              begin
                for k:=i to length(debuggerthread.registermodificationBPs)-2 do
                  registermodificationBPs[k]:=registermodificationBPs[k+1];

                setlength(registermodificationBPs,length(registermodificationBPs)-1);
                break;
              end;


            setbreakpoints;
            releasesemaphore(semaphore,1,nil);
            result:=false;
            exit;
          end;


        if length(userbreakpoints)=3 then
          raise exception.Create('The current implementation doesn''t support more than 3 breakpoints. (4 debug registers, but 1 is needed for stepping-over code)');

        setlength(userbreakpoints,length(userbreakpoints)+1);
        userbreakpoints[length(userbreakpoints)-1]:=address;

        setbreakpoints;
        result:=true;
      end
      else
      begin
        //int3 breakpoints
        //find it in the breakpointlist
        for i:=0 to length(int3userbreakpoints)-1 do
        begin
          if int3userbreakpoints[i].address=address then
          begin
            //remove it
            RewriteCode(processhandle,address,@int3userbreakpoints[i].originalbyte,1);

            for j:=i to length(int3userbreakpoints)-2 do
            begin
              int3userbreakpoints[j].address:=int3userbreakpoints[j+1].address;
              int3userbreakpoints[j].originalbyte:=int3userbreakpoints[j+1].originalbyte;
            end;

            setlength(int3userbreakpoints,length(int3userbreakpoints)-1);
            result:=false;
            exit;
          end;
        end;

        //no, it's not in the list, so ADD:
        setlength(int3userbreakpoints,length(int3userbreakpoints)+1);
        int3userbreakpoints[length(int3userbreakpoints)-1].address:=address;

        readprocessmemory(processhandle,pointer(address),@int3userbreakpoints[length(int3userbreakpoints)-1].originalbyte,1,a);
        if a=1 then
          RewriteCode(processhandle,address,@int3,1)
        else
          setlength(int3userbreakpoints,length(int3userbreakpoints)-1);

        result:=true;
      end;
    finally
      memorybrowser.updatebplist;
      releasesemaphore(semaphore,1,nil);
    end;
  end;
  *)

end;

procedure RemoveDebuggerDetection(processhandle: thandle);
var Newfunction: array [0..2] of byte;
    original,written: dword;
begin
  if IsDebuggerPresentLocation=0 then exit;
  {
    xor eax,eax
    ret
  }
  Newfunction[0]:=$31;
  NewFunction[1]:=$c0;
  NewFunction[2]:=$c3;

  RewriteCode(processhandle,IsDebuggerPresentLocation,@newfunction[0],3);
end;

function DebugBreakProstitute(x: Thandle):boolean;
begin
  result:=false;
end;

function DebugSetProcessKillOnExitProtitute(x: boolean):boolean;
begin
  result:=false;
end;

function DebugActiveProcessStopProstitute(x: dword): boolean;
begin
  result:=false;
end;

function startdebuggerifneeded(ask:boolean): boolean; overload;
var mes: string;
    reS:boolean;
    i: integer;
begin
  result:=false;
  if processhandle=0 then raise exception.create('You must first open a process');




  if debuggerthread2<>nil then
  begin
    if messagedlg('The kerneldebugger is currently active. Enabling the default windows debugger will cause the kernel debugger to terminate itself. Continue?',mtwarning,[mbyes,mbno],0)=mrno then exit;
    freeandnil(debuggerthread2);
  end;



  if (debuggerthread=nil) then
  begin
    if @DebugActiveProcessStop=@DebugActiveProcessStopProstitute then
      mes:='This will attach the debugger of Cheat Engine to the current process. If you close Cheat Engine while the game is running, the game will close too. Are you sure you want to do this?'
    else
      mes:='This will attach the debugger of Cheat Engine to the current process. Continue?';

    {$ifndef net}
    if ask then
      res:=Messagedlg(mes,mtConfirmation,[mbYes, mbNo],0)=mrYes
    else
      res:=true;

    if res then
    {$endif}
    begin
      {$ifndef net}
      if not advancedoptions.Pausebutton.Down then
        @ntsuspendprocess:=nil; //lets use the debugger for this
      {$endif}

      //start the debugger on the current process
      //check for a debugger
      try
        Debuggerthread:=TDebuggerThread.MyCreate2(processid);
      except
        raise exception.Create('I couldn''t attach the debugger to this process! You could try to open the process using the processpicker and try that! If that also doesn''t work check if you have debugging rights.');
      end;

      {$ifndef netserver}
      //Enable the debugger screen for the memorybrowser
      memorybrowser.splitter1.Visible:=true;
      memorybrowser.panel1.Visible:=true;

      memorybrowser.view1.Visible:=true;
      memorybrowser.Debug1.Visible:=true;
      memorybrowser.Splitter2.Visible:=true;
      memorybrowser.RegisterView.Visible:=true;

      Memorybrowser.UpdateRegisterview;
      {$endif}

      result:=true;
      exit;
    end
    {$ifndef net}
    else
    begin
      result:=false;
      exit;
    end;
    {$endif}
  end;
  result:=true;

end;

function startdebuggerifneeded: boolean;  overload;
begin
  result:=startdebuggerifneeded(true);
end;

function Breakthread(threadhandle:thandle):boolean;
var c: _context;
begin
  if GetSystemType<3 then raise exception.Create('You can''t break on this OS. Windows ME or later is required. (You CAN set breakpoints at key locations)');

  zeromemory(@c,sizeof(c));

  c.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT or CONTEXT_DEBUG_REGISTERS;

  debuggerthread.suspend;
  suspendthread(threadhandle);
  if not getthreadcontext(threadhandle,@c) then exit;

  (*
  debuggerthread.userisdebugging:=true;


  with debuggerthread do
  begin
    DRRegs.ContextFlags:=CONTEXT_DEBUG_REGISTERS;
    DRRegs.Dr7:=reg0set or reg1set or reg2set or reg3set;
    DRRegs.Dr0:=0;
    drregs.dr1:=0;
    drregs.dr2:=0;
    DRRegs.Dr3:=c.{$ifdef CPU64}Rip{$else}Eip{$endif};
    if setthreadcontext(threadhandle,drregs) then result:=true;
  end;
  *)


  resumethread(threadhandle);
  debuggerthread.Resume;
end;

procedure TDebugger.waitforattach;
var i: integer;
    wr: TWaitresult;
begin
  i:=0;
  while i<=90 do //90 do
  begin
    wr:=attachedevent.WaitFor(100);
    if wr=wrSignaled then break;
    if wr<>wrTimeout then exit;

    if i=100 then exit;
    inc(i);
  end;
  if not attached then terminate;
  WaitFor;
end;


Constructor TDebugger.MyCreate(filenm: String);
begin
(*
  debuggerthread:=self;
  *)
  attachedevent:=tevent.Create(nil, true,false,'');

  {$ifndef netserver}
  if debuggerthread2<>nil then raise exception.Create('Please stop the kernelmode debugging routines and breakpoints before starting this debugger');

  if formsettings.cbUndoMemoryChanges.checked then CheckForChanges; //place this line at important places

  if formsettings.cbBreakOnAttach.checked and (ProcessWindow<>nil) then    //created using the process list
    createdusingprocesswindow:=true;
  {$endif}

  userisdebugging:=false;
  {$ifndef netserver}
  handlebreakpoints:=formsettings.CheckBox1.Checked;
  hidedebugger:=formsettings.checkbox1.checked;
  canusedebugregs:=formsettings.rbDebugAsBreakpoint.checked;
  formdebugstrings.listbox1.clear;
  {$endif}

(*  attaching:=true;*)
  filename:=filenm;
  Inputquery(filename,'Parameters:',parameters);

  debugging:=true;
  ProcessHandler.ProcessHandle:=2;
  howtocontinue:=0;
  createAprocess:=true;

  inherited Create(false);   //I know, I know, these initalizes could also be done in the execute part.

  waitforattach;
end;

Constructor TDebugger.MyCreate2(processID: THandle);
var
  debugportoffset:dword;
  debugport:dword;
  ar:dword;
  peprocess: dword;
begin
(*  debuggerthread:=self;*)
  attachedevent:=tevent.Create(nil, true,false,'');

  {$ifndef netserver}
  if debuggerthread2<>nil then
  begin
    if messagedlg('The kerneldebugger is currently active. Enabling the default windows debugger will cause the kernel debugger to terminate itself. Continue?',mtwarning,[mbyes,mbno],0)=mrno then exit;
    freeandnil(debuggerthread2);
  end;

  if formsettings.cbUndoMemoryChanges.checked then CheckForChanges; //place this line at important places

  if formsettings.cbBreakOnAttach.checked and (ProcessWindow<>nil) then    //created using the process list
    createdusingprocesswindow:=true;
  {$endif}

  userisdebugging:=false;

  {$ifndef netserver}
  handlebreakpoints:=formsettings.cbHandleBreakpoints.checked;
  hidedebugger:=formsettings.checkbox1.checked;
  canusedebugregs:=formsettings.rbDebugAsBreakpoint.checked;
  formdebugstrings.listbox1.clear;
  {$endif}

(*  attaching:=true;*)
  debugging:=true;
  howtocontinue:=0;
  createAprocess:=false;
  OpenprocessID:=processid;

  ProcessHandler.ProcessHandle:=OpenProcess(process_all_access,false,processid);

  {$ifndef net}
  //check for a debugger

  if (getsystemtype>5) and (formsettings.cbKernelOpenProcess.Checked) then
  begin
    debugportoffset:=GetDebugportOffset;

    if debugportoffset<>0 then
    begin
      peprocess:=getpeprocess(processid);
      if peprocess<>0 then
      begin
        debugport:=0;
        if KernelReadProcessMemory(processhandle,pointer(peprocess+debugportoffset),@debugport,4,ar) then
        begin
          //readable
          if (debugport<>0) and
             (messagedlg('There is already a debugger attached to this process. Do you want to override it ? (Only works in winxp+)',mtconfirmation,[mbyes,mbno],0)=mryes) then
          begin
            debugport:=0;
            KernelWriteProcessMemory(processhandle,pointer(peprocess+debugportoffset),@debugport,4,ar);
            //debugger gone
          end;
        end;
      end;
    end; //else no way to check or fix it
  end;

  {$endif}

  inherited Create(false);   //I know, I know, these initalizes could also be done in the execute part.
  waitforattach;
end;

destructor TDebugger.Destroy;
begin
  //clear all breakpoints
  RemoveBreakpoint;
  attachedevent.free;

  inherited Destroy;
end;

procedure TDebugger.tracersync;
{$ifndef net}
var s: string;
    i: integer;
    d: TTraceDebugInfo;

{$endif}
begin
{$ifndef net}
  if frmtracer<>nil then
  begin
    s:=disassemble(context.{$ifdef CPU64}rip{$else}eip{$endif});
    i:=posex('-',s);
    i:=posex('-',s,i+1);
    s:=copy(s,i+2,length(s));
    s:=inttohex(context.{$ifdef CPU64}rip{$else}eip{$endif},8)+' - '+s;

    d:=TTraceDebugInfo.Create;
    d.c:=context;
    frmtracer.ListBox1.Items.AddObject(s,d);
  end;
{$endif}
end;

function TDebugger.tracer(devent: _Debug_EVENT):boolean;
var i,c: integer;
    b: boolean;
    h: thandle;
    hid: dword;
begin
  c:=0;
  b:=true;
  result:=false;

  hid:=devent.dwThreadId;
  for i:=0 to length(threadlist)-1 do
  begin
    if threadlist[i,0]=devent.dwThreadId then
    begin
      h:=threadlist[i,1];
      break;
    end;
  end;


  while b and (c<tracecount) do
  begin
    for i:=0 to length(threadlist)-1 do
    begin
      if threadlist[i,0]=devent.dwThreadId then
      begin
        context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
        getthreadcontext(threadlist[i,1],@context);
        break;
      end;
    end;

    if devent.dwThreadId=hid then
      synchronize(tracersync);

    inc(c);
    if c<tracecount then
    begin
      if devent.dwThreadId=hid then
        SetSingleStepping(hid);

      b:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
      if b then b:=waitfordebugevent(devent,4000);
    end;
  end;

  if b then
    result:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
end;

procedure TDebugger.RestoreNTOpenProcess;
var NTOpenProcessPos: dword;
    MyNTOpenProcessPos: dword;
    x: dword;
    y: array [0..4] of byte;
    mykernel: thandle;
begin
  NTOpenProcessPos:=dword(getprocaddress(ntdlllib,'NtOpenProcess'));

  LoadDbk32;
  if GetLoadedState then
  begin
    MyNTOpenProcessPos:=dword(getprocaddress(DarkByteKernel,'NOP'));

    If MyNTOpenProcessPos<>0 then
    begin
      if VirtualProtect(pointer(NTOpenProcessPos-50),55,PAGE_EXECUTE_READWRITE,x) then
      begin
        y[0]:=$e9;
        pdword(@y[1])^:=MyNTOpenProcessPos-NTOpenProcessPos-5;

        try
          asm
            push edi
            push esi
            lea esi,y[0]
            mov edi,NTOpenProcessPos
            movsd
            movsb
            pop esi
            pop edi
          end;
        except

        end;
      end;
    end;
  end;
end;


procedure TDebugger.AddDebugString;
begin
  {$ifndef netserver}
  formdebugstrings.listbox1.Items.add(temps);
  {$endif}
end;

procedure TDebugger.addtochangeslist;
var i: integer;
    lbs: string;
    newitem: TListItem;
    x: pointer;
begin
  {$ifndef netserver}


  with memorybrowser do
  begin
    EAXv:=context.{$ifdef CPU64}Rax{$else}Eax{$endif};
    EBXv:=context.{$ifdef CPU64}Rbp{$else}Ebx{$endif};
    ECXv:=context.{$ifdef CPU64}Rcx{$else}Ecx{$endif};
    EDXv:=context.{$ifdef CPU64}Rdx{$else}Edx{$endif};
    ESIv:=context.{$ifdef CPU64}Rsi{$else}Esi{$endif};
    EDIv:=context.{$ifdef CPU64}Rdi{$else}Edi{$endif};
    EBPv:=context.{$ifdef CPU64}Rbp{$else}Ebp{$endif};
    ESPv:=context.{$ifdef CPU64}Rsp{$else}Esp{$endif};
    EIPv:=context.{$ifdef CPU64}Rip{$else}Eip{$endif};
  end;

  try
    lastbreakpoint:=getaddress(temps);
  except
    //
  end;


  lbs:=inttohex(lastbreakpoint,8);
  for i:=0 to frmchangedaddresses.Changedlist.Items.Count-1 do
    if frmchangedaddresses.Changedlist.Items[i].Caption=lbs then exit; //already in the list

  newitem:=frmchangedaddresses.Changedlist.Items.Add;
  getmem(x,sizeof(_CONTEXT));
  CopyMemory(x,@context,sizeof(_CONTEXT));
  newitem.Data:=x;
  newitem.Caption:=lbs;

  //enable the timer if needed, there's data to be handled
  if not frmchangedaddresses.Timer1.Enabled then
    frmchangedaddresses.Timer1.Enabled:=true;


  {$endif}
end;

procedure TDebugger.debuginfo;
begin

end;

procedure TDebugger.ProcessCreated;
begin
  {$ifndef netserver}

  if frmCreatedProcessList=nil then
  begin
    frmCreatedProcesslist:=tfrmcreatedprocesslist.create(nil);
    frmCreatedProcesslist.show;
  end;

  frmcreatedprocesslist.ListBox1.Items.Add(IntToHex(newprocesses[currentprocess].processid,8));

  {$endif}

end;

procedure TDebugger.FoundOne;
var desc,opcode: string;
    address: uint_ptr;

    {$ifdef netserver}
    i: integer;
    threadlist: tlist;
    output: array [0..41] of byte;
    {$endif}
begin
  {$ifndef netserver}

  (*
  if foundcodedialog<>nil then
  with foundcodedialog do
  begin
    address:=addressfound;
    opcode:=disassemble(address,desc);

    setlength(coderecords,length(coderecords)+1);
    coderecords[length(coderecords)-1].address:=addressfound;
    coderecords[length(coderecords)-1].size:=address-addressfound;
    coderecords[length(coderecords)-1].opcode:=opcode;
    coderecords[length(coderecords)-1].desciption:=desc;


    coderecords[length(coderecords)-1].eax:=context.{$ifdef CPU64}rax{$else}Eax{$endif};
    coderecords[length(coderecords)-1].ebx:=context.{$ifdef CPU64}rbx{$else}Ebx{$endif};
    coderecords[length(coderecords)-1].ecx:=context.{$ifdef CPU64}rcx{$else}Ecx{$endif};
    coderecords[length(coderecords)-1].edx:=context.{$ifdef CPU64}rdx{$else}Edx{$endif};
    coderecords[length(coderecords)-1].esi:=context.{$ifdef CPU64}rsi{$else}Esi{$endif};
    coderecords[length(coderecords)-1].edi:=context.{$ifdef CPU64}rdi{$else}Edi{$endif};
    coderecords[length(coderecords)-1].ebp:=context.{$ifdef CPU64}rbp{$else}Ebp{$endif};
    coderecords[length(coderecords)-1].esp:=context.{$ifdef CPU64}rsp{$else}Esp{$endif};
    coderecords[length(coderecords)-1].eip:=context.{$ifdef CPU64}rip{$else}Eip{$endif};
    Foundcodelist.Items.Add(opcode);
  end;
  {$else}
  //send the state information to the client(s)
  //FoundCode(Address: dword;eax:dword; ebx:dword; ecx:dword; edx:dword;esi:dword;edi:dword;ebp:dword;esp:dword;eip:dword)

  setlength(coderecords,length(coderecords)+1);
  coderecords[length(coderecords)-1]:=addressfound;
  output[0]:=SC_FoundCode;
  pdword(@output[1])^:=addressfound;
  pdword(@output[5])^:=context.{$ifdef CPU64}rax{$else}Eax{$endif};
  pdword(@output[9])^:=context.{$ifdef CPU64}rbx{$else}Ebx{$endif};
  pdword(@output[13])^:=context.{$ifdef CPU64}rcx{$else}Ecx{$endif};
  pdword(@output[17])^:=context.{$ifdef CPU64}rdx{$else}Edx{$endif};
  pdword(@output[21])^:=context.{$ifdef CPU64}rsi{$else}Esi{$endif};
  pdword(@output[25])^:=context.{$ifdef CPU64}rdi{$else}Edi{$endif};
  pdword(@output[29])^:=context.{$ifdef CPU64}rbp{$else}Ebp{$endif};
  pdword(@output[33])^:=context.{$ifdef CPU64}rsp{$else}Esp{$endif};
  pdword(@output[37])^:=context.{$ifdef CPU64}rip{$else}Eip{$endif};


  threadlist:=form1.idtcpserver1.Threads.LockList;
  try
    for i:=0 to threadlist.count-1 do
      TIdPeerThread(threadlist[i]).Connection.WriteBuffer(output[0],41);
  finally
    form1.idtcpserver1.Threads.UnlockList;
  end;
  *)

  {$endif}
end;

procedure tdebugger.suspendallthreads;
var i: integer;
begin
  for i:=0 to length(threadlist)-1 do
    suspendthread(threadlist[i,1]);
end;

procedure tdebugger.resumeallthreads;
var i: integer;
begin
  for i:=0 to length(threadlist)-1 do
    resumethread(threadlist[i,1]);
end;


procedure TDebugger.UpdateRegisters;
var temp: string;
    i: integer;
begin
 //removed for a while
 {$ifndef netserver}

  with memorybrowser do
  begin
    //enable debug mode
    run1.Enabled:=true;
    step1.Enabled:=true;
    stepover1.Enabled:=true;
    runtill1.Enabled:=true;
    stacktrace1.Enabled:=true;

    for i:=0 to length(threadlist)-1 do
    begin
      if threadlist[i,1]=pausedthreadhandle then
      begin
        caption:='Memory Viewer - Currently debugging thread '+inttohex(threadlist[i,0],8);
        break;
      end;
    end;

    if frmstacktrace<>nil then
    begin
(*      frmstacktrace.stacktrace(debuggerthread.pausedthreadhandle,debuggerthread.context);*)
    end;

    Disassembleraddress:=context.{$ifdef CPU64}rip{$else}eip{$endif};
    dselected:=context.{$ifdef CPU64}rip{$else}eip{$endif};

    temp:='EAX '+IntToHex(context.{$ifdef CPU64}rax{$else}eax{$endif},8);
    if temp<>eaxlabel.Caption then
    begin
      eaxlabel.Font.Color:=clred;
      eaxlabel.Caption:=temp;
    end else eaxlabel.Font.Color:=clWindowText;

    temp:='EBX '+IntToHex(context.{$ifdef CPU64}rbx{$else}ebx{$endif},8);
    if temp<>ebxlabel.Caption then
    begin
      ebxlabel.Font.Color:=clred;
      ebxlabel.Caption:=temp;
    end else ebxlabel.Font.Color:=clWindowText;

    temp:='ECX '+IntToHex(context.{$ifdef CPU64}rcx{$else}ecx{$endif},8);
    if temp<>eCxlabel.Caption then
    begin
      eCXlabel.Font.Color:=clred;
      eCXlabel.Caption:=temp;
    end else eCXlabel.Font.Color:=clWindowText;

    temp:='EDX '+IntToHex(context.{$ifdef CPU64}rdx{$else}edx{$endif},8);
    if temp<>eDxlabel.Caption then
    begin
      eDxlabel.Font.Color:=clred;
      eDxlabel.Caption:=temp;
    end else eDxlabel.Font.Color:=clWindowText;

    temp:='ESI '+IntToHex(context.{$ifdef CPU64}rsi{$else}esi{$endif},8);
    if temp<>eSIlabel.Caption then
    begin
      eSIlabel.Font.Color:=clred;
      eSIlabel.Caption:=temp;
    end else eSIlabel.Font.Color:=clWindowText;

    temp:='EDI '+IntToHex(context.{$ifdef CPU64}rdi{$else}edi{$endif},8);
    if temp<>eDIlabel.Caption then
    begin
      eDIlabel.Font.Color:=clred;
      eDIlabel.Caption:=temp;
    end else eDIlabel.Font.Color:=clWindowText;

    temp:='EBP '+IntToHex(context.{$ifdef CPU64}rbp{$else}ebp{$endif},8);
    if temp<>eBPlabel.Caption then
    begin
      eBPlabel.Font.Color:=clred;
      eBPlabel.Caption:=temp;
    end else eBPlabel.Font.Color:=clWindowText;

    temp:='ESP '+IntToHex(context.{$ifdef CPU64}rsp{$else}esp{$endif},8);
    if temp<>eSPlabel.Caption then
    begin
      eSPlabel.Font.Color:=clred;
      eSPlabel.Caption:=temp;
    end else eSPlabel.Font.Color:=clWindowText;

    temp:='EIP '+IntToHex(context.{$ifdef CPU64}rip{$else}eip{$endif},8);
    if temp<>eIPlabel.Caption then
    begin
      eIPlabel.Font.Color:=clred;
      eIPlabel.Caption:=temp;
    end else eIPlabel.Font.Color:=clWindowText;

    temp:='CS '+IntToHex(context.SEGCS,4);
    if temp<>CSlabel.Caption then
    begin
      CSlabel.Font.Color:=clred;
      CSlabel.Caption:=temp;
    end else CSlabel.Font.Color:=clWindowText;

    temp:='DS '+IntToHex(context.SEGDS,4);
    if temp<>DSlabel.Caption then
    begin
      DSlabel.Font.Color:=clred;
      DSlabel.Caption:=temp;
    end else DSLabel.Font.Color:=clWindowText;

    temp:='SS '+IntToHex(context.SEGSS,4);
    if temp<>SSlabel.Caption then
    begin
      SSlabel.Font.Color:=clred;
      SSlabel.Caption:=temp;
    end else SSlabel.Font.Color:=clWindowText;

    temp:='ES '+IntToHex(context.SEGES,4);
    if temp<>ESlabel.Caption then
    begin
      ESlabel.Font.Color:=clred;
      ESlabel.Caption:=temp;
    end else ESlabel.Font.Color:=clWindowText;

    temp:='FS '+IntToHex(context.SEGFS,4);
    if temp<>FSlabel.Caption then
    begin
      FSlabel.Font.Color:=clred;
      FSlabel.Caption:=temp;
    end else FSlabel.Font.Color:=clWindowText;

    temp:='GS '+IntToHex(context.SEGGS,4);
    if temp<>GSlabel.Caption then
    begin
      GSlabel.Font.Color:=clred;
      GSlabel.Caption:=temp;
    end else GSlabel.Font.Color:=clWindowText;

    temp:='CF '+IntToStr(GetBitOf(context.EFLAgs,0));
    if temp<>cflabel.Caption then
    begin
      CFlabel.Font.Color:=clred;
      CFlabel.caption:=temp;
    end else cflabel.Font.Color:=clWindowText;

    temp:='PF '+IntToStr(GetBitOf(context.EFlags,2));
    if temp<>Pflabel.Caption then
    begin
      Pflabel.Font.Color:=clred;
      Pflabel.caption:=temp;
    end else Pflabel.Font.Color:=clWindowText;

    temp:='AF '+IntToStr(GetBitOf(context.EFlags,4));
    if temp<>Aflabel.Caption then
    begin
      Aflabel.Font.Color:=clred;
      Aflabel.caption:=temp;
    end else Aflabel.Font.Color:=clWindowText;

    temp:='ZF '+IntToStr(GetBitOf(context.EFlags,6));
    if temp<>Zflabel.Caption then
    begin
      Zflabel.Font.Color:=clred;
      Zflabel.caption:=temp;
    end else Zflabel.Font.Color:=clWindowText;

    temp:='SF '+IntToStr(GetBitOf(context.EFlags,7));
    if temp<>Sflabel.Caption then
    begin
      Sflabel.Font.Color:=clred;
      Sflabel.caption:=temp;
    end else Sflabel.Font.Color:=clWindowText;

    temp:='DF '+IntToStr(GetBitOf(context.EFlags,10));
    if temp<>Dflabel.Caption then
    begin
      Dflabel.Font.Color:=clred;
      Dflabel.caption:=temp;
    end else Dflabel.Font.Color:=clWindowText;

    temp:='OF '+IntToStr(GetBitOf(context.EFlags,11));
    if temp<>Oflabel.Caption then
    begin
      Oflabel.Font.Color:=clred;
      Oflabel.caption:=temp;
    end else Oflabel.Font.Color:=clWindowText;


    EAXv:=context.{$ifdef CPU64}rax{$else}eax{$endif};
    EBXv:=context.{$ifdef CPU64}rbx{$else}ebx{$endif};
    ECXv:=context.{$ifdef CPU64}rcx{$else}ecx{$endif};
    EDXv:=context.{$ifdef CPU64}rdx{$else}edx{$endif};
    ESIv:=context.{$ifdef CPU64}rsi{$else}esi{$endif};
    EDIv:=context.{$ifdef CPU64}rdi{$else}edi{$endif};
    EBPv:=context.{$ifdef CPU64}rbp{$else}ebp{$endif};
    ESPv:=context.{$ifdef CPU64}rsp{$else}esp{$endif};
    EIPv:=context.{$ifdef CPU64}rip{$else}eip{$endif};

    if frmFloatingpointpanel<>nil then
    begin
      frmFloatingpointpanel.SetContextPointer(@context);
      frmFloatingpointpanel.UpdatedContext;
    end;


    updatedisassemblerview;
  end;

  {$endif}

end;

procedure TDebugger.SetSingleStepping(Threadid: dword);
var i: integer;
begin
  for i:=0 to length(threadlist)-1 do
    if threadlist[i,0]=ThreadId then
    begin
      context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
      getthreadcontext(threadlist[i,1],@context);

      context.ContextFlags:=CONTEXT_FULL; //CONTEXT_CONTROL;
      context.EFlags:=context.EFlags or $100;
      setthreadcontext(threadlist[i,1],@context);
      break;
    end;
end;

procedure TDebugger.ResetBreakpoint;
var zerobreak: _context;
    i: integer;
    int3: byte;
    original,a,written:dword;
begin
  if canusedebugregs then
  for i:=0 to length(threadlist)-1 do
  begin
    suspendthread(threadlist[i,1]);
    setthreadcontext(threadlist[i,1],@drregs);
    resumethread(threadlist[i,1]);
  end
  else
  begin
    int3:=$cc;
    for i:=0 to length(int3userbreakpoints)-1 do
      RewriteCode(processhandle,int3userbreakpoints[i].address,@int3,1);

    if int3CEBreakpoint.address>0 then
      RewriteCode(processhandle,int3CEBreakpoint.address,@int3,1);

  end;
end;

procedure TDebugger.RemoveBreakpoint;
var zerobreak: _context;
    i: integer;
    original,written,a:dword;
begin
(*
  if canusedebugregs then
  begin
    zeromemory(@zerobreak,sizeof(zerobreak));
    zerobreak.ContextFlags:=CONTEXT_DEBUG_REGISTERS;
    zerobreak.Dr7:=reg0set or reg1set or reg2set or reg3set;

    for i:=0 to length(threadlist)-1 do
    begin
      suspendthread(threadlist[i,1]);
      setthreadcontext(threadlist[i,1],@zerobreak);
      resumethread(threadlist[i,1]);
    end;
  end
  else
  begin
    //temporarily disable all breakpoints
    for i:=0 to length(int3userbreakpoints)-1 do
      RewriteCode(processhandle,int3userbreakpoints[i].address,@int3userbreakpoints[i].originalbyte,1);

    if int3CEBreakpoint.address>0 then
    begin
      RewriteCode(processhandle,int3CEbreakpoint.address,@int3cebreakpoint.originalbyte,1);
      int3CEBreakpoint.address:=0;
    end;
  end;
  *)
end;


function TDebugger.handledebuggerplugins(devent:PDebugEvent):integer;
begin
  {$ifndef net}
  //go through the list of debugger plugins
  result:=pluginhandler.handledebuggerplugins(devent);
  {$endif}
end;

//I always knew viruses are usefull
function TDebugger.injectcode(AddressOfEntryPoint:dword;processhandle:thandle):dword;
{$ifndef net}
var LoadLibraryPtr: pointer;
    GetProcAddressPtr: Pointer;

    injectedlocation: pointer;
    h: Thandle;

    inject: array [0..255] of byte;
    x:dword;

    outp:TAssemblerBytes;
    position,position2: dword;

    dllLocation: string;
    startaddresS: dword;
    IHWCIloc: dword;
{$endif}
begin
  {$ifndef net}
  h:=LoadLibrary('Kernel32.dll');
  if h=0 then raise exception.Create('No kernel32.dll loaded');

  try
    GetProcAddressPtr:=GetProcAddress(h,'GetProcAddress');
    if getprocaddressptr=nil then raise exception.Create('GetProcAddress not found');

    LoadLibraryPtr:=GetProcAddress(h,'LoadLibraryA');
    if LoadLibraryptr=nil then raise exception.Create('LoadLibraryA not found');

    injectedlocation:=VirtualAllocEx(processhandle,nil,4096,MEM_COMMIT,PAGE_EXECUTE_READWRITE);

    if injectedlocation=nil then raise exception.Create('Failed to allocate memory');

    dlllocation:=extractfilepath(application.exename)+'CEHOOK.DLL';

    position:=dword(injectedlocation);
    position2:=0;
    copymemory(@inject[0],pchar(dllLocation+#0),length(dllLocation)+1);
    inc(position,length(dllLocation)+1);
    inc(position2,length(dlllocation)+1);

    ihwciloc:=position;
    copymemory(@inject[position2],pchar('IHWCI'#0),6);
    inc(position,6);
    inc(position2,6);
    startaddress:=position;

    assemble('PUSHAD',position,outp);
    copymemory(@inject[position2],outp,length(outp));
    inc(position,length(outp));
    inc(position2,length(outp));

    //loadlibrary(cehook);
    assemble('PUSH '+IntToHex(dword(injectedlocation),8),position,outp);
    copymemory(@inject[position2],outp,length(outp));
    inc(position,length(outp));
    inc(position2,length(outp));

    assemble('CALL '+IntToHex(dword(LoadLibraryPtr),8),position,outp);
    copymemory(@inject[position2],outp,length(outp));
    inc(position,length(outp));
    inc(position2,length(outp));

    //getprocaddress
    assemble('PUSH '+IntToHex(ihwciloc,8),position,outp);
    copymemory(@inject[position2],outp,length(outp));
    inc(position,length(outp));
    inc(position2,length(outp));

    assemble('PUSH EAX',position,outp);
    copymemory(@inject[position2],outp,length(outp));
    inc(position,length(outp));
    inc(position2,length(outp));

    assemble('CALL '+IntToHex(dword(GetProcAddressPtr),8),position,outp);
    copymemory(@inject[position2],outp,length(outp));
    inc(position,length(outp));
    inc(position2,length(outp));

    assemble('CALL EAX',position,outp);
    copymemory(@inject[position2],outp,length(outp));
    inc(position,length(outp));
    inc(position2,length(outp));

    assemble('POPAD',position,outp);
    copymemory(@inject[position2],outp,length(outp));
    inc(position,length(outp));
    inc(position2,length(outp));


    //jump to original start address
    assemble('JMP '+IntToHex(AddressOfEntryPoint,8),position,outp);
    copymemory(@inject[position2],outp,length(outp));
    inc(position,length(outp));
    inc(position2,length(outp));


    //call the routine

    writeprocessmemory(processhandle,injectedlocation,@inject[0],position2,x);
    result:=startaddress;
    //showmessage('Injected code at: '+IntToHex(dword(injectedlocation),8)+' startaddress='+IntToHex(startaddress,8));
  finally
    FreeLibrary(h);
  end;


  {$endif}
end;


procedure TDebugger.Execute;
//todo: splitup in seperate sections
var startupinfo: TSTARTUPINFO;
    int3: byte;
    devent: _Debug_EVENT;
    processinfo:_Process_information;       //make an array of this
    i,j: integer;
    original: dword;
    tobefrozen: integer;  //-1 is none
    notinlist: boolean;
    tid,thnd: dword;

    //----
    temp: dword;
    desc,opcode: string;

    seperator: integer;
    fb: integer;
    nb: integer;
    address: string;
    offset:uint_ptr;

    errorlog: textfile;
    times: integer;
    debugpatch: integer;
    previousexception: dword;
    mbi : _MEMORY_BASIC_INFORMATION;

    hthr: thandle;

    asciistring: pansichar;
    unicodestring: pwidechar;
    bytesread: dword;
    err:string;

    found: boolean;

    a,b,c: dword;
    ab: byte;
    SelectorEntry:_LDT_ENTRY;

    oldcontext: _CONTEXT;

    exefile: File;
    dosheader: ^IMAGE_DOS_HEADER;
    Optionalheader: ^IMAGE_OPTIONAL_HEADER;
    ExeHeader: array [0..4095] of byte;
    AddressOfEntryPoint:dword;

    dotnet: boolean;
    firstbreak: boolean;
    creationhandleD: boolean;


begin

  AddressOfEntryPoint:=0;
  setlength(processes,0);
  creationhandleD:=false;

  zeromemory(@drregs,sizeof(drregs));
  drregs.ContextFlags:=CONTEXT_DEBUG_REGISTERS;

  previousexception:=0;
  times:=0;
  debugpatch:=0;

  int3:=$cc;
  breakpointset:=false;
(*  attached:=false;*)
  readonlyset:=false;

  ProcessHandler.ProcessHandle:=0;

  tobefrozen:=-1;
  //GetStartUpInfo(STARTUPINFO);
  FillMemory(@STARTUPINFO,sizeof(STARTUPINFO),0);
  STARTUPINFO.cb :=sizeof(STARTUPINFO);

  try

    if createaprocess then
    begin
      //create a process
      filemode:=0;
      assignfile(exefile,filename);
      try
        reset(exefile,1);
      except
(*        attaching:=false;*)
        attachedevent.SetEvent;
        err:='The file can''t be opened!';
        messagebox(0,pchar(err),'Create failure',mb_ok);
        exit;
      end;

      try
        temp:=0;
        blockread(exefile,exeheader[0],4096,temp);
        if temp>0 then
        begin
          dosheader:=@exeheader[0];
          optionalheader:=pointer(dword(dosheader)+dosheader._lfanew+24);

          AddressOfEntryPoint:=optionalheader.ImageBase+optionalheader.AddressOfEntryPoint;
        end;
      finally
        closefile(exefile);
      end;

      if not Createprocess(nil,pchar(filename+' '+parameters),nil,nil,false,(CREATE_SUSPENDED or DEBUG_PROCESS or NORMAL_PRIORITY_CLASS),nil,nil,Startupinfo,processinfo) then
      begin
//        attaching:=false;
        attachedevent.SetEvent;


        err:='Cheat Engine failed to create the process. (Error='+pchar(IntToStr(GetLastError))+')';
        messagebox(0,pchar(err),'Create failure',mb_ok);

        {$ifndef net}
        postmessage(mainform.Handle,WM_USER+1,0,0);
        {$endif}
        exit;
      end;

      readprocessmemory(processinfo.hProcess,pointer(addressofentrypoint),@originalbyte,1,temp);

   {   dotneT:=false;
      firstbreak:=true;
      if originalbyte=$ff then
      begin
        readprocessmemory(processinfo.hProcess,pointer(addressofentrypoint+1),@ab,1,temp);
        if ab=$25 then dotnet:=true;
      end;

      if not dotnet then writeprocessmemory(processinfo.hProcess,pointeR(AddressOfEntryPoint),@int3,1,temp);
         }
    //  messagebox(0,pchar(IntToHex(context.{$ifdef CPU64}rax{$else}eax{$endif},8)),pchar(IntTOHex(context.{$ifdef CPU64}rax{$else}eax{$endif},8)),MB_OK	);
      resumethread(processinfo.hThread);
    end
    else
    begin


  //    BOOL DebugActiveProcess(DWORD dwProcessId)=
      {
        if(DbgUiConnectToDbg())
        {
          HANDLE hProcess = ProcessIdToHandle(dwProcessId);

          if(hProcess)
          {
            DbgUiDebugActiveProcess(hProcess);
            NtClose(hProcess);
          }
  //      }
  //      return FALSE;
  //    }

      //attach to a running process
      //rewrite nt

      {$ifdef netserver}
      RestoreNTOpenProcess;
      {$else}
      if formsettings.cbKernelOpenProcess.checked then
        RestoreNTOpenProcess;
      {$endif}


      if not DebugActiveProcess(Openprocessid) then
      begin
        //attaching:=false;
        attachedevent.SetEvent;

        {$ifndef net}
        err:='Cheat Engine failed to attach to the process. (Error='+IntToStr(GetLastError)+')';
        messagebox(0,pchar(err),'Attach failure',mb_ok);
        postmessage(mainform.Handle,WM_USER+1,0,0);  //set debuggerthread to nil
        {$endif}
        exit;
      end;

    end;

    (*InitializeThreadHandlersList;*)
    DebugSetProcessKillOnExit(true);
    running:=true;




    while not terminated and debugging do
    begin

      if WaitForDebugEvent(devent,4000) then  //4 seconds wont cause a hog I hope
      begin
        {$ifndef net}
        if handledebuggerplugins(@devent)=1 then
          continue;
        {$endif}

        //working on a debug thing
        (*
        debugging:=HandleDebugEvent(devent);
        *)
        continue;

        (*
        currentprocess:=-1;
        for i:=0 to length(newprocesses)-1 do
        begin
          if newprocesses[i].processid=devent.dwProcessId then
          begin
            currentprocess:=i;
            break;
          end;
        end;

        if length(newprocesses)>0 then
          ProcessHandler.ProcessHandle:=newprocesses[length(newprocesses)-1].processhandle;

        case devent.dwDebugEventCode of
          EXCEPTION_DEBUG_EVENT:
          begin


            if newprocesses[currentprocess].firstbreak then
            begin
              if newprocesses[currentprocess].dotnet then
              begin
                //jump near  (I assume the next byte is a $25
                if readprocessmemory(newprocesses[currentprocess].processhandle,pointer(addressofentrypoint+2),@a,4,temp) then
                begin
                  readprocessmemory(newprocesses[currentprocess].processhandle,pointer(a),@b,4,temp);
                  newprocesses[currentprocess].EntryPoint:=b;

                  readprocessmemory(newprocesses[currentprocess].processhandle,pointer(b),@newprocesses[currentprocess].OriginalEntryByte,1,temp);
                  writeprocessmemory(newprocesses[currentprocess].processhandle,pointeR(b),@int3,1,temp);
                end;
              end;

              if hidedebugger then RemoveDebuggerDetection(newprocesses[currentprocess].processhandle);
                newprocesses[currentprocess].firstbreak:=false;
            end;

            //  if devent.Excdeeption.dwFirstChance=0 then MessageBox(0,'seconde time','second time',MB_OK);
            //this is called after the threadlist has been initialised

            for i:=0 to length(threadlist)-1 do
              if threadlist[i,0]=devent.dwThreadId then
              begin
                context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
                getthreadcontext(threadlist[i,1],@context);
                pausedthreadhandle:=threadlist[i,1];
                break;
              end;


            if createaprocess and (context.{$ifdef CPU64}rip{$else}eip{$endif}=newprocesses[currentprocess].EntryPoint+1) then
            begin
              creationHandled:=true;

              writeprocessmemory(newprocesses[currentprocess].processhandle,pointeR(newprocesses[currentprocess].EntryPoint),@newprocesses[currentprocess].OriginalEntryByte  ,1,temp);
              context.ContextFlags:=context_full;
              outputdebugstring('Injecting dll');
              context.{$ifdef CPU64}rip{$else}eip{$endif}:=injectcode(newprocesses[currentprocess].EntryPoint,newprocesses[currentprocess].processhandle)+1;
              SetThreadcontext(pausedthreadhandle,@context);


              if createdusingprocesswindow and (currentprocess=0) then
              begin
                //set a breakpoint at the AddressOfEntryPoint
                if not attached then
                begin
                  attached:=true;
                  attachedevent.SetEvent;


                  synchronize(ProcessCreated);
                end;

                debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                continue;
              end;
            end;



            if attached and {hidedebugger and} (not userisdebugging) then
              if readprocessmemory(newprocesses[currentprocess].processhandle,pointeR(context.{$ifdef CPU64}rip{$else}eip{$endif}-1),@ab,1,a) then
              begin
                if ab=int3 then
                begin
                  if (not createaprocess) or (creationHandled) then
                  begin
                    if (context.{$ifdef CPU64}rip{$else}eip{$endif}-1)<>DbgBreakPointLocation then
                    begin
                      if handlebreakpoints then
                        debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE)
                      else
                        debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);

                      outputdebugstring('err1');
                      continue;
                    end;
                  end;
                end;
              end;

            if not attached then
            begin
              attached:=true;
              attachedevent.SetEvent;
              synchronize(ProcessCreated);
            end;


            i:=0;

            //find out what exception it is
            if devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_BREAKPOINT then
            begin
              if userisdebugging then
              begin
                //check if this is a exception breakpoint that was made by cheat engine
                addressfound:=dword(devent.Exception.ExceptionRecord.ExceptionAddress);

                found:=(int3CEBreakpoint.address=addressfound);

                crdebugging.enter;
                try
                  for i:=0 to length(int3userbreakpoints)-1 do
                    if (int3userbreakpoints[i].address=addressfound) then found:=true;
                finally
                  crdebugging.Leave;
                end;

                if found or createdusingprocesswindow then
                begin
                  //we have a confirmation....
                  //set the byte back
                  if not createdusingprocesswindow then
                    removebreakpoint;

                  createdusingprocesswindow:=false;
                  if int3CEBreakpoint.address=addressfound then
                  begin
                    int3CEBreakpoint.address:=0;
                    int3CEBreakpoint.originalbyte:=0;
                  end;

                  //set eip back
                  dec(context.{$ifdef CPU64}rip{$else}eip{$endif});

                  notinlist:=true;
                  crdebugging.enter;


                      try

                        for i:=0 to length(registermodificationBPs)-1 do
                        begin
                          if registermodificationBPs[i].address=context.{$ifdef CPU64}rip{$else}eip{$endif} then
                          begin
                            notinlist:=false;

                            //modify the context of this thread according to the data in registermodificationBPs[i]
                            if registermodificationBPs[i].change_eax then context.{$ifdef CPU64}rax{$else}eax{$endif}:=registermodificationBPs[i].new_eax;
                            if registermodificationBPs[i].change_ebx then context.{$ifdef CPU64}rbx{$else}ebx{$endif}:=registermodificationBPs[i].new_ebx;
                            if registermodificationBPs[i].change_ecx then context.{$ifdef CPU64}rcx{$else}ecx{$endif}:=registermodificationBPs[i].new_ecx;
                            if registermodificationBPs[i].change_edx then context.{$ifdef CPU64}rdx{$else}edx{$endif}:=registermodificationBPs[i].new_edx;
                            if registermodificationBPs[i].change_esi then context.{$ifdef CPU64}rsi{$else}esi{$endif}:=registermodificationBPs[i].new_esi;
                            if registermodificationBPs[i].change_edi then context.{$ifdef CPU64}rdi{$else}edi{$endif}:=registermodificationBPs[i].new_edi;
                            if registermodificationBPs[i].change_ebp then context.{$ifdef CPU64}rbp{$else}ebp{$endif}:=registermodificationBPs[i].new_ebp;
                            if registermodificationBPs[i].change_esp then context.{$ifdef CPU64}rsp{$else}esp{$endif}:=registermodificationBPs[i].new_esp;
                            if registermodificationBPs[i].change_eip then context.{$ifdef CPU64}rip{$else}eip{$endif}:=registermodificationBPs[i].new_eip;

                            if registermodificationBPs[i].change_cf then
                              if registermodificationBPs[i].new_cf then
                                context.EFlags:=context.EFlags or $1 //enable the bit
                              else
                                context.EFlags:=context.EFlags and (not $1);

                            if registermodificationBPs[i].change_pf then
                              if registermodificationBPs[i].new_pf then
                                context.EFlags:=context.EFlags or $4 //enable the bit
                              else
                                context.EFlags:=context.EFlags and (not $4);

                            if registermodificationBPs[i].change_af then
                              if registermodificationBPs[i].new_af then
                                context.EFlags:=context.EFlags or $10 //enable the bit
                              else
                                context.EFlags:=context.EFlags and (not $10);

                            if registermodificationBPs[i].change_zf then
                              if registermodificationBPs[i].new_zf then
                                context.EFlags:=context.EFlags or $40 //enable the bit
                              else
                                context.EFlags:=context.EFlags and (not $40);

                            if registermodificationBPs[i].change_sf then
                              if registermodificationBPs[i].new_sf then
                                context.EFlags:=context.EFlags or $80 //enable the bit
                              else
                                context.EFlags:=context.EFlags and (not $80);

                            if registermodificationBPs[i].change_of then
                              if registermodificationBPs[i].new_of then
                                context.EFlags:=context.EFlags or $800 //enable the bit
                              else
                                context.EFlags:=context.EFlags and (not $800);

                            context.ContextFlags:=CONTEXT_FULL;
                            setthreadcontext(pausedthreadhandle,@context);

                            SetSingleStepping(devent.dwThreadId);
                            debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);

                            j:=devent.dwThreadId;
                            if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);

                            while j<>devent.dwthreadid do
                            begin
                              debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                              if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
                            end;
                            resetbreakpoint;

                            debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                            break;
                          end;
                        end; //registermodificiation loop

                      finally
                        crdebugging.Leave;
                      end;

                      if (traceaddress<>0) and (self.traceaddress=dword(devent.Exception.ExceptionRecord.ExceptionAddress)) then //do tracecount steps
                      begin
                        context.ContextFlags:=CONTEXT_FULL;
                        setthreadcontext(pausedthreadhandle,@context);

                        if tracecount>0 then
                        begin
                          debugging:=tracer(devent);
                          tracecount:=0;
                        end else debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                        resetbreakpoint;

                        continue;
                      end;

                      if not notinlist then continue; //exit, we've done what was needed


                      context.ContextFlags:=CONTEXT_FULL;
                      setthreadcontext(pausedthreadhandle,@context);


                      continueprocess:=false;
                      continuehow:=0;
                      suspendallthreads;

                      synchronize(updateregisters);

                      running:=false;
                      while not continueprocess do sleep(10);

                      WaitForSingleObject(semaphore,infinite);
                      running:=true; //meaning, keep your fucking hands of the registers

                      resumeallthreads;

                      //make a step
                      SetSingleStepping(devent.dwThreadId);
                      debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);

                      j:=devent.dwThreadId;
                      if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);

                      while j<>devent.dwthreadid do
                      begin
                        debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                        if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
                      end;

                      //set the breakpoint back if needed
                      resetbreakpoint;
                      releasesemaphore(semaphore,1,nil);

                      if continuehow=1 then //it was a step, so
                      begin
                        continueprocess:=false;
                        continuehow:=0;
                        suspendallthreads;

                        for j:=0 to length(threadlist)-1 do
                          if threadlist[j,0]=devent.dwThreadId then
                          begin
                            context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
                            getthreadcontext(threadlist[j,1],@context);
                            pausedthreadhandle:=threadlist[j,1];
                            break;
                          end;


                        synchronize(updateregisters);

                        running:=true; //meaning, keep your fucking hands of the registers
                        while not continueprocess do sleep(10);
                        running:=true; //meaning, keep your fucking hands of the registers

                        resumeallthreads;

                        //make a step
                        if continuehow=1 then SetSingleStepping(devent.dwThreadId);
                      end;
                    end;

                    debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                    continue;
                  end;


                  debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                  Outputdebugstring(pchar('Breakpoint:'+IntToHex(dword(devent.Exception.ExceptionRecord.ExceptionAddress),8)));
                  continue;
                end;

                //check if it is because the single stepping mode is enabled
                if devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_SINGLE_STEP then
                begin //it is in single step mode
                //--added
                  if (traceaddress<>0) and (dword(devent.Exception.ExceptionRecord.ExceptionAddress)=traceaddress) then
                  begin
                    RemoveBreakpoint;
                    if tracecount>0 then
                      debugging:=tracer(devent)
                    else
                      debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);

                    tracecount:=0;
                    ResetBreakpoint;
                    continue;
                  end;

                  if findwriter2 then
                  begin
                    addressfound:=dword(devent.Exception.ExceptionRecord.ExceptionAddress);

                    offset:=addressfound;
                    opcode:=disassemble(offset,desc);


                    if pos('REP',opcode)>0 then
                    begin
                      if context.{$ifdef CPU64}rcx{$else}ecx{$endif}=0 then addressfound:=previousopcode(addressfound);

                    end else
                    addressfound:=previousopcode(addressfound);

                    //check if the address is in the list
                    notinlist:=true;
                    try
                      {$ifndef net}

                      for i:=0 to length(foundcodedialog.coderecords)-1 do
                        if foundcodedialog.coderecords[i].address=addressfound then //if it is in the list then set notinlist to false and go out of the loop
                        begin
                          notinlist:=false;
                          break;
                        end;

                      {$else}
                      //check if it is in the list
                      for i:=0 to length(coderecords)-1 do
                        if coderecords[i]=addressfound then
                        begin
                          notinlist:=false;
                          break;
                        end;
                      {$endif}

                    except
                      //list got shortened or invalid
                    end;

                    if notinlist then synchronize(foundone); //add this memory address to the foundcode window.
                  end;

                  if findreaderset then
                  begin
                    WaitForSingleObject(semaphore,infinite);
                    if findreaderset then VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,PAGE_NOACCESS,original);
                    releasesemaphore(semaphore,1,nil);
                  end;

                  if breakpointset then
                  begin
                    if breakpointaddress=dword(devent.Exception.ExceptionRecord.ExceptionAddress) then
                    begin
                      //find out what the address points to
                      offset:=breakpointaddress;
                      opcode:=disassemble(offset,desc);

                      fb:=pos('[',opcode);
                      if fb>0 then
                      begin
                        nb:=pos(']',opcode);

                        if nb>fb then //just a simple check to verify the opcode is ok
                        begin
                          temps:=copy(opcode,fb+1,nb-fb-1);
                          //lastbreakpoint:=address;
                          synchronize(addtochangeslist);
                          //and add that address to a list

                          removebreakpoint;
                          SetSingleStepping(devent.dwThreadId);
                          debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);

                          if WaitForDebugEvent(devent,10000)=false then application.MessageBox('error2-3','error2-3',0);
                          while devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP do
                          begin
                            if devent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT then
                            begin
                              //I hate it when this happens
                              debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
                              outputdebugstring('err2');

                              break;
                            end;
                          end;

                          if breakpointset then resetbreakpoint;
                        end;
                      end;

                    end;

                  end;

                  if userisdebugging then
                  begin
                    //got a breakpoint
                    //set the breakpoint
                    if not canusedebugregs then
                      removebreakpoint;


                    if (canusedebugregs) and (drregs.dr3=context.{$ifdef CPU64}rip{$else}eip{$endif}) then
                    begin
                      drregs.Dr3:=0;
                      resetbreakpoint;
                    end;

                    //check if it is a regmodification bp
                    crdebugging.Enter;
                    try
                      notinlist:=true;
                      for i:=0 to length(registermodificationBPs)-1 do
                      begin
                        if registermodificationBPs[i].address=context.{$ifdef CPU64}rip{$else}eip{$endif} then
                        begin
                          notinlist:=false;

                          //modify the context of this thread according to the data in registermodificationBPs[i]
                          if registermodificationBPs[i].change_eax then context.{$ifdef CPU64}rax{$else}eax{$endif}:=registermodificationBPs[i].new_eax;
                          if registermodificationBPs[i].change_ebx then context.{$ifdef CPU64}rbx{$else}ebx{$endif}:=registermodificationBPs[i].new_ebx;
                          if registermodificationBPs[i].change_ecx then context.{$ifdef CPU64}rcx{$else}ecx{$endif}:=registermodificationBPs[i].new_ecx;
                          if registermodificationBPs[i].change_edx then context.{$ifdef CPU64}rdx{$else}edx{$endif}:=registermodificationBPs[i].new_edx;
                          if registermodificationBPs[i].change_esi then context.{$ifdef CPU64}rsi{$else}esi{$endif}:=registermodificationBPs[i].new_esi;
                          if registermodificationBPs[i].change_edi then context.{$ifdef CPU64}rdi{$else}edi{$endif}:=registermodificationBPs[i].new_edi;
                          if registermodificationBPs[i].change_ebp then context.{$ifdef CPU64}rbp{$else}ebp{$endif}:=registermodificationBPs[i].new_ebp;
                          if registermodificationBPs[i].change_esp then context.{$ifdef CPU64}rsp{$else}esp{$endif}:=registermodificationBPs[i].new_esp;
                          if registermodificationBPs[i].change_eip then context.{$ifdef CPU64}rip{$else}eip{$endif}:=registermodificationBPs[i].new_eip;

                          if registermodificationBPs[i].change_cf then
                            if registermodificationBPs[i].new_cf then
                              context.EFlags:=context.EFlags or $1 //enable the bit
                            else
                              context.EFlags:=context.EFlags and (not $1);

                          if registermodificationBPs[i].change_pf then
                            if registermodificationBPs[i].new_pf then
                              context.EFlags:=context.EFlags or $4 //enable the bit
                            else
                              context.EFlags:=context.EFlags and (not $4);

                          if registermodificationBPs[i].change_af then
                            if registermodificationBPs[i].new_af then
                              context.EFlags:=context.EFlags or $10 //enable the bit
                            else
                              context.EFlags:=context.EFlags and (not $10);

                          if registermodificationBPs[i].change_zf then
                            if registermodificationBPs[i].new_zf then
                              context.EFlags:=context.EFlags or $40 //enable the bit
                            else
                              context.EFlags:=context.EFlags and (not $40);

                          if registermodificationBPs[i].change_sf then
                            if registermodificationBPs[i].new_sf then
                              context.EFlags:=context.EFlags or $80 //enable the bit
                            else
                              context.EFlags:=context.EFlags and (not $80);

                          if registermodificationBPs[i].change_of then
                            if registermodificationBPs[i].new_of then
                              context.EFlags:=context.EFlags or $800 //enable the bit
                            else
                              context.EFlags:=context.EFlags and (not $800);

                          context.ContextFlags:=CONTEXT_FULL;
                          setthreadcontext(pausedthreadhandle,@context);

                          removebreakpoint;
                          SetSingleStepping(devent.dwThreadId);
                          debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);

                          j:=devent.dwThreadId;
                          if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);

                          while j<>devent.dwthreadid do
                          begin
                            debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                            if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);
                          end;
                          resetbreakpoint;

                          debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                          break;
                        end;
                      end;

                    finally
                      crdebugging.Leave;
                    end;

                    if not notinlist then continue;

                    //-----------------------------------


                    continueprocess:=false;
                    continuehow:=0;
                    suspendallthreads;

                    synchronize(updateregisters);

                    running:=false;
                    while not continueprocess do sleep(10);
                    running:=true; //meaning, keep your fucking hands of the registers

                    resumeallthreads;

                    removebreakpoint;
                    SetSingleStepping(devent.dwThreadId);
                    debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);

                    i:=devent.dwThreadId;
                    if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);

                    while i<>devent.dwthreadid do
                    begin
                      debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                      if not WaitForDebugEvent(devent,10000) then application.MessageBox('userbreakpoint bug','Cheat Engine Debugger',0);

                    end;

                    resetbreakpoint;

                    //get new context info
                    for i:=0 to length(threadlist)-1 do
                      if threadlist[i,0]=devent.dwThreadId then
                      begin
                       context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
                       getthreadcontext(threadlist[i,1],@context);
                       pausedthreadhandle:=threadlist[i,1];
                       break;
                      end;

                    if ((canusedebugregs) and (drregs.Dr3=context.{$ifdef CPU64}rip{$else}eip{$endif})) or (continuehow=1) then
                    begin

                      if (canusedebugregs) and (drregs.dr3=context.{$ifdef CPU64}rip{$else}eip{$endif}) then
                      begin
                        drregs.Dr3:=0;
                        resetbreakpoint;
                      end;

                      synchronize(updateregisters);
                      continueprocess:=false;
                      continuehow:=0;
                      running:=false;
                      while not continueprocess do;
                      running:=true;

                      if continuehow=1 then SetSingleStepping(devent.dwThreadId);
                    end;

                  end;

                  if not canusedebugregs then
                    resetbreakpoint;


                  //it's a single step exception (most likely made by me, so I'd better fix it)
                  debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                  continue;
                end;

                //if it was a access violation, and it was a WRITE access violation then
                if readonlyset and
                   (devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_ACCESS_VIOLATION) and
                   (devent.Exception.ExceptionRecord.ExceptionInformation[0]=1)
                   then
                begin
                  //now check if this address is in our locked array?
                  if (devent.Exception.ExceptionRecord.ExceptionInformation[1]>=readonly.pagebase) and
                     (devent.Exception.ExceptionRecord.ExceptionInformation[1]<=readonly.pagebase+readonly.pagesize) then
                  begin
                    //the address that created an exception is in a page I locked

                    //check if the exception is at the same spot as the entry in the read-only list
                    if (devent.Exception.ExceptionRecord.ExceptionInformation[1]>=readonly.Address-readonly.size+1) and
                       (devent.Exception.ExceptionRecord.ExceptionInformation[1]<=readonly.Address+readonly.size-1) then
                    begin
                      //it is!!!
                      addressfound:=dword(devent.Exception.ExceptionRecord.ExceptionAddress);

                      //check if the address is in the list
                      notinlist:=true;
                      //go through the list

                      try
                        {$ifndef net}

                        for i:=0 to length(foundcodedialog.coderecords)-1 do
                          if foundcodedialog.coderecords[i].address=addressfound then //if it is in the list then set notinlist to false and go out of the loop
                          begin
                            notinlist:=false;
                            break;
                          end;

                       {$else}
                       //check if it is in the list
                       for i:=0 to length(coderecords)-1 do
                         if coderecords[i]=addressfound then
                         begin
                           notinlist:=false;
                           break;
                         end;
                      {$endif}
                      except

                      end;


                      if notinlist then synchronize(foundone); //add this memory address to the foundcode window.

                    end;

                    //set the protection back to the original, in single stepping mode
                    VirtualProtectEx(processhandle,pointer(readonly.Address),readonly.size,readonly.originalprotection,original);

                    //set the process to single stepping
                    //find the threadhandle

                    SetSingleStepping(devent.dwThreadId);

                    debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);

                    if WaitForDebugEvent(devent,30000) then
                    while debugging and readonlyset and (devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP) do
                    begin
                      inc(debugpatch);
                      //This SHOULD never come up...
                      if devent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT then   //in case of a thread that does something
                      begin
                        for i:=0 to length(threadlist)-1 do
                          if threadlist[i,0]=devent.dwThreadId then
                          begin
                            hthr:=threadlist[i,1];
                            context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
                            getthreadcontext(hthr,@context);

                            context.ContextFlags:=CONTEXT_FULL; //CONTEXT_CONTROL;
                            context.EFlags:=context.EFlags or $100;
                            setthreadcontext(hthr,@context);
                            break;
                          end;
                        debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
                        outputdebugstring('err3');

                        break;
                      end;

                      for i:=0 to length(threadlist)-1 do
                        if threadlist[i,0]=devent.dwThreadId then
                        begin
                          hthr:=threadlist[i,1];
                          context.ContextFlags:=CONTEXT_FULL or CONTEXT_FLOATING_POINT;
                          getthreadcontext(hthr,@context);

                          context.ContextFlags:=CONTEXT_FULL; //CONTEXT_CONTROL;
                          context.EFlags:=context.EFlags or $100;
                          setthreadcontext(hthr,@context);
                          break;
                        end;

                      debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                      if WaitForDebugEvent(devent,30000)=false then application.MessageBox('Write Exception Error. (2)','Debugger error',0)
                    end; {while}

                    //set the protection back to read-only if the readonly flag is still needed
                    WaitForSingleObject(semaphore,infinite);
                    if readonlyset then VirtualProtectEx(processhandle,pointer(readonly.Address),readonly.size,PAGE_EXECUTE_READ,original);
                    releasesemaphore(semaphore,1,nil);

                    if devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP then continue;

                    //debugloghelp
                    inc(times);
                    previousexception:=devent.Exception.ExceptionRecord.ExceptionInformation[1];
                  end;

                  //release the semaphore so other threads that want to use the rea-only list can access this

                  //the access violation was created by me, so handle it myself, and not the app
                  debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                  continue;
                end; {access violation}


                //read access violations
                if findreaderset then
                begin
                  if (devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_ACCESS_VIOLATION) and
                     ((devent.Exception.ExceptionRecord.ExceptionInformation[0]=0) or (alsowrites))  //0=read
                     then //0=read
                  begin
                    //check if the exception is at the same spot as the entry in the read-only list
                    if ((devent.Exception.ExceptionRecord.ExceptionInformation[1]>=findreader.Address) and
                       (devent.Exception.ExceptionRecord.ExceptionInformation[1]<findreader.Address+findreader.size))
                       or
                       ((devent.Exception.ExceptionRecord.ExceptionInformation[1]<=findreader.Address) and
                       (devent.Exception.ExceptionRecord.ExceptionInformation[1]>findreader.Address-findreader.size))
                    then
                    begin
                      //it is!!!
                      addressfound:=dword(devent.Exception.ExceptionRecord.ExceptionAddress);

                      //check if the address is in the list
                      notinlist:=true;
                      //go through the list

                      try
                        {$ifndef net}

                        for i:=0 to length(foundcodedialog.coderecords)-1 do
                          if foundcodedialog.coderecords[i].address=addressfound then //if it is in the list then set notinlist to false and go out of the loop
                          begin
                            notinlist:=false;
                            break;
                          end;

                       {$else}
                       //check if it is in the list
                       for i:=0 to length(coderecords)-1 do
                         if coderecords[i]=addressfound then
                         begin
                           notinlist:=false;
                           break;
                         end;
                      {$endif}
                      except

                      end;


                      if notinlist then synchronize(foundone); //add this memory address to the foundcode window.

                    end;

                    //set the protection back to the original, in single stepping mode
                    VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,findreader.originalprotection,original);

                    //set the process to single stepping
                    SetSingleStepping(devent.dwThreadId);
                    debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);

                    if WaitForDebugEvent(devent,10000)=false then application.MessageBox('error1-3','error1-3',0);
                    while devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP do
                    begin
                      if devent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT then
                      begin
                        //I hate it when this happens
                        debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
                        outputdebugstring('err4');

                        break;
                      end;

                      debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                      if WaitForDebugEvent(devent,30000)=false then application.MessageBox('error2','error2',0)
                    end;


                    //set the protection back to read-only if the readonly flag is still needed
                    WaitForSingleObject(semaphore,infinite);
                    if findreaderset then VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,original,original);
                    releasesemaphore(semaphore,1,nil);

                    if devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP then continue;

                    debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                    continue;
                  end; {read access violation}

                  //check if it was a write access violation
                  if (devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_ACCESS_VIOLATION) and
                     (devent.Exception.ExceptionRecord.ExceptionInformation[0]=1) then //1=write
                  begin
                    VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,findreader.originalprotection,original);

                    //set the process to single stepping
                    SetSingleStepping(devent.dwThreadId);
                    debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);

                    if WaitForDebugEvent(devent,10000)=false then application.MessageBox('error1-1','error1-1',0);
                    while devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP do
                    begin
                      if devent.dwDebugEventCode=EXCEPTION_DEBUG_EVENT then
                      begin
                        debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
                        outputdebugstring('err5');
                        continue;
                      end;

                      debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                      if WaitForDebugEvent(devent,30000)=false then application.MessageBox('error2','error2',0)
                    end;

                    //set the protection back to no access if the readonly flag is still needed
                    WaitForSingleObject(semaphore,infinite);
                    if findreaderset then VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,original,original);
                    releasesemaphore(semaphore,1,nil);

                    if devent.Exception.ExceptionRecord.ExceptionCode<>EXCEPTION_SINGLE_STEP then continue;

                  end;

                  debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                  continue;

                end {if findreaderset}
                else
                begin
                  if (devent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_ACCESS_VIOLATION) then
                  begin
                    if findreader.Address<>0 then
                    begin
                      //check if the exception is at the same spot as the entry in the read-only list
                      if ((devent.Exception.ExceptionRecord.ExceptionInformation[1]>=(findreader.Address div $1000) * $1000) and
                         (devent.Exception.ExceptionRecord.ExceptionInformation[1]<((findreader.Address+$1000 div $1000) * $1000)+findreader.size)) then
                      begin
                        //perhaps a leftover or not, lets handle it anyhow
                        VirtualProtectEx(processhandle,pointer(findreader.Address),findreader.size,findreader.originalprotection,original);
                        debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                        continue;
                      end;
                    end;
                  end;

                end;


                //not captured

                if findreaderremoved or readonlyremoved then
                begin
                  findreaderremoved:=false;
                  readonlyremoved:=false;
                  debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
                end
                else
                begin
                  debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
                  outputdebugstring(pchar('err6:'+IntToStr(currentprocess)+ '/'+IntToStr(length(newprocesses)-1)+ ' eip='+IntToHex(context.{$ifdef CPU64}rip{$else}eip{$endif},8)+' esi='+IntToHex(context.{$ifdef CPU64}rsi{$else}esi{$endif},8)+' edi='+IntToHex(context.{$ifdef CPU64}rdi{$else}edi{$endif},8) ));
                end;


                continue;
              end;


            CREATE_THREAD_DEBUG_EVENT:
            begin

              //seems this is a way of unhooking the debugger
              ProcessHandler.ProcessHandle:=newprocesses[currentprocess].processhandle;

              setlength(threadlist,length(threadlist)+1);
              threadlist[length(threadlist)-1,0]:=devent.dwThreadId;
              threadlist[length(threadlist)-1,1]:=devent.CreateThread.hThread;

              threadlist[length(threadlist)-1,2]:=dword(devent.CreateThread.lpStartAddress);
              threadlist[length(threadlist)-1,3]:=dword(devent.CreateThread.lpThreadLocalBase);


              if canusedebugregs and (findwriter2 or breakpointset or userisdebugging) then  //set the debug registers here too
                setthreadcontext(devent.CreateThread.hThread,@drregs);

              {$ifndef net}
              if frmthreadlist<>nil then synchronize(frmthreadlist.fillthreadlist);
              {$endif}

              //let the application/OS handle this debug event
              debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
            end;

            EXIT_THREAD_DEBUG_EVENT:
            begin
              //search the threadid in the threadid list
              tid:=devent.dwThreadId;
              for i:=0 to length(threadlist)-1 do
                if threadlist[i,0]=tid then
                begin
                  for j:=i to length(threadlist)-2 do threadlist[j]:=threadlist[j+1];
                  setlength(threadlist,length(threadlist)-1);
                  break;
                end;

              debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);
            end;

            CREATE_PROCESS_DEBUG_EVENT:
            begin
              currentprocess:=length(newprocesses);
              setlength(newprocesses,length(newprocesses)+1);
              newprocesses[currentprocess].processid:=devent.dwProcessId;
              newprocesses[currentprocess].processhandle:=devent.CreateProcessInfo.hProcess;
              newprocesses[currentprocess].filehandle:=devent.CreateProcessInfo.hFile;
              newprocesses[currentprocess].EntryPoint:=dword(devent.CreateProcessInfo.lpStartAddress);
              newprocesses[currentprocess].FirstBreak:=true;

              attached:=true;
              attachedevent.SetEvent;

              if attached then synchronize(ProcessCreated);

              readprocessmemory(newprocesses[currentprocess].processhandle,pointer(newprocesses[currentprocess].EntryPoint),@newprocesses[currentprocess].OriginalEntryByte,1,temp);

              newprocesses[currentprocess].dotnet:=false;
              if newprocesses[currentprocess].OriginalEntryByte=$ff then
              begin
                readprocessmemory(newprocesses[currentprocess].processhandle,pointer(newprocesses[currentprocess].EntryPoint),@ab,1,temp);
                if ab=$25 then newprocesses[currentprocess].dotnet:=true;
              end;

              if not newprocesses[currentprocess].dotnet then if not writeprocessmemory(newprocesses[currentprocess].processhandle,pointeR(newprocesses[currentprocess].EntryPoint),@int3,1,temp) then outputdebugstring('failed to set break');




              setlength(threadlist,length(threadlist)+1);
              threadlist[length(threadlist)-1,0]:=devent.dwThreadId;
              threadlist[length(threadlist)-1,1]:=devent.CreateProcessInfo.hThread;

              setlength(Processes,length(processes)+1);
              processes[length(processes)-1].ProcessID:=devent.dwProcessId;
              processes[length(processes)-1].running:=true;

              if processhandle=0 then
              begin
                ProcessHandler.ProcessHandle:=devent.CreateProcessInfo.hProcess;
                ProcessHandler.processid:=devent.dwProcessId;
                processinfo.hProcess:=processhandle;
                processinfo.hThread:=devent.CreateProcessInfo.hThread;
                if devent.CreateProcessInfo.hProcess<>processhandle then BEEP;
              end;
              debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE);
              {$ifndef net}
              memorybrowser.thhandle:=devent.CreateProcessInfo.hThread;
              {$endif}

              symhandler.reinitialize;
            end;


            EXIT_PROCESS_DEBUG_EVENT:
            begin
              //a process stopped
              //find the process in the process list
              for i:=0 to length(processes)-1 do
              begin
                if devent.dwProcessId=Processes[i].ProcessID then
                begin
                  processes[i].running:=false;
                  break;
                end;
              end;

              for i:=0 to length(newprocesses)-1 do
                if devent.dwProcessId=newprocesses[i].processid then
                begin
                  for j:=i to length(newprocesses)-2 do
                    newprocesses[i]:=newprocesses[i+1];
                  break;
                end;

              debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);

              //check if there are processes left
              debugging:=false;
              for i:=0 to length(processes)-1 do
                if processes[i].running then debugging:=true;
            end;

            OUTPUT_DEBUG_STRING_EVENT:
            begin
              if devent.DebugString.fUnicode=0 then
              begin
                //8 bit ascii
                try
                  getmem(asciistring,devent.DebugString.nDebugStringLength+1);
                  try
                    ReadProcessMemory(newprocesses[currentprocess].processhandle,devent.DebugString.lpDebugStringData,asciistring,devent.DebugString.nDebugStringLength,bytesread);
                    asciistring[devent.DebugString.nDebugStringLength]:=#0;
                    temps:=asciistring;
                  finally
                    freemem(asciistring);
                  end;
                except
                  ;
                end;
              end
              else
              begin
                //16 bit unicode
                try
                  getmem(unicodestring,devent.DebugString.nDebugStringLength+1);
                  try
                    ReadProcessMemory(newprocesses[currentprocess].processhandle,devent.DebugString.lpDebugStringData,unicodestring,devent.DebugString.nDebugStringLength,bytesread);
                    unicodestring[devent.DebugString.nDebugStringLength div 2]:=#0;
                    temps:=unicodestring;
                  finally
                    freemem(unicodestring);
                  end;
                except

                end;
              end;
              synchronize(AddDebugString);

              if hidedebugger then //double bevcause of the way it is implementedin the os
                debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED)
              else
                debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_CONTINUE)

            end;

            else debugging:=ContinueDebugEvent(devent.dwProcessId,devent.dwThreadId,DBG_EXCEPTION_NOT_HANDLED);

        end; //case
            *)

      end;

    end;

  except
    messagebox(0,'Debugger crash','CE Debugger',mb_ok);
  end;

  OutputDebugString('End of debuggerthread');


  setlength(userbreakpoints,0);
  setlength(int3userbreakpoints,0);


  for i:=0 to length(processes)-1 do
    if processes[i].running then DebugActiveProcessStop(processes[i].ProcessID); //this sometimes works

  attached:=false;
  attachedevent.SetEvent;

  setlength(threadlist,0);
  {$ifndef net}
  postmessage(mainform.Handle,WM_USER+1,0,0);
  {$endif}
end;

initialization
  CRDebugging:=TCriticalSection.Create;

  krn := LoadLibrary('Kernel32.dll');
  if krn <> 0 then
  begin

    @DebugBreakProcess := GetProcAddress(krn, 'DebugBreakProcess');
    @DebugSetProcessKillOnExit :=GetProcAddress(krn,'DebugSetProcessKillOnExit');
    @DebugActiveProcessStop :=GetProcAddress(krn,'DebugActiveProcessStop');
    @IsDebuggerPresent:=GetProcAddress(krn,'IsDebuggerPresent');
    IsDebuggerPresentLocation:=DworD(GetProcAddress(krn,'IsDebuggerPresent'));

    if @DebugBreakProcess=nil then
      @DebugBreakProcess:=@DebugBreakProstitute;

    if @DebugSetProcessKillOnExit=nil then
      @DebugSetProcessKillOnExit:=@DebugSetProcessKillOnExitProtitute;

    if @DebugActiveProcessStop=nil then
      @DebugActiveProcessStop:=@DebugActiveProcessStopProstitute;

  end;

  ntdlllib:=LoadLibrary('ntdll.dll');
  if ntdlllib<>0 then
  begin
    DbgUIDebugActiveProcess:=getprocaddress(ntdlllib,'DbgUiDebugActiveProcess');
    DbgBreakPointLocation:=dword(getprocaddress(ntdlllib,'DbgBreakPoint'));

    NtQueryInformationProcess:=nil;
    NtQueryInformationProcess:=GetProcAddress(ntdlllib,'NtQueryInformationProcess');

    ntsuspendprocess:=nil;
    ntsuspendprocess:=GetProcAddress(ntdlllib,'NtSuspendProcess');
    ntresumeprocess:=GetProcAddress(ntdlllib,'NtResumeProcess');
    freelibrary(ntdlllib);
  end;


finalization
  if CRDebugging<>nil then
    CRDebugging.Free;

end.

