IsDebuggerPresent和DebugBreak在Windows项目中的应用

首先要从一篇文档说起,https://docs.microsoft.com/zh-cn/windows/win32/debug/basic-debugging,这篇文档介绍了Windows调试的相关内容,比如相关的调试术语Debugging Terminology,可以看一下,举个例子,为什么调试器可以动态修改变量的值,这里Process Functions for Debugging就给出了解释:

The OpenProcess function enables a debugger to obtain the identifier of an existing process. (The DebugActiveProcess function uses this identifier to attach the debugger to the process.) Typically, debuggers open a process with the PROCESS_VM_READ and PROCESS_VM_WRITE flags. Using these flags enables the debugger to read from and write to the virtual memory of the process by using the ReadProcessMemory and WriteProcessMemory functions. For more information, see Processes and Threads.

这里重点介绍下Debugging Functions中的两个函数:

IsDebuggerPresent

这个函数可以探测当前进程是否被调试器所调试。可以作反调试用途,如果发现自己被调试就立马进程自杀,当然,这是最简单的反调试手段,使用API Monitor可以看下相关进程有无使用反调试手段。

在这里插入图片描述

看资料说这种简单的反调试手段可以轻松过滤掉。

这里介绍一下我司项目中是如何应用这个函数的,背景是这样的:客户端程序有时候会莫名奔溃,且崩溃的地方发生在启动阶段,程序的PDB是有的。这样调试很不方便的,因为当你想远程调试的时候,程序已经奔溃了(虽然有DUMP,但也是Minidump),直接用VS启动远程调试也不如直接附加进程来得方便。因此,就用到了IsDebuggerPresent函数。

void Application::CheckWaitDebug()
{
    if (Config().GetBool(ZEUS_APP_WAIT_DEBUG, false))
    {
        LOG_INFO << "Wait Debug";
        while (!IsDebuggerPresent())
        {
            std::this_thread::sleep_for(1s);
        }
    }
}

main函数入口添加CheckWaitDebug,然后通过配置文件或者环境变量来控制是否等待调试器附加上。这样就可以很方便地用VS从程序的最开始来逐步调试目标程序,一步一步找到奔溃的地方,或者目标程序奔溃也是可以用VS来跟踪住的。

DebugBreak

这个函数可以在程序被调试的过程中触发调试断点,在VS调试器中进行压测的时候非常有用,当出现你想要的条件时,直接调用DebugBreak,触发断点,休眠所有线程,这个时候就可以查看现场了。

在这里插入图片描述

你可能会说直接在调试器中打断点不就好了吗,这样也是可以的。那就看一个Google的GTEST中用到这个函数的地方吧。

首先,利用googletest命令行参数简化单元测试中有一个参数--gtest_break_on_failure,这个函数可以在单元测试失败的时候在调试器中断点。这样可以保留单元测试失败的程序现场,便于查找问题。看看它是怎么实现的:googletest/googletest/src/gtest.cc

if (result_type != TestPartResult::kSuccess &&
      result_type != TestPartResult::kSkip) {
    // gtest_break_on_failure takes precedence over
    // gtest_throw_on_failure.  This allows a user to set the latter
    // in the code (perhaps in order to use Google Test assertions
    // with another testing framework) and specify the former on the
    // command line for debugging.
    if (GTEST_FLAG(break_on_failure)) {
#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
      // Using DebugBreak on Windows allows gtest to still break into a debugger
      // when a failure happens and both the --gtest_break_on_failure and
      // the --gtest_catch_exceptions flags are specified.
      DebugBreak();
#elif (!defined(__native_client__)) &&            \
    ((defined(__clang__) || defined(__GNUC__)) && \
     (defined(__x86_64__) || defined(__i386__)))
      // with clang/gcc we can achieve the same effect on x86 by invoking int3
      asm("int3");
#else
      // Dereference nullptr through a volatile pointer to prevent the compiler
      // from removing. We use this rather than abort() or __builtin_trap() for
      // portability: some debuggers don't correctly trap abort().
      *static_cast<volatile int*>(nullptr) = 1;
#endif  // GTEST_OS_WINDOWS

可以看到在Windows上就是用DebugBreak来实现的,同时可以看到Linux上使用汇编代码asm("int3")来实现GDB的断点。

其他

  • CheckRemoteDebuggerPresent <=> 查看指定进程是否被调试器附加
  • DebugBreakProcess <=> 给指定进程断点
  • OutputDebugString <=> 给调试器发送字符串,在上一幅图中,VS中的输出模块输出了FlushHip
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值