SEH学习笔记

0x00

​ 博客地址:https://bbs.pediy.com/thread-249592.htm

​ SEH(Structured Exception Handling:结构化异常处理)是windows系统中的异常处理机制,还可用于反调试程序,源代码中SEH体现在try,except等关键字上。

OS异常处理的方法:

  1. 进程出现异常,首先看看进程中有没有存在异常处理的代码(try或except)之类的,如果没有OS就启动默认的异常处理机制,中止程序运行。
  2. 当进程处于调试状态时,出现异常后首先由调试器(x32dbg之类的)处理,这时候我们就:
    1. 直接修改异常(代码、寄存器与内存)。
    2. 将异常抛回被调试程序,F8直接运行。
    3. OS默认的异常处理机制。

常见异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
EXCEPTION_ACCESS_VIOLATION         0xC0000005     程序企图读写一个不可访问的地址时引发的异常。例如企图读取0地址处的内存。
EXCEPTION_ARRAY_BOUNDS_EXCEEDED 0xC000008C 数组访问越界时引发的异常。
EXCEPTION_BREAKPOINT 0x80000003 触发断点时引发的异常。
EXCEPTION_DATATYPE_MISALIGNMENT 0x80000002 程序读取一个未经对齐的数据时引发的异常。
EXCEPTION_FLT_DENORMAL_OPERAND 0xC000008D 如果浮点数操作的操作数是非正常的,则引发该异常。所谓非正常,即它的值太小以至于不能用标准格式表示出来。
EXCEPTION_FLT_DIVIDE_BY_ZERO 0xC000008E 浮点数除法的除数是0时引发该异常。
EXCEPTION_FLT_INEXACT_RESULT 0xC000008F 浮点数操作的结果不能精确表示成小数时引发该异常。
EXCEPTION_FLT_INVALID_OPERATION 0xC0000090 该异常表示不包括在这个表内的其它浮点数异常。
EXCEPTION_FLT_OVERFLOW 0xC0000091 浮点数的指数超过所能表示的最大值时引发该异常。
EXCEPTION_FLT_STACK_CHECK 0xC0000092 进行浮点数运算时栈发生溢出或下溢时引发该异常。
EXCEPTION_FLT_UNDERFLOW 0xC0000093 浮点数的指数小于所能表示的最小值时引发该异常。
EXCEPTION_ILLEGAL_INSTRUCTION 0xC000001D 程序企图执行一个无效的指令时引发该异常。
EXCEPTION_IN_PAGE_ERROR 0xC0000006 程序要访问的内存页不在物理内存中时引发的异常。
EXCEPTION_INT_DIVIDE_BY_ZERO 0xC0000094 整数除法的除数是0时引发该异常。
EXCEPTION_INT_OVERFLOW 0xC0000095 整数操作的结果溢出时引发该异常。
EXCEPTION_INVALID_DISPOSITION 0xC0000026 异常处理器返回一个无效的处理的时引发该异常。
EXCEPTION_NONCONTINUABLE_EXCEPTION 0xC0000025 发生一个不可继续执行的异常时,如果程序继续执行,则会引发该异常。
EXCEPTION_PRIV_INSTRUCTION 0xC0000096 程序企图执行一条当前CPU模式不允许的指令时引发该异常。
EXCEPTION_SINGLE_STEP 0x80000004 标志寄存器的TF位为1时,每执行一条指令就会引发该异常。主要用于单步调试。
EXCEPTION_STACK_OVERFLOW 0xC00000FD 栈溢出时引发该异常。

​ 直接copy过来的。

SEH链

​ SEH以链的形式存在,如果这个异常处理中没有发现相关异常,就直接传到下一个异常处理器。

​ SEH是由_EXCEPTION_REGISTRATION_RECORD结构体组成的链表。

1
2
3
4
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION
}

​ Next指向下一个_EXCEPTION_REGISTRATION_RECORD结构,Handler表示异常处理函数。若Next成员的值为FFFFFFFF,则表示它是链表最后一个结点。

image-20221202205834915

​ 异常处理函数的定义如下:

1
2
3
4
5
6
EXCEPTION_DISPOSITION __cdecl _except_handler (
EXCEPTION_RECORD *pRecord,
EXCEPTION_REGISTRATION_RECORD *pFrame,
CONTEXT *pContext,
PVOID pValue
);

​ 其中第1个参数是EXCEPTION_RECORD(异常记录),其结构为:

1
2
3
4
5
6
7
8
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; //异常代码
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress; //异常发生地址
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;

​ 第3个参数是指向CONTEXT结构体的指针,用来备份CPU的值。

​ 异常发生的时候,程序就会执行SEH,此时OS会将程序结构体的指针传递给异常处理函数,传递的内容中有eip,异常处理函数会将这个eip进行修改,这样之前暂停的线程会执行新的eip地址处的代码(反调试中经常使用这个技术)。

0x01 回调函数

简单解释

​ MDN的解释:被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。如果代码立即被执行就称为同步回调,如果在之后晚点的某个时间再执行,则称之为异步回调。

​ 例如:

1
2
3
4
5
6
7
8
9
10
function greeting(name) {
alert('Hello ' + name);
}

function processUserInput(callback) {
var name = prompt('Please enter your name.');
callback(name);
}

processUserInput(greeting);

​ 其中greeting就是一个回调函数,这是同步执行的,然而回调函数大多数用于异步操作

为什么使用回调?

​ 回调函数的好处是解耦。(解耦的意思就是让程序积木化,程序模块化

​ 如下图所示:

image-20221202212002375

​ 如下是一段程序来程序上图意思:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include<softwareLib.h> // 包含Library函数所在的头文件

int Callback()
{
printf("callback")
return 0;
}
int main() // Main program
{
Library(Callback);

return 0;
}

​ 我们能把我们自己写的函数作为参数传入到Library函数中,并且丝毫不需要修改库函数的实现。

当需要降低耦合度的时候,更应该使用回调函数。

​ 回调函数实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<stdio.h>

int Callback_1(int x) // Callback Function 1
{
printf("Hello, this is Callback_1: x = %d ", x);
return 0;
}

int Callback_2(int x) // Callback Function 2
{
printf("Hello, this is Callback_2: x = %d ", x);
return 0;
}

int Callback_3(int x) // Callback Function 3
{
printf("Hello, this is Callback_3: x = %d ", x);
return 0;
}

int Handle(int y, int (*Callback)(int))
{
printf("Entering Handle Function. ");
Callback(y);
printf("Leaving Handle Function. ");
}

int main()
{
int a = 2;
int b = 4;
int c = 6;
printf("Entering Main Function. ");
Handle(a, Callback_1);
Handle(b, Callback_2);
Handle(c, Callback_3);
printf("Leaving Main Function. ");
return 0;
}

留言

2022-12-02

© 2024 wd-z711

⬆︎TOP