停止码0xEF出现的可能原因如下:
(1)硬件故障,尤其是内存和磁盘;
(2)安全软件例如杀毒软件;
(3)windows自身原因;
第一个原因是以上三个原因中最普遍的原因,尤其是加载到内存中的程序因为一些未处理的异常而中止自身运行是最典型的情况。之后造成操作系统突然panic并抛出0xEF停止码。另一方面,如果安全软件不信任二进制运行文件并中止其运行。如果对应的程序是critical关键进程(例如scvhost.exe),这会造成灾难性的后果。但是第二种情况较少出现。
然而,在这篇文章中,我将会将注意力集中到第三个原因上,当启动了影子栈shadow stack功能,并且监测到影子栈与调用栈间存在差异,windows系统会中止运行进程,并进而导致0xEF停止码被抛出。这也是我把windows自身作为0xEF异常产生原因的理由,尽管这种情况也有可能是因为异常的驱动程序或内存引起的。
如果启用了基于硬件的栈保护,处理器会保持两份调用栈的拷贝,第二份拷贝就是知名的影子栈。该栈旨在控制线程的运行流,如果任一栈的返回地址存在异常,则一个特殊的硬件异常被抛出进而造成进程被windows中止。
CRITICAL_PROCESS_DIED (ef)
A critical system process died
Arguments:
Arg1: ffffaf08b56a90c0, Process object or thread object
Arg2: 0000000000000000, If this is 0, a process died. If this is 1, a thread died.
Arg3: 0000000000000000
Arg4: 0000000000000000
3: kd> knL
# Child-SP RetAddr Call Site
00 ffff8c0a`14b3ed38 fffff801`0ed0d122 nt!KeBugCheckEx
01 ffff8c0a`14b3ed40 fffff801`0ec0c7a3 nt!PspCatchCriticalBreak+0x10e
02 ffff8c0a`14b3ede0 fffff801`0ea99290 nt!PspTerminateAllThreads+0x172917
03 ffff8c0a`14b3ee50 fffff801`0ea9908c nt!PspTerminateProcess+0xe0
04 ffff8c0a`14b3ee90 fffff801`0e80f8f8 nt!NtTerminateProcess+0x9c << Terminate our svchost.exe process which then bugchecks the system
05 ffff8c0a`14b3ef00 fffff801`0e800ca0 nt!KiSystemServiceCopyEnd+0x28
06 ffff8c0a`14b3f098 fffff801`0e860d9d nt!KiServiceLinkage
07 ffff8c0a`14b3f0a0 fffff801`0e8106a4 nt!KiDispatchException+0x17941d
08 ffff8c0a`14b3f8e0 fffff801`0e80e03c nt!KiFastFailDispatch+0xe4
09 ffff8c0a`14b3fac0 00007ffc`f18833c6 nt!KiControlProtectionFault+0x2fc << Throws #CP (Control Protection) exception
0a 0000002f`4637f820 000001b1`ae000340 ntdll!RtlpGetActivationContextData+0x52
0b 0000002f`4637f828 000001b1`ae002480 0x000001b1`ae000340
0c 0000002f`4637f830 00000000`00000001 0x000001b1`ae002480
0d 0000002f`4637f838 000001b1`000000f0 0x1
0e 0000002f`4637f840 00000000`00000002 0x000001b1`000000f0
0f 0000002f`4637f848 00000050`00000000 0x2
10 0000002f`4637f850 00000000`00000002 0x00000050`00000000
11 0000002f`4637f858 00000000`000000f0 0x2
12 0000002f`4637f860 00000000`00000000 0xf0
如果检查传递给nt!KiDispatchException函数的第一个参数,就可以看到抛出的异常类型信息。
07 ffff8c0a14b3f0a0 fffff8010e8106a4 nt!KiDispatchException+17941d (perf)
Parameter[0] = ffff8c0a14b3fa18
Parameter[1] = 0000000000000000
Parameter[2] = ffffffffffffff80
Parameter[3] = 0000002f4637f820
3: kd> .exr ffff8c0a14b3fa18
ExceptionAddress: 00007ffcf18833c6 (ntdll!RtlpGetActivationContextData+0x0000000000000052)
ExceptionCode: c0000409 (Security check failure or stack buffer overrun)
ExceptionFlags: 00000001
NumberParameters: 1
Parameter[0]: 0000000000000039
Subcode: 0x39 FAST_FAIL_CONTROL_INVALID_RETURN_ADDRESS Shadow stack violation
进一步检查异常,当启用了cet(Control-flow Enforcement Technology,控制流执行技术)时,运行调用指令,两个返回地址被压入栈:一个进入了调用栈,另一个进入影子栈。之后当返回指令运行时,会检查并比较两个返回地址,如果两个地址不匹配,则cp异常就会像之前提到的那样被抛出。但是cet仅在使用call调用指令时起作用,当使用压栈指令将地址压栈时,cet不起作用,因此影子栈中也不存在返回地址。考虑到这一点,推荐使用cfg保护的jmp跳转指令。cfg的行为与cet类似,如果发现了异常,则中止违规进程。如果进程是critical关键进程,抛出0xEF停止码。
参考链接:
2.https://learn.microsoft.com/en-us/windows/win32/secbp/control-flow-guard;
原文链接:https://bsodtutorials.wordpress.com/2023/12/09/debugging-stop-0xef-critical_process_died/