65.9K
CodeProject is changing. Read more.
Home

Self-debugger attach to process

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5 votes)

Sep 14, 2011

CPOL
viewsIcon

29200

Launching the JIT debugger when you want. When you use SetWindowsHookEx function or are doing API hooking, most times you need to inject (manually or automatically) a DLL into a target process. Touching some registry entries, you can make some process to be launched always under the control of a debugger but it is not always the desired method specially if you are dealing with system components or when more than one application instance is active. Also adding __asm int 3; or calling DebugBreak() API do not always raise an exception as one might expect. And if you are fixing bugs, launching the target application inside another instance of Visual Studio may became a pain if you have to repeat the process many times. With the following code snippets, you can do a self-attach by calling the jit debugger. For e.g., when some exported function of your library is called.
BOOL AttachCurrentProcessToDebugger()
{
  STARTUPINFO sSi;
  PROCESS_INFORMATION sPi;
  TCHAR szBufW[2560];
  SIZE_T i;
  BOOL b;
  DWORD dwExitCode;

  if (::IsDebuggerPresent() == FALSE) {
    memset(&sSi, 0, sizeof(sSi));
    memset(&sPi, 0, sizeof(sPi));
    szBufW[0] = L'"';
    ::GetSystemDirectoryW(szBufW+1, 2000);
    i = wcslen(szBufW);
    if (i>0 && szBufW[i]!=L'/' && szBufW[i]!=L'\\')
      szBufW[i++] = L'\\';
    swprintf_s(szBufW+i, 2560-i, L"VSJitDebugger.exe\" -p %lu", ::GetCurrentProcessId());
    b = ::CreateProcessW(NULL, szBufW, NULL, NULL, FALSE, 0, NULL, NULL, &sSi, &sPi);
    if (b != FALSE) {
      ::WaitForSingleObject(sPi.hProcess, INFINITE);
      ::GetExitCodeProcess(sPi.hProcess, &dwExitCode);
      if (dwExitCode != 0) //if exit code is zero, a debugger was selected
        b = FALSE;
    }
    if (sPi.hThread != NULL)
      ::CloseHandle(sPi.hThread);
    if (sPi.hProcess != NULL)
      ::CloseHandle(sPi.hProcess);
    if (b == FALSE)
      return FALSE;
    for (i=0; i<5*60; i++) {
      if (::IsDebuggerPresent() != FALSE)
        break;
      ::Sleep(200);
    }
  }
#if defined _M_IX86
  _asm { int 3 };
#elif defined _M_X64
  __debugbreak();
#else
  ::DebugBreak();
#endif
  return TRUE;
}
The code is simple. If the process is not under the control of a debugger, it launches the jit-debugger (usually located in the System folder) and waits for the debugger attachment.