1

I have coded a very simple Debugger for x64, I am using currently Win10.

I am trying to set some Hardware Breakpoints to be trapped by my Debugger Loop as follows:

inline void SETBITS(DWORD64 *dw, int lowBit, int bits, int newValue) {

     int mask = (1 << bits) - 1;
     *dw = (*dw & ~(mask << lowBit)) | (newValue << lowBit);

}

BOOL SetHardwareBP(HANDLE hThread, __int64 Address, DWORD Length, int Condition)
{

    CONTEXT context = { CONTEXT_DEBUG_REGISTERS };
    int i;
    if (!GetThreadContext(hThread, &context)) return -1;

    // find available hardware register

    for (i = 0; i < 4; i++)
    {
    if ((context.Dr7 & (1 << (i * 2))) == 0)
    {
        *(&context.Dr0 + i) = Address;

        SETBITS(&context.Dr7, 16 + i * 4, 2, Condition);
        SETBITS(&context.Dr7, 18 + i * 4, 2, Length);
        SETBITS(&context.Dr7, i * 2, 1, 1);

        if (!SetThreadContext(hThread, &context))
            return -1;

        return i;
    }
  }

   return -1;
}

int SetDebugPrivileges(void) {

      TOKEN_PRIVILEGES priv = { 0 };
      HANDLE hToken = NULL;

      if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
        priv.PrivilegeCount = 1;
        priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

        if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) {
        if (AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL) == 0) {
            printf("AdjustTokenPrivilege Error! [%u]\n", GetLastError());
        }
    }

       CloseHandle(hToken);
  }
       return GetLastError();
}


void ThreadsLoop(DWORD mPID) {

   HANDLE         hProcessSnap = NULL;

   SetDebugPrivileges();
   hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

   if (hProcessSnap == INVALID_HANDLE_VALUE) return;

    else
    {
    THREADENTRY32 the;
    the.dwSize = sizeof(THREADENTRY32);

    BOOL bret = Thread32First(hProcessSnap, &the);
    while (bret)
    {


        if (the.th32OwnerProcessID == mPID)
        {

            HANDLE hthread = OpenThread(THREAD_ALL_ACCESS, false, the.th32ThreadID);
            SuspendThread(hthread);

                           //call with length and condition set to 0 for a Code Execution type
            int hr = SetHardwareBP(hthread, addr, 0, 0);
            ResumeThread(hthread);

            CloseHandle(hthread);
        }
        bret = Thread32Next(hProcessSnap, &the);
    }
    CloseHandle(hProcessSnap);
}
}

I am calling ThreadsLoop(pi.dwProcessId); where pi is the the PROCESS_INFORMATION structure returned from my initial call:

CreateProcess(pname, NULL, NULL, NULL, false, DEBUG_ONLY_THIS_PROCESS, NULL,NULL, &si, &pi);

None of the Hardware Breakpoints is Hit. I did several attempts changing the DR0-DR7 (and condition and length variables above) setup without luck. All calls to SetThreadContext returned successfully.

I did the same test with a Software Breakpoint via the following code and works perfect:

BYTE p[] = { 0xcc }; 
SIZE_T d = 0;
WriteProcessMemory(pi.hProcess, (void*)addr, p, sizeof(p), &d);

What could be wrong in this code/approach?

Thanks

10
  • Has "SETBITS" function implemented in a way that changes the DR7 value and then you set it by calling SetThreadContext?...
    – EWD-0-
    Dec 5, 2017 at 16:20
  • 1
    To make it more clear you send some inputs to the functions, and receiving NO output and just setting the context which has the same DR7 :) I think you now understand what's wrong...
    – EWD-0-
    Dec 5, 2017 at 16:38
  • Thanks for pointing out this. I fixed the code. Still the breakpoints not working.
    – fred26
    Dec 5, 2017 at 17:23
  • 1
    Now again you have a problem... Look again. You're changing the pointer itself not the value which the pointer points to.
    – EWD-0-
    Dec 5, 2017 at 18:11
  • Thanks, I fixed it. It was a typo. The code, is still not BP.
    – fred26
    Dec 5, 2017 at 18:25

1 Answer 1

1

There are some issues you have to fix

  1. Please take a look at This where is explains which event is triggered in the hardware breakpoint case. You have to add EXCEPTION_SINGLE_STEP in DebuggerThreadProc() function

  2. You need to be attached to the target process in order to be able to debug it. For this you need to use DebugActiveProcess

  3. This one is quite a semantic error. As you create the target process in the same app you already have an open handle to both the process and the main thread so there is NO need to create snapshot and searching for the target! This is completely redundant. Please remove all these parts and pass the handles as input to those functions.

  4. Always keep in mind to close the handles you've opened.

8
  • your 2. is wrong. not need call DebugActiveProcess if we create process with DEBUG_ONLY_THIS_PROCESS or DEBUG_PROCESS flags. also need handle not only EXCEPTION_SINGLE_STEP but also STATUS_WX86_SINGLE_STEP - this will be if we debug wow64 process
    – RbMm
    Dec 14, 2017 at 14:49
  • Last one first, during the questions I've asked it was mentioned that the target is x64. If not, there are other APIs which has to be added to make it compatible not only STATUS_WX86_SINGLE_STEP. Other thing which I didn't mention here but in the chat is that though the values might be the same it's better to use EXCEPTION_BREAKPOINT instead of STATUS...
    – EWD-0-
    Dec 14, 2017 at 15:04
  • the STATUS_WX86_SINGLE_STEP (and STATUS_WX86_BREAKPOINT on breakpoint) come when exception was in wow64 code. and debugger is 64bit process. if we debug x64 process we of course never get this exception codes. however always better handle it too, for not restrict self for x64 apps only. there are other APIs which has to be added to make it compatible dont know what you mean, but x64 debugger perfect can debug both 32 and 64 processes with single code base without any special api calls
    – RbMm
    Dec 14, 2017 at 15:12
  • As Igor also mentioned in chat, consider Wow64Get/SetThreadContext as an example. I think the best way is to add your points as another answer... Better than in the comments.
    – EWD-0-
    Dec 14, 2017 at 16:48
  • And about starting with DEBUG_ONLY_THIS_PROCESS/DEBUG_PROCESS you're right, this is ALSO another way to receive dbg events. The "2" is not wrong it's one of the ways and the one you've mentioned another way.
    – EWD-0-
    Dec 14, 2017 at 16:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.