分享一次 C++ PWN 出题经历——深入研究异常处理机制
字数 1593 2025-08-20 18:17:07

C++异常处理机制与PWN出题实践深度解析

1. C++异常处理机制基础

1.1 SEH (Structured Exception Handling)

SEH是Windows操作系统提供的结构化异常处理机制,主要由以下组件构成:

  • EXCEPTION_REGISTRATION_RECORD:异常处理链节点结构
  • EXCEPTION_DISPOSITION:异常处理函数返回值类型
  • _EXCEPTION_POINTERS:包含异常信息的结构体

关键数据结构:

typedef struct _EXCEPTION_REGISTRATION_RECORD {
    struct _EXCEPTION_REGISTRATION_RECORD* Next;
    PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD;

typedef enum _EXCEPTION_DISPOSITION {
    ExceptionContinueExecution,
    ExceptionContinueSearch,
    ExceptionNestedException,
    ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;

typedef struct _EXCEPTION_POINTERS {
    PEXCEPTION_RECORD ExceptionRecord;
    PCONTEXT ContextRecord;
} EXCEPTION_POINTERS;

1.2 C++异常处理实现

C++异常处理在Windows上通过__CxxFrameHandler3实现,主要流程:

  1. 异常发生时,系统遍历SEH链
  2. 调用每个处理器的Handler函数
  3. __CxxFrameHandler3检查是否能处理当前异常类型
  4. 若能处理则执行栈展开(Stack Unwind)和catch块代码

2. 异常处理机制逆向分析

2.1 关键函数调用链

异常处理的核心调用链:

_KiUserExceptionDispatcher
→ RtlDispatchException
→ RtlpExecuteHandlerForException
→ __CxxFrameHandler3
→ CatchIt (找到匹配的catch块)
→ __CxxCallCatchBlock

2.2 栈展开过程

栈展开(Stack Unwind)的关键步骤:

  1. 调用当前栈帧中局部对象的析构函数
  2. 调用_UnwindNestedFrames进行嵌套展开
  3. 更新SEH链和线程环境块(TEB)中的信息

逆向分析要点:

  • 查找__unwindfunclet$开头的函数,这些是展开时调用的析构函数
  • scopetable数组存储了每个try块的展开信息
  • FuncInfo结构体包含异常处理的关键信息

3. C++异常处理漏洞利用

3.1 常见攻击面

  1. SEH链覆盖:通过缓冲区溢出覆盖SEH链中的Handler指针
  2. 虚函数表篡改:利用异常处理期间的对象析构过程
  3. 异常处理信息篡改:修改FuncInfo或scopetable数据

3.2 利用技术细节

SEH链覆盖利用步骤:

  1. 通过溢出覆盖SEH链节点
  2. 将Handler指针指向攻击者控制的shellcode或ROP链
  3. 触发异常使控制流跳转到篡改的Handler

虚函数表攻击步骤:

  1. 溢出覆盖对象的虚函数表指针
  2. 触发异常导致对象析构
  3. 析构时通过虚函数表调用被篡改的函数指针

4. PWN题目设计实践

4.1 题目设计思路

基于C++异常处理机制的PWN题目典型设计:

  1. 实现一个存在缓冲区溢出漏洞的C++类
  2. 在类中使用异常处理机制(try-catch)
  3. 精心构造内存布局使溢出可覆盖关键异常处理结构

4.2 示例题目分析

class Vulnerable {
public:
    Vulnerable(int size) {
        buffer = new char[size];
        bufferSize = size;
    }
    
    ~Vulnerable() {
        delete[] buffer;
    }
    
    void SetData(const char* data, unsigned int len) {
        if (len > bufferSize) {
            throw std::runtime_error("Buffer overflow");
        }
        memcpy(buffer, data, len);  // 漏洞点:无边界检查的拷贝
    }
    
private:
    char* buffer;
    int bufferSize;
};

void Test() {
    try {
        Vulnerable v(32);
        char data[64];
        memset(data, 'A', sizeof(data));
        v.SetData(data, sizeof(data));  // 触发溢出
    } catch (std::exception& e) {
        // 异常处理
    }
}

4.3 漏洞利用链构造

  1. 溢出覆盖虚函数表指针或SEH Handler
  2. 通过异常触发控制流转移
  3. 利用ROP或shellcode实现权限提升

5. 防护与检测技术

5.1 现代防护机制

  1. SafeSEH:校验SEH Handler是否在合法模块中
  2. SEHOP:验证SEH链完整性
  3. CFG (Control Flow Guard):保护间接调用
  4. 堆栈Cookie:防止栈缓冲区溢出

5.2 绕过技术

  1. ROP链构造:使用合法模块中的gadget
  2. 堆喷射:在可预测地址布置shellcode
  3. 信息泄露:绕过ASLR保护

6. 调试与分析技巧

6.1 关键调试命令

Windbg常用命令:

!exchain                  # 查看SEH链
.frame                    # 切换栈帧
!heap                     # 查看堆信息
!vprot                    # 查看内存页属性

6.2 异常处理断点

设置关键断点:

bm /a *!__CxxFrameHandler3
bm /a *!_CxxThrowException

7. 进阶研究方向

  1. C++异常处理与CFG的交互:研究控制流保护下的异常处理
  2. 跨平台异常处理差异:对比Windows SEH与Linux DWARF实现
  3. 异常处理与虚拟化保护:分析加壳程序中的异常处理机制
  4. C++20协程与异常处理:研究新特性对异常机制的影响

附录:关键数据结构与函数原型

FuncInfo结构体

struct FuncInfo {
    int magicNumber;
    int maxState;
    int pUnwindMap;        // 展开映射表
    int pTryBlockMap;      // try块映射表
    int dwFrameSize;       // 帧大小
    // ... 其他成员
};

scopetable条目

struct scopetable_entry {
    int enclosingLevel;    // 封闭层级
    int filterFunc;        // 过滤器函数
    int handlerFunc;       // 处理函数
};

_CxxThrowException函数

void __stdcall _CxxThrowException(
    void* pExceptionObject, 
    _ThrowInfo* pThrowInfo
);

通过深入理解这些机制,安全研究人员可以更好地分析C++程序的异常处理流程,发现潜在漏洞,并设计出高质量的PWN题目。

C++异常处理机制与PWN出题实践深度解析 1. C++异常处理机制基础 1.1 SEH (Structured Exception Handling) SEH是Windows操作系统提供的结构化异常处理机制,主要由以下组件构成: EXCEPTION_ REGISTRATION_ RECORD :异常处理链节点结构 EXCEPTION_ DISPOSITION :异常处理函数返回值类型 \_EXCEPTION_ POINTERS :包含异常信息的结构体 关键数据结构: 1.2 C++异常处理实现 C++异常处理在Windows上通过 __CxxFrameHandler3 实现,主要流程: 异常发生时,系统遍历SEH链 调用每个处理器的Handler函数 __CxxFrameHandler3 检查是否能处理当前异常类型 若能处理则执行栈展开(Stack Unwind)和catch块代码 2. 异常处理机制逆向分析 2.1 关键函数调用链 异常处理的核心调用链: 2.2 栈展开过程 栈展开(Stack Unwind)的关键步骤: 调用当前栈帧中局部对象的析构函数 调用 _UnwindNestedFrames 进行嵌套展开 更新SEH链和线程环境块(TEB)中的信息 逆向分析要点: 查找 __unwindfunclet$ 开头的函数,这些是展开时调用的析构函数 scopetable 数组存储了每个try块的展开信息 FuncInfo 结构体包含异常处理的关键信息 3. C++异常处理漏洞利用 3.1 常见攻击面 SEH链覆盖 :通过缓冲区溢出覆盖SEH链中的Handler指针 虚函数表篡改 :利用异常处理期间的对象析构过程 异常处理信息篡改 :修改FuncInfo或scopetable数据 3.2 利用技术细节 SEH链覆盖利用步骤: 通过溢出覆盖SEH链节点 将Handler指针指向攻击者控制的shellcode或ROP链 触发异常使控制流跳转到篡改的Handler 虚函数表攻击步骤: 溢出覆盖对象的虚函数表指针 触发异常导致对象析构 析构时通过虚函数表调用被篡改的函数指针 4. PWN题目设计实践 4.1 题目设计思路 基于C++异常处理机制的PWN题目典型设计: 实现一个存在缓冲区溢出漏洞的C++类 在类中使用异常处理机制(try-catch) 精心构造内存布局使溢出可覆盖关键异常处理结构 4.2 示例题目分析 4.3 漏洞利用链构造 溢出覆盖虚函数表指针或SEH Handler 通过异常触发控制流转移 利用ROP或shellcode实现权限提升 5. 防护与检测技术 5.1 现代防护机制 SafeSEH :校验SEH Handler是否在合法模块中 SEHOP :验证SEH链完整性 CFG (Control Flow Guard):保护间接调用 堆栈Cookie :防止栈缓冲区溢出 5.2 绕过技术 ROP链构造 :使用合法模块中的gadget 堆喷射 :在可预测地址布置shellcode 信息泄露 :绕过ASLR保护 6. 调试与分析技巧 6.1 关键调试命令 Windbg常用命令: 6.2 异常处理断点 设置关键断点: 7. 进阶研究方向 C++异常处理与CFG的交互 :研究控制流保护下的异常处理 跨平台异常处理差异 :对比Windows SEH与Linux DWARF实现 异常处理与虚拟化保护 :分析加壳程序中的异常处理机制 C++20协程与异常处理 :研究新特性对异常机制的影响 附录:关键数据结构与函数原型 FuncInfo结构体 scopetable条目 _ CxxThrowException函数 通过深入理解这些机制,安全研究人员可以更好地分析C++程序的异常处理流程,发现潜在漏洞,并设计出高质量的PWN题目。