异常处理机制流程分析
字数 1311 2025-08-05 08:20:14

异常处理机制流程分析与反调试技术

前言

异常处理机制在二进制安全领域常用于动态反调试技术。当正常运行的进程发生异常时,在SEH(Structured Exception Handling)机制作用下,操作系统会接收异常并调用进程中注册的SEH处理程序。但如果进程正在被调试器调试,调试器会先于SEH接收处理异常。利用这一特性可以判断进程是正常运行还是调试运行,从而实现反调试。

异常处理流程分析

32位异常处理完整流程

  1. KiUserDispatcher阶段

    • 首先判断是64位异常还是32位异常
  2. VEH(Vectored Exception Handling)链处理

    • 遍历所有注册的VEH处理程序
  3. SEH(Structured Exception Handling)链处理

    • 遍历SEH处理程序
    • 倒数第二个SEH会调用UnHandledExceptionFilter
      • 内部调用NtQueryInformationProcess查询DebugPort
      • 然后调用TopLevelExceptionFilter处理
  4. 返回阶段

    • 通过zwcontinue进行返回

异常分发详细机制

  1. 首次分发

    • KiDispatchException尝试将异常分发给用户态调试器
    • 如果DebugPort不为空,将异常发送给调试子系统
    • 调试子系统将异常发送给调试器
    • 如果调试器处理了异常,分发结束
  2. 调试器未处理的情况

    • KiDispatchException修改用户态栈
    • 返回用户层后执行KiUserExceptionDispatcher
      • 调用RtlDispatchException寻找异常处理器
        • 首先遍历VEH
        • 然后遍历SEH
    • 如果RtlDispatchException返回FALSE且进程在被调试:
      • 调用ZwRaiseException并将FirstChance设置为FALSE(第二轮分发)
    • 如果没有被调试,结束进程
  3. 第二轮分发

    • ZwRaiseException通过内核服务NtRaiseException把异常传递给KiDispatchException
    • 第二次将异常传递给调试器
    • 如果仍未处理,将异常分配给ExceptionPort异常端口监听者处理
    • 如果返回FALSE,结束进程

实例分析

程序主逻辑

v7 = 0;
memset(&v8, 0, 0x4Fu);
v10 = dword_401360;
sub_401060("input your flag : ", v6);
v5 = (int **)80;
sub_4010D0("%s", (unsigned int)&v7);
v13 = &v7;
v12 = &v8;
v13 += strlen(v13);
v11 = ++v13 - &v8;
if (v13 - &v8 == 16) // 判断输入长度是否为16
{
    v9 = &v4;
    v5 = &v10;
    wsprintfA(&v4, "%s", &v10);
    if ((unsigned __int8)sub_401860(&v7))
        sub_401060("Good ,u success! \n", v6);
    else
        sub_401060("Wrong,u lose! \n", v6);
    j___fgetchar();
    result = j___fgetchar();
}
else
{
    sub_401060("wrong u lose \n", v6);
    result = 0;
}
return result;

关键函数分析

sub_401860函数

bool __cdecl sub_401860(_BYTE *a1)
{
    signed int i; // [esp+0h] [ebp-4h]
    *a1 *= 2;
    a1[1] >>= 3;
    a1[2] >>= 4;
    a1[3] >>= 88;
    a1[4];
    a1[4] = 0;
    a1[7] ^= 0xAu;
    a1[8] += 61;
    a1[9] /= 8;
    a1[10] %= 4;
    a1[11] ^= 0xCu;
    for (i = 0; i < 10; ++i)
        a1[i] = sub_4017E0(a1[i], a1[i+1]);
    a1[12] ^= a1[11];
    a1[13] ^= a1[12];
    a1[14] ^= a1[13];
    a1[15] ^= a1[14];
    return sub_401390(i) != 0;
}

sub_401390函数

int __usercall sub_401390@<eax>(int a1@<eax>, int a2@<edx>, int a3@<ecx>, int a4@<ebx>, 
                               int a5@<ebp>, int a6@<edi>, int a7@<esi>, int a8, 
                               const char **a9, const char **a10)
{
    *(_DWORD *)(a5 - 4) = a1;
    *(_DWORD *)(a5 - 8) = a4;
    *(_DWORD *)(a5 - 12) = a3;
    *(_DWORD *)(a5 - 16) = a2;
    *(_DWORD *)(a5 - 20) = a6;
    *(_DWORD *)(a5 - 24) = a7;
    if (*(_DWORD *)(a5 - 4) == 1466715468 && 
        *(_DWORD *)(a5 - 8) == 794374773 && 
        *(_DWORD *)(a5 - 12) == 1664641876 && 
        *(_DWORD *)(a5 - 16) == 727266099 && 
        *(_DWORD *)(a5 - 20) == 1984706644)
    {
        *(_DWORD *)(a5 - 24);
    }
    return main(a8, a9, a10);
}

异常处理关键点

  1. 程序在0x401360处触发异常
  2. 自定义VEH处理程序
    LONG __stdcall Handler(struct _EXCEPTION_POINTERS *ExceptionInfo)
    {
        // 处理逻辑...
        return 0;
    }
    
  3. TopLevelExceptionFilter处理
    LONG __stdcall TopLevelExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)
    {
        ExceptionInfo->ContextRecord->Eax = ExceptionInfo->ExceptionRecord->ExceptionInformation[0];
        ExceptionInfo->ContextRecord->Ebx = ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
        ExceptionInfo->ContextRecord->Ecx = ExceptionInfo->ExceptionRecord->ExceptionInformation[2];
        ExceptionInfo->ContextRecord->Edx = ExceptionInfo->ExceptionRecord->ExceptionInformation[3];
        ExceptionInfo->ContextRecord->Edi = ExceptionInfo->ExceptionRecord->ExceptionInformation[4];
        ExceptionInfo->ContextRecord->Esi = ExceptionInfo->ExceptionRecord->ExceptionInformation[5];
        ExceptionInfo->ContextRecord->Eip = (DWORD)sub_401390;
        SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
        RemoveVectoredExceptionHandler(Handler);
        return -1;
    }
    

