Programmatically Setting Breakpoints on Multi-threaded 64-bit Applications

Viewed 61

I have adapted some code from other sources (primarily here) and had no avail in my circumstance. I am attaching to a 64-bit application that has upwards of 100 threads at any given time. There are two issues I have, somewhat unrelated:

  1. Whenever the code hits DebugActiveProcess(pid), the attached application freezes.
  2. The code I have only sets a breakpoint on the main thread, but I need it on more than one - ideally all.

I have confirmed that when the code hits SetThreadContext(hThread, &ctx) the dr0 and dr7 registers change as intended, so that much is fine. The only issues I can see right now are the process freezing and setting more than one breakpoint (I thought of iterating for every single thread which would be fine one-time, but when it gets into the while loop that would obviously be problematic and very resource-consuming). I should also note that I can attach the CheatEngine debugger to the application, and breakpoints work fine with no issues. Below is the code I'm using:

DWORD GetProcessThreadID(DWORD dwProcessID)
{
    THREADENTRY32 te = { sizeof(THREADENTRY32) };
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

    if (Thread32First(hSnapshot, &te))
        while (Thread32Next(hSnapshot, &te))
            if (te.th32OwnerProcessID == dwProcessID)
                return te.th32ThreadID;

    return NULL;
}

BOOL SetDebugPrivilege(BOOL State)
{
    HANDLE hToken;
    TOKEN_PRIVILEGES token_privileges;
    DWORD dwSize;

    ZeroMemory(&token_privileges, sizeof(token_privileges));
    token_privileges.PrivilegeCount = 1;

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
        return FALSE;

    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &token_privileges.Privileges[0].Luid))
    {
        CloseHandle(hToken);
        return FALSE;
    }

    if (State)
        token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_REMOVED;

    if (!AdjustTokenPrivileges(hToken, FALSE, &token_privileges, 0, NULL, &dwSize))
    {
        CloseHandle(hToken);
        return FALSE;
    }

    return CloseHandle(hToken);
}

void CreateBreakpoint(DWORD pid, DWORD64 addr) {

    DebugActiveProcess(pid);
    DebugSetProcessKillOnExit(false);

    DWORD_PTR dwThreadID = GetProcessThreadID(pid);
    std::cout << std::hex << dwThreadID << std::endl;

    HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID);

    SetDebugPrivilege(true);

    CONTEXT ctx = { 0 };
    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS | CONTEXT_INTEGER;
    ctx.Dr0 = addr;
    ctx.Dr7 = 0x00000001;

    SetThreadContext(hThread, &ctx);

    DEBUG_EVENT dbgEvent;
    while (true) {
        if (WaitForDebugEvent(&dbgEvent, INFINITE) == 0)
            break;

        if (dbgEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT &&
            dbgEvent.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP) // EXCEPTION_BREAKPOINT
        {
            if (dbgEvent.u.Exception.ExceptionRecord.ExceptionAddress == (LPVOID)addr)
            {
                GetThreadContext(hThread, &ctx);
                std::cout << ctx.Rax << "\n";
                ctx.Dr7 = 0;
                SetThreadContext(hThread, &ctx);
            }
        }
        ContinueDebugEvent(dbgEvent.dwProcessId, dbgEvent.dwThreadId, DBG_CONTINUE);
    }
}

It may be worth noting that this is a C++ extension to a python script, but that shouldn't really be relevant, and even if I run the code standalone it runs into the same issues. Also, I'm not an expert in C++ so I apologize if there are any misunderstandings.

1 Answers

I agree with Taekahn, unless you give more information on what you are trying to do, why not use a normal debugger? if you just want to read the registers, any debugger can do that. You can even set breakpoints in disassembly or random addresses, or set data breakpoints.
IDK about gdb, but even in visual studio you can set conditions and actions on breakpoints. It's not as powerfull as c++ code but again, without more informations...

If you want to modify the behavior of the application, that's not how we do it, and using a debugger can actually cause issues.

Related