| 1 | unit KernelDebugger; |
|---|
| 2 | |
|---|
| 3 | interface |
|---|
| 4 | |
|---|
| 5 | uses windows,sysutils,SyncObjs, dialogs,classes,debugger,disassembler,newkernelhandler,foundcodeunit, |
|---|
| 6 | tlhelp32,ComCtrls,addressparser, graphics, cefuncproc; |
|---|
| 7 | |
|---|
| 8 | type |
|---|
| 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 | |
|---|
| 102 | var KDebugger: TKDebugger; |
|---|
| 103 | |
|---|
| 104 | implementation |
|---|
| 105 | |
|---|
| 106 | uses frmProcessWatcherUnit,formchangedaddresses,memorybrowserformunit, frmstacktraceunit; |
|---|
| 107 | |
|---|
| 108 | |
|---|
| 109 | |
|---|
| 110 | Procedure TKDebugger.StartDebugger; |
|---|
| 111 | begin |
|---|
| 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); |
|---|
| 119 | end; |
|---|
| 120 | |
|---|
| 121 | Procedure TKDebugger.StopDebugger; |
|---|
| 122 | begin |
|---|
| 123 | if (DebuggerThread<>nil) then |
|---|
| 124 | begin |
|---|
| 125 | Debuggerthread.Terminate; |
|---|
| 126 | Debuggerthread.WaitFor; |
|---|
| 127 | FreeAndNil(Debuggerthread); |
|---|
| 128 | end; |
|---|
| 129 | end; |
|---|
| 130 | |
|---|
| 131 | function TKDebugger.breaklengthToByteLength(breakLength: TBreakLength): integer; |
|---|
| 132 | begin |
|---|
| 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 | |
|---|
| 141 | end; |
|---|
| 142 | |
|---|
| 143 | function TKDebugger.isExecutableBreakpoint(a: dword): boolean; |
|---|
| 144 | var i: integer; |
|---|
| 145 | begin |
|---|
| 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 | |
|---|
| 164 | end; |
|---|
| 165 | |
|---|
| 166 | function TKDebugger.getNumberOfBreakpoints: integer; |
|---|
| 167 | var i: integer; |
|---|
| 168 | begin |
|---|
| 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; |
|---|
| 176 | end; |
|---|
| 177 | |
|---|
| 178 | procedure 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 |
|---|
| 180 | var atleastone: boolean; |
|---|
| 181 | begin |
|---|
| 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 | |
|---|
| 220 | end; |
|---|
| 221 | |
|---|
| 222 | procedure 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 |
|---|
| 224 | var debugreg: integer; |
|---|
| 225 | i: integer; |
|---|
| 226 | begin |
|---|
| 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; |
|---|
| 300 | end; |
|---|
| 301 | |
|---|
| 302 | procedure TKDebugger.ToggleBreakpoint(address: dword); |
|---|
| 303 | var i: integer; |
|---|
| 304 | found :boolean; |
|---|
| 305 | begin |
|---|
| 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; |
|---|
| 328 | end; |
|---|
| 329 | |
|---|
| 330 | procedure TKDebugger.DisableAllBreakpoints; |
|---|
| 331 | var i: integer; |
|---|
| 332 | begin |
|---|
| 333 | for i:=0 to 3 do |
|---|
| 334 | DisableBreakpoint(i); |
|---|
| 335 | end; |
|---|
| 336 | |
|---|
| 337 | procedure TKDebugger.DisableBreakpoint(bp: integer); |
|---|
| 338 | begin |
|---|
| 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; |
|---|
| 356 | end; |
|---|
| 357 | |
|---|
| 358 | procedure TKDebugger.AddThread(ThreadID: Dword); |
|---|
| 359 | var Threadhandle: thandle; |
|---|
| 360 | begin |
|---|
| 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; |
|---|
| 373 | end; |
|---|
| 374 | |
|---|
| 375 | procedure TKDebugger.ApplyDebugRegistersForThread(threadhandle: DWORD); |
|---|
| 376 | begin |
|---|
| 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; |
|---|
| 402 | end; |
|---|
| 403 | |
|---|
| 404 | procedure TKDebugger.ApplyDebugRegisters; |
|---|
| 405 | var i: integer; |
|---|
| 406 | begin |
|---|
| 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; |
|---|
| 417 | end; |
|---|
| 418 | |
|---|
| 419 | procedure TKDebugger.setGlobalDebug(x: boolean); |
|---|
| 420 | begin |
|---|
| 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; |
|---|
| 435 | end; |
|---|
| 436 | |
|---|
| 437 | function TKDebugger.isActive: boolean; |
|---|
| 438 | begin |
|---|
| 439 | result:=DebuggerThread <> nil; |
|---|
| 440 | end; |
|---|
| 441 | |
|---|
| 442 | procedure TKDebugger.GetContext(var context: _CONTEXT); |
|---|
| 443 | begin |
|---|
| 444 | if DebuggerThread<>nil then |
|---|
| 445 | context:=DebuggerThread.tempcontext; |
|---|
| 446 | end; |
|---|
| 447 | |
|---|
| 448 | procedure TKDebugger.Continue(continueOption: TContinueOption; runtilladdress: dword=0); |
|---|
| 449 | begin |
|---|
| 450 | if debuggerthread<>nil then |
|---|
| 451 | debuggerthread.Continue(continueoption, runtilladdress); |
|---|
| 452 | end; |
|---|
| 453 | |
|---|
| 454 | constructor TKDebugger.create; |
|---|
| 455 | begin |
|---|
| 456 | breakpointCS:=TCriticalSection.Create; |
|---|
| 457 | generaldebugregistercontext.ContextFlags:=CONTEXT_DEBUG_REGISTERS; |
|---|
| 458 | |
|---|
| 459 | end; |
|---|
| 460 | |
|---|
| 461 | //--------------------------------------- |
|---|
| 462 | |
|---|
| 463 | constructor TKDebuggerThread.create(owner: TKDebugger; suspended:boolean); |
|---|
| 464 | var ths: thandle; |
|---|
| 465 | tE: threadentry32; |
|---|
| 466 | i,j: integer; |
|---|
| 467 | found: boolean; |
|---|
| 468 | temp: thandle; |
|---|
| 469 | begin |
|---|
| 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; |
|---|
| 534 | end; |
|---|
| 535 | |
|---|
| 536 | procedure TKDebuggerThread.ConvertDebuggerStateToContext(debuggerstate: TDebuggerstate; var context: _CONTEXT); |
|---|
| 537 | begin |
|---|
| 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; |
|---|
| 554 | end; |
|---|
| 555 | |
|---|
| 556 | procedure TKDebuggerThread.Continue(continueOption: TContinueOption; runtilladdress: dword=0); |
|---|
| 557 | begin |
|---|
| 558 | self.continueOption:=continueOption; |
|---|
| 559 | self.runtilladdress:=runtilladdress; |
|---|
| 560 | continueEvent.SetEvent; |
|---|
| 561 | end; |
|---|
| 562 | |
|---|
| 563 | procedure TKDebuggerThread.UpdateGui; |
|---|
| 564 | var |
|---|
| 565 | temp: string; |
|---|
| 566 | begin |
|---|
| 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 | |
|---|
| 747 | |
|---|
| 748 | |
|---|
| 749 | |
|---|
| 750 | |
|---|
| 751 | |
|---|
| 752 | |
|---|
| 753 | |
|---|
| 754 | |
|---|
| 755 | |
|---|
| 756 | showDebugPanels:=true; |
|---|
| 757 | reloadStacktrace; |
|---|
| 758 | end; |
|---|
| 759 | end; |
|---|
| 760 | |
|---|
| 761 | procedure TKDebuggerThread.AddToChangesList; |
|---|
| 762 | var i: integer; |
|---|
| 763 | lbs: string; |
|---|
| 764 | newitem: TListItem; |
|---|
| 765 | x: PContext; |
|---|
| 766 | bpa: dword; |
|---|
| 767 | begin |
|---|
| 768 | |
|---|
| 769 | |
|---|
| 770 | |
|---|
| 771 | |
|---|
| 772 | |
|---|
| 773 | |
|---|
| 774 | |
|---|
| 775 | |
|---|
| 776 | |
|---|
| 777 | |
|---|
| 778 | |
|---|
| 779 | |
|---|
| 780 | |
|---|
| 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; |
|---|
| 800 | end; |
|---|
| 801 | |
|---|
| 802 | procedure TKDebuggerThread.foundone; |
|---|
| 803 | var desc,opcode: string; |
|---|
| 804 | address: dword; |
|---|
| 805 | begin |
|---|
| 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; |
|---|
| 829 | end; |
|---|
| 830 | |
|---|
| 831 | function TKDebuggerThread.getDebugReason: integer; |
|---|
| 832 | //breakreason -2 = error |
|---|
| 833 | //breakreason -1 = single step |
|---|
| 834 | //breakreason x = used breakpoint |
|---|
| 835 | var i,j: integer; |
|---|
| 836 | bsize: integer; |
|---|
| 837 | address: dword; |
|---|
| 838 | begin |
|---|
| 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; |
|---|
| 907 | end; |
|---|
| 908 | |
|---|
| 909 | function TKDebuggerThread.HandleBreak:boolean; |
|---|
| 910 | var wr: TWaitResult; |
|---|
| 911 | address: dword; |
|---|
| 912 | begin |
|---|
| 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 | |
|---|
| 974 | end; |
|---|
| 975 | |
|---|
| 976 | function TKDebuggerThread.HandleChangeRegister(breakreason: integer): boolean; |
|---|
| 977 | begin |
|---|
| 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; |
|---|
| 1005 | end; |
|---|
| 1006 | |
|---|
| 1007 | function TKDebuggerThread.HandleFindCode: boolean; |
|---|
| 1008 | var |
|---|
| 1009 | i: integer; |
|---|
| 1010 | temp: dword; |
|---|
| 1011 | opcode,desc: string; |
|---|
| 1012 | |
|---|
| 1013 | begin |
|---|
| 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 | |
|---|
| 1047 | end; |
|---|
| 1048 | |
|---|
| 1049 | function TKDebuggerThread.HandleFindWhatCodeAccesses: boolean; |
|---|
| 1050 | //find window |
|---|
| 1051 | //evaluate code between brackets |
|---|
| 1052 | //store address |
|---|
| 1053 | //continue |
|---|
| 1054 | var opcode,desc: string; |
|---|
| 1055 | offset: dword; |
|---|
| 1056 | fb,nb: integer; |
|---|
| 1057 | begin |
|---|
| 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; |
|---|
| 1075 | end; |
|---|
| 1076 | |
|---|
| 1077 | procedure TKDebuggerThread.execute; |
|---|
| 1078 | var |
|---|
| 1079 | breakreason: integer; |
|---|
| 1080 | breakoption: TBreakOption; |
|---|
| 1081 | handled: boolean; |
|---|
| 1082 | begin |
|---|
| 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; |
|---|
| 1168 | end; |
|---|
| 1169 | |
|---|
| 1170 | initialization |
|---|
| 1171 | KDebugger:=TKDebugger.create; |
|---|
| 1172 | |
|---|
| 1173 | |
|---|
| 1174 | end. |
|---|