加密流程分析

  1. 4轮异或操作
  2. 自定义Base64加密
    • 字母表被修改为:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/
  3. 解密脚本示例
import binascii

base64_table = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/'
base_encode = str(raw_input(u"请输入解密字符"))
counter = base_encode.count("=")
length = len(base_encode)
encode = ""
encode_re = ""

if (counter == 2):
    a = base64_table.find(base_encode[length-4:length-3])  # 取前六位
    a = a << 2
    b = base64_table.find(base_encode[length-3:length-2])  # 取2位
    b = b >> 4
    encode_re = chr(a + b)

if (counter == 1):
    a = base64_table.find(base_encode[length-4:length-3])  # 第一个字符前6位
    a = a << 2
    b = base64_table.find(base_encode[length-3:length-2])  # 第二个字符前2位
    b = b >> 4
    encode_re1 = chr(a + b)
    
    a = base64_table.find(base_encode[length-3:length-2])  # 第二个字符后4位
    a = (a & 0xf) << 4
    b = base64_table.find(base_encode[length-2:length-1])  # 第三个字符前4位
    b = b >> 2
    encode_re2 = chr(a + b)
    encode_re = encode_re1 + encode_re2
    length = length - 4

if (counter == 0):
    length = length + 4

for i in range(0, length, 4):  # 以4个字符为一组
    a = base64_table.find(base_encode[i:i+1])  # 第一个字符6位
    a = a << 2
    b = base64_table.find(base_encode[i+1:i+2])  # 第二个字符前2位
    b = b >> 4
    encode = encode + chr(a + b)
    
    a = base64_table.find(base_encode[i+1:i+2])  # 第二个字符后4位
    a = ((a & 0xf) << 4)
    b = base64_table.find(base_encode[i+2:i+3])  # 第三个字符前4位
    b = b >> 2
    encode = encode + chr(a + b)
    
    a = base64_table.find(base_encode[i+2:i+3])  # 取第三个字符后2位
    a = (a & 3) << 6
    b = base64_table.find(base_encode[i+3:i+4])  # 取第四个字符6位
    encode = encode + chr(a + b)

encode = encode + encode_re
print(binascii.b2a_hex(encode))
print('\n')

总结

通过分析异常处理机制,我们可以:

  1. 理解Windows平台下异常处理的完整流程
  2. 掌握利用异常处理机制实现反调试的技术原理
  3. 学习如何分析复杂的加密和异常处理结合的二进制程序
  4. 掌握自定义Base64等加密算法的逆向分析方法

最终通过逆向分析得到的flag为:flag{Ex<ept10n~}

异常处理机制流程分析与反调试技术 前言 异常处理机制在二进制安全领域常用于动态反调试技术。当正常运行的进程发生异常时,在SEH(Structured Exception Handling)机制作用下,操作系统会接收异常并调用进程中注册的SEH处理程序。但如果进程正在被调试器调试,调试器会先于SEH接收处理异常。利用这一特性可以判断进程是正常运行还是调试运行,从而实现反调试。 异常处理流程分析 32位异常处理完整流程 KiUserDispatcher阶段 首先判断是64位异常还是32位异常 VEH(Vectored Exception Handling)链处理 遍历所有注册的VEH处理程序 SEH(Structured Exception Handling)链处理 遍历SEH处理程序 倒数第二个SEH会调用 UnHandledExceptionFilter 内部调用 NtQueryInformationProcess 查询DebugPort 然后调用 TopLevelExceptionFilter 处理 返回阶段 通过 zwcontinue 进行返回 异常分发详细机制 首次分发 KiDispatchException 尝试将异常分发给用户态调试器 如果DebugPort不为空,将异常发送给调试子系统 调试子系统将异常发送给调试器 如果调试器处理了异常,分发结束 调试器未处理的情况 KiDispatchException 修改用户态栈 返回用户层后执行 KiUserExceptionDispatcher 调用 RtlDispatchException 寻找异常处理器 首先遍历VEH 然后遍历SEH 如果 RtlDispatchException 返回FALSE且进程在被调试: 调用 ZwRaiseException 并将FirstChance设置为FALSE(第二轮分发) 如果没有被调试,结束进程 第二轮分发 ZwRaiseException 通过内核服务 NtRaiseException 把异常传递给 KiDispatchException 第二次将异常传递给调试器 如果仍未处理,将异常分配给ExceptionPort异常端口监听者处理 如果返回FALSE,结束进程 实例分析 程序主逻辑 关键函数分析 sub_ 401860函数 sub_ 401390函数 异常处理关键点 程序在0x401360处触发异常 自定义VEH处理程序 TopLevelExceptionFilter处理 加密流程分析 4轮异或操作 自定义Base64加密 字母表被修改为: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/ 解密脚本示例 总结 通过分析异常处理机制,我们可以: 理解Windows平台下异常处理的完整流程 掌握利用异常处理机制实现反调试的技术原理 学习如何分析复杂的加密和异常处理结合的二进制程序 掌握自定义Base64等加密算法的逆向分析方法 最终通过逆向分析得到的flag为: flag{Ex<ept10n~}