【逆向工程核心原理:SEH】

SEH

SEH是Windows操作系统提供的异常处理机制,在程序源代码中使用__ try、 __except、__finally关键字来具体实现。主要用在反调试中。


SEH与C++中的try. catch 异常处理具有不同结构。从时间上看,与C++的try、catch异常处理相比,微软先创建出了SEH机制,然后才将它搭载到VC++中。所以SEH是一种从属于VC++开发工具和Windows操作系统的异常处理机制。

调试运行时的异常处理

若被调试进程内部发生异常:
OS会首先把异常抛给调试进程处理调试器几乎拥有被调试者的所有权限,它不仅可以运行、终止被调试者,还拥有被调试进程的虚拟内存、寄存器的读写权限。需要特别指出的是,被调试者内部发生的所有异常( 错误)都由调试器处理。所以调试过程中发生的所有异常( 错误)都要先交由调试器管理(被调试者的SEH依据优先顺序推给调试器)。像这样,被调试者发生异常时,调试器就会暂停运行,必须采取某种措施来处理异常,完成后继续调试。

  • 遇到异常时经常采用的几种处理方法:
  1. 直接修改异常:代码、寄存器、内存
  2. 将异常抛给被调试者处理
  3. OS默认的异常处理机制
操作系统定义的异常
EXCEPTION_DATATYPE_MISALIGNMENT		(0X80000002)
EXCEPTION_BREAKPOINT				(0X80000003)
EXCEPTION_SINGLE_STEP				(0x80000004)
EXCEPTION_ACCESS_VIOLATION			(0xC0000005)
EXCEPTION_IN_PAGE_ERROR				(0xC0000006)
EXCEPTION_ILLEGAL_INSTRUCTION		(0xC000001D)
EXCEPTION_NONCONTINUABLE_EXCEPTION 	(0xC0000025)
EXCEPTION_INVALID_DISPOSITION		(0xC0000026)
EXCEPTION_ARRAY_BOUNDS_EXCEEDED		(0xC000008C)
EXCEPTION_FLT_DENORMAL_OPERAND		(0xC000008D)
EXCEPTION_FLT_DIVIDE_BYZERO			(0xC000008E)
EXCEPTION_FLT_INEXACT_RESULT		(0xC000008F)
EXCEPTION_FLT_INVALID_OPERATION		(0xC0000090)
EXCEPTION_FLT_OVERFLOW				(0xC0000091)
EXCEPTION_FLT_STACK_CHECK			(0xC0000092)
EXCEPTION_FLT_UNDERFLOW				(0xC0000093)
EXCEPTION_INT_DIVIDE_BY_ZERO		(0xC0000094)
EXCEPTION_INT_OVERFLOW				(0xC0000095)
EXCEPTION_PRIV_INSTRUCTION			(0xC0000096)
EXCEPTION_STACK_OVERFLOW			(0xC00000FD)
  • 常见的异常

EXCEPTION_ACCESS_VIOLATION(C0000005):非法访问异常,试图访问不存在或不具访问权限的内存区域。

EXCEPTION_BREAKPOINT(80000003):运行的代码被设置了断点之后,CPU尝试执行该地址处的指令时,就会发生EXCEPTION_BREAKPOINT异常。原理是,设置断点会将该处的指令修改为0xCC,但Ollydbg不会将临时断点的实际指令显示出来,可以使用PE Tools工具转储进程内存之后,使用Hex Editor打开文件,即可看到真是指令。

EXCEPTION_ILLEGAL_INSTRUCTION(C000001D):CPU遇到无法解析的指令时引发该异常。

EXCEPTION_INT_DIVIDE_BY_ZERO(C0000094):除法运算中若分母为零就会发生除零异常。程序中可能出现的情况是分母为变量,变量在某个时刻变为0,再执行除法运算时就会出现异常。

EXCEPTION_SINGLE_STEP(80000004):将EFLAGS寄存器的第八位TF(Trap Flag陷阱标志)设置为1之后,CPU就会进入单步工作模式。每执行一条指令之后,就会暂停,抛出异常。

SEH链

SEH以链的形式存在,由_EXCEPTION_REGISTRATION_RECORD结构体组成的链表
第一个异常处理器如果未处理相关异常,它就会被传递到下一个异常处理器,直到得到处理。

  • SEH结构体
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
	// 链表以Next成员为FFFFFFFF的结构体结束,表示链表的最后一个结点
	PEXCEPTION_REGISTRATION_RECORD Next;
	// Handler:异常处理函数
	PEXCEPTION_DISPOSITION Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

// 异常顺着链传递,直到有异常处理器处理

用图表示
在这里插入图片描述

  • 异常处理函数
    这个函数是一个回调函数,由系统来调用,系统会提供函数需要的参数
    由定义可以看出,这个函数需要四个参数,返回值是一个enum类型
EXCEPTION_DISPOSITION _except_handler
(
	EXCEPTION_RECORD *pRecord,
	EXCEPTION_REGISTRATIOIN_RECORD *pFrame,
	CONTEXT *pContext,
	PVOID pValue
);

