Debugging

Justas Masiulis, 2020

What We'll cover

  • High Level Overview
  • Basic Debugger Implementation
  • Anti-Debug Consoderations

High Level Overview

What does windows give us?

  • Debugger Events
  • Ability to consume those events
  • Ability modify process state
  • API to parse debug information files

What needs to be implemented by the developer

  • Breakpoints
  • Disassembly
  • GUI

Specialized debuggers

  • WinDbg
  • x64dbg
  • OllyDbg

Debug Loop

Most Basic Implementation


DebugActiveProcess(processId);

while(true) {
  WaitForDebugEvent(&event, INFINITE);
  switch event.dwDebugEventCode {
    case CREATE_PROCESS_DEBUG_EVENT: ...
    case CREATE_THREAD_DEBUG_EVENT: ...
    case LOAD_DLL_DEBUG_EVENT: ...
    case EXCEPTION_DEBUG_EVENT: ...
  }

  // most likely DBG_CONTINUE or DBG_EXCEPTION_NOT_HANDLED
  ContinueDebugEvent(event.dwProcessId, event.dwThreadId, continueStatus);
}
					

What Microsoft hides


bool DebugActiveProcess(uint32_t dwProcessId) {
  NtCreateDebugObject(&NtCurrentTeb()->DbgSsReserved[1], ...);
  NtOpenProcess(&process, ...);
  NtDebugActiveProcess(processHandle, NtCurrentTeb()->DbgSsReserved[1]);
  NtCreateThreadEx(processHandle, DbgUiRemoteBreakin, ...)
}
					

void DbgUiRemoteBreakin() {
  if(NtCurrentPeb()->IsDebuggerPresent)
    DebugBreak();
  NtTerminateThread(NtCurrentThread(), 0);
}
					

bool WaitForDebugEvent(DEBUG_EVENT* DebugEvent, uint32_t Milliseconds) {
  NtWaitForDebugEvent(NtCurrentTeb()->DbgSsReserved[1],
                      DebugEvent
                      true /* whether wait is alertable */,
                      Milliseconds);
  ...
}
					

bool ContinueDebugEvent(uint32_t ProcessId,
                        uint32_t ThreadId,
                        uint32_t ContinueStatus) {
  NtDebugContinue(NtCurrentTeb()->DbgSsReserved[1],
                  { ProcessId, ThreadId }, 
                  ContinueStatus);
  ...
}