Windows逆向工程提升之IMAGE_RUNTIME_FUNCTION_ENTRY

目录

异常处理信息在 PE 文件中的存放

核心数据结构

RUNTIME_FUNCTION

UNWIND_INFO

UNWIND_CODE

SCOPE_TABLE

图解

与 x86 平台 SEH 的对比

x86 SEH

x64 SEH

执行图解

IMAGE_DATA_DIRECTORY

IMAGE_RUNTIME_FUNCTION

UNWIND_INFO

UNWIND_CODE

SCOPE_TABLE


异常处理信息在 PE 文件中的存放

  • PE异常目录: 在 PE 文件的 Data Directory 中,IMAGE_DIRECTORY_ENTRY_EXCEPTION 指向异常表,通常位于 .pdata 段。该段中包含整个模块中所有函数的异常信息条目。
  • 功能: Windows x64 不在每个函数的栈中设置 SEH 帧,而是在静态数据(即异常表中)记录每个函数的异常和栈展开信息,在异常发生时,通过查询该表快速定位出当前函数对应的 RUNTIME_FUNCTION 数据,从而执行栈展开。

核心数据结构

RUNTIME_FUNCTION

typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {  
  DWORD BeginAddress;   // 函数起始 RVA,相对于模块基址  
  DWORD EndAddress;     // 函数结束 RVA  
  union {  
    DWORD UnwindInfoAddress;  // 指向 UNWIND_INFO 结构的 RVA(通常)   
    DWORD UnwindData;  
  } DUMMYUNIONNAME;  
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;  
  •  每个 RUNTIME_FUNCTION 条目描述一个函数的异常与栈展开信息。
  • 在异常处理过程中,系统会根据错误地址在该表中查找匹配的函数范围。如果找到匹配的 BeginAddress 与 EndAddress,则根据 UnwindInfoAddress 获取对应的展开信息。

UNWIND_INFO

typedef struct _UNWIND_INFO {  
    UCHAR Version : 3;           // 版本号(通常为1)  
    UCHAR Flags : 5;             // 标志(如是否包含异常处理程序)
    UCHAR SizeOfProlog;          // 函数序言(Prolog)的字节数  
    UCHAR CountOfCodes;          // 展开代码条目数量(数组中 UNWIND_CODE 个数)  
    UCHAR FrameRegister : 4;     // 帧指针寄存器(如 RBP、RDI 等)  
    UCHAR FrameOffset   : 4;     // 帧指针偏移,用 16 字节单位(FP = RSP + FrameOffset * 16)  
    UNWIND_CODE UnwindCode[1];   // 不定长度数组,描述具体的栈展开操作  
    // 后续紧跟可选字段:  
    // 1. 如果设置了 UNW_FLAG_EHANDLER 或 UNW_FLAG_UHANDLER,则紧跟有 ExceptionHandler 和 ExceptionData。  
    // 2. 如果设置了 UNW_FLAG_CHAININFO,则后续为一个 RUNTIME_FUNCTION 结构。  
} UNWIND_INFO, *PUNWIND_INFO;  

UNWIND_CODE

typedef union _UNWIND_CODE {  
    struct {  
        UBYTE CodeOffset;     // 在函数中相对于 Prolog 起始处的偏移量,指定该操作的开始位置  
        UBYTE UnwindOp : 4;   // 展开操作类型,属于 UNWIND_OP_CODES 之一  
        UBYTE OpInfo   : 4;   // 补充信息,根据不同的 UnwindOp 含义不同  
    };  
    USHORT FrameOffset;       // 某些操作直接用来描述栈帧内偏移的值  
} UNWIND_CODE, *PUNWIND_CODE;  

typedef enum _UNWIND_OP_CODES {
    UWOP_PUSH_NONVOL = 0, /* info == register number */
    UWOP_ALLOC_LARGE,     /* no info, alloc size in next 2 slots */
    UWOP_ALLOC_SMALL,     /* info == size of allocation / 8 - 1 */
    UWOP_SET_FPREG,       /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
    UWOP_SAVE_NONVOL,     /* info == register number, offset in next slot */
    UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
    UWOP_SAVE_XMM128 = 8, /* info == XMM reg number, offset in next slot */
    UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
    UWOP_PUSH_MACHFRAME   /* info == 0: no error-code, 1: error-code */
} UNWIND_CODE_OPS;

SCOPE_TABLE

typedef struct _SCOPE_TABLE {  
    DWORD Count;  
    struct {  
        DWORD BeginAddress;  
        DWORD EndAddress;  
        DWORD HandlerAddress;  
        DWORD JumpTarget;  
    } ScopeRecord[1];  
} SCOPE_TABLE, *PSCOPE_TABLE;  

图解

┌──────────────────────┐        ┌──────────────────────┐
│ RUNTIME_FUNCTION     │        │ UNWIND_INFO          │
├──────────────────────┤        ├──────────────────────┤
│ BeginAddress         │        │ Version/Flags        │
│ EndAddress           │        │ SizeOfProlog         │
│ UnwindInfoAddress    |─────── | CountOfCodes         │
└──────────────────────┘        │ FrameRegister/Offset │
                                │ UnwindCode[]         │
                                │ ExceptionHandler      ─┐
                                └──────────────────────┘ │
                                                         │
                                                         ▼
                                ┌──────────────────────┐
                                │ SCOPE_TABLE          │
                                ├──────────────────────┤
                                │ Count                │
                                │ ScopeRecord[0..N]    │
                                └──────────────────────┘

与 x86 平台 SEH 的对比

x86 SEH

  • 基于链表机制,在每个函数的栈上构造 SEH 帧(链表节点)来捕获异常信息。
  • 异常处理信息嵌入在函数栈区,处理过程依赖于栈上注册的异常处理例程。

x64 SEH

  • 将所有异常相关信息集中存放于静态数据区(PE 文件中的异常目录),运行时通过查询定位当前函数展开信息。
  • 栈展开完全依赖预编译生成的 UNWIND_INFO 和 UNWIND_CODE,由操作系统提供支持。

执行图解

IMAGE_DATA_DIRECTORY

IMAGE_RUNTIME_FUNCTION

UNWIND_INFO

UNWIND_CODE

SCOPE_TABLE

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值