本帖最后由 Nattevak 于 2021-12-8 13:53 编辑
一、处理int3异常的函数为KiTrap03
在开始异常处理之初,先构造KTRAP_FRAME陷阱帧结构,陷阱帧是指一个结构体,用来保存系统调用、中断、异常发生时的寄存器现场,方便以后回到用户空间/回到中断处时,恢复那些寄存器的值,继续执行。
1.1
FS进入内核层就不是TEB了
[Asm] 纯文本查看 复制代码 83e8e5c0 6a00 push 0
83e8e5c2 66c74424020000 mov word ptr [esp+2],0
83e8e5c9 55 push ebp
83e8e5ca 53 push ebx
83e8e5cb 56 push esi
83e8e5cc 57 push edi
83e8e5cd 0fa0 push fs
TRAP_FRAME 用于在处理完异常之后,还能还原这些寄存器
保存寄存器环境 构建了一个陷阱帧
[Asm] 纯文本查看 复制代码 83e8e5cf bb30000000 mov ebx,30h
83e8e5d4 668ee3 mov fs,bx
83e8e5d7 648b1d00000000 mov ebx,dword ptr fs:[0]
83e8e5de 53 push ebx
83e8e5df 83ec04 sub esp,4
83e8e5e2 50 push eax
83e8e5e3 51 push ecx
83e8e5e4 52 push edx
83e8e5e5 1e push ds
83e8e5e6 06 push es
83e8e5e7 0fa8 push gs
83e8e5e9 66b82300 mov ax,23h
83e8e5ed 83ec30 sub esp,30h
EBP 此时就是栈顶,陷阱帧的起始位置
[Asm] 纯文本查看 复制代码 83e8e5f0 668ed8 mov ds,ax
83e8e5f3 668ec0 mov es,ax
83e8e5f6 8bec mov ebp,esp
线程的内核对象
[Asm] 纯文本查看 复制代码 83e8e5f8 f744247000000200 test dword ptr [esp+70h],20000h
83e8e600 7596 jne nt!V86_kit3_a (83e8e598)
83e8e602 648b0d24010000 mov ecx,dword ptr fs:[124h]
[Asm] 纯文本查看 复制代码 83e8e609 fc cld
83e8e60a 83652c00 and dword ptr [ebp+2Ch],0
83e8e60e f64103df test byte ptr [ecx+3],0DFh
83e8e612 0f8500ffffff jne nt!Dr_kit3_a (83e8e518)
用户层的EBP
[Asm] 纯文本查看 复制代码 83e8e618 8b5d60 mov ebx,dword ptr [ebp+60h]
产生异常时的EIP
[Asm] 纯文本查看 复制代码 83e8e61b 8b7d68 mov edi,dword ptr [ebp+68h]
以下4行还是在构建陷阱帧
[Asm] 纯文本查看 复制代码 83e8e61e 89550c mov dword ptr [ebp+0Ch],edx
83e8e621 c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h
83e8e628 895d00 mov dword ptr [ebp],ebx
83e8e62b 897d04 mov dword ptr [ebp+4],edi
检测一些什么属性,不合适就跳走
[Asm] 纯文本查看 复制代码 83e8e62e 803dd923f98300 cmp byte ptr [nt!PoHiberInProgress (83f923d9)],0
83e8e635 7507 jne nt!KiTrap03+0x7e (83e8e63e)
83e8e637 f0ff05fc81f883 lock inc dword ptr [nt!KiHardwareTrigger (83f881fc)]
83e8e63e b800000000 mov eax,0
83e8e643 f6457202 test byte ptr [ebp+72h],2
83e8e647 752f jne nt!KiTrap03+0xb8 (83e8e678)
83e8e649 f6456c01 test byte ptr [ebp+6Ch],1
83e8e64d 7508 jne nt!KiTrap03+0x97 (83e8e657)
83e8e64f f6457102 test byte ptr [ebp+71h],2
83e8e653 740a je nt!KiTrap03+0x9f (83e8e65f)
83e8e655 eb07 jmp nt!KiTrap03+0x9e (83e8e65e)
83e8e657 66837d6c1b cmp word ptr [ebp+6Ch],1Bh
83e8e65c 751a jne nt!KiTrap03+0xb8 (83e8e678)
线程内核对象地址给了esi
[Asm] 纯文本查看 复制代码 83e8e65e fb sti
83e8e65f 8bf1 mov esi,ecx
被覆盖为EDX
[Asm] 纯文本查看 复制代码 83e8e661 8bfa mov edi,edx
EAX是0 cc是一个陷阱
[Asm] 纯文本查看 复制代码 83e8e663 8bd0 mov edx,eax
产生异常的下一条的EIP给了ebx
[Asm] 纯文本查看 复制代码 83e8e665 8b5d68 mov ebx,dword ptr [ebp+68h]
0xCC的地址
[Asm] 纯文本查看 复制代码 83e8e668 4b dec ebx
异常号 3号
[Asm] 纯文本查看 复制代码 83e8e669 b903000000 mov ecx,3
错误编码
[Asm] 纯文本查看 复制代码 83e8e66e b803000080 mov eax,80000003h
[Asm] 纯文本查看 复制代码 [b]83e8e673 e814f7ffff call nt!CommonDispatchException (83e8dd8c)[/b]
1.2
注意到KiTrap03 实际上调用了 CommonDispatchException
二、分析CommdispatchException
刚刚构建的结构体的地址
[Asm] 纯文本查看 复制代码 nt!CommonDispatchException:
83e8dd8c 83ec50 sub esp,50h
83e8dd8f 890424 mov dword ptr [esp],eax
83e8dd92 33c0 xor eax,eax
83e8dd94 89442404 mov dword ptr [esp+4],eax
83e8dd98 89442408 mov dword ptr [esp+8],eax
83e8dd9c 895c240c mov dword ptr [esp+0Ch],ebx
83e8dda0 894c2410 mov dword ptr [esp+10h],ecx
83e8dda4 83f900 cmp ecx,0
83e8dda7 740c je nt!CommonDispatchException+0x29 (83e8ddb5)
83e8dda9 8d5c2414 lea ebx,[esp+14h]
83e8ddad 8913 mov dword ptr [ebx],edx
83e8ddaf 897304 mov dword ptr [ebx+4],esi
83e8ddb2 897b08 mov dword ptr [ebx+8],edi
83e8ddb5 8bcc mov ecx,esp
[Asm] 纯文本查看 复制代码 83e8ddb7 f6457202 test byte ptr [ebp+72h],2
83e8ddbb 7407 je nt!CommonDispatchException+0x38 (83e8ddc4)
83e8ddbd b8ffff0000 mov eax,0FFFFh
83e8ddc2 eb03 jmp nt!CommonDispatchException+0x3b (83e8ddc7)
83e8ddc4 8b456c mov eax,dword ptr [ebp+6Ch]
83e8ddc7 83e001 and eax,1
83e8ddca 6a01 push 1
83e8ddcc 50 push eax
83e8ddcd 55 push ebp
83e8ddce 6a00 push 0
83e8ddd0 51 push ecx
83e8ddd1 e80a610700 call nt!KiDispatchException (83f03ee0)
通过ECX的后两位能够判断异常产生是在0环(高权限的代码,内核层代码) 还是3环(低权限的代码,用户层代码)。
判断是用户异常还是内核异常使用的是CS段寄存器的最后两位
2.2
3环 1B
0环段寄存器 08 后两位为00 操作系统只使用0、3环,1、2环没有用
3环权限低,0环权限高
构建了一个异常记录的结构之后实际上调用了 KiDispatchException 来处理异常
2.1
三、分析KIDispathException
内核层异常处理
先让调试器处理,处理了就继续执行,处理不了就继续向下传
让 RtlDispatchException去处理,处理了就结束,没处理就继续向下传
又让内核调试器处理,处理了就继续执行,处理不了就会导致崩溃(直接蓝屏)
用户层异常处理
如果是第一次分发
注意:通过进程内核对象中的 DebugPort字段去判断有没有用户调试器的
先检测有没有用户调试器,如果没有用户调试器,就给内核调试器。处理的就继续,没处理就继续向下传
发给用户层调试器,如果处理了就继续执行,没处理就继续向下传
设置EIP为 KeUserExceptionDispatcher (在用户层叫做KiUserExceptionDispatcher)
然后返回,返回到用户层就会执行KiUserExceptionDispatcher
KiUserExceptionDispatcher 会调用RtlDispathchException
依次调用 VEH ---SEH ----VCH
如果都没有处理,就会调用RaiseException发起第二次异常。
如果处理了,就调用ZwContinue恢复程序继续执行。
如果是第二次分发(用户层自己处理不了,调用RaiseException 产生的)
交给用户调试器处理,如果处理不了,程序就直接被结束
四、分析KiUserExceptionDispatcher
可以在微软的服务器下载符号,分析ntdll以及别的windowsdll中的函数
使用IDA配合符号分析相应的DLL
4.1
RltDispatcheException函数功能:
先进行 VEH的处理,处理了调用 VCH 然后return true、VEH处理不了就让SEH处理, 循环挨个SEH节点处理。如果能够处理调用VCH return true。SEH也处理不了,就出来,然后产生二次异常。
4.2
RltDispatcheException函数源码:
[C] 纯文本查看 复制代码 BOOLEAN RtlDispatchException (
IN PEXCEPTION_RECORD ExceptionRecord,
IN PCONTEXT ContextRecord
)
{
PEXCEPTION_REGISTRATION_RECORD RegistrationFrame;
PEXCEPTION_REGISTRATION_RECORD NestedFrame = NULL;
DISPATCHER_CONTEXT DispatcherContext;
EXCEPTION_RECORD ExceptionRecord2;
EXCEPTION_DISPOSITION Disposition;
ULONG_PTR StackLow, StackHigh;
ULONG_PTR RegistrationFrameEnd;
// 调用向量化异常处理程序(VEH)
if (RtlCallVectoredExceptionHandlers(ExceptionRecord, Context))
{
// 异常被处理了就调用向量化异常处理程序(VCH)
RtlCallVectoredContinueHandlers(ExceptionRecord, Context);
// 返回已处理
return TRUE;
}
// 获取当前栈顶以及栈限长
RtlpGetStackLimits(&StackLow, &StackHigh);
RegistrationFrame = RtlpGetExceptionList();
// 不断循环遍历所有的 SEH 结构
while (RegistrationFrame != EXCEPTION_CHAIN_END)
{
// 检查是否为空,一个被注册的异常结构永远不为空
ASSERT(RegistrationFrame != NULL);
// 获取异常结构的结束位置
RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
sizeof(EXCEPTION_REGISTRATION_RECORD);
// 检查异常结构是否在栈内,并且是否对齐
if ((RegistrationFrameEnd > StackHigh) ||
((ULONG_PTR)RegistrationFrame < StackLow) ||
((ULONG_PTR)RegistrationFrame & 0x3) ||
!RtlIsValidHandler(SehBase->Handler))
{
// 如果不满足条件
ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
// 返回处理失败
return FALSE;
}
// 如果启用了调试异常的记录,则记录异常
RtlpCheckLogException(ExceptionRecord,
Context,
RegistrationFrame,
sizeof(*RegistrationFrame));
// 调用异常处理函数, exception_handler4
Disposition = RtlpExecuteHandlerForException(ExceptionRecord,
RegistrationFrame,
Context,
&DispatcherContext,
RegistrationFrame->Handler);
// 如果是嵌套异常处理结构体
if (RegistrationFrame == NestedFrame)
{
// 屏蔽嵌套标志位
ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL;
NestedFrame = NULL;
}
// 根据 SEH 函数的处理结果进行性对应的操作
switch (Disposition)
{
// 继续执行
case ExceptionContinueExecution:
// 如果是一个不可继续的异常
if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{
// 设置异常信息
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
ExceptionRecord2.NumberParameters = 0;
RtlRaiseException(&ExceptionRecord2);
}
else
{
// 调用向量化异常处理程序(VCH)
RtlCallVectoredContinueHandlers(ExceptionRecord,
Context);
return TRUE;
}
// 搜索下一层异常处理函数
case ExceptionContinueSearch:
break;
// 如果是嵌套异常
case ExceptionNestedException:
ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
if (DispatcherContext.RegistrationPointer > NestedFrame)
{
NestedFrame = DispatcherContext.RegistrationPointer;
}
break;
// 其它操作
default:
// 设置异常信息结构
ExceptionRecord2.ExceptionRecord = ExceptionRecord;
ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
ExceptionRecord2.NumberParameters = 0;
RtlRaiseException(&ExceptionRecord2);
break;
}
// 获取下一个异常处理函数
RegistrationFrame = RegistrationFrame->Next;
}
// 如果没有处理就返回 FALSE
return FALSE;
} |