第一个参数

typedef struct _EXCEPTION_RECORD {
	DWORD ExceptionCode;						//异常代码,用来指明异常类型
	DWORD ExceptionFlags;
	struct _EXCEPTION_RECORD * ExceptionRecord;	
	PVOID ExceptionAddress;						//发生异常的代码地址
	DWORD NumberParameters;
	ULONG_PTR ExceptionInformation[EXCEPTION_AMXIMUM_PARAMETERS];//15
} EXCEPTION_RECORD, *PEXCEPTION_RECORD

第三个参数

struct CONTEXT
{
	DWORD ContextFlags;
	DWORD Dr0;
	DWORD Dr1;
	DWORD Dr2;
	DWORD Dr3;
	DWORD Dr6;
	DWORD Dr7;
	FLOATING_SAVE_AREA FloatSave;
	DWORD SegGs;
	DWORD SegFs;
	DWORD SegEs;
	DWORD SegDs;
	DWORD Edi;
	DWORD Esi;
	DWORD Ebx;
	DWORD Edx;
	DWORD Ecx;
	DWORD Eax;
	DWORD Ebp;
	DWORD Eip;
	DWORD SegCs;
	DWORD EFlags;
	DWORD Esp;
	DWORD SegSs;
	BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];//512
}

返回值

typedef enum _EXCEPTION_DISPOSITION
{
	ExceptionContinueExecution = 0,	//继续执行异常代码
	ExceptionContinueSearch = 1,	//运行下一个异常处理器
	ExceptionNestedException = 2,	//在OS内部使用
	ExceptionCollidedUnwind = 3		//在OS内部使用
} EXCEPTION_DISPOSITION;

异常处理器处理异常后会返回ExceptionContinueExecution(0),从发生异常的代码出继续运行。若无法处理异常,则返回ExceptionContinueSearch(1),将一场派送到SEH链的下一个异常处理器。

  • 访问进程的SEH链的方法

通过TEB结构体的NtTib成员
TEB.NtTib.ExceptionList = FS:[0]

  • 安装SEH

在C语言中,使用__try、__except、__finally关键字可以向代码添加SEH。

汇编

PUSH @MyHandler
PUSH DWORD PTR FS:[0]
MOV DWORD PTR FS:[0], ESP

FS:[0]这里存放的是TEB的地址,TEB的第一个成员是 _NT_TIB 结构体,_NT_TIB 结构体的第一个成员是 _EXCEPTION_REGISTRATION_RECORD 类型的结构体指针,也就是 SEH 链的首地址

  • TEB结构
// MSDN给的参考
typedef struct _TEB {
  PVOID Reserved1[12];
  PPEB  ProcessEnvironmentBlock;
  PVOID Reserved2[399];
  BYTE  Reserved3[1952];
  PVOID TlsSlots[64];
  BYTE  Reserved4[8];
  PVOID Reserved5[26];
  PVOID ReservedForOle;
  PVOID Reserved6[4];
  PVOID TlsExpansionSlots;
} TEB, *PTEB;

WinDbg看到的

+0x000 NtTib            : _NT_TIB
...
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB

这是两个重要的成员,ProcessEnvironmentBlock是PEB的地址

NtTib结构:

typedef struct _NT_TIB          //sizeof  1ch
{
 00h   struct _EXCEPTION_REGISTRATION_RECORD  *ExceptionList;          //SEH链入口
 04h   PVOID StackBase;              //堆栈基址
 08h   PVOID StackLimit;             //堆栈大小
 0ch   PVOID SubSystemTib;
       union {
           PVOID FiberData;
 10h       DWORD Version;
       };
 14h   PVOID ArbitraryUserPointer;
 18h   struct _NT_TIB *Self;                  //本NT_TIB结构自身的线性地址
}NT_TIB;
 
typedef NT_TIB *PNT_TIB;

其他

学习来源

  • 当进程中发生异常时,若SEH未处理或注册的SEH不存在。此时会调用系统的kernel32!UnhandledExceptionFIlter()API。
  • 该API会运行系统的最后一个异常处理器——Top Level Exception FilterLast Exception Filter(通常行为是弹出错误消息框、终止进程)。
  • kernel32!UnhandledExceptionFilter()调用了ntdll!QueryInformationProcess(ProcessDebugPort)。来判断是否正在调试进程。如果正在进行调试,则将异常传递给调试器。否则系统异常处理器终止进程。
  • 通过kernel32!SetUnhandledExceptionFilter()可以修改系统最后的异常处理器。函数定义如下:
//返回的是上一个Top Level Exception Filter的地址,方便恢复
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter(
	__in LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
  • Top Level Exception Filter的函数定义:
typedef struct _EXCEPTION_POINTERS{
	PEXCEPTION_RECORD ExceptionRecord;
	PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

LONG TopLevelExceptionFilter(
	PEXCEPTION_POINTER pExcept;
);
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值