SEH异常处理以及反调试

seh反调试原本用于异常处理,也可以用于逆向工程的反调试

基于try excpt finally关键字的函数实现

1.异常处理方法

1.正常的异常处理方法

有处理代码顺利处理,没有seh则启动默认处理(终止进程)

2.调试运行的异常处理

调试器几乎拥有被调试者的所有权限,异常优先由调试器处理

此时调试器会暂停,处理异常后继续调试

处理方式:

调试器直接修改异常

被调试者自己处理

OS默认方式处理(不知道是啥)

2.常见的异常

1.EXCEPTION_ACESS_VIOLATION

/*
* 该段代码定义了两个宏,用于表示断点异常。
* EXCEPTION_BREAKPOINT 代表断点异常。
* STATUS_BREAKPOINT 代表断点异常的状态码,其值为 0x80000003L。
* 这些宏的作用是方便在代码中引用断点异常相关的状态码,
* 可以通过判断程序是否发生了断点异常来进行相应的处理。
* 合理使用这些状态码可以帮助开发者进行调试和异常处理工作。
*/
#define EXCEPTION_BREAKPOINT               STATUS_BREAKPOINT
#define STATUS_BREAKPOINT               ((DWORD   )0x80000003L)

触发

mov dword ptr ds:[0], 1  ; 将值 1 存储到数据段的偏移地址为 0 的内存中
add dword ptr ds:[401000], 1 ; 将数据段偏移地址为 0x401000 的内存中的值加上 1
xor dword ptr ds:[8000 0000], 1234h ; 将数据段偏移地址为 0x80000000 的内存中的值与 0x1234 进行异或操作

2.EXCEPTION_BREAKPOINT

#define EXCEPTION_BREAKPOINT                STATUS_BREAKPOINT
#define STATUS_BREAKPOINT               ((DWORD   )0x80000003L)    
  • EXCEPTION_BREAKPOINT 是一个异常常量,表示断点异常
  • STATUS_BREAKPOINT 是一个状态常量,表示断点状态
  • STATUS_BREAKPOINT 的值为 0x80000003,表示断点状态的数值

调试器就是利用这个异常实现断点功能

例如:

f2设置cc(od中f2设置的int3断点)后会触发,但反汇编窗口并不会显示cc

用PETools好东西(PE Tools_petools-CSDN博客)将进程内存dump后就能看到cc

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

3.EXCEPTION_ILLEGAL_INSTRUCTION

/*
* 该段代码定义了两个宏,用于表示除零异常。
* EXCEPTION_INT_DIVIDE_BY_ZERO 代表整数除零异常。
* STATUS_FLOAT_DIVIDE_BY_ZERO 代表浮点数除零异常的状态码,其值为 0xC000008EL。
* 这些宏的作用是方便在代码中引用除零异常相关的状态码,
* 可以通过判断程序是否发生了除零异常来进行相应的处理。
* 合理使用这些状态码可以帮助开发者进行调试和异常处理工作。
*/
#define EXCEPTION_INT_DIVIDE_BY_ZERO       STATUS_INTEGER_DIVIDE_BY_ZERO
#define STATUS_FLOAT_DIVIDE_BY_ZERO     ((DWORD   )0xC000008EL)

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

4.EXCEPTION_INT_DIVIDE_BY_ZERO

 / * 该段代码定义了两个宏,用于表示单步异常
* EXCEPTION_SINGLE_STEP 代表单步异常
* STATUS_SINGLE_STEP 代表单步异常的状态码,其值为 0x80000004L
* 这两个宏的作用是方便在代码中引用单步异常相关的状态码
* 可以通过判断程序是否发生了单步异常来进行相应的处理
* 该状态码的合理使用可以帮助开发者进行调试和异常处理工作
*/
#define EXCEPTION_SINGLE_STEP       STATUS_SINGLE_STEP
#define STATUS_SINGLE_STEP         ((DWORD   )0x80000004L)
mov eax, 1
xor ebx, ebx
div ebx

5.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,则说明这是链表最后一个节点

回调函数

回调处理函数原型

// 定义异常处理程序函数指针类型 PEXCEPTION_ROUTINE
typedef
_IRQL_requires_same_ // 指示函数必须在相同的 IRQL 下执行
_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; // 定义异常处理程序函数指针类型 PEXCEPTION_ROUTINE

ExceptionRecord

// 定义异常记录结构体
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 {
// 这些标志位控制了 CONTEXT 记录的内容。
// 如果 CONTEXT 记录作为输入参数使用,那么对于每个由值设置的标志位控制的记录部分,假设该记录部分包含有效的上下文。
// 如果 CONTEXT 记录用于修改线程的上下文,那么只有该线程上下文的相应部分将被修改。
// 如果 CONTEXT 记录作为 IN OUT 参数用于捕获线程的上下文,那么只有与设置标志位对应的线程上下文部分将被返回。
// CONTEXT 记录永远不会被作为 OUT 参数使用。
DWORD ContextFlags;
// 如果 ContextFlags 中设置了 CONTEXT_DEBUG_REGISTERS 标志位,则指定/返回此部分。注意,CONTEXT_DEBUG_REGISTERS 不包含在 CONTEXT_FULL 中。
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
// 如果 ContextFlags 字里面包含 CONTEXT_FLOATING_POINT 标志位,则指定/返回此部分。
FLOATING_SAVE_AREA FloatSave;
// 如果 ContextFlags 字里面包含 CONTEXT_SEGMENTS 标志位,则指定/返回此部分。
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
// 如果 ContextFlags 字里面包含 CONTEXT_INTEGER 标志位,则指定/返回此部分。
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
// 如果 ContextFlags 字里面包含 CONTEXT_CONTROL 标志位,则指定/返回此部分。
//
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // 必须经过清洗
DWORD EFlags; // 必须经过清洗
DWORD Esp;
DWORD SegSs;
//
// 如果 ContextFlags 字里面包含 CONTEXT_EXTENDED_REGISTERS 标志位,则指定/返回此部分。
// 其格式和上下文是处理器特定的。
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
typedef CONTEXT *PCONTEXT

返回一个枚举类型

// Exception disposition return values
typedef enum _EXCEPTION_DISPOSITION
{
ExceptionContinueExecution, // 继续执行正常程序流程
ExceptionContinueSearch, // 继续搜索其他异常处理程序
ExceptionNestedException, // 当前异常为嵌套异常,需要进一步处理
ExceptionCollidedUnwind // 当前异常与取消展开的异常冲突,需要处理
} EXCEPTION_DISPOSITION;
  1. 继续执行异常代码;
  2. 传给下一个异常处理器;
  3. 2和3是在OS内部使用

4.调试

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

mov eax, fs:[0]

SEH添加和删除

添加

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

删除

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

调试一下理解原理(示例)

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

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

2.执行下面的指令

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

3.引发中断后,观察栈

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

参数解析

1.pEceptionRecord

和普通函调用不同

esp+4是第一个参数ExceptionRecord指针

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

2.pFrame

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

3.pContext

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

008FF4FC  82 20 1B 01

4.(可忽略)

异常处理如何反调试

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选项

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

默认选中

2.向被调试者传递的异常

这些复选框中有:

  • int 3
  • single-step break
  • mem access violation
  • 除0
  • 无效或特权指令
  • All FPU exceptions
  • Float point unit//浮点运算单元,它有自己的一套指令,与普通x86结构不同

3.忽略其它异常

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