Assume a black-box pen-test with a Win32 target that has perfectdebugger detection (disregarding how hard “perfect” is toachieve). Arbitrarily, assume no access to the kernel; in fact, noadministrator privilege at all. We simply run in processes alongsidethe target with the same credentials.

How much control do we have over the target?

  • We can get a Win32 handle to the process with OpenProcess.
  • We can read process memory by virtual offset and length withReadProcessMemory
  • We can enumerate the threads in the target with Toolhelp32
  • We can suspend or resume individual threads with OpenThread,SuspendThread, and ResumeThread
  • We can write process memory by virtual offset and length withWriteProcessMemory
  • We can allocate memory within the target with VirtualAlloc
  • We can change memory protection with VirtualProtectEx
  • We can enumerate modules and offsets within the target withToolhelp32.
  • We can map out memory regions with VirtualQueryEx.
  • We can excute code in the context of the process withCreateRemoteThread (and RtlRemoteCall).
How much control would a debugger have given us?

  • We’d be able to suspend and resume threads, which we can do anyways.
  • We’d be able to read and write memory, which we can do anyways.
  • We’d be able to set breakpoints.
  • We’d be able to single-step the program.
  • We’d be able to read register contents.
  • We’d be able to call functions, which we can do anyways.
  • We’d be able to search memory for strings, which we can do anyways.
Without anything more interesting than the MSDN man pages, we comereasonably close to this without invoking the debug interface. Inexchange for not showing up in NtQuerySystemInformation or muckingwith the UEF, we give up easy-to-use breakpoints, single-stepping, andregister access. But:

  • We can trivially get single-use, nonrecoverable breakpoints; just inject INT 3 into the text with WriteProcessMemory.
  • We can potentially get access to registers using NtSetThreadContext.
  • We can hook functions, in all the usual ways.
How close can we get to a fully-functional debugger without Windowsknowing about it? I’ve been playing with this for a couple weeks,using Python and ctypes, turning IDLE into a debugger prompt. And itseems like the answer is “extremely close”. More later, but if this istotally obvious, pointers, links, and comments are welcome.