SEH及逆向调试

14 篇文章 0 订阅

SEH


SEH除了异常处理,还大量用于反调试。

SEH在代码中使用__try, __except, __finally关键字实现。

与c++的异常处理不同,不要混淆。微软SEH要早一些。

1. 异常处理方法

正常运行的异常处理

有处理代码则顺利处理,没有实现SEH则启动默认处理,终止进程。

调试运行的异常处理

调试器几乎拥有被调试者的所有权限,异常也优先由调试器处理。这时调试器会暂停,处理异常后继续调试。

下面是几种处理方法。

  • 调试器直接修改异常:代码、寄存器、内存
  • 抛给被调试者,shift+f7/f8/f9即可
  • 调试器和被调试者都不能处理,则由OS默认方式处理

2. 常见异常

EXCEPTION_RECORD结构体有一个ExceptionCode成员,存有异常种类码值。

下一部分会再说这个结构体,先说下几个重要的ExceptionCode。

EXCEPTION_ACCESS_VIOLATION

#define EXCEPTION_ACCESS_VIOLATION          STATUS_ACCESS_VIOLATION
#define STATUS_ACCESS_VIOLATION          ((DWORD   )0xC0000005L)  

触发:

mov dword ptr ds:[0], 1
add dword ptr ds:[401000], 1
xor dword ptr ds:[8000 0000], 1234h ;kernel address

EXCEPTION_BREAKPOINT

#define EXCEPTION_BREAKPOINT                STATUS_BREAKPOINT
#define STATUS_BREAKPOINT                ((DWORD   )0x80000003L)    

调试器就是利用这个异常实现断点功能的。比如f2设置cc后会触发,但反汇编窗口并不会显示cc,用PETools将进程内存dump后就能看到cc了。

有一个调试方法,在注册表中将默认调试器改为OD,可以把一个目标文件的EP处字节改为cc,一运行就直接attach。

EXCEPTION_ILLEGAL_INSTRUCTION

#define EXCEPTION_ILLEGAL_INSTRUCTION       STATUS_ILLEGAL_INSTRUCTION
#define STATUS_ILLEGAL_INSTRUCTION       ((DWORD   )0xC000001DL)    

遇到无法解析的OPCODE时会触发,比如x86没有0FFF

EXCEPTION_INT_DIVIDE_BY_ZERO

#define EXCEPTION_INT_DIVIDE_BY_ZERO        STATUS_INTEGER_DIVIDE_BY_ZERO
#define STATUS_FLOAT_DIVIDE_BY_ZERO      ((DWORD   )0xC000008EL)    
mov eax, 1
xor ebx, ebx
div ebx

EXCEPTION_SINGLE_STEP

#define EXCEPTION_SINGLE_STEP               STATUS_SINGLE_STEP
#define STATUS_SINGLE_STEP               ((DWORD   )0x80000004L)    

cpu在单步模式下,每执行一条指令就会触发该异常。将EFLAG的TF(trap,陷阱)置1,就进入单步模式。

3. SEH详细说明

SEH以链的形式存在,将异常依次传递,直到处理。

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

若Next为-1,则说明这是链表最后一个节点。

回调函数

回调处理函数原型如下:

typedef
_IRQL_requires_same_
_Function_class_(EXCEPTION_ROUTINE)
EXCEPTION_DISPOSITION
NTAPI
EXCEPTION_ROUTINE (
    _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
    _In_ PVOID EstablisherFrame,
    _Inout_ struct _CONTEXT *ContextRecord,
    _In_ PVOID DispatcherContext
    );

typedef EXCEPTION_ROUTINE *PEXCEPTION_ROUTINE;

ExceptionRecord

第一个参数是刚刚整理异常时提到的结构体指针。

//
// Exception record definition.
//

typedef struct _EXCEPTION_RECORD {
    DWORD    ExceptionCode;
    DWORD ExceptionFlags;
    struct _EXCEPTION_RECORD *ExceptionRecord;
    PVOID ExceptionAddress;
    DWORD NumberParameters;
    ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
    } EXCEPTION_RECORD;

typedef EXCEPTION_RECORD *PEXCEPTION_RECORD;

EstablisherFrame

指向第一个节点(低地址)的位置。

ContextRecord

ContextRecord指向Context结构体,用来备份寄存器的值,也就是书上常说的上下文环境。

每个线程都有这么一个结构体。

