root/Cheat Engine/KernelDebugger.pas @ 313

Revision 313, 38.0 kB (checked in by dark_byte, 7 months ago)

Release candidate 1 for 5.6

Line 
1unit KernelDebugger;
2
3interface
4
5uses windows,sysutils,SyncObjs, dialogs,classes,debugger,disassembler,newkernelhandler,foundcodeunit,
6     tlhelp32,ComCtrls,addressparser, graphics, cefuncproc;
7
8type
9  TContinueOption = (co_run, co_stepinto, co_stepover, co_runtill);
10  TKDebugger=class;
11  TKDebuggerThread=class(TThread)
12  private
13    addressfound: dword;
14    currentdebuggerstate: TDebuggerstate; //when a debug event has occured this will be set to the current state
15    threadlistCS: TCriticalSection;
16    threadlist: array of dword;
17
18    owner: TKDebugger;
19    stepping: boolean; //is true if the previous event was a break or a single step and the user hasn't stopped single stepping yet
20
21    tempaddressspecifier: string;
22
23    continueEvent: Tevent;
24    continueOption: TcontinueOption;
25    runtilladdress: dword;
26
27    procedure ConvertDebuggerStateToContext(debuggerstate: TDebuggerstate; var context: _CONTEXT);
28
29    //mainthread:
30    procedure foundone;
31    procedure AddToChangesList;
32    procedure UpdateGui;
33
34    //thread:
35    function HandleBreak:boolean;
36    function HandleChangeRegister(breakreason: integer): boolean;
37    function HandleFindCode: boolean;
38    function HandleFindWhatCodeAccesses: boolean;
39
40    function getDebugReason: integer;
41
42
43  public
44    active: boolean;
45    tempcontext: _CONTEXT;
46
47    procedure Continue(continueOption: TContinueOption; runtilladdress: dword=0);
48    procedure execute; override;
49    constructor create(owner: TKDebugger; suspended:boolean);
50//    destructor destroy;  override;
51  end;
52
53  TBreakOption = (bo_Break=0, bo_ChangeRegister=1, bo_FindCode=2, bo_FindWhatCodeAccesses=3);
54  TKDebugger=class
55  private
56    DebuggerThread: TKDebuggerThread;
57
58    breakpointCS: TCriticalSection;
59    breakpoint: array [0..3] of record
60      active: boolean;
61      Address: DWORD;
62      BreakType: TBreakType;
63      BreakLength: TBreakLength;
64      BreakOption: TBreakOption;
65      ChangeRegisterData: TRegistermodificationBP;
66      BreakOnce: boolean; //if set it gets deleted when broken on once
67      ThreadID: dword; //threadid determines wwhich thread it should break on.
68    end;
69
70    generaldebugregistercontext: TContext;
71    fGlobalDebug: boolean;
72    procedure setGlobalDebug(x: boolean);
73    function breaklengthToByteLength(breakLength: TBreakLength): integer;
74    function getNumberOfBreakpoints: integer;
75  public
76    procedure AddThread(ThreadID: Dword);
77    procedure ApplyDebugRegistersForThread(threadhandle: DWORD);
78    procedure ApplyDebugRegisters;
79    procedure StartDebugger;
80    procedure StopDebugger;
81    procedure SetBreakpoint(address: dword; BreakType: TBreakType; BreakLength: integer; BreakOption: TBreakOption=bo_break; ChangeReg: PRegistermodificationBP=nil; threadid: dword=0; breakOnce: boolean=false); overload;
82    procedure SetBreakpoint(address: dword; BreakType: TBreakType; BreakLength: TBreakLength; BreakOption: TBreakOption=bo_break; ChangeReg: PRegistermodificationBP=nil; threadid: dword=0; breakOnce: boolean=false); overload;
83
84    procedure DisableBreakpoint(bp: integer);
85    procedure DisableAllBreakpoints;
86
87
88    procedure GetContext(var context: _CONTEXT);
89    procedure Continue(continueOption: TContinueOption; runtilladdress: dword=0);
90
91    //address specific toggle bp
92    procedure ToggleBreakpoint(address: dword);
93
94    function isActive: boolean;
95
96    function isExecutableBreakpoint(a: dword): boolean;
97    property GlobalDebug: boolean read fGlobalDebug write setGlobalDebug;
98    constructor create;
99    property nrofbreakpoints: integer read getNumberOfBreakpoints;
100  end;
101
102var KDebugger: TKDebugger;
103
104implementation
105
106uses frmProcessWatcherUnit,formchangedaddresses,memorybrowserformunit, frmstacktraceunit;
107
108
109
110Procedure TKDebugger.StartDebugger;
111begin
112  if processid=0 then raise exception.Create('Please open a process first');
113
114  if not loaddbvmifneeded then raise exception.Create('You can''t currently use the kernel debugger');
115
116
117  if Debuggerthread=nil then
118    Debuggerthread:=TKDebuggerThread.create(self,false);
119end;
120
121Procedure TKDebugger.StopDebugger;
122begin
123  if (DebuggerThread<>nil) then
124  begin
125    Debuggerthread.Terminate;
126    Debuggerthread.WaitFor;
127    FreeAndNil(Debuggerthread);
128  end;
129end;
130
131function TKDebugger.breaklengthToByteLength(breakLength: TBreakLength): integer;
132begin
133  case breaklength of
134    bl_1byte: result:=1;
135    bl_2byte: result:=2;
136    bl_4byte: result:=4;
137    bl_8byte: result:=8;
138    else result:=1; //(error)
139  end;
140
141end;
142
143function TKDebugger.isExecutableBreakpoint(a: dword): boolean;
144var i: integer;
145begin
146  result:=false;
147  breakpointcs.enter;
148  try
149    for i:=0 to 3 do
150      if breakpoint[i].active then
151      begin
152        if (breakpoint[i].BreakType=bt_OnInstruction) and (breakpoint[i].address=a) then
153        begin
154          result:=true;
155          exit;
156        end;
157
158      end;
159
160  finally
161    breakpointcs.leave;
162  end;
163
164end;
165
166function TKDebugger.getNumberOfBreakpoints: integer;
167var i: integer;
168begin
169  result:=0;
170  breakpointcs.enter;
171  for i:=0 to 3 do
172    if breakpoint[i].active then
173      inc(result);
174
175  breakpointcs.leave;
176end;
177
178procedure TKDebugger.SetBreakpoint(address: dword; BreakType: TBreakType; BreakLength: integer; BreakOption: TBreakOption=bo_Break; ChangeReg: PRegistermodificationBP=nil; threadid: dword=0; breakOnce: boolean=false);
179//split up into seperate SetBreakpoint calls
180var atleastone: boolean;
181begin
182  OutputDebugString(format('SetBreakpoint integerlength (%d)',[breaklength]));
183  if not isactive then
184    StartDebugger;
185
186  atleastone:=false;
187  try
188    while (breaklength>0) do
189    begin
190      Outputdebugstring(format('address=%x breaklength=%d',[address,breaklength]));
191      if (breaklength=1) or (address mod 2 > 0) then
192      begin
193        atleastone:=true;
194        SetBreakpoint(address, BreakType, bl_1byte, BreakOption, ChangeReg, threadid, breakonce);
195        inc(address,1);
196        dec(BreakLength,1);
197      end
198      else
199      if (breaklength=2) or (address mod 4 > 0) then
200      begin
201        atleastone:=true;
202        SetBreakpoint(address, BreakType, bl_2byte, BreakOption, ChangeReg, threadid, breakonce);
203        inc(address,2);
204        dec(BreakLength,2);
205      end else
206      if (breaklength>=4) then
207      begin
208        atleastone:=true;
209        SetBreakpoint(address, BreakType, bl_4byte, BreakOption, ChangeReg, threadid, breakonce);
210        inc(address,4);
211        dec(breaklength,4);
212      end;
213    end;
214  except
215    on e:Exception do
216      if not atleastone then
217        raise e;
218  end;
219
220end;
221
222procedure TKDebugger.SetBreakpoint(address: dword; BreakType: TBreakType; BreakLength: TBreakLength; BreakOption: TBreakOption=bo_break; ChangeReg: PRegistermodificationBP=nil; threadid: dword=0; breakOnce: boolean=false);
223//only call this from the main thread
224var debugreg: integer;
225    i: integer;
226begin
227  OutputDebugString('SetBreakpoint predefinedlength for address:'+inttohex(address,8));
228
229  //find a debugreg spot not used yet
230  if not isactive then
231    StartDebugger;
232   
233  debugreg:=-1;
234
235 
236  breakpointCS.Enter;
237  for i:=0 to 3 do
238    if not breakpoint[i].active then
239    begin
240      breakpoint[i].Address:=address;
241      breakpoint[i].BreakType:=BreakType;
242      breakpoint[i].BreakLength:=BreakLength;
243      breakpoint[i].BreakOption:=breakoption;
244      if changereg<>nil then
245        breakpoint[i].ChangeRegisterData:=changereg^;
246
247      breakpoint[i].threadid:=threadid;
248      breakpoint[i].BreakOnce:=breakonce;
249      breakpoint[i].active:=true;
250     
251
252      outputdebugstring(format('using debug reg %d with BreakOption %d',[i,integer(breakoption)]));
253      debugreg:=i;
254      break;
255    end;
256  breakpointCS.Leave;
257
258
259  if debugreg=-1 then
260    raise exception.Create('Out of debug registers');
261
262  //apply the breakpoint
263  if fGlobalDebug then
264  begin
265    //don't set the debugregs manually, let the taskswitching do the work for us
266    //(for global debug the debugreg is just a recommendation, so don't watch for a dr6 result with this exit)\
267    OutputDebugString('fGlobalDebug=true, Setting breakpoint using global debug');
268    outputdebugstring('extra:');
269    OutputDebugString(format('DBKDebug_GD_SetBreakpoint(true, %d, %x, %d, %d)',[debugreg,address, integer(breaktype),integer(breaklength)]));
270    DBKDebug_GD_SetBreakpoint(true,debugreg,address,breaktype,breaklength);
271  end
272  else
273  begin
274    //manually set the breakpoints in the global debug register context
275    OutputDebugString('fGlobalDebug=false, Setting breakpoint manually');
276    OutputDebugString(pchar(format('debugreg=%d breaktype=%d breaklength=%d',[debugreg, integer(breaktype), integer(breaklength)])));
277
278    generaldebugregistercontext.Dr7:=generaldebugregistercontext.Dr7 or (1 shl 10); //just to make it look nicer set the flag that will always be 1
279
280    //unset the bits for the given debugreg
281    generaldebugregistercontext.Dr7:=generaldebugregistercontext.Dr7 and (not ((1 shl (debugreg*2)) or ($f shl 16+debugreg*2))); // disable local bp and unset the 4 bits for LEN and R/W
282
283    //set the specific bits
284    generaldebugregistercontext.Dr7:=generaldebugregistercontext.Dr7 or (integer(breaklength) shl (16+debugreg*2+2)) or (integer(breaktype) shl (16+debugreg*2));
285
286    //enable local breakpoint
287    generaldebugregistercontext.Dr7:=generaldebugregistercontext.Dr7 or (1 shl (debugreg*2));
288    OutputDebugString(pchar(format('Setting DR7 to %x',[generaldebugregistercontext.Dr7])));
289
290    case debugreg of
291      0: generaldebugregistercontext.Dr0:=address;
292      1: generaldebugregistercontext.Dr1:=address;
293      2: generaldebugregistercontext.Dr2:=address;
294      3: generaldebugregistercontext.Dr3:=address;
295    end;
296
297    //and apply
298    ApplyDebugRegisters;
299  end;
300end;
301
302procedure TKDebugger.ToggleBreakpoint(address: dword);
303var i: integer;
304    found :boolean;
305begin
306  outputdebugstring(format('ToggleBreakpoint(%x)',[address]));
307  found:=false;
308  breakpointcs.Enter;
309  for i:=0 to 3 do
310  begin
311    //only affect breakpoints of type instruction
312    if (breakpoint[i].active) and (breakpoint[i].Address=address) and (breakpoint[i].BreakType=bt_onInstruction) then
313    begin
314      outputdebugstring('Found breakpoint. Disabling it');
315      //found, so disable
316      DisableBreakpoint(i);
317      found:=true; //let's not exit just yet...
318    end;
319  end;
320
321  breakpointcs.Leave;
322
323  if not found then
324  begin
325    outputdebugstring(format('Not found. Calling SetBreakpoint(%x, bt_OnInstruction, bl_1byte, bo_Break)',[address]));
326    SetBreakpoint(address, bt_OnInstruction, bl_1byte, bo_Break);
327  end;
328end;
329
330procedure TKDebugger.DisableAllBreakpoints;
331var i: integer;
332begin
333  for i:=0 to 3 do
334    DisableBreakpoint(i);
335end;
336
337procedure TKDebugger.DisableBreakpoint(bp: integer);
338begin
339  if fGlobalDebug then
340  begin
341    DBKDebug_GD_SetBreakpoint(false, bp, 0, bt_OnInstruction, bl_1byte);
342  end
343  else
344  begin
345    generaldebugregistercontext.Dr7:=generaldebugregistercontext.Dr7 and (not ((1 shl bp) or ($f shl 16+bp*2)));
346
347    case bp of
348      0: generaldebugregistercontext.Dr0:=0;
349      1: generaldebugregistercontext.Dr1:=0;
350      2: generaldebugregistercontext.Dr2:=0;
351      3: generaldebugregistercontext.Dr3:=0;
352    end;
353    ApplyDebugRegisters;
354  end;
355  breakpoint[bp].active:=false;
356end;
357
358procedure TKDebugger.AddThread(ThreadID: Dword);
359var Threadhandle: thandle;
360begin
361  if not GlobalDebug then
362  begin
363    Debuggerthread.threadlistCS.Enter;
364    try
365      setlength(Debuggerthread.threadlist,length(Debuggerthread.threadlist)+1);
366      threadhandle:=Openthread(STANDARD_RIGHTS_REQUIRED or windows.synchronize or $3ff,true,ThreadID);
367      Debuggerthread.threadlist[length(Debuggerthread.threadlist)-1]:=threadhandle;
368      ApplyDebugRegistersForThread(threadhandle);
369    finally
370      Debuggerthread.threadlistCS.Leave;
371    end;
372  end;
373end;
374
375procedure TKDebugger.ApplyDebugRegistersForThread(threadhandle: DWORD);
376begin
377  OutputDebugString(format('Calling TKDebugger.ApplyDebugRegistersForThread with handle %x',[threadhandle]));
378  if not globaldebug then
379  begin
380    Debuggerthread.threadlistCS.Enter;
381    try
382      if debuggerthread.stepping then
383      begin
384        OutputDebugString('debuggerthread.stepping is TRUE');
385        debuggerthread.currentdebuggerstate.dr0:=generaldebugregistercontext.Dr0;
386        debuggerthread.currentdebuggerstate.dr1:=generaldebugregistercontext.Dr1;
387        debuggerthread.currentdebuggerstate.dr2:=generaldebugregistercontext.Dr2;
388        debuggerthread.currentdebuggerstate.dr3:=generaldebugregistercontext.Dr3;
389        debuggerthread.currentdebuggerstate.dr6:=generaldebugregistercontext.Dr6;
390        debuggerthread.currentdebuggerstate.dr7:=generaldebugregistercontext.Dr7;
391      end
392      else
393      begin
394        OutputDebugString('debuggerthread.stepping is FALSE');     
395        if not setthreadcontext(threadhandle, generaldebugregistercontext) then
396          OutputDebugString(format('Failed setting debug registers on thread %x with error %d',[threadhandle, GetLastError]));
397      end;
398    finally
399      Debuggerthread.threadlistCS.Leave;
400    end;
401  end;
402end;
403
404procedure TKDebugger.ApplyDebugRegisters;
405var i: integer;
406begin
407  if not globaldebug then
408  begin
409    Debuggerthread.threadlistCS.Enter;
410    try
411      for i:=0 to length(Debuggerthread.threadlist)-1 do
412        ApplyDebugRegistersForThread(Debuggerthread.threadlist[i]);
413    finally
414      Debuggerthread.threadlistCS.Leave;
415    end;
416  end;
417end;
418
419procedure TKDebugger.setGlobalDebug(x: boolean);
420begin
421  fGlobalDebug:=x;
422
423  if isActive then
424  begin
425    LoadDBK32;
426    if x then
427      OutputDebugString('setGlobalDebug(true)')
428    else
429      OutputDebugString('setGlobalDebug(false)');
430
431
432    if assigned(newkernelhandler.DBKDebug_SetGlobalDebugState) then
433      DBKDebug_SetGlobalDebugState(x);
434  end;
435end;
436
437function TKDebugger.isActive: boolean;
438begin
439  result:=DebuggerThread <> nil;
440end;
441
442procedure TKDebugger.GetContext(var context: _CONTEXT);
443begin
444  if DebuggerThread<>nil then
445    context:=DebuggerThread.tempcontext;
446end;
447
448procedure TKDebugger.Continue(continueOption: TContinueOption; runtilladdress: dword=0);
449begin
450  if debuggerthread<>nil then
451    debuggerthread.Continue(continueoption, runtilladdress);
452end;
453
454constructor TKDebugger.create;
455begin
456  breakpointCS:=TCriticalSection.Create;
457  generaldebugregistercontext.ContextFlags:=CONTEXT_DEBUG_REGISTERS;
458 
459end;
460
461//---------------------------------------
462
463constructor TKDebuggerThread.create(owner: TKDebugger; suspended:boolean);
464var ths: thandle;
465    tE: threadentry32;
466    i,j: integer;
467    found: boolean;
468    temp: thandle;
469begin
470  OutputDebugString('TKDebuggerThread.create');
471  continueEvent:=Tevent.Create(nil, false, false, '');
472
473  DBKDebug_StartDebugging(ProcessID);
474  active:=true;
475  self.owner:=owner;
476
477  threadlistCS:=TCriticalSection.Create;
478  inherited create(true);
479
480
481  if not owner.GlobalDebug then
482  begin
483
484    //try to find this process in the processwatch window.
485    found:=false;
486    for i:=0 to length(frmprocesswatcher.processes)-1 do
487    begin
488      if frmprocesswatcher.processes[i].processid=processid then
489      begin
490        //open the threads
491        for j:=0 to length(frmprocesswatcher.processes[i].threadlist)-1 do
492        begin
493
494          temp:=Openthread(STANDARD_RIGHTS_REQUIRED or windows.synchronize or $3ff,true,frmprocesswatcher.processes[i].threadlist[j].threadid);
495          if temp<>0 then
496          begin       
497            setlength(threadlist,length(threadlist)+1);
498            threadlist[length(threadlist)-1]:=temp;
499          end;
500        end;
501
502        found:=true;
503        break;
504
505      end;
506    end;
507
508    if not found then
509    begin
510      //if it wasn't found try to add it (and tell the user it's best to start the process after ce has started)
511      ths:=CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,processid);
512      try
513        if ths<>0 then
514        begin
515          te.dwSize:=sizeof(te);
516          if Thread32First(ths,te) then
517          begin
518            repeat
519              if te.th32OwnerProcessID=processid then
520              begin
521                setlength(threadlist,length(threadlist)+1);
522                threadlist[length(threadlist)-1]:=Openthread(STANDARD_RIGHTS_REQUIRED or windows.synchronize or $3ff,true,te.th32ThreadID);
523              end;
524            until not thread32Next(ths,te);
525          end;
526        end;
527      finally
528        closehandle(ths);
529      end;
530    end;
531  end;
532
533  if not suspended then resume;
534end;
535
536procedure TKDebuggerThread.ConvertDebuggerStateToContext(debuggerstate: TDebuggerstate; var context: _CONTEXT);
537begin
538  ZeroMemory(@context,sizeof(_CONTEXT));
539  context.Eax:=debuggerstate.Eax;
540  context.Ebx:=debuggerstate.Ebx;
541  context.Ecx:=debuggerstate.Ecx;
542  context.Edx:=debuggerstate.Edx;
543  context.Esi:=debuggerstate.Esi;
544  context.Edi:=debuggerstate.Esi;
545  context.Ebp:=debuggerstate.Ebp;
546  context.Esp:=debuggerstate.Esp;
547  context.Eip:=debuggerstate.Eip;
548  context.Dr0:=debuggerstate.dr0;
549  context.Dr1:=debuggerstate.dr1;
550  context.Dr2:=debuggerstate.dr2;
551  context.Dr3:=debuggerstate.dr3;
552  context.Dr6:=debuggerstate.dr6;
553  context.Dr7:=debuggerstate.dr7;
554end;
555
556procedure TKDebuggerThread.Continue(continueOption: TContinueOption; runtilladdress: dword=0);
557begin
558  self.continueOption:=continueOption;
559  self.runtilladdress:=runtilladdress;
560  continueEvent.SetEvent;
561end;
562
563procedure TKDebuggerThread.UpdateGui;
564var
565  temp: string;
566begin
567  with memorybrowser do
568  begin
569    //enable debug mode
570    run1.Enabled:=true;
571    step1.Enabled:=true;
572    stepover1.Enabled:=true;
573    runtill1.Enabled:=true;
574    stacktrace1.Enabled:=true;
575    Executetillreturn1.Enabled:=true;
576    if stepping then
577      caption:='Memory Viewer - Currently debugging thread'
578    else
579      caption:='Memory Viewer - Running...';
580
581    if frmstacktrace<>nil then
582    begin
583      ConvertDebuggerStateToContext(currentdebuggerstate, tempcontext);
584      frmstacktrace.stacktrace(currentdebuggerstate.threadid,tempcontext);
585    end;
586
587    disassemblerview.SelectedAddress:=currentdebuggerstate.Eip;
588
589    temp:='EAX '+IntToHex(currentdebuggerstate.Eax,8);
590    if temp<>eaxlabel.Caption then
591    begin
592      eaxlabel.Font.Color:=clred;
593      eaxlabel.Caption:=temp;
594    end else eaxlabel.Font.Color:=clWindowText;
595
596    temp:='EBX '+IntToHex(currentdebuggerstate.Ebx,8);
597    if temp<>ebxlabel.Caption then
598    begin
599      ebxlabel.Font.Color:=clred;
600      ebxlabel.Caption:=temp;
601    end else ebxlabel.Font.Color:=clWindowText;
602
603    temp:='ECX '+IntToHex(currentdebuggerstate.ECx,8);
604    if temp<>eCxlabel.Caption then
605    begin
606      eCXlabel.Font.Color:=clred;
607      eCXlabel.Caption:=temp;
608    end else eCXlabel.Font.Color:=clWindowText;
609
610    temp:='EDX '+IntToHex(currentdebuggerstate.EDx,8);
611    if temp<>eDxlabel.Caption then
612    begin
613      eDxlabel.Font.Color:=clred;
614      eDxlabel.Caption:=temp;
615    end else eDxlabel.Font.Color:=clWindowText;
616
617    temp:='ESI '+IntToHex(currentdebuggerstate.ESI,8);
618    if temp<>eSIlabel.Caption then
619    begin
620      eSIlabel.Font.Color:=clred;
621      eSIlabel.Caption:=temp;
622    end else eSIlabel.Font.Color:=clWindowText;
623
624    temp:='EDI '+IntToHex(currentdebuggerstate.EDI,8);
625    if temp<>eDIlabel.Caption then
626    begin
627      eDIlabel.Font.Color:=clred;
628      eDIlabel.Caption:=temp;
629    end else eDIlabel.Font.Color:=clWindowText;
630
631    temp:='EBP '+IntToHex(currentdebuggerstate.EBP,8);
632    if temp<>eBPlabel.Caption then
633    begin
634      eBPlabel.Font.Color:=clred;
635      eBPlabel.Caption:=temp;
636    end else eBPlabel.Font.Color:=clWindowText;
637
638    temp:='ESP '+IntToHex(currentdebuggerstate.ESP,8);
639    if temp<>eSPlabel.Caption then
640    begin
641      eSPlabel.Font.Color:=clred;
642      eSPlabel.Caption:=temp;
643    end else eSPlabel.Font.Color:=clWindowText;
644
645    temp:='EIP '+IntToHex(currentdebuggerstate.EIP,8);
646    if temp<>eIPlabel.Caption then
647    begin
648      eIPlabel.Font.Color:=clred;
649      eIPlabel.Caption:=temp;
650    end else eIPlabel.Font.Color:=clWindowText;
651
652    temp:='CS '+IntToHex(currentdebuggerstate.cs,4);
653    if temp<>CSlabel.Caption then
654    begin
655      CSlabel.Font.Color:=clred;
656      CSlabel.Caption:=temp;
657    end else CSlabel.Font.Color:=clWindowText;
658
659    temp:='DS '+IntToHex(currentdebuggerstate.ds,4);
660    if temp<>DSlabel.Caption then
661    begin
662      DSlabel.Font.Color:=clred;
663      DSlabel.Caption:=temp;
664    end else DSLabel.Font.Color:=clWindowText;
665
666    temp:='SS '+IntToHex(currentdebuggerstate.ss,4);
667    if temp<>SSlabel.Caption then
668    begin
669      SSlabel.Font.Color:=clred;
670      SSlabel.Caption:=temp;
671    end else SSlabel.Font.Color:=clWindowText;
672
673    temp:='ES '+IntToHex(currentdebuggerstate.es,4);
674    if temp<>ESlabel.Caption then
675    begin
676      ESlabel.Font.Color:=clred;
677      ESlabel.Caption:=temp;
678    end else ESlabel.Font.Color:=clWindowText;
679
680    temp:='FS '+IntToHex(currentdebuggerstate.fs,4);
681    if temp<>FSlabel.Caption then
682    begin
683      FSlabel.Font.Color:=clred;
684      FSlabel.Caption:=temp;
685    end else FSlabel.Font.Color:=clWindowText;
686
687    temp:='GS '+IntToHex(currentdebuggerstate.gs,4);
688    if temp<>GSlabel.Caption then
689    begin
690      GSlabel.Font.Color:=clred;
691      GSlabel.Caption:=temp;
692    end else GSlabel.Font.Color:=clWindowText;
693
694    temp:='CF '+IntToStr(GetBitOf(currentdebuggerstate.EFLAgs,0));
695    if temp<>cflabel.Caption then
696    begin
697      CFlabel.Font.Color:=clred;
698      CFlabel.caption:=temp;
699    end else cflabel.Font.Color:=clWindowText;
700
701    temp:='PF '+IntToStr(GetBitOf(currentdebuggerstate.EFlags,2));
702    if temp<>Pflabel.Caption then
703    begin
704      Pflabel.Font.Color:=clred;
705      Pflabel.caption:=temp;
706    end else Pflabel.Font.Color:=clWindowText;
707
708    temp:='AF '+IntToStr(GetBitOf(currentdebuggerstate.EFlags,4));
709    if temp<>Aflabel.Caption then
710    begin
711      Aflabel.Font.Color:=clred;
712      Aflabel.caption:=temp;
713    end else Aflabel.Font.Color:=clWindowText;
714
715    temp:='ZF '+IntToStr(GetBitOf(currentdebuggerstate.EFlags,6));
716    if temp<>Zflabel.Caption then
717    begin
718      Zflabel.Font.Color:=clred;
719      Zflabel.caption:=temp;
720    end else Zflabel.Font.Color:=clWindowText;
721
722    temp:='SF '+IntToStr(GetBitOf(currentdebuggerstate.EFlags,7));
723    if temp<>Sflabel.Caption then
724    begin
725      Sflabel.Font.Color:=clred;
726      Sflabel.caption:=temp;
727    end else Sflabel.Font.Color:=clWindowText;
728
729    temp:='DF '+IntToStr(GetBitOf(currentdebuggerstate.EFlags,10));
730    if temp<>Dflabel.Caption then
731    begin
732      Dflabel.Font.Color:=clred;
733      Dflabel.caption:=temp;
734    end else Dflabel.Font.Color:=clWindowText;
735
736    temp:='OF '+IntToStr(GetBitOf(currentdebuggerstate.EFlags,11));
737    if temp<>Oflabel.Caption then
738    begin
739      Oflabel.Font.Color:=clred;
740      Oflabel.caption:=temp;
741    end else Oflabel.Font.Color:=clWindowText;
742
743
744    ConvertDebuggerStateToContext(currentdebuggerstate, lastdebugcontext);
745 {
746    EAXv:=currentdebuggerstate.Eax;
747    EBXv:=currentdebuggerstate.Ebx;
748    ECXv:=currentdebuggerstate.Ecx;
749    EDXv:=currentdebuggerstate.Edx;
750    ESIv:=currentdebuggerstate.ESi;
751    EDIv:=currentdebuggerstate.Edi;
752    EBPv:=currentdebuggerstate.Ebp;
753    ESPv:=currentdebuggerstate.Esp;
754    EIPv:=currentdebuggerstate.Eip;  }
755
756    showDebugPanels:=true;
757    reloadStacktrace;
758  end;
759end;
760
761procedure TKDebuggerThread.AddToChangesList;
762var i: integer;
763    lbs: string;
764    newitem: TListItem;
765    x: PContext;
766    bpa: dword;
767begin
768{
769  with memorybrowser do
770  begin
771    EAXv:=currentdebuggerstate.Eax;
772    EBXv:=currentdebuggerstate.Ebx;
773    ECXv:=currentdebuggerstate.Ecx;
774    EDXv:=currentdebuggerstate.Edx;
775    ESIv:=currentdebuggerstate.Esi;
776    EDIv:=currentdebuggerstate.Edi;
777    EBPv:=currentdebuggerstate.Ebp;
778    ESPv:=currentdebuggerstate.Esp;
779    EIPv:=currentdebuggerstate.Eip;
780  end;   }
781
782  try
783    bpa:=getaddress(tempaddressspecifier);
784    lbs:=inttohex(bpa,8);
785    for i:=0 to frmchangedaddresses.Changedlist.Items.Count-1 do
786      if frmchangedaddresses.Changedlist.Items[i].Caption=lbs then exit;
787
788    newitem:=frmchangedaddresses.Changedlist.Items.Add;
789    getmem(x,sizeof(_CONTEXT));
790    ConvertDebuggerStateToContext(currentdebuggerstate, x^);
791    newitem.Data:=x;
792    newitem.Caption:=lbs;
793
794    //enable the timer if needed, there's data to be handled
795    if not frmchangedaddresses.Timer1.Enabled then
796      frmchangedaddresses.Timer1.Enabled:=true;
797  except
798    //
799  end;
800end;
801
802procedure TKDebuggerThread.foundone;
803var desc,opcode: string;
804    address: dword;
805begin
806  with foundcodedialog do
807  begin
808    address:=addressfound;
809    opcode:=disassemble(address,desc);
810
811    setlength(coderecords,length(coderecords)+1);
812    coderecords[length(coderecords)-1].address:=addressfound;
813    coderecords[length(coderecords)-1].size:=address-addressfound;
814    coderecords[length(coderecords)-1].opcode:=opcode;
815    coderecords[length(coderecords)-1].description:=desc;
816
817    coderecords[length(coderecords)-1].eax:=currentdebuggerstate.EAX;
818    coderecords[length(coderecords)-1].ebx:=currentdebuggerstate.EBX;
819    coderecords[length(coderecords)-1].ecx:=currentdebuggerstate.ECX;
820    coderecords[length(coderecords)-1].edx:=currentdebuggerstate.EDX;
821    coderecords[length(coderecords)-1].esi:=currentdebuggerstate.Esi;
822    coderecords[length(coderecords)-1].edi:=currentdebuggerstate.Edi;
823    coderecords[length(coderecords)-1].ebp:=currentdebuggerstate.Ebp;
824    coderecords[length(coderecords)-1].esp:=currentdebuggerstate.Esp;
825    coderecords[length(coderecords)-1].eip:=currentdebuggerstate.Eip;
826    coderecords[length(coderecords)-1].context.ContextFlags:=0;
827    Foundcodelist.Items.Add(opcode);
828  end;
829end;
830
831function TKDebuggerThread.getDebugReason: integer;
832//breakreason -2 = error
833//breakreason -1 = single step
834//breakreason x = used breakpoint
835var i,j: integer;
836    bsize: integer;
837    address: dword;
838begin
839
840  outputdebugstring('EIP='+inttohex(currentdebuggerstate.eip,8));
841  outputdebugstring('DR6='+inttohex(currentdebuggerstate.dr6,8));
842  outputdebugstring('DR7='+inttohex(currentdebuggerstate.dr7,8));
843   
844  result:=-2;
845  owner.breakpointCS.Enter;
846  try
847    for i:=0 to 3 do
848    begin
849      if getbit(i, currentdebuggerstate.dr6)=1 then
850      begin
851        //find which debug breakpoint it actually is, and that it didn't get overwritten
852        outputdebugstring(format('bit %d in DR6 is 1',[i]));
853
854        case i of
855          0: address:=currentdebuggerstate.dr0;
856          1: address:=currentdebuggerstate.dr1;
857          2: address:=currentdebuggerstate.dr2;
858          3: address:=currentdebuggerstate.dr3;
859        end;
860
861        for j:=0 to 3 do
862        begin
863          if owner.breakpoint[j].active then
864          begin
865            outputdebugstring(format('bp %d: is %x the same as %x ?',[j, owner.breakpoint[j].address, address]));
866
867
868            if owner.breakpoint[j].address=address then
869            begin
870              outputdebugstring('yes it is');
871              result:=j;
872              exit;
873            end else outputdebugstring('nope');
874          end
875          else
876            outputdebugstring(format('breakpoint %d is not active',[j]));
877        end;
878      end
879      else
880        outputdebugstring(format('bit %d in DR6 is 0',[i]));
881    end;
882  finally
883    owner.breakpointCS.Leave;
884  end;
885
886  if result=-2 then
887  begin
888    OutputDebugString('Result = -2, is it a single step?');
889    //single step then ?
890    if getbit(14,currentdebuggerstate.dr6)=1 then //check if single step bit is 1
891    begin
892      OutputDebugString('Yes, it is a single step');
893      //yes, it's a single step
894      //is the user single stepping ?
895      if Stepping then
896      begin
897        OutputDebugString('Single step while stepping is on. So break');
898        result:=-1;
899      end
900      else
901      begin
902        OutputDebugString('Single step while stepping is off, caused by program itself ?');
903        result:=-1; //remove this if you want to cause an exception
904      end;
905    end;
906  end;
907end;
908
909function TKDebuggerThread.HandleBreak:boolean;
910var wr: TWaitResult;
911    address: dword;
912begin
913  OutputDebugString('HandleBreak');
914
915  outputdebugstring(format('initial eflags=%x',[currentdebuggerstate.eflags]));
916
917  result:=true; //unless the single step isn't intended, but that check should have happened in the kernel
918  stepping:=true;
919
920  //update gui state
921  synchronize(updategui);
922
923  //sleep until the user sets the continue event
924  continueEvent.ResetEvent; //make sure it's unset so unwanted usercommands don't cause a continue
925  continueEvent.WaitFor(INFINITE);
926
927  case continueoption of
928    co_run:
929    begin
930      OutputDebugString('run');
931      currentdebuggerstate.eflags:=eflags_setRF(currentdebuggerstate.eflags,1); //skip current instruction bp
932      currentdebuggerstate.eflags:=eflags_setTF(currentdebuggerstate.eflags,0);
933      stepping:=false;
934    end;
935
936    co_stepinto:
937    begin
938      OutputDebugString('step into');
939      currentdebuggerstate.eflags:=eflags_setRF(currentdebuggerstate.eflags,1); //skip current instruction bp
940      currentdebuggerstate.eflags:=eflags_setTF(currentdebuggerstate.eflags,1); //trap to execute on next instruction
941      stepping:=true;
942    end;
943
944    co_stepover:
945    begin
946      OutputDebugString('step over. Stepping='+booltostr(stepping,true));
947      currentdebuggerstate.eflags:=eflags_setRF(currentdebuggerstate.eflags,1); //skip current instruction bp
948      currentdebuggerstate.eflags:=eflags_setTF(currentdebuggerstate.eflags,0);
949      //find next instruction address
950      address:=currentdebuggerstate.eip;
951      disassemble(address);
952
953      //set breakpoint here. (one time only bp for this specific threadid)
954      KDebugger.SetBreakpoint(address, bt_OnInstruction, 1, bo_Break, nil, currentdebuggerstate.threadid, true);
955      stepping:=false;
956    end;
957
958    co_runtill:
959    begin
960      OutputDebugString('run till');   
961      currentdebuggerstate.eflags:=eflags_setRF(currentdebuggerstate.eflags,1); //skip current instruction bp
962      currentdebuggerstate.eflags:=eflags_setTF(currentdebuggerstate.eflags,0);
963      OutputDebugString('Setting breakpoint to '+inttohex(runtilladdress,8));
964      KDebugger.SetBreakpoint(runtilladdress, bt_OnInstruction, 1, bo_Break, nil, currentdebuggerstate.threadid, true);
965      stepping:=false;
966    end;
967
968  end;
969
970  DBKDebug_SetDebuggerState(@currentdebuggerstate);
971
972  //continue debugged thread
973
974end;
975
976function TKDebuggerThread.HandleChangeRegister(breakreason: integer): boolean;
977begin
978  OutputDebugString('HandleChangeRegister');
979
980  owner.breakpointCS.Enter;
981  if owner.breakpoint[breakreason].ChangeRegisterData.change_eax then currentdebuggerstate.eax:=owner.breakpoint[breakreason].ChangeRegisterData.new_eax;
982  if owner.breakpoint[breakreason].ChangeRegisterData.change_ebx then currentdebuggerstate.ebx:=owner.breakpoint[breakreason].ChangeRegisterData.new_ebx;
983  if owner.breakpoint[breakreason].ChangeRegisterData.change_ecx then currentdebuggerstate.ecx:=owner.breakpoint[breakreason].ChangeRegisterData.new_ecx;
984  if owner.breakpoint[breakreason].ChangeRegisterData.change_edx then currentdebuggerstate.edx:=owner.breakpoint[breakreason].ChangeRegisterData.new_edx;
985  if owner.breakpoint[breakreason].ChangeRegisterData.change_esi then currentdebuggerstate.esi:=owner.breakpoint[breakreason].ChangeRegisterData.new_esi;
986  if owner.breakpoint[breakreason].ChangeRegisterData.change_edi then currentdebuggerstate.edi:=owner.breakpoint[breakreason].ChangeRegisterData.new_edi;
987  if owner.breakpoint[breakreason].ChangeRegisterData.change_ebp then currentdebuggerstate.ebp:=owner.breakpoint[breakreason].ChangeRegisterData.new_ebp;
988  if owner.breakpoint[breakreason].ChangeRegisterData.change_esp then currentdebuggerstate.esp:=owner.breakpoint[breakreason].ChangeRegisterData.new_esp;
989  if owner.breakpoint[breakreason].ChangeRegisterData.change_eip then currentdebuggerstate.eip:=owner.breakpoint[breakreason].ChangeRegisterData.new_eip;
990
991  if owner.breakpoint[breakreason].ChangeRegisterData.change_cf then currentdebuggerstate.eflags:=eflags_setCF(currentdebuggerstate.eflags, integer(owner.breakpoint[breakreason].ChangeRegisterData.new_cf));
992  if owner.breakpoint[breakreason].ChangeRegisterData.change_pf then currentdebuggerstate.eflags:=eflags_setPF(currentdebuggerstate.eflags, integer(owner.breakpoint[breakreason].ChangeRegisterData.new_pf));
993  if owner.breakpoint[breakreason].ChangeRegisterData.change_af then currentdebuggerstate.eflags:=eflags_setAF(currentdebuggerstate.eflags, integer(owner.breakpoint[breakreason].ChangeRegisterData.new_af));
994  if owner.breakpoint[breakreason].ChangeRegisterData.change_zf then currentdebuggerstate.eflags:=eflags_setZF(currentdebuggerstate.eflags, integer(owner.breakpoint[breakreason].ChangeRegisterData.new_zf));
995  if owner.breakpoint[breakreason].ChangeRegisterData.change_sf then currentdebuggerstate.eflags:=eflags_setSF(currentdebuggerstate.eflags, integer(owner.breakpoint[breakreason].ChangeRegisterData.new_sf));
996  if owner.breakpoint[breakreason].ChangeRegisterData.change_of then currentdebuggerstate.eflags:=eflags_setOF(currentdebuggerstate.eflags, integer(owner.breakpoint[breakreason].ChangeRegisterData.new_of));
997
998  owner.breakpointCS.Leave;
999
1000
1001  //set resume flag so the next time this instruction is executed it won't break (yes, eip could be changed, but let's do it anyhow)
1002  currentdebuggerstate.eflags:=eflags_setRF(currentdebuggerstate.eflags,1);
1003  DBKDebug_SetDebuggerState(@currentdebuggerstate);
1004  result:=true;
1005end;
1006
1007function TKDebuggerThread.HandleFindCode: boolean;
1008var
1009  i: integer;
1010  temp: dword;
1011  opcode,desc: string;
1012
1013begin
1014  //update gui with debugevent data
1015  //continue debugged thread
1016  OutputDebugString('HandleFindCode');
1017
1018  result:=true;
1019  i:=0;
1020  if (foundcodedialog<>nil) then
1021  begin
1022    addressfound:=currentdebuggerstate.eip;
1023    opcode:=disassemble(addressfound,desc);
1024
1025    if (pos('REP',opcode)=0) then
1026    begin
1027      addressfound:=previousopcode(currentdebuggerstate.eip);
1028    end
1029    else
1030    begin
1031      if (currentdebuggerstate.ecx=0) then
1032        addressfound:=previousopcode(currentdebuggerstate.eip)
1033      else
1034        addressfound:=currentdebuggerstate.eip;
1035    end;
1036
1037
1038    for i:=0 to length(foundcodedialog.coderecords)-1 do
1039      if (foundcodedialog.coderecords[i].address=addressfound) then exit; //already in the list, handled, and continue
1040
1041    //still here so not in the list
1042    synchronize(foundone);
1043
1044  end;
1045  //else no handler, let's try to continue...
1046
1047end;
1048
1049function TKDebuggerThread.HandleFindWhatCodeAccesses: boolean;
1050//find window
1051//evaluate code between brackets
1052//store address
1053//continue
1054var opcode,desc: string;
1055    offset: dword;
1056    fb,nb: integer;
1057begin
1058  offset:=currentdebuggerstate.eip;
1059  opcode:=disassemble(offset,desc);
1060
1061  fb:=pos('[',opcode);
1062  if fb>0 then
1063    nb:=pos(']',opcode);
1064
1065  if (fb>0) and (nb>0) then //instruction has brackets
1066  begin
1067    tempaddressspecifier:=copy(opcode,fb+1,nb-fb-1);
1068    synchronize(addtochangeslist);
1069  end;
1070
1071  //and continue
1072  currentdebuggerstate.eflags:=eflags_setRF(currentdebuggerstate.eflags,1);
1073  DBKDebug_SetDebuggerState(@currentdebuggerstate);
1074  result:=true;
1075end;
1076
1077procedure TKDebuggerThread.execute;
1078var
1079  breakreason: integer;
1080  breakoption: TBreakOption;
1081  handled: boolean;
1082begin
1083  active:=true;
1084  KDebugger.setGlobalDebug(KDebugger.GlobalDebug); //actually set it now
1085
1086  try
1087
1088    while not terminated do
1089    begin
1090      if DBKDebug_WaitForDebugEvent(1000) then
1091      begin
1092        OutputDebugString('KDebug event');
1093
1094        DBKDebug_GetDebuggerState(@currentdebuggerstate);
1095        breakreason:=GetDebugReason;
1096
1097        OutputDebugString(format('breakreason=%d',[breakreason]));
1098
1099        if breakreason>=-1 then
1100        begin
1101          //it has been determined this is a break caused by ce
1102
1103          breakoption:=bo_Break;
1104          if breakreason<>-1 then //no single step (if it is, bo_break)
1105          begin
1106            //breakpoint triggered
1107            //fetch bp data
1108            owner.breakpointcs.Enter;
1109            try
1110              breakoption:=owner.breakpoint[breakreason].BreakOption;
1111
1112              if (owner.breakpoint[breakreason].ThreadID=0) or (owner.breakpoint[breakreason].ThreadID=currentdebuggerstate.threadid) then
1113              begin
1114                //delete if it belongs to this thread and it's a one time only break
1115                if owner.breakpoint[breakreason].BreakOnce then
1116                begin
1117                  stepping:=true;
1118                  KDebugger.DisableBreakpoint(breakreason);
1119                end;
1120              end
1121              else
1122              begin
1123                //it's a breakpoint that's not designed for this thread, skip it, but don't tell windows it happened
1124                OutputDebugString('Thread specific breakpoint. Break didn''t happen in target thread. Skipping breakpoint');
1125                currentdebuggerstate.eflags:=eflags_setRF(currentdebuggerstate.eflags,1);
1126                DBKDebug_SetDebuggerState(@currentdebuggerstate);
1127                DBKDebug_ContinueDebugEvent(true);
1128                System.Continue;
1129              end;
1130            finally
1131              owner.breakpointCS.Leave;
1132            end;
1133           
1134          end;
1135
1136
1137
1138          case breakoption of
1139            bo_break:           handled:=HandleBreak;
1140            bo_ChangeRegister:  handled:=HandleChangeRegister(breakreason);
1141            bo_FindCode:        handled:=HandleFindCode;
1142            bo_FindWhatCodeAccesses: handled:=HandleFindWhatCodeAccesses;
1143            else
1144            begin
1145              OutputDebugString('Invalid breakoption');
1146              handled:=false;
1147            end;
1148          end;
1149
1150          DBKDebug_ContinueDebugEvent(handled);
1151        end
1152        else DBKDebug_ContinueDebugEvent(false);  //not handled
1153      end;
1154      //timeout
1155
1156    end;
1157
1158    //terminated
1159
1160  except
1161
1162  end;
1163
1164  //tell the kerneldriver to whipe out the debuggeerdprocesslist
1165  DBKDebug_Stopdebugging;
1166
1167  active:=false;
1168end;
1169
1170initialization
1171  KDebugger:=TKDebugger.create;
1172
1173
1174end.
Note: See TracBrowser for help on using the browser.