七哥向前冲

You got a dream, you gotta to protect it!

站内搜索

选择搜索引擎,输入关键词开始搜索

Google
Bing
Yahoo
百度
💡 小贴士:选择不同的搜索引擎可能会得到不同的搜索结果

软件反调试(8)- 基于SEH异常的检测

发布日期:2025-08-04 |文章分类: 逆向分析

反调原理

异常处理

SEH(Structured Exception Handling)是 windows 提供的一种结构化异常处理机制,用于捕捉并处理运行时异常(如除以零、访问无效内存等)

基本结构如下

__try {
    // 可能抛出异常的代码
}
__except(EXCEPTION_EXECUTE_HANDLER) {
    // 异常处理代码
}

目标程序在被调试时,程序触发的异常会被调试器优先处理,而不是程序的 SEH 来处理

调试器先收到 “first-chance exception” 通知,如果调试器选择不处理,异常将会传递给程序的 SEH 来进行处理

如果 SEH 也不处理该异常,调试器将会再次收到 “second-chance exception” 通知,这个就是 x64dbg 异常配置中第一次、第二次机会的含义

基于这个处理思路,那么可以在程序中主动触发一个异常,然后设置自己的 SEH 来处理它

如果程序能捕获并处理该异常,说明当前没有调试器干扰;如果异常没有进入自己的处理函数,被调试器抢先处理,说明当前正在被调试中

但是在现代的调试器中,大部分的异常都不会被调试器进行处理,比如程序抛出的除零异常

在 vs2022 中,如果勾选了除零异常(在 “调试” -> “窗口” -> “异常设置” 中),调试的时候就会中断在异常位置,F5 会继续执行,调试器并不会处理该异常

如果不勾选,那么异常发生的时候,是不会出现中断提示的

但是,有一种异常调试器是必须得处理的,那么就是 int 3 异常,这个就是调试器的异常,要理解这个异常,需要对程序是怎么调试有一点简单理解

调试异常

软件调试的断点分为三种:软件断点、内存断点、硬件断点,这里仅对软件断点进行描述

软件断点的本质是,在进行调试时候下的断点,断点指令的第一个字节被替换成为了 0xcc,也就是汇编 int 3 指令

当 eip 指令到该位置的时候,发现 int 3 指令,将会触发一个异常中断,这个异常就被调试器捕捉到了,因此程序在调试器中被中断下来

断点中断的时候,0xcc 恢复为原来的指令,中断运行过后,又被恢复为 0xcc,断点的指令情况可以从下面两个图来了解

但是在 vs2022 的汇编指令中,并不会显示 0xcc 指令,而是显示原来真实的指令,因此这里使用了 Process Hacker 查看进程的内存地址

代码中下了两个断点,第一个截图是中断在断点上的内存信息,第二个截图是调试到两个断点中间的内存信息

代码实现

基于以上的知识点,就可以进行反调代码的编写了,这里使用到了 int 3 指令,是需要在代码中嵌入一小段汇编

在 vs2022 的 64 位 c++ 程序不支持在代码中内嵌汇编,只能将汇编代码独立为一个额外的 asm 文件,如果需要内嵌汇编只能编译 32 位的版本

以下是反调的完整代码实现


#include <iostream>
#include <thread>
#include <string>
#include <windows.h>  

bool CheckProcessIsDebugging()
{
    bool isDebugged = true;
    __try {
        _asm int 3;
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        isDebugged = false;
    }
    return isDebugged;
}

void ThreadProc()
{
    while (true)
    {
        if (CheckProcessIsDebugging())
        {
            std::cout << "Debugging..." << std::endl;
        }
        else
        {
            std::cout << "Running..." << std::endl;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

int main()
{
    std::thread thrd(ThreadProc);
    thrd.join();
    return 0;
}

编译后在 vs2022 中调试运行,终端输出 Debugging... 的提示信息,符合预期的反调试;直接运行程序终端输出 Running... 信息

但是在 vs2022 调试器对 int 3 做了特殊处理,发现 int 3 的时候,始终会中断程序,无视你的异常设置

因此,取消 Breakpoint 的勾选并,不会导致 vs2022 不会中断在代码行上,如下图所示

逆向处理

鉴于 int 3 这些异常处理的反调试太过于基础,x32dbg/x64dbg 已经对这些反调进行了免疫,只需要将异常处理者设为 “被调试程序”,那么所以异常统统都不处理

其实这个反调已经没有逆向的必要了,如果需要逆向绕过的话,可以将这段 int 3 指令直接 nop 掉即可,这里就不描述了