typedef struct _CONTEXT {

    //
    // The flags values within this flag control the contents of
    // a CONTEXT record.
    //
    // If the context record is used as an input parameter, then
    // for each portion of the context record controlled by a flag
    // whose value is set, it is assumed that that portion of the
    // context record contains valid context. If the context record
    // is being used to modify a threads context, then only that
    // portion of the threads context will be modified.
    //
    // If the context record is used as an IN OUT parameter to capture
    // the context of a thread, then only those portions of the thread's
    // context corresponding to set flags will be returned.
    //
    // The context record is never used as an OUT only parameter.
    //

    DWORD ContextFlags;

    //
    // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
    // set in ContextFlags.  Note that CONTEXT_DEBUG_REGISTERS is NOT
    // included in CONTEXT_FULL.
    //

    DWORD   Dr0;
    DWORD   Dr1;
    DWORD   Dr2;
    DWORD   Dr3;
    DWORD   Dr6;
    DWORD   Dr7;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
    //

    FLOATING_SAVE_AREA FloatSave;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_SEGMENTS.
    //

    DWORD   SegGs;
    DWORD   SegFs;
    DWORD   SegEs;
    DWORD   SegDs;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_INTEGER.
    //

    DWORD   Edi;
    DWORD   Esi;
    DWORD   Ebx;
    DWORD   Edx;
    DWORD   Ecx;
    DWORD   Eax;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_CONTROL.
    //

    DWORD   Ebp;
    DWORD   Eip;
    DWORD   SegCs;              // MUST BE SANITIZED
    DWORD   EFlags;             // MUST BE SANITIZED
    DWORD   Esp;
    DWORD   SegSs;

    //
    // This section is specified/returned if the ContextFlags word
    // contains the flag CONTEXT_EXTENDED_REGISTERS.
    // The format and contexts are processor specific
    //

    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

} CONTEXT;

typedef CONTEXT *PCONTEXT;

返回一个枚举类型

// Exception disposition return values
typedef enum _EXCEPTION_DISPOSITION
{
    ExceptionContinueExecution, 
    ExceptionContinueSearch,
    ExceptionNestedException,
    ExceptionCollidedUnwind
} EXCEPTION_DISPOSITION;

从0到3,依次为:
0. 继续执行异常代码;

  1. 传给下一个异常处理器;

2和3是在OS内部使用。

4. 调试

FS寄存器指向当前活动线程的TEB结构,0偏移处就是SEH第一个节点的地址,所以可以这样获得SEH链:

mov eax, fs:[0]

然后可以打开SEH chain窗口对比。

SEH添加和删除

添加:

push @MyHandler
push dword ptr fs:[0]
mov dword ptr fs:[0], esp

删除:

pop dword ptr fs:[0]
add esp, 4

这里不太好理解,调试一下就懂了。

EXCEPTION_ACCESS_VIOLATION调试示例

查看SEH Chain,在第一个节点地址772086D0下断点。

008FF97C    ntdll.772086D0
008FF994    ntdll.772151B2

执行下面的指令:

011B2080 >  33C0            xor     eax, eax
011B2082    C700 01000000   mov     dword ptr [eax], 1

引发中断后,观察栈:

ESP ==>  > 77215062  返回到 ntdll.77215062
ESP+4    > 008FF3F4
ESP+8    > 008FF97C
ESP+C    > 008FF444
ESP+10   > 008FF37C

参数一:pExceptionRecord

和普通函数调用不同,esp+4是第一个参数ExceptionRecord指针。

008FF3F4  05 00 00 C0 00 00 00 00 00 00 00 00 82 20 1B 01  

c0000005就是EXCEPTION_ACCESS_VIOLATION的code值。

011b2082就是异常代码的地址。

参数二:pFrame

008FF97C其实就是上面SEH Chain中的起始地址。

参数三:pContext

特别注意eip,是ContextRecord偏移B8的地方。

008FF4FC  82 20 1B 01

参数四

最后一个参数供系统内部使用,可忽略。

异常处理如何反调试

mov esi, dword ptr ss:[esp+c]
mov eax, dword ptr fs:[30]
cmp byte ptr ds:[eax+2], 1
jnz ...
mov dword ptr ds:[esi + B8], xxxxxxxx
xor eax, eax
retn

伪代码:

esi = &context
eax = &PEB
if(eax.BeingDebugged)
...

next_eip = xxxxxxxx;

return 0;

最后返回的0,就是ExceptionContinueExecution

5. 设置OD选项

下面介绍Debugging options - exception的几个选项。

合理配置可以在不暂停调试器的情况下基于规避SEH实现的反调试招数。

忽略在kernel32中发生的内存非法访问异常

默认选中。也就是说,碰到的时候不会暂停。

向被调试者传递的异常

这些复选框中有:

  • int 3
  • single-step break
  • mem access violation
  • 除0
  • 无效或特权指令
  • All FPU exceptions

最后一个,是Float point unit,浮点运算单元,它有自己的一套指令,与普通x86结构不同。

勾选的异常会传递给被调试者处理。

忽略其它异常

这里添加的异常会发送给被调试者。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本资源为非常不错的一套王网传资源,是继之前上传的基础班的升级版,更加全面,资源过大,上传乃是下载链接,如失效请留言!!!资源远大于5积分,不多说,下面直接上目录: APC机制 I5 J$ i: U0 f1 r: O9 B( Q" b │ 01 APC的本质.mp4 │ 02 备用Apc队列.mp4: U8 p7 ]3 f" w$ b0 ?5 Z9 `0 H8 G* [ │ 03 APC挂入过程.mp48 g! H4 s1 V; ]+ b4 Y9 H0 L- B │ 04 内核APC执行过程.mp4 │ 05 用户APC执行过程.mp4 │ ├─事件等待' x% `" J' } ?& S: t' ]# I5 \5 G │ 01临界区.mp4- o( U$ W9 O+ ` ~0 u4 ~, @. \ │ 02 自旋锁.mp4) c3 ~. J& L, V& s. Q8 x/ [. w │ 03 线程等待与唤醒.mp4# b* ^" k$ d# O3 f8 t8 a3 k │ 04 WaitForSingleObject函数分析.mp4$ V7 L' C3 I( W │ 05 事件.mp4 │ 06 信号量.mp4 │ 07 互斥体.mp4 │ ├─保护模式- }! n! C$ O/ s" Q │ 014 中断门.mp4, B' i, r7 Y: B3 |! N( ^6 { l9 F │ 015 陷阱门.mp4 │ 017 任务段_下.mp4, |/ M# A: K3 T7 i* Q/ ? I& o& D; p │ 018 任务门.mp46 m. D+ f4 _/ V) ~9 S& B │ 019 10-10-12分页.mp4 │ 020 PDE_PTE.mp4 │ 021 PDE_PTE属性(P_RW).mp43 ~/ ]1 x5 {4 u: {$ I │ 022 PDE_PTE属性(US_PS_A_D).mp4 │ 023 页目录表基址.mp4 │ 024 页表基址.mp4$ A f' [+ g6 }5 F; e │ 025 2-9-9-12分页.mp4 │ 026 2-9-9-12分页(下).mp4- ~' ~9 i0 T5 f" p2 U$ j │ 027 TLB.mp4 │ 028 中断与异常.mp4 │ 029 控制寄存器.mp46 j2 l3 j) O# {% {4 w │ 030 PWT_PCD属性.mp4 │ 031 保护模式阶段测试.mp4 │ _001 保护模式.mp4, I; c5 X ~) t1 d1 }8 S# f3 i: b │ _002 段寄存器结构.mp48 n- |- i( H$ ^* f │ _003 段寄存器属性探测.mp4 │ _004 段描述符与段选择子.mp4 │ _005 段描述符属性_P位_G位.mp4 │ _006 段描述符属性_S位_TYPE域.mp4 │ _007 段描述符属性_DB位.mp4 │ _008 段权限检查.mp4 │ _009 代码跨段跳转流程.mp4& S# i9 i- \0 D" @1 U- P │ _010 代码跨段跳转实验.mp4" @* S2 Y- a- S6 n7 n: ~ │ _011 长调用与短调用.mp4 │ _012 调用门_上.mp4; [) _2 c8 A5 F% }! u% ]: ~. N │ _013 调用门_下.mp4 │ ├─内存管理 │ 01 线性地址的管理.mp4; ? |+ ^5 i& } │ 02 Private Memory.mp4* @3 B( Y6 ^ y- { │ 03 Mapped Memory.mp4 │ 04 物理内存的管理.mp4' [8 C6 q \1 H8 w" H2 ]0 Y │ 05 无处不在的缺页异常.mp4 │ ├─句柄表 │ 01 句柄表.mp4 │ 02 全局句柄表.mp4 │ 5 h" u" i& {+ G4 T+ E ├─异常 │ 01 CPU异常记录.mp4 │ 02 模拟异常记录.mp4: K0 J( d1 n4 ] Q │ 03 内核异常的处理流程.mp4 │ 04 用户异常的分发.mp4 │ 05 VEH.mp4 C F6 A% j# M* @- h% N │ 06 SEH.mp4 │ 07 编译器扩展SEH课堂代码_1.mp42 I" @1 i1 b% G6 o4 O% j: t │ 08 编译器扩展SEH_2.mp4 │ 09 编译器扩展SEH_3.mp4 │ 10 编译器扩展SEH_4.mp4 │ 11 未处理异常.mp4. m' z+ `$ v- R/ K. `/ \2 M, S │ ├─消息机制0 y7 o3 ?7 X7 Z, F" I( Q │ 01 消息队列在哪.mp4$ {& n5 ]* g' H, W# k6 |+ M │ 02 窗口与线程.mp4 │ 03 消息的接收.mp4- a8 k- Q8 {! I* T8 L7 j │ 04 消息的分发.mp4- M* `$ q% z, y, R │ 05 内核回调机制.mp4 │ 0 ]( v: v$ e% _/ v, e ├─系统调用: |5 y7 Y% q' w, J │ 001 API函数的调用过程(3环部分).mp4; }0 Z8 P$ g# I6 \! _ y │ 002 API函数的调用过程(3环进0环 上).mp4- g. o" u+ M1 Y) x │ 003 API函数的调用过程(3环进0环 下).mp46 p* w2 @* j9 ?% Z3 e$ \: ? │ 004 API函数的调用过程(保存现场).mp44 G8 |/ j3 ^8 ?1 D9 Y │ 005 API函数的调用过程(系统服务表).mp4 │ 006 API函数的调用过程(SSDT).mp4 │ ! A- ~, L8 M. l ├─软件调试% a8 o, z* m) E$ M( \" P D! m7 x4 B │ 01 调试对象.mp4 c- K+ k3 F( v3 p2 R9 E$ n$ f: Z6 k) @ │ 02 调试事件的采集.mp4 │ 03 调试事件的处理.mp4( w" W. m) o: D3 P7 ? │ 04 异常的处理流程.mp4 │ 05 软件断点.mp4 │ 06 内存断点.mp4 │ 07 硬件断点.mp4 │ 08 单步异常.mp4% P5 e* U+ M# a1 j3 D6 n; n │ 09 单步步过.mp4 │ 10 硬件HOOK过检测.mp46 H5 q2 K& X1 u$ ]/ E │ ├─进程与线程 │ 001 进程结构体.mp4 │ 002 线程结构体.mp41 `5 e+ [1 U) j │ 003 KPCR.mp4 │ 004 等待链表_调度链表.mp41 m! T& `3 t' U& U# A- @+ _ │ 005 模拟线程切换.mp4& ?, D% H/ z- d# _& \$ X- X$ U │ 006 Windows线程切换_主动切换.mp4 │ 007 Windows线程切换_时钟中断切换.mp4/ s& N% Y5 B" @2 g │ 008 Windows线程切换_时间片管理.mp48 X( v. g" T0 ~- k! v* Q │ 009 Windows线程切换_TSS.mp4 n+ A9 L5 B2 t* M# H │ 010 Windows线程切换_FS.mp4 │ 011 Windows线程切换_线程优先级.mp4 A! a% n1 c. Y5 y# ~ L4 P │ 012 进程挂靠.mp4 │ 013 跨进程读写内存.mp4, E0 ^0 U I1 h │ $ {. ?; O) C* w1 K. q) K └─驱动开发 01 驱动开发环境配置.mp4* W) g2 z& T/ _; @1 V" n 02 第一个驱动程序.mp4 03 如何调试驱动程序.mp4; [8 U2 T) B' V 04 内核编程基础.mp4 05 内核空间与内核模块.mp4 06 0环与3环通信(常规方式).mp46 O: Z; `3 `( L 07 SSTD HOOK.mp46 D t( {, u1 D/ x) h! ]: g 08 Inline Hook.mp4 09 多核同步之临界区.mp40 l& ^, e3 J( E1 d( b2 S 10 多核同步之自旋锁.mp4 11 重载内核

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值