-
-
[原创]Windows内核学习笔记之异常(下)
-
发表于: 2021-12-31 21:24 14621
-
在实际开发中,直接通过手动在栈中加入结构化异常处理器的方式存在以下两点明显不足:
需要编写符合SehHandler函数原型的处理函数,要理解_CONTEXT等较复杂的数据结构和概念
因为需要直接操作栈指针,所以无法将登记与销毁异常处理器的代码封装到一个普通的C/C++函数中。这就需要在每个需要保护的程序块前后插入两点嵌入式汇编代码,这会影响程序的简洁性
为了解决上述的问题,编译器对该机制进行了优化,主要完成以下两项任务:
定义必要的关键字来表示异常处理逻辑,供编程人员使用。比如,VC编译器定义了try,except和__finally这3个扩展关键字,运行C和C++程序使用这套关键字来编写异常处理代码
实现对以上关键字的编译,将使用这些关键字编写的异常处理代码与操作系统的SEH机制衔接起来
VC编译器优化以后的SEH为程序员提供了如下两种功能:
异常处理功能:用于接收和处理被保护块中的代码所发生的异常
终结处理功能:保证终结处理块始终可以得到执行
异常处理的语法结构如下:
除了try和except关键字以外,Visual C++编译器还提供了如下两个宏来辅助编写异常处理代码:
DWORD GetExceptionCode():返回异常代码。只能在过滤表达式或异常处理块中使用这个宏
LPEXCEPTION_POINTERS GetExceptionInformation():返回一个指向EXCEPTION_POINTERS结构的指针。只能在过滤表达式中使用这个宏
过滤表达式既可以是常量,函数调用,也可以是用条件表达式或其他表达式,只要表达式的结果为0,1,-1这三个值之一,它们的含义如下:
以下是常见的三种编写过滤表达式的方法:
直接使用常量:比如except(EXCEPTION_EXECUTE_HANDLER),或者直接写为except(-1)等
使用条件运算符:比如__except(GetExceptionCode == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)。其含义是,如果发生的异常是非法访问异常,那么久执行异常处理块;否则,就继续搜索其他异常保护块
调用其他函数,通常将GetExceptionCode()得到的异常代码或GetExceptionInformation()得到的异常信息作为参数传给该函数。例如__except(ExcptFilter(GetExceptionInformation()))
可见,可以根据具体需要设计不同复杂度的表达式,可短到一个常熟,可长到编写过滤函数进行一系列操作。
以下代码是对于上述内容的样例:
程序的运行结果如下:
终结处理的语法结构如下:
终结处理由两部分构造,使用try关键字定义的被保护体和使用_finally关键字定义的终结处理块。终结处理的目标是只要被保护体被执行,那么终结处理块就也会被执行,除非被保护体中的代码终止了当前线程(比如调用ExitThread或ExitProcess退出线程或整个进程)。因为终结处理块的这种特征,终结处理非常适合做状态恢复或资源释放等工作。比如释放被保护块中获得的信号量以防止被保护块内发生意外时因没有释放这个信号量而导致线程死锁的问题。
根据被保护块的执行路线,SEH把被保护块的退出(执行完毕)分为正常结束和非正常结束两种。如果被保护块得到自然执行并顺序进入终结处理块,就认为被保护块是正常结束的。如果被保护块是因为发生异常或由于return, goto, break或continue等流程控制语句离开被保护块的,就认为被保护块是非正常结束的。在终结处理块中可以调用以下函数来知道被保护块的退出方式:
如果被保护块正常结束,那么该函数返回FALSE;否则,返回TRUE。该函数只能在终结块中调用。
除了上面出现的try和finally关键字,终结处理还有一个关键字leave。该关键字的作用是立即离开(停止执行)被保护块,或者理解为立即跳转到被保护块的末尾(try块的右大括号)。__leave关键字只能出现在被保护体中,使用该关键字的退出属于正常退出。
以下代码是上述内容的简单样例:
输出如下:
不同的编译器对SEH的编译优化结果稍有不同,对于Visual Studio2017来说,其主要做了以下两个方面的修改:
编译器编译try{}catch()结构时,总是将统一的__except_handler3登记为异常处理函数。这样做有利于代码复用和减少目标文件的大小
为了使用统一的异常处理函数(excepthandler3)来满足不同SEH块的需求。在栈上准备EXCEPTION_REGISTRATION_RECORD结构前,编译器产生的代码会压入一个trylevel的整数和一个执行scopetable_entry结构的scopetable指针
总之,编译器会将EXCEPTION_REGISTRATION_RECORD结构扩展为如下的形式:
其中前两个字段是操作系统规定的标准登记结构,后三个字段是编译器扩展的。登记处理函数整数依靠这几个扩展字段来寻找过滤表达式和异常处理块。
为了描述应用程序代码中的tryexcept结构,编译器在编译每个使用此结构的函数时会为其建立一个数组,并存储在模块文件的数据区(通常称为异常处理范围表)中。数组的每个元素是一个scopetable_entry结构,用来描述一个tryexcept结构。
其中lpfnFilter和lpfnHandler分别用来描述try{}except结构的过滤表达式和异常处理块的起始地址。
编译器是以函数为单位来登记异常处理器的,在函数的入口处进行登记,在出口处进行注销。为了确定导致异常的代码是否在保护块中,以及,如果在有多个保护块的情况下,判断属于哪个保护块。编译器为每个try结构进行编号,然后使用一个局部变量来记录当前处于哪个try结构中,这个局部变量称为trylevel,也就是在栈上形成的异常处理结构的trylevel字段。
编号从0开始,常量TRYLEVEL_NONE(-1)作为特殊值代表不再任何try结构中。也就是说,trylevel变量被初始化为-1,然后执行到_try结构中时,便将它的编号赋给TryLevel变量。
当位于__try{}结构保护块中的代码发生异常时,异常分发函数便会调用_except_handler3这样的处理函数。_except_handler3函数所指向的主要操作如下:
将第二个参数pRegistrationRecord从系统默认的EXCEPTION_REGISTRATION_RECORD结构强制转换为包含扩展字段的_EXCEPTION_REGISTRATION结构
先从pRegistrationRecord结构中取出trylevel字段的值并且赋给一个局部变量nTryLevel ,然后根据nTryLevel的值从scopetable字段所 指定的数组中找到一个scopetable_entry结构
从scopetable_entry结构中取出lpfnFilter字段,如果不为空,则调用这个函数,即评估过滤表达式,如果为空,则跳到第5步
如果lpfnFilter函数的返回值不等于EXCEPTION_CONTINUE_SEARCH,则准备指向lpfnHanlder字段所指定的函数,并且不再返回。如果过滤表达式返回的是EXCEPTION_CONTINUE_SEARCH,则自然进入(fall through)第五步
判断scopetable_entry结构的previousTryLevel字段的取值。如果它不等于-1,则将previousTryLevel赋给nTryLevel并返回第2步继续循环;如果previousTryLevel等于-1,那么继续第6步
返回DISPOSITION_CONTINUE_SEARCH,让系统(RtlDispatchException)继续寻找其他异常处理器
以以下代码为例,体会上述内容:
以下是对test函数的反汇编结果
根据上面的代码,可以得出如下结论:
无论是多少个tryexcept结构,在函数中只挂载了一个异常处理器
无论是当进入一个try块,还是离开一个try块,都会修改trylevel的值,来代表目前处于哪个try结构中
try块,条件过滤表达式, except块的代码是按顺序保存下来,但是每个块后面都会有跳转语句,避免其顺序执行(jmp, ret)
此时查看scopetable数组的内容:
可以看到数组中的每个元素都对应了try块的信息,这样就可以让异常处理函数根据每个数组元素保存的数据来找到相应的处理函数。
相比于tryexcept结构的异常处理,tryfinally结构的终结处理是为了保证finally块中的代码可以执行。比如以下的代码,无论是否将出现异常的代码注释掉,finally块的代码都会被执行:
根据以下的反汇编代码可以知道,当使用tryfinally结构的时候,程序会在离开try块之前的代码加入一个call指令,该call指令的地址就是finally块的地址。
但是出现异常的时候,是无法执行离开try块的代码的。而此时之所以finally块的代码会被执行,就依赖于栈展开机制。
此时查看scopetable数组的值,可以看到此时的注册表达式的起始地址为0,异常处理块的起始地址变为finally块的起始地址。此时异常处理函数在执行代码时,根据注册表达式的起始地址为0得知,此时需要将第三个参数作为finally块的地址进行调用。
而对于以下的代码,程序在输出异常处理块的内容之前会先输出finally块中的内容
代码的反汇编结果就是异常处理块和终结处理块的组合
此时查看socpetable数组的值,可以看到外层try块的元素内容没有变化,而内层try块的条件过滤表达式变为0,异常处理块地址也变成了finally块的地址。因此,异常处理函数在执行的时候,就可以根据条件过滤表达式是否为0来判断要不要执行第三个成员保存的函数地址。
异常处理函数就通过这种方式实现了在执行except块的代码之前先执行finally块的代码,结果如下:
一个新建进程的起始地址其实并不是PE文件中的AddressOfEntryPoint字段的偏移地址,而是kernel32.dll中BaseProcessStart函数,该函数的伪代码如下:
根据以上伪代码可以知道,在应用程序的入口函数被调用前,BaseProcessStart会为其先设置一个结构化异常处理器,它是初始线程中最早注册的异常处理器。因为当分发异常时,系统是从最晚注册的异常处理器来查找的,所以这个最早注册的结构化异常处理器是最后得到处理机会的。这样便保证了只有当应用程序自己设计的代码没有处理异常时,这个默认的结构化异常处理器才会得到处理机会。
以上代码中的lpfnEntryPoint指向的就是入口函数。在try块中,根据函数的返回值作为参数来调用ExitThread函数退出线程。而如果执行到了异常处理块中,则会将GetExceptionCode函数的返回值作为参数调用ExitThread函数。
当程序出现异常的时候,是否要执行异常块中的代码则是由函数UnhandledExceptionFilter决定的,该函数的执行流程如下:
通过NtQueryInformationProcess查询当前进程是否被调试。如果正在被调试,函数将会返回EXCEPTION_CONTINUE_SEARCH,随后调用ZwRaiseException来进行第二次异常分发
如果当前进程没有被调试:
查询是否通过SetUnhandlerExceptionFilter注册了处理函数,如果已经注册了,那么就调用已注册的处理函数
如果没有注册处理函数,就会弹出窗口让用户选择是终止程序还是启动即时调试器。如果用户启动了即时调试器,接下来就会由调试器来接管进程,函数就会返回EXCEPTION_EXECUTION_HANDLER
《软件调试(第二版)》卷一
《软件调试(第二版)》卷二
__try
{
/
/
被保护体,也就是要保护的代码块
}
__except(过滤表达式)
{
/
/
异常处理块(exception
-
handling block)
}
__try
{
/
/
被保护体,也就是要保护的代码块
}
__except(过滤表达式)
{
/
/
异常处理块(exception
-
handling block)
}
值 | 名称 | 含义 |
---|---|---|
0 | EXCEPTION_CONTINUE_SEARCH | 本保护块不处理该异常,让系统继续寻找其他异常保护块 |
-1 | EXCEPTION_CONTINUE_EXECUTION | 已经处理异常,让程序回到异常发生点继续执行,如果导致异常的情况没有被消除,那么可能还会再次发生异常 |
1 | EXCEPTION_EXECUTE_HANDLER | 这是本保护块预计到的异常,让系统执行本块中的异常处理代码,执行完后会继续执行本异常处理块下面的代码,即except块之后的第一条指令 |
#include <cstdio>
#include <Windows.h>
DWORD x, y, z;
int
ExceptFilter(LPEXCEPTION_POINTERS pException)
{
/
/
如果是除
0
异常
if
(pException
-
>ExceptionRecord
-
>ExceptionCode
=
=
EXCEPTION_INT_DIVIDE_BY_ZERO)
{
y
=
10
;
printf(
"ExceptFilter函数执行,异常已被处理\n"
);
return
EXCEPTION_CONTINUE_EXECUTION;
/
/
继续执行出错的代码
}
return
EXCEPTION_CONTINUE_SEARCH;
/
/
该异常块不处理
}
void test1()
{
__try
{
z
=
x
/
y;
printf(
"在try中输出z的值为:%d\n"
, z);
}
__except (ExceptFilter(GetExceptionInformation()))
{
printf(
"test1的 except块被执行\n"
);
}
}
void test2()
{
__try
{
z
=
x
/
y;
}
/
/
判断是否为除
0
错误,如果是执行错误处理代码,否则不执行
__except (GetExceptionCode()
=
=
EXCEPTION_INT_DIVIDE_BY_ZERO ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
y
=
10
;
z
=
x
/
y;
printf(
"test2的except被执行,错误已被处理, z的值为:%d\n"
, z);
}
}
int
main()
{
x
=
1900
, y
=
0
, z
=
0
;
test1();
x
=
1900
, y
=
0
, z
=
0
;
test2();
return
0
;
}
#include <cstdio>
#include <Windows.h>
DWORD x, y, z;
int
ExceptFilter(LPEXCEPTION_POINTERS pException)
{
/
/
如果是除
0
异常
if
(pException
-
>ExceptionRecord
-
>ExceptionCode
=
=
EXCEPTION_INT_DIVIDE_BY_ZERO)
{
y
=
10
;
printf(
"ExceptFilter函数执行,异常已被处理\n"
);
return
EXCEPTION_CONTINUE_EXECUTION;
/
/
继续执行出错的代码
}
return
EXCEPTION_CONTINUE_SEARCH;
/
/
该异常块不处理
}
void test1()
{
__try
{
z
=
x
/
y;
printf(
"在try中输出z的值为:%d\n"
, z);
}
__except (ExceptFilter(GetExceptionInformation()))
{
printf(
"test1的 except块被执行\n"
);
}
}
void test2()
{
__try
{
z
=
x
/
y;
}
/
/
判断是否为除
0
错误,如果是执行错误处理代码,否则不执行
__except (GetExceptionCode()
=
=
EXCEPTION_INT_DIVIDE_BY_ZERO ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
y
=
10
;
z
=
x
/
y;
printf(
"test2的except被执行,错误已被处理, z的值为:%d\n"
, z);
}
}
int
main()
{
x
=
1900
, y
=
0
, z
=
0
;
test1();
x
=
1900
, y
=
0
, z
=
0
;
test2();
return
0
;
}
__try
{
/
/
被保护体(guarded body),也就是要保护的代码块
}
__finally
{
/
/
终结代码块
}
__try
{
/
/
被保护体(guarded body),也就是要保护的代码块
}
__finally
{
/
/
终结代码块
}
BOOL
AbnormalTermination(void);
BOOL
AbnormalTermination(void);
#include <cstdio>
#include <windows.h>
int
main()
{
__try
{
printf(
"开始执行__try中的代码\n"
);
printf(
"执行__leave之前的代码\n"
);
__leave;
printf(
"执行__leave之后的代码\n"
);
}
__finally
{
printf(
"执行__finally的代码\n"
);
}
return
0
;
}
#include <cstdio>
#include <windows.h>
int
main()
{
__try
{
printf(
"开始执行__try中的代码\n"
);
printf(
"执行__leave之前的代码\n"
);
__leave;
printf(
"执行__leave之后的代码\n"
);
}
__finally
{
printf(
"执行__finally的代码\n"
);
}
return
0
;
}
struct _EXCEPTION_REGISTRATION
{
struct _EXCEPTION_REGISTRATION
*
prev;
void (
*
handler)(PEXCEPTION_RECORD, PEXCEPTION_ REGISTRATION, PCONTEXT, PEXCEPTION_RECORD);
struct scopetable_entry
*
scopetable;
int
trylevel;
int
_ebp;
};
struct _EXCEPTION_REGISTRATION
{
struct _EXCEPTION_REGISTRATION
*
prev;
void (
*
handler)(PEXCEPTION_RECORD, PEXCEPTION_ REGISTRATION, PCONTEXT, PEXCEPTION_RECORD);
struct scopetable_entry
*
scopetable;
int
trylevel;
int
_ebp;
};
字段 | 作用 |
---|---|
prev | 指向上一个结构体的地址 |
handler | 指向异常处理函数 |
scopetable | 范围表的起始地址 |
trylevel | 这个结构对应的__try块的编号 |
ebp | 栈帧的基地址 |
struct scopetable_entry
{
DWORD previousTryLevel
/
/
上一个
try
{}结构编号
PARPROC lpfnFilter
/
/
过滤函数的起始地址
PARPROC lpfnHandler
/
/
异常处理程序的地址
};
struct scopetable_entry
{
DWORD previousTryLevel
/
/
上一个
try
{}结构编号
PARPROC lpfnFilter
/
/
过滤函数的起始地址
PARPROC lpfnHandler
/
/
异常处理程序的地址
};
DWORD x
=
1900
, y
=
0
, z
=
0
;
void test()
{
__try
{
__try
{
z
=
x
/
y;
}
__except (EXCEPTION_CONTINUE_SEARCH)
{
}
}
__except (GetExceptionCode()
=
=
EXCEPTION_INT_DIVIDE_BY_ZERO ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
y
=
10
;
z
=
x
/
y;
printf(
"z = %d\n"
, z);
}
}
DWORD x
=
1900
, y
=
0
, z
=
0
;
void test()
{
__try
{
__try
{
z
=
x
/
y;
}
__except (EXCEPTION_CONTINUE_SEARCH)
{
}
}
__except (GetExceptionCode()
=
=
EXCEPTION_INT_DIVIDE_BY_ZERO ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
y
=
10
;
z
=
x
/
y;
printf(
"z = %d\n"
, z);
}
}
void test()
{
00401000
push ebp
/
/
填充ebp
00401001
mov ebp,esp
00401003
push
0FFFFFFFFh
/
/
填充trylevel为
-
1
00401005
push
48ADD8h
/
/
填充scopetable范围表的起始地址
0040100A
push offset _except_handler3 (
0402968h
)
/
/
填充异常处理函数
0040100F
mov eax,dword ptr fs:[
00000000h
]
00401015
push eax
/
/
填充prev
00401016
mov dword ptr fs:[
0
],esp
/
/
将异常处理器挂到fs:[
0
]中
0040101D
add esp,
0FFFFFFB0h
00401020
push ebx
00401021
push esi
00401022
push edi
00401023
mov dword ptr [ebp
-
18h
],esp
00401026
mov ecx,offset _D3472036_test@cpp (
048F012h
)
0040102B
call __CheckForDebuggerJustMyCode (
04011D0h
)
__try
00401030
mov dword ptr [ebp
-
4
],
0
/
/
进入
try
块,将trylevel赋值为
0
{
__try
00401037
mov dword ptr [ebp
-
4
],
1
/
/
进入
try
块,将trylevel赋值为
1
{
z
=
x
/
y;
0040103E
mov eax,dword ptr [x (
048D000h
)]
/
/
执行
try
块代码
00401043
xor edx,edx
00401045
div eax,dword ptr [y (
048D910h
)]
0040104B
mov dword ptr [z (
048D914h
)],eax
}
00401050
mov dword ptr [ebp
-
4
],
0
/
/
离开
try
块,将trylevel赋值为
0
00401057
jmp test
+
66h
(
0401066h
)
/
/
执行跳转,略过
except
块中内容
__except (EXCEPTION_CONTINUE_SEARCH)
00401059
xor eax,eax
/
/
对过滤表达式进行运算,这里返回值是
0
(EXCEPTION_CONTINUE_SEARCH)
$LN16:
0040105B
ret
$LN13:
0040105C
mov esp,dword ptr [ebp
-
18h
]
}
0040105F
mov dword ptr [ebp
-
4
],
0
/
/
离开
try
块,将trylevel赋值为
0
{
}
}
00401066
mov dword ptr [ebp
-
4
],
0FFFFFFFFh
/
/
离开
try
块,将trylevel赋值为
-
1
0040106D
jmp $LN9
+
39h
(
04010CFh
)
/
/
执行跳转,略过
except
块中内容
__except (GetExceptionCode()
=
=
EXCEPTION_INT_DIVIDE_BY_ZERO ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
0040106F
mov eax,dword ptr [ebp
-
14h
]
00401072
mov ecx,dword ptr [eax]
00401074
mov edx,dword ptr [ecx]
00401076
mov dword ptr [ebp
-
5Ch
],edx
00401079
cmp
dword ptr [ebp
-
5Ch
],
0C0000094h
00401080
jne test
+
8Bh
(
040108Bh
)
00401082
mov dword ptr [ebp
-
60h
],
1
00401089
jmp test
+
92h
(
0401092h
)
0040108B
mov dword ptr [ebp
-
60h
],
0
00401092
mov eax,dword ptr [ebp
-
60h
]
/
/
对过滤表达式进行运算, 这里返回值是
1
(EXCEPTION_EXECUTE_HANDLER)
$LN17:
00401095
ret
$LN9:
00401096
mov esp,dword ptr [ebp
-
18h
]
{
y
=
10
;
00401099
mov dword ptr [y (
048D910h
)],
0Ah
/
/
执行异常处理代码
z
=
x
/
y;
004010A3
mov eax,dword ptr [x (
048D000h
)]
004010A8
xor edx,edx
004010AA
div eax,dword ptr [y (
048D910h
)]
004010B0
mov dword ptr [z (
048D914h
)],eax
printf(
"z = %d\n"
, z);
004010B5
mov eax,dword ptr [z (
048D914h
)]
004010BA
push eax
004010BB
push offset string
"z = %d\n"
(
04701B0h
)
004010C0
call printf (
0401180h
)
004010C5
add esp,
8
{
}
}
004010C8
mov dword ptr [ebp
-
4
],
0FFFFFFFFh
/
/
离开
try
块,将trylevel赋值为
-
1
}
}
004010CF
mov ecx,dword ptr [ebp
-
10h
]
004010D2
mov dword ptr fs:[
0
],ecx
/
/
将异常处理器从fs:[
0
]中摘除
004010D9
pop edi
004010DA
pop esi
004010DB
pop ebx
004010DC
mov esp,ebp
004010DE
pop ebp
004010DF
ret
void test()
{
00401000
push ebp
/
/
填充ebp
00401001
mov ebp,esp
00401003
push
0FFFFFFFFh
/
/
填充trylevel为
-
1
00401005
push
48ADD8h
/
/
填充scopetable范围表的起始地址
0040100A
push offset _except_handler3 (
0402968h
)
/
/
填充异常处理函数
0040100F
mov eax,dword ptr fs:[
00000000h
]
00401015
push eax
/
/
填充prev
00401016
mov dword ptr fs:[
0
],esp
/
/
将异常处理器挂到fs:[
0
]中
0040101D
add esp,
0FFFFFFB0h
00401020
push ebx
00401021
push esi
00401022
push edi
00401023
mov dword ptr [ebp
-
18h
],esp
00401026
mov ecx,offset _D3472036_test@cpp (
048F012h
)
0040102B
call __CheckForDebuggerJustMyCode (
04011D0h
)
__try
00401030
mov dword ptr [ebp
-
4
],
0
/
/
进入
try
块,将trylevel赋值为
0
{
__try
00401037
mov dword ptr [ebp
-
4
],
1
/
/
进入
try
块,将trylevel赋值为
1
{
z
=
x
/
y;
0040103E
mov eax,dword ptr [x (
048D000h
)]
/
/
执行
try
块代码
00401043
xor edx,edx
00401045
div eax,dword ptr [y (
048D910h
)]
0040104B
mov dword ptr [z (
048D914h
)],eax
}
00401050
mov dword ptr [ebp
-
4
],
0
/
/
离开
try
块,将trylevel赋值为
0
00401057
jmp test
+
66h
(
0401066h
)
/
/
执行跳转,略过
except
块中内容
__except (EXCEPTION_CONTINUE_SEARCH)
00401059
xor eax,eax
/
/
对过滤表达式进行运算,这里返回值是
0
(EXCEPTION_CONTINUE_SEARCH)
$LN16:
0040105B
ret
$LN13:
0040105C
mov esp,dword ptr [ebp
-
18h
]
}
0040105F
mov dword ptr [ebp
-
4
],
0
/
/
离开
try
块,将trylevel赋值为
0
{
}
}
00401066
mov dword ptr [ebp
-
4
],
0FFFFFFFFh
/
/
离开
try
块,将trylevel赋值为
-
1
0040106D
jmp $LN9
+
39h
(
04010CFh
)
/
/
执行跳转,略过
except
块中内容
__except (GetExceptionCode()
=
=
EXCEPTION_INT_DIVIDE_BY_ZERO ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
0040106F
mov eax,dword ptr [ebp
-
14h
]
00401072
mov ecx,dword ptr [eax]
00401074
mov edx,dword ptr [ecx]
00401076
mov dword ptr [ebp
-
5Ch
],edx
00401079
cmp
dword ptr [ebp
-
5Ch
],
0C0000094h
00401080
jne test
+
8Bh
(
040108Bh
)
00401082
mov dword ptr [ebp
-
60h
],
1
00401089
jmp test
+
92h
(
0401092h
)
0040108B
mov dword ptr [ebp
-
60h
],
0
00401092
mov eax,dword ptr [ebp
-
60h
]
/
/
对过滤表达式进行运算, 这里返回值是
1
(EXCEPTION_EXECUTE_HANDLER)
$LN17:
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
- [原创]CVE-2022-21882提权漏洞学习笔记 16383
- [原创]CVE-2021-1732提权漏洞学习笔记 19489
- [原创]CVE-2014-1767提权漏洞学习笔记 15192
- [原创]CVE-2018-8453提权漏洞学习笔记 18526
- [原创]CVE-2020-1054提权漏洞学习笔记 13542