function SetDebugPriv: Boolean; var Token: THandle; tkp: TTokenPrivileges; begin Result := false; if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, Token) then begin if LookupPrivilegeValue(nil, PChar('SeDebugPrivilege'), tkp.Privileges[0].Luid) then begin tkp.PrivilegeCount := 1; tkp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; Result := AdjustTokenPrivileges(Token, False, tkp, 0, PTokenPrivileges(nil)^, PDWord(nil)^); end; end; end;
function TFWDebugerCore.AttachToProcess(ProcessID: DWORD; SentEntryPointBreakPoint: Boolean): Boolean; begin Result := False; if FProcessInfo.ProcessID <> 0 then Exit; FSetEntryPointBreakPoint := SentEntryPointBreakPoint; FProcessInfo.ProcessID := ProcessID; Result := DebugActiveProcess(ProcessID); end;
function TFWDebugerCore.DebugNewProcess(const FilePath: string; SentEntryPointBreakPoint: Boolean): Boolean; var PI: TProcessInformation; SI: TStartupInfo; begin Result := False; if FProcessInfo.ProcessID <> 0 then Exit; FSetEntryPointBreakPoint := SentEntryPointBreakPoint; ZeroMemory(@SI, SizeOf(TStartupInfo)); SI.cb := SizeOf(TStartupInfo); Result := CreateProcess(PChar(FilePath), nil, nil, nil, False, DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS, nil, nil, SI, PI); if Result then begin FProcessInfo.ProcessID := PI.dwProcessId; FProcessInfo.CreatedProcessHandle := PI.hProcess; FProcessInfo.CreatedThreadHandle := PI.hThread; end; end;
procedure TFWDebugerCore.RunMainLoop; var DebugEvent: TDebugEvent; CallNextLoopIteration: Boolean; ThreadIndex: Integer; begin CallNextLoopIteration := False; repeat ContinueStatus := DBG_CONTINUE; if not WaitForDebugEvent(DebugEvent, MainLoopWaitPeriod) then begin if GetLastError = ERROR_SEM_TIMEOUT then begin DoIdle; if FProcessInfo.ProcessID = 0 then Exit; CallNextLoopIteration := True; Continue; end else begin DoMainLoopFailed; Break; end; end; case DebugEvent.dwDebugEventCode of CREATE_THREAD_DEBUG_EVENT: DoCreateThread(DebugEvent); CREATE_PROCESS_DEBUG_EVENT: DoCreateProcess(DebugEvent); EXIT_THREAD_DEBUG_EVENT: DoExitThread(DebugEvent); EXIT_PROCESS_DEBUG_EVENT: begin DoExitProcess(DebugEvent); Break; end; LOAD_DLL_DEBUG_EVENT: DoLoadDll(DebugEvent); UNLOAD_DLL_DEBUG_EVENT: DoUnLoadDll(DebugEvent); OUTPUT_DEBUG_STRING_EVENT: DoDebugString(DebugEvent); RIP_EVENT: DoRip(DebugEvent); EXCEPTION_DEBUG_EVENT: begin ThreadIndex := GetThreadIndex(DebugEvent.dwThreadId); case DebugEvent.Exception.ExceptionRecord.ExceptionCode of EXCEPTION_BREAKPOINT: ProcessExceptionBreakPoint(ThreadIndex, DebugEvent); EXCEPTION_SINGLE_STEP: ProcessExceptionSingleStep(ThreadIndex, DebugEvent); EXCEPTION_GUARD_PAGE: ProcessExceptionGuardPage(ThreadIndex, DebugEvent); else CallUnhandledExceptionEvents(ThreadIndex, CodeDataToExceptionCode( DebugEvent.Exception.ExceptionRecord.ExceptionCode), DebugEvent); end; end; end; CallNextLoopIteration := ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, ContinueStatus); until not CallNextLoopIteration; end;
PDebugEvent = ^TDebugEvent; _DEBUG_EVENT = record dwDebugEventCode: DWORD; dwProcessId: DWORD; dwThreadId: DWORD; case Integer of 0: (Exception: TExceptionDebugInfo); 1: (CreateThread: TCreateThreadDebugInfo); 2: (CreateProcessInfo: TCreateProcessDebugInfo); 3: (ExitThread: TExitThreadDebugInfo); 4: (ExitProcess: TExitProcessDebugInfo); 5: (LoadDll: TLoadDLLDebugInfo); 6: (UnloadDll: TUnloadDLLDebugInfo); 7: (DebugString: TOutputDebugStringInfo); 8: (RipInfo: TRIPInfo); end; {$EXTERNALSYM _DEBUG_EVENT} TDebugEvent = _DEBUG_EVENT; DEBUG_EVENT = _DEBUG_EVENT; {$EXTERNALSYM DEBUG_EVENT}
procedure TFWDebugerCore.DoCreateProcess(DebugEvent: TDebugEvent); begin // FProcessInfo.AttachedFileHandle := DebugEvent.CreateProcessInfo.hFile; FProcessInfo.AttachedProcessHandle := DebugEvent.CreateProcessInfo.hProcess; FProcessInfo.AttachedThreadHandle := DebugEvent.CreateProcessInfo.hThread; FProcessInfo.EntryPoint := DWORD(DebugEvent.CreateProcessInfo.lpStartAddress); AddThread(DebugEvent.dwThreadId, FProcessInfo.AttachedThreadHandle); // BreakPoint if FSetEntryPointBreakPoint then SetBreakpoint(FProcessInfo.EntryPoint, 'Process Entry Point Breakpoint'); if Assigned(FCreateProcess) then begin FCreateProcess(Self, GetThreadIndex(DebugEvent.dwThreadId), DebugEvent.CreateProcessInfo); DoResumeAction(GetThreadIndex(DebugEvent.dwThreadId)); end; end;
procedure TFWDebugerCore.DoLoadDll(DebugEvent: TDebugEvent); begin if Assigned(FLoadDll) then begin FLoadDll(Self, GetThreadIndex(DebugEvent.dwThreadId), DebugEvent.LoadDll); DoResumeAction; end; CloseHandle(DebugEvent.LoadDll.hFile); end;
procedure TdlgDebuger.OnLoadDll(Sender: TObject; ThreadIndex: Integer; Data: TLoadDLLDebugInfo); const FormatStrKnownDLL = 'Load Dll at instance %p handle %d "%s"'; FormatStrUnknownDLL = 'Load unknown Dll at instance %p handle %d'; var DllName: AnsiString; IsUnicodeData: Boolean; begin FCore.ContinueStatus := DBG_EXCEPTION_NOT_HANDLED; IsUnicodeData := Data.fUnicode = 1; DllName := FCore.GetDllName(Data.lpImageName, Data.lpBaseOfDll, IsUnicodeData); if DllName <> '' then begin if IsUnicodeData then Writeln(Format(FormatStrKnownDLL, [Data.lpBaseOfDll, Data.hFile, PWideChar(@DllName[1])])) else Writeln(Format(FormatStrKnownDLL, [Data.lpBaseOfDll, Data.hFile, PAnsiChar(@DllName[1])])); end else Writeln(Format(FormatStrUnknownDLL, [Data.lpBaseOfDll, Data.hFile])); end;
function TFWDebugerCore.ReadData(AddrPrt, ResultPtr: Pointer; DataSize: Integer): Boolean; var Dummy: DWORD; begin Result := ReadProcessMemory(FProcessInfo.AttachedProcessHandle, AddrPrt, ResultPtr, DataSize, Dummy) and (Integer(Dummy) = DataSize); end; function TFWDebugerCore.ReadStringA(AddrPrt: Pointer; DataSize: Integer): AnsiString; begin SetLength(Result, DataSize); if not ReadData(AddrPrt, @Result[1], DataSize) then Result := ''; end; function GetMappedFileNameA(hProcess: THandle; lpv: Pointer; lpFilename: LPSTR; nSize: DWORD): DWORD; stdcall; external 'psapi.dll'; function TFWDebugerCore.GetDllName(lpImageName, lpBaseOfDll: Pointer; var Unicode: Boolean): AnsiString; var DllNameAddr: Pointer; MappedName: array [0..MAX_PATH - 1] of AnsiChar; begin if ReadData(lpImageName, @DllNameAddr, 4) then Result := ReadStringA(DllNameAddr, MAX_PATH); if Result = '' then begin if GetMappedFileNameA(FProcessInfo.AttachedProcessHandle, lpBaseOfDll, @MappedName[0], MAX_PATH) > 0 then begin Result := PAnsiChar(@MappedName[0]); Unicode := False; end; end; end;
procedure TFWDebugerCore.DoCreateThread(DebugEvent: TDebugEvent); begin AddThread(DebugEvent.dwThreadId, DebugEvent.CreateThread.hThread); if Assigned(FCreateThread) then begin FCreateThread(Self, GetThreadIndex(DebugEvent.dwThreadId), DebugEvent.CreateThread); DoResumeAction; end; end;
procedure TFWDebugerCore.DoDebugString(DebugEvent: TDebugEvent); begin if Assigned(FDebugString) then begin FDebugString(Self, GetThreadIndex(DebugEvent.dwThreadId), DebugEvent.DebugString); DoResumeAction; end; end;
procedure TdlgDebuger.OnDebugString(Sender: TObject; ThreadIndex: Integer; Data: TOutputDebugStringInfo); begin if Data.fUnicode = 1 then Writeln('DebugString: ' + PWideChar(FCore.ReadStringW(Data.lpDebugStringData, Data.nDebugStringLength))) else Writeln('DebugString: ' + PAnsiChar(FCore.ReadStringA(Data.lpDebugStringData, Data.nDebugStringLength))); end;
EXCEPTION_DEBUG_EVENT: begin ThreadIndex := GetThreadIndex(DebugEvent.dwThreadId); case DebugEvent.Exception.ExceptionRecord.ExceptionCode of EXCEPTION_BREAKPOINT: ProcessExceptionBreakPoint(ThreadIndex, DebugEvent); EXCEPTION_SINGLE_STEP: ProcessExceptionSingleStep(ThreadIndex, DebugEvent); EXCEPTION_GUARD_PAGE: ProcessExceptionGuardPage(ThreadIndex, DebugEvent); else CallUnhandledExceptionEvents(ThreadIndex, CodeDataToExceptionCode( DebugEvent.Exception.ExceptionRecord.ExceptionCode), DebugEvent); end; end;
// ( ) TBreakpointType = ( btBreakpoint, // WriteProcessMemoryEx + 0xCC btMemoryBreakpoint // VirtualProtectEx + PAGE_GUARD ); // TInt3Breakpoint = record Address: Pointer; ByteCode: Byte; end; TMemotyBreakPoint = record Address: Pointer; Size: DWORD; BreakOnWrite: Boolean; RegionStart: Pointer; RegionSize: DWORD; PreviosRegionProtect: DWORD; end; TBreakpoint = packed record bpType: TBreakpointType; Description: ShortString; Active: Boolean; case Integer of 0: (Int3: TInt3Breakpoint;); 1: (Memory: TMemotyBreakPoint); end; TBreakpointList = array of TBreakpoint;
function TFWDebugerCore.SetBreakpoint(Address: DWORD; const Description: string): Boolean; var Breakpoint: TBreakpoint; OldProtect: DWORD; Dummy: DWORD; begin ZeroMemory(@Breakpoint, SizeOf(TBreakpoint)); Breakpoint.bpType := btBreakpoint; Breakpoint.Int3.Address := Pointer(Address); Breakpoint.Description := Description; Check(VirtualProtectEx(FProcessInfo.AttachedProcessHandle, Pointer(Address), 1, PAGE_READWRITE, OldProtect)); try Check(ReadProcessMemory(FProcessInfo.AttachedProcessHandle, Pointer(Address), @Breakpoint.Int3.ByteCode, 1, Dummy)); Check(WriteProcessMemory(FProcessInfo.AttachedProcessHandle, Pointer(Address), @BPOpcode, 1, Dummy)); finally Check(VirtualProtectEx(FProcessInfo.AttachedProcessHandle, Pointer(Address), 1, OldProtect, OldProtect)); end; Result := AddNewBreakPoint(Breakpoint); end;
procedure TFWDebugerCore.ToggleInt3Breakpoint(Index: Integer; Active: Boolean); var OldProtect: DWORD; Dummy: DWORD; begin CheckBreakpointIndex(Index); if FBreakpointList[Index].bpType <> btBreakpoint then Exit; if FBreakpointList[Index].Active = Active then Exit; Check(VirtualProtectEx(FProcessInfo.AttachedProcessHandle, FBreakpointList[Index].Int3.Address, 1, PAGE_READWRITE, OldProtect)); try if Active then Check(WriteProcessMemory(FProcessInfo.AttachedProcessHandle, FBreakpointList[Index].Int3.Address, @BPOpcode, 1, Dummy)) else Check(WriteProcessMemory(FProcessInfo.AttachedProcessHandle, FBreakpointList[Index].Int3.Address, @FBreakpointList[Index].Int3.ByteCode, 1, Dummy)); finally Check(VirtualProtectEx(FProcessInfo.AttachedProcessHandle, FBreakpointList[Index].Int3.Address, 1, OldProtect, OldProtect)); end; FBreakpointList[Index].Active := Active; end;
asm db $0F, $1F, $00 xor eax, eax inc eax neg eax end;
asm db $EB, $01 // jmp +1 ( xor " ") db $B8 // "" xor eax, eax // inc eax neg eax not eax sub edx, eax imul eax, edx nop nop end;
const EFLAGS_TF = $100; // 8- ... Context.EFlags := Context.EFlags or EFLAGS_TF;
procedure TFWDebugerCore.ProcessExceptionBreakPoint(ThreadIndex: Integer; DebugEvent: TDebugEvent); var ReleaseBP: Boolean; BreakPointIndex: Integer; begin ReleaseBP := False; BreakPointIndex := GetBPIndex( DWORD(DebugEvent.Exception.ExceptionRecord.ExceptionAddress)); if BreakPointIndex >= 0 then begin if Assigned(FBreakPoint) then FBreakPoint(Self, ThreadIndex, DebugEvent.Exception.ExceptionRecord, BreakPointIndex, ReleaseBP) else CallUnhandledExceptionEvents(ThreadIndex, ecBreakpoint, DebugEvent); ToggleInt3Breakpoint(BreakPointIndex, False); SetSingleStepMode(ThreadIndex, True); if ReleaseBP then RemoveBreakpoint(BreakPointIndex) else FRestoreBPIndex := BreakPointIndex; end else CallUnhandledExceptionEvents(ThreadIndex, ecBreakpoint, DebugEvent); end;
procedure TFWDebugerCore.SetSingleStepMode(ThreadIndex: Integer; RestoreEIPAfterBP: Boolean); var Context: TContext; begin ZeroMemory(@Context, SizeOf(TContext)); Context.ContextFlags := CONTEXT_FULL; Check(GetThreadContext(FThreadList[ThreadIndex].ThreadHandle, Context)); if RestoreEIPAfterBP then Dec(Context.Eip); Context.EFlags := Context.EFlags or EFLAGS_TF; Check(SetThreadContext(FThreadList[ThreadIndex].ThreadHandle, Context)); end;
procedure TFWDebugerCore.RemoveBreakpoint(Index: Integer); var Len: Integer; begin ToggleBreakpoint(Index, False); Len := BreakpointCount; if Len = 1 then SetLength(FBreakpointList, 0) else begin FBreakpointList[Index] := FBreakpointList[Len - 1]; SetLength(FBreakpointList, Len - 1); end; end;
function TFWDebugerCore.SetMemoryBreakpoint(Address: Pointer; Size: DWORD; BreakOnWrite: Boolean; const Description: string): Boolean; var Breakpoint: TBreakpoint; MBI: TMemoryBasicInformation; Index: Integer; begin Index := GetMBPIndex(DWORD(Address)); if (Index >= 0) and (FBreakpointList[Index].bpType = btMemoryBreakpoint) then begin MBI.BaseAddress := FBreakpointList[Index].Memory.RegionStart; MBI.RegionSize := FBreakpointList[Index].Memory.RegionSize; MBI.Protect := FBreakpointList[Index].Memory.PreviosRegionProtect; end else Check(VirtualQueryEx(DebugProcessData.AttachedProcessHandle, Address, MBI, SizeOf(TMemoryBasicInformation)) > 0); ZeroMemory(@Breakpoint, SizeOf(TBreakpoint)); Breakpoint.bpType := btMemoryBreakpoint; Breakpoint.Description := ShortString(Description); Breakpoint.Memory.Address := Address; Breakpoint.Memory.Size := Size; Breakpoint.Memory.BreakOnWrite := BreakOnWrite; Breakpoint.Memory.RegionStart := MBI.BaseAddress; Breakpoint.Memory.RegionSize := MBI.RegionSize; Check(VirtualProtectEx(FProcessInfo.AttachedProcessHandle, Address, Size, MBI.Protect or PAGE_GUARD, Breakpoint.Memory.PreviosRegionProtect)); if Index >= 0 then Breakpoint.Memory.PreviosRegionProtect := MBI.Protect; Result := AddNewBreakPoint(Breakpoint); end;
procedure TFWDebugerCore.ProcessExceptionGuardPage(ThreadIndex: Integer; DebugEvent: TDebugEvent); var CurrentMBPIndex: Integer; function CheckWriteMode: Boolean; begin Result := not FBreakpointList[CurrentMBPIndex].Memory.BreakOnWrite; if not Result then Result := DebugEvent.Exception.ExceptionRecord.ExceptionInformation[0] = 1; end; var MBPIndex: Integer; ReleaseMBP: Boolean; dwGuardedAddr: DWORD; begin ReleaseMBP := False; dwGuardedAddr := DebugEvent.Exception.ExceptionRecord.ExceptionInformation[1]; MBPIndex := GetMBPIndex(dwGuardedAddr); if MBPIndex >= 0 then begin CurrentMBPIndex := MBPIndex; while not CheckIsAddrInRealMemoryBPRegion(CurrentMBPIndex, dwGuardedAddr) do begin CurrentMBPIndex := GetMBPIndex(dwGuardedAddr, CurrentMBPIndex + 1); if CurrentMBPIndex < 0 then Break; end; if CurrentMBPIndex >= 0 then begin MBPIndex := CurrentMBPIndex; if Assigned(FBreakPoint) and CheckWriteMode then FBreakPoint(Self, ThreadIndex, DebugEvent.Exception.ExceptionRecord, MBPIndex, ReleaseMBP) else CallUnhandledExceptionEvents(ThreadIndex, ecGuard, DebugEvent); end else CallUnhandledExceptionEvents(ThreadIndex, ecGuard, DebugEvent); FBreakpointList[MBPIndex].Active := False; SetSingleStepMode(ThreadIndex, False); if ReleaseMBP then RemoveBreakpoint(MBPIndex) else FRestoreMBPIndex := MBPIndex; end else CallUnhandledExceptionEvents(ThreadIndex, ecGuard, DebugEvent); end;
procedure TFWDebugerCore.ToggleMemoryBreakpoint(Index: Integer; Active: Boolean); var Dummy: DWORD; begin CheckBreakpointIndex(Index); if FBreakpointList[Index].bpType <> btMemoryBreakpoint then Exit; if FBreakpointList[Index].Active = Active then Exit; if Active then Check(VirtualProtectEx(FProcessInfo.AttachedProcessHandle, FBreakpointList[Index].Memory.Address, FBreakpointList[Index].Memory.Size, FBreakpointList[Index].Memory.PreviosRegionProtect or PAGE_GUARD, Dummy)) else Check(VirtualProtectEx(FProcessInfo.AttachedProcessHandle, FBreakpointList[Index].Memory.Address, FBreakpointList[Index].Memory.Size, FBreakpointList[Index].Memory.PreviosRegionProtect, Dummy)); FBreakpointList[Index].Active := Active; end;
THWBPIndex = 0..3; THWBPSize = (hsByte, hdWord, hsDWord); THWBPMode = (hmExecute, hmWrite, hmIO, hmReadWrite); THardwareBreakpoint = packed record Address: array [THWBPIndex] of Pointer; Size: array [THWBPIndex] of THWBPSize; Mode: array [THWBPIndex] of THWBPMode; Description: array [THWBPIndex] of ShortString; Active: array [THWBPIndex] of Boolean; end;
TThreadData = record ThreadID: DWORD; ThreadHandle: THandle; Breakpoint: THardwareBreakpoint; end; TThreadList = array of TThreadData;
procedure TFWDebugerCore.SetHardwareBreakpoint(ThreadIndex: Integer; Address: Pointer; Size: THWBPSize; Mode: THWBPMode; HWIndex: THWBPIndex; const Description: string); begin if ThreadIndex < 0 then Exit; FThreadList[ThreadIndex].Breakpoint.Address[HWIndex] := Address; FThreadList[ThreadIndex].Breakpoint.Size[HWIndex] := Size; FThreadList[ThreadIndex].Breakpoint.Mode[HWIndex] := Mode; FThreadList[ThreadIndex].Breakpoint.Description[HWIndex] := ShortString(Description); FThreadList[ThreadIndex].Breakpoint.Active[HWIndex] := True; UpdateHardwareBreakpoints(ThreadIndex); end;
procedure TFWDebugerCore.ToggleHardwareBreakpoint(ThreadIndex: Integer; Index: THWBPIndex; Active: Boolean); begin if ThreadIndex < 0 then Exit; if FThreadList[ThreadIndex].Breakpoint.Active[Index] = Active then Exit; FThreadList[ThreadIndex].Breakpoint.Active[Index] := Active; UpdateHardwareBreakpoints(ThreadIndex); end;
procedure TFWDebugerCore.DropHardwareBreakpoint(ThreadIndex: Integer; Index: THWBPIndex); begin if ThreadIndex < 0 then Exit; if FThreadList[ThreadIndex].Breakpoint.Address[Index] = nil then Exit; FThreadList[ThreadIndex].Breakpoint.Address[Index] := nil; UpdateHardwareBreakpoints(ThreadIndex); end;
procedure TFWDebugerCore.UpdateHardwareBreakpoints(ThreadIndex: Integer); const DR7_SET_LOC_DR0 = $01; DR7_SET_GLB_DR0 = $02; DR7_SET_LOC_DR1 = $04; DR7_SET_GLB_DR1 = $08; DR7_SET_LOC_DR2 = $10; DR7_SET_GLB_DR2 = $20; DR7_SET_LOC_DR3 = $40; DR7_SET_GLB_DR3 = $80; DR7_SET_LOC_ON = $100; DR7_SET_GLB_ON = $200; DR7_PROTECT = $2000; DR_SIZE_BYTE = 0; DR_SIZE_WORD = 1; DR_SIZE_DWORD = 3; DR_MODE_E = 0; DR_MODE_W = 1; DR_MODE_I = 2; DR_MODE_R = 3; DR7_MODE_DR0_E = DR_MODE_E shl 16; DR7_MODE_DR0_W = DR_MODE_W shl 16; DR7_MODE_DR0_I = DR_MODE_I shl 16; DR7_MODE_DR0_R = DR_MODE_R shl 16; DR7_SIZE_DR0_B = DR_SIZE_BYTE shl 18; DR7_SIZE_DR0_W = DR_SIZE_WORD shl 18; DR7_SIZE_DR0_D = DR_SIZE_DWORD shl 18; DR7_MODE_DR1_E = DR_MODE_E shl 20; DR7_MODE_DR1_W = DR_MODE_W shl 20; DR7_MODE_DR1_I = DR_MODE_I shl 20; DR7_MODE_DR1_R = DR_MODE_R shl 20; DR7_SIZE_DR1_B = DR_SIZE_BYTE shl 22; DR7_SIZE_DR1_W = DR_SIZE_WORD shl 22; DR7_SIZE_DR1_D = DR_SIZE_DWORD shl 22; DR7_MODE_DR2_E = DR_MODE_E shl 24; DR7_MODE_DR2_W = DR_MODE_W shl 24; DR7_MODE_DR2_I = DR_MODE_I shl 24; DR7_MODE_DR2_R = DR_MODE_R shl 24; DR7_SIZE_DR2_B = DR_SIZE_BYTE shl 26; DR7_SIZE_DR2_W = DR_SIZE_WORD shl 26; DR7_SIZE_DR2_D = DR_SIZE_DWORD shl 26; DR7_MODE_DR3_E = DR_MODE_E shl 28; DR7_MODE_DR3_W = DR_MODE_W shl 28; DR7_MODE_DR3_I = DR_MODE_I shl 28; DR7_MODE_DR3_R = DR_MODE_R shl 28; DR7_SIZE_DR3_B = DR_SIZE_BYTE shl 30; DR7_SIZE_DR3_W = DR_SIZE_WORD shl 30; DR7_SIZE_DR3_D = $C0000000; //DR_SIZE_DWORD shl 30; DR_On: array [THWBPIndex] of DWORD = ( DR7_SET_LOC_DR0, DR7_SET_LOC_DR1, DR7_SET_LOC_DR2, DR7_SET_LOC_DR3 ); DR_Mode: array [THWBPIndex] of array [THWBPMode] of DWORD = ( (DR7_MODE_DR0_E, DR7_MODE_DR0_W, DR7_MODE_DR0_I, DR7_MODE_DR0_R), (DR7_MODE_DR1_E, DR7_MODE_DR1_W, DR7_MODE_DR1_I, DR7_MODE_DR1_R), (DR7_MODE_DR2_E, DR7_MODE_DR2_W, DR7_MODE_DR2_I, DR7_MODE_DR2_R), (DR7_MODE_DR3_E, DR7_MODE_DR3_W, DR7_MODE_DR3_I, DR7_MODE_DR3_R) ); DR_Size: array [THWBPIndex] of array [THWBPSize] of DWORD = ( (DR7_SIZE_DR0_B, DR7_SIZE_DR0_W, DR7_SIZE_DR0_D), (DR7_SIZE_DR1_B, DR7_SIZE_DR1_W, DR7_SIZE_DR1_D), (DR7_SIZE_DR2_B, DR7_SIZE_DR2_W, DR7_SIZE_DR2_D), (DR7_SIZE_DR3_B, DR7_SIZE_DR3_W, DR7_SIZE_DR3_D) ); var Context: TContext; I: THWBPIndex; begin if ThreadIndex < 0 then Exit; ZeroMemory(@Context, SizeOf(TContext)); Context.ContextFlags := CONTEXT_DEBUG_REGISTERS; for I := 0 to 3 do begin if not FThreadList[ThreadIndex].Breakpoint.Active[I] then Continue; if FThreadList[ThreadIndex].Breakpoint.Address[I] <> nil then begin Context.Dr7 := Context.Dr7 or DR7_SET_LOC_ON; case I of 0: Context.Dr0 := DWORD(FThreadList[ThreadIndex].Breakpoint.Address[I]); 1: Context.Dr1 := DWORD(FThreadList[ThreadIndex].Breakpoint.Address[I]); 2: Context.Dr2 := DWORD(FThreadList[ThreadIndex].Breakpoint.Address[I]); 3: Context.Dr3 := DWORD(FThreadList[ThreadIndex].Breakpoint.Address[I]); end; Context.Dr7 := Context.Dr7 or DR_On[I]; Context.Dr7 := Context.Dr7 or DR_Mode[I, FThreadList[ThreadIndex].Breakpoint.Mode[I]]; Context.Dr7 := Context.Dr7 or DR_Size[I, FThreadList[ThreadIndex].Breakpoint.Size[I]]; end; end; Check(SetThreadContext(FThreadList[ThreadIndex].ThreadHandle, Context)); end;
Context.Dr7 := Context.Dr7 or DR_On[I]; Context.Dr7 := Context.Dr7 or DR_Mode[I, FThreadList[ThreadIndex].Breakpoint.Mode[I]]; Context.Dr7 := Context.Dr7 or DR_Size[I, FThreadList[ThreadIndex].Breakpoint.Size[I]];
function TFWDebugerCore.ProcessHardwareBreakpoint(ThreadIndex: Integer; DebugEvent: TDebugEvent): Boolean; var Index: Integer; Context: TContext; ReleaseBP: Boolean; begin ZeroMemory(@Context, SizeOf(TContext)); Context.ContextFlags := CONTEXT_DEBUG_REGISTERS; Check(GetThreadContext(FThreadList[ThreadIndex].ThreadHandle, Context)); Result := Context.Dr6 and $F <> 0; if not Result then Exit; Index := -1; if Context.Dr6 and 1 <> 0 then Index := 0; if Context.Dr6 and 2 <> 0 then Index := 1; if Context.Dr6 and 4 <> 0 then Index := 2; if Context.Dr6 and 8 <> 0 then Index := 3; if Index < 0 then begin Result := False; Exit; end; ReleaseBP := False; if Assigned(FHardwareBreakpoint) then FHardwareBreakpoint(Self, ThreadIndex, DebugEvent.Exception.ExceptionRecord, Index, ReleaseBP); ToggleHardwareBreakpoint(ThreadIndex, Index, False); SetSingleStepMode(ThreadIndex, False); if ReleaseBP then DropHardwareBreakpoint(ThreadIndex, Index) else begin // HWBP , // .. // ProcessExceptionSingleStep, HWBP // HWBP if (FRestoredThread >= 0) and (FRestoredHWBPIndex >= 0) then ToggleHardwareBreakpoint(FRestoredThread, FRestoredHWBPIndex, True); FRestoredHWBPIndex := Index; FRestoredThread := ThreadIndex; end; end;
procedure TFWDebugerCore.ProcessExceptionSingleStep(ThreadIndex: Integer; DebugEvent: TDebugEvent); var Handled: Boolean; begin // HWBP Handled := ProcessHardwareBreakpoint(ThreadIndex, DebugEvent); // - HWPB HWBP if not Handled and (FRestoredThread >= 0) and (FRestoredHWBPIndex >= 0) then begin ToggleHardwareBreakpoint(FRestoredThread, FRestoredHWBPIndex, True); FRestoredThread := -1; FRestoredHWBPIndex := -1; end; // if FRestoreBPIndex >= 0 then begin CheckBreakpointIndex(FRestoreBPIndex); if FBreakpointList[FRestoreBPIndex].bpType = btBreakpoint then ToggleInt3Breakpoint(FRestoreBPIndex, True); FRestoreBPIndex := -1; end; // M if FRestoreMBPIndex >= 0 then begin CheckBreakpointIndex(FRestoreMBPIndex); if FBreakpointList[FRestoreMBPIndex].bpType = btMemoryBreakpoint then ToggleMemoryBreakpoint(FRestoreMBPIndex, True); FRestoreMBPIndex := -1; end; // // if ResumeAction <> raRun then begin CallUnhandledExceptionEvents(ThreadIndex, ecSingleStep, DebugEvent); // DoResumeAction(ThreadIndex); end; end;
type TdlgDebuger = class(TForm) Panel1: TPanel; btnStart: TButton; btnStop: TButton; edLog: TRichEdit; procedure btnStartClick(Sender: TObject); procedure btnStopClick(Sender: TObject); private FCore: TFWDebugerCore; FNeedStop: Boolean; procedure Writeln(const Value: string = ''); end; ... procedure TdlgDebuger.btnStartClick(Sender: TObject); var Path: string; begin FNeedStop := False; // Path := ExtractFilePath(ParamStr(0)) + '..\test_app\test_app.exe'; FCore := TFWDebugerCore.Create(50); try btnStart.Enabled := False; btnStop.Enabled := True; if not FCore.DebugNewProcess(Path, True) then RaiseLastOSError; FCore.RunMainLoop; finally FCore.Free; btnStart.Enabled := True; btnStop.Enabled := False; end; Writeln; Writeln('Debug stop'); end; procedure TdlgDebuger.Writeln(const Value: string); begin edLog.Lines.Add(Value); end; procedure TdlgDebuger.btnStopClick(Sender: TObject); begin FNeedStop := True; end;
procedure TdlgDebuger.OnIdle(Sender: TObject); begin if FNeedStop then FCore.StopDebug else Application.ProcessMessages; end;
procedure TdlgDebuger.OnCreateProcess(Sender: TObject; ThreadIndex: Integer; Data: TCreateProcessDebugInfo); var T: TThreadData; begin T := FCore.GetThreadData(ThreadIndex); Writeln(Format('CreateThread ID: %d', [T.ThreadID])); Writeln(Format('ProcessStart ID: %d', [FCore.DebugProcessData.ProcessID])); end;
procedure TdlgDebuger.OnLoadDll(Sender: TObject; ThreadIndex: Integer; Data: TLoadDLLDebugInfo); const FormatStrKnownDLL = 'Load Dll at instance %p handle %d "%s"'; FormatStrUnknownDLL = 'Load unknown Dll at instance %p handle %d'; var DllName: AnsiString; IsUnicodeData: Boolean; begin FCore.ContinueStatus := DBG_EXCEPTION_NOT_HANDLED; IsUnicodeData := Data.fUnicode = 1; DllName := FCore.GetDllName(Data.lpImageName, Data.lpBaseOfDll, IsUnicodeData); if DllName <> '' then begin if IsUnicodeData then Writeln(Format(FormatStrKnownDLL, [Data.lpBaseOfDll, Data.hFile, PWideChar(@DllName[1])])) else Writeln(Format(FormatStrKnownDLL, [Data.lpBaseOfDll, Data.hFile, PAnsiChar(@DllName[1])])); end else Writeln(Format(FormatStrUnknownDLL, [Data.lpBaseOfDll, Data.hFile])); end;
procedure TdlgDebuger.OnBreakPoint(Sender: TObject; ThreadIndex: Integer; ExceptionRecord: Windows.TExceptionRecord; BreakPointIndex: Integer; var ReleaseBreakpoint: Boolean); begin // Writeln(Format('!!! --> Breakpoint "%s"', [FCore.BreakpointItem(BreakPointIndex).Description])); // ( ) ReleaseBreakpoint := True; // FCore.ResumeAction := raTraceInto; // FStepCount := 0; end;
procedure TdlgDebuger.OnSingleStep(Sender: TObject; ThreadIndex: Integer; ExceptionRecord: Windows.TExceptionRecord); begin // Inc(FStepCount); Writeln(Format('!!! --> trace step №%d at addr 0x%p', [FStepCount, ExceptionRecord.ExceptionAddress])); // if FStepCount > 10 then FCore.ResumeAction := raRun else FCore.ResumeAction := raTraceInto; end;
// // // ============================================================================= procedure TForm1.btnDebugStringClick(Sender: TObject); begin OutputDebugString('Test debug string'); end;
procedure TdlgDebuger.OnDebugString(Sender: TObject; ThreadIndex: Integer; Data: TOutputDebugStringInfo); begin if Data.fUnicode = 1 then Writeln('DebugString: ' + PWideChar(FCore.ReadStringW(Data.lpDebugStringData, Data.nDebugStringLength))) else Writeln('DebugString: ' + PAnsiChar(FCore.ReadStringA(Data.lpDebugStringData, Data.nDebugStringLength))); end;
// // // ============================================================================= procedure TForm1.btnExceptClick(Sender: TObject); begin try asm int 3 end; ShowMessage('Debugger detected.'); except ShowMessage('Debugger not found.'); end; end;
procedure TdlgDebuger.OnUnknownBreakPoint(Sender: TObject; ThreadIndex: Integer; ExceptionRecord: Windows.TExceptionRecord); var ApplicationBP: Boolean; begin ApplicationBP := (DWORD(ExceptionRecord.ExceptionAddress) > FCore.DebugProcessData.EntryPoint) and (DWORD(ExceptionRecord.ExceptionAddress) < $500000); Writeln; if ApplicationBP then begin Writeln(Format('!!! --> Unknown application breakpoint at addr 0X%p', [ExceptionRecord.ExceptionAddress])); Writeln('!!! --> Exception not handled.'); FCore.ContinueStatus := DBG_EXCEPTION_NOT_HANDLED; end else begin Writeln(Format('!!! --> Unknown breakpoint at addr 0X%p', [ExceptionRecord.ExceptionAddress])); Writeln('!!! --> Exception handled.'); FCore.ContinueStatus := DBG_CONTINUE; end; Writeln; end;
// // // ============================================================================= procedure TForm1.btnKillStackClick(Sender: TObject); procedure T; var HugeBuff: array [0..10000] of DWORD; begin if HugeBuff[0] <> HugeBuff[10000] then Inc(HugeBuff[0]); T; end; begin try T; except T; end; end;
procedure TdlgDebuger.OnUnknownException(Sender: TObject; ThreadIndex: Integer; ExceptionRecord: Windows.TExceptionRecord); var Cause: string; begin Writeln; case ExceptionRecord.ExceptionCode of EXCEPTION_STACK_OVERFLOW: begin Writeln('!!! --> Stack overflow detected. Probe to continue.'); FCore.ContinueStatus := DBG_CONTINUE; end; EXCEPTION_ACCESS_VIOLATION: begin { The first element of the array contains a read-write flag that indicates the type of operation that caused the access violation. If this value is zero, the thread attempted to read the inaccessible data. If this value is 1, the thread attempted to write to an inaccessible address. If this value is 8, the thread causes a user-mode data execution prevention (DEP) violation. The second array element specifies the virtual address of the inaccessible data. } case ExceptionRecord.ExceptionInformation[0] of 0: Cause := 'read'; 1: Cause := 'write'; 8: Cause := 'DEP violation'; else Cause := 'unknown cause'; end; Writeln(Format('!!! --> Access violation at addr 0x%p %s of address 0x%p', [ ExceptionRecord.ExceptionAddress, Cause, Pointer(PDWORD(@ExceptionRecord.ExceptionInformation[1])^) ])); Writeln('!!! --> Process Stopped.'); FCore.ContinueStatus := DBG_CONTROL_C; end; else Writeln(Format('!!! --> Unknown exception code %p at addr 0x%p', [ Pointer(ExceptionRecord.ExceptionCode), ExceptionRecord.ExceptionAddress ])); end; Writeln; end;
Source: https://habr.com/ru/post/178159/
All Articles