简单的调试器和硬件断点在x64中的Windows 10


1

我已经编写了一个非常简单的调试器x64,我目前使用的是Win10。

我想设置一些硬件断点由我调试循环如下困:

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); 
} 
} 

我打电话 ThreadsLoop(pi.dwProcessId);其中pi是在PROCESS_INFORMATION结构从我最初的调用返回:

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

没有任何硬件断点被击中。我做了几次改变DR0-DR7(以及条件和长度变量)的尝试,但没有成功。所有对SetThreadContext的调用都成功返回。

我通过下面的代码做了与软件断点相同的测试和完美的作品:

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

什么可以在这个代码/做法是错误的?

感谢

  0

已经以改变DR7的值,然后通过调用GetThreadContext来设置它的方式实现“SETBITS”功能?... 05 12月. 172017-12-05 16:20:39

+1

为了更清楚你发送一些输入的功能,并没有收到输出只是设置具有相同的DR7的上下文:)我想你现在明白了什么是错误的... 05 12月. 172017-12-05 16:38:14

  0

感谢您指出这一点。我修复了代码。仍然断点不起作用。 05 12月. 172017-12-05 17:23:51

+1

现在你又遇到了问题......再看一遍。你正在改变指针本身而不是指针指向的值。 05 12月. 172017-12-05 18:11:30

  0

谢谢,我解决了。这是一个错字。该代码仍然不是英国石油公司。 05 12月. 172017-12-05 18:25:22

  0

我也没有看到你定义硬件bp类型的任何地方 05 12月. 172017-12-05 19:41:38

  0

让我们[在聊天中继续讨论](http://chat.stackexchange.com/rooms/69771/discussion-between-fred26-and-ewd-0) 05 12月. 172017-12-05 21:00:39

  0

那些把这个搁置,因为他们认为这与逆转无关,必须获得更多有关逆转的信息......逆转不仅仅是使用调试器!这也是关于开发调试/反转和分析可执行文件的工具。与调试器相关的问题都与该主题相关! 11 12月. 172017-12-11 10:32:45

  0

由于@ EWD-0-表示关于如何实现调试器API的文档很少或根本没有,所以我认为这个问题是开放的,因为它是逆向工程的一部分。 12 12月. 172017-12-12 11:31:04

  0

@ fre26非常好的一点......其实我们正在做sm倒车,看看它是如何工作的,虽然我发布了链接,解释了在硬件情况下我们收到哪个事件。 12 12月. 172017-12-12 11:50:12

1

有一些问题需要修复

  1. 请看一看This哪里是解释了事件的硬件断点情况下被触发。你必须添加EXCEPTION_SINGLE_STEPDebuggerThreadProc()函数

  2. 你需要为了能够调试它被附加到目标进程。为此,您需要使用 DebugActiveProcess

  3. 这是一个相当的语义错误。当您在同一个应用程序中创建目标进程时,您已经拥有对进程和主线程的打开句柄,因此不需要创建快照并搜索目标!这完全是多余的。请删除所有这些部件,并将这些手柄作为输入传递给这些功能。

  4. 请务必记住关闭您打开的手柄。

  0

你的** 2 **是错误的。如果我们使用'DEBUG_ONLY_THIS_PROCESS'或'DEBUG_PROCESS'标志创建进程,则不需要调用'DebugActiveProcess'。也不仅需要处理'EXCEPTION_SINGLE_STEP',而且还需要处理'STATUS_WX86_SINGLE_STEP' - 这将是我们调试wow64进程 14 12月. 172017-12-14 14:49:51

  0

的最后一个问题,在我提出的问题中,提到目标是x64。如果没有,还有其他API必须添加,以使它不仅兼容STATUS_WX86_SINGLE_STEP。在这里我没有提到的其他事情是,尽管值可能相同,但使用EXCEPTION_BREAKPOINT而不是STATUS ... 14 12月. 172017-12-14 15:04:34

  0

'STATUS_WX86_SINGLE_STEP'(和断点上的“STATUS_WX86_BREAKPOINT”)在异常时发生在wow64代码中。而调试器是64位进程。如果我们调试x64进程,我们当然永远不会得到这个异常代码。然而,总是更好地处理它,因为不仅限于x64应用程序的自我。 *还有其他API必须添加,以使其兼容*不知道你的意思,但完美的X64调试器可以在没有任何特殊API调用的情况下使用单个代码库调试32和64进程 14 12月. 172017-12-14 15:12:19

  0

正如Igor在聊天中提到的,考虑以Wow64Get/SetThreadContext为例。我认为最好的办法是把你的观点加入另一个答案......比评论更好。 14 12月. 172017-12-14 16:48:51

  0

关于从DEBUG_ONLY_THIS_PROCESS/DEBUG_PROCESS开始,你是对的,这也是另一种接收dbg事件的方式。 “2”没有错,它是其中一种方式,也是你提到的另一种方式。 14 12月. 172017-12-14 16:57:20

  0

当我说不需要单独的'DebugActiveProcess'我的意思是只有具体的情况 - 我们开始创建过程与调试。 ''DebugActiveProcess'需要当我们想要调试已经运行的进程时,我们不创建。但是,即使在这种情况下,这个api也不是最好的,因为在目标进程中创建额外的线程,这绝对不是必需的。我更喜欢'DbgUiDebugActiveProcess'在这里使用。 14 12月. 172017-12-14 19:17:53

  0

关于单独的Wow64上下文 - 这是可选的,几乎不需要。当我们从线程中得到异常时 - 总是需要使用原生的64位上下文。完全调用Get/SetThreadContext。当我们调试wow64代码时 - wow64上下文与64位上下文相同(在意义上,Eip == Rip,Esp == Rsp,Eax == Rax .. *)。在我们进入64门('CpupReturnFromSimulatedCode')之后 - 前几条指令Wow64上下文无效,然后Wow64上下文指向我们跳转到本地代码的上下文,直到我们返回到wow64。因此调试器必须为主要任务准确调用Get/SetThreadContext。 14 12月. 172017-12-14 19:17:59

  0

可能会说Get/SetThreadContext总是返回线程的真实/实际用户模式上下文(无关紧要,这是来自32或64位进程的线程)。 wow64上下文实际上只是线程执行的一部分的子上下文 - 当线程在wow64段时等于本地上下文,并且直到线程在本地段中执行时才改变。它也可以通过'ZwQueryInformationThread(.. ThreadWow64Context ..)'查询 14 12月. 172017-12-14 19:30:29