会话管理器进程SMSS.exe是系统启动后的第一个用户态进程,负责启动和监护windows子系统进程:CSRSS.exe和登陆管理进程:WinLogon
SMSS.exe从注册表中查询子系统exe文件的位置,并且启动它CSRSS是windows子系统进程,自NT4以后窗口管理和GDI的主体实现被移出了CSRSS放到了win32k.sys中。
CSRSS监管着所有windows线程和进程,每个进程在创建后都要到这里登记才能运行,退出时也要报告注销。(维护了Windows子系统层面上的记录结构)
CSRSS具有桌面管理、终端登录、控制台管理、HardError报告等方面的重要作用。
为了适应恶劣环境下的错误提示的需要,Windows定义了硬错误提示机制。
硬错误本意是硬件有关错误,后来泛指严重的错误。
硬错误可以在用户态使用也可以在内核态使用。
硬错误的核心处理函数是内核中的ExpRaiseHardError函数。
在我们看这个函数的源码前,先想一想,发送硬错误消息,首先要的是找到发送硬错误的端口。
这个端口怎么找?是通过一系列的设置和标志位来判断的。
如下是具体的程序内容
1 NTSTATUS 2 ExpRaiseHardError ( 3 IN NTSTATUS ErrorStatus, 4 IN ULONG NumberOfParameters, 5 IN ULONG UnicodeStringParameterMask, 6 IN PULONG_PTR Parameters, 7 IN ULONG ValidResponseOptions, 8 OUT PULONG Response 9 ) 10 { 11 PTEB Teb; 12 PETHREAD Thread; 13 PEPROCESS Process; 14 ULONG_PTR MessageBuffer[PORT_TOTAL_MAXIMUM_MESSAGE_LENGTH/sizeof(ULONG_PTR)]; 15 PHARDERROR_MSG m; 16 NTSTATUS Status; 17 HANDLE ErrorPort; 18 KPROCESSOR_MODE PreviousMode; 19 BOOLEAN DoingShutdown; 20 21 PAGED_CODE(); 22 //要发送的消息结构 23 m = (PHARDERROR_MSG)&MessageBuffer[0]; 24 PreviousMode = KeGetPreviousMode(); 25 26 DoingShutdown = FALSE; 27 //如果参数要求关机,就检查是否有关机权限 28 if (ValidResponseOptions == OptionShutdownSystem) { 29 30 // 31 // Check to see if the caller has the privilege to make this call. 32 // 33 34 if (!SeSinglePrivilegeCheck (SeShutdownPrivilege, PreviousMode)) { 35 return STATUS_PRIVILEGE_NOT_HELD; 36 } 37 38 ExReadyForErrors = FALSE; 39 HardErrorState = SHUTDOWN; 40 DoingShutdown = TRUE; 41 } 42 43 Thread = PsGetCurrentThread(); 44 Process = PsGetCurrentProcess(); 45 46 // 47 // If the default handler is not installed, then 48 // call the fatal hard error handler if the error 49 // status is error 50 // 51 // Let GDI override this since it does not want to crash the machine 52 // when a bad driver was loaded via MmLoadSystemImage. 53 // 54 55 if ((Thread->CrossThreadFlags & PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED) == 0) { 56 //如果满足这个标志位说明用户态HardError系统还没有准备好,只能在内核态去提示这个HardError了 57 if (NT_ERROR(ErrorStatus) && (HardErrorState == STARTING || DoingShutdown)) { 58 //而所谓的在内核态提示HardError就是执行这个函数了,实质上就是一个KeBugCheckEx,也就是说想着内核态提示HardError只能是通过抛出蓝屏 59 ExpSystemErrorHandler ( 60 ErrorStatus, 61 NumberOfParameters, 62 UnicodeStringParameterMask, 63 Parameters, 64 (BOOLEAN)((PreviousMode != KernelMode) ? TRUE : FALSE)); 65 } 66 } 67 68 // 69 // If the process has an error port, then if it wants default 70 // handling, use its port. If it disabled default handling, then 71 // return the error to the caller. If the process does not 72 // have a port, then use the registered default handler. 73 // 74 75 ErrorPort = NULL; 76 //这个就是通过各种标志位来判断硬错误端口是什么了。对于一个进程来说只有异常端口和调试端口两个东西,并没有单独指定硬错误端口的结构。 77 if (Process->ExceptionPort) { 78 //异常端口存在时 79 if (Process->DefaultHardErrorProcessing & 1) { 80 //这个标志位存在时,异常端口就是硬错误端口 81 ErrorPort = Process->ExceptionPort; 82 } else { 83 84 // 85 // If error processing is disabled, check the error override 86 // status. 87 // 88 89 if (ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE) { 90 //满足这个条件的话,也是异常端口 91 ErrorPort = Process->ExceptionPort; 92 } 93 } 94 } else { 95 if (Process->DefaultHardErrorProcessing & 1) { 96 ErrorPort = ExpDefaultErrorPort; 97 } else { 98 99 //100 // If error processing is disabled, check the error override101 // status.102 //103 104 if (ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE) {105 ErrorPort = ExpDefaultErrorPort;106 }107 }108 }109 //虽然上面比较的那么热闹。。但是ExpDefaultErrorPort和ExeceptionPort的值其实是一样的110 //都是CSRSS进程的\Windows\ApiPort端口。这是一个LPC端口对象111 112 if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED) != 0) {113 ErrorPort = NULL;114 }115 //设置了禁止硬错误就把ErrorPort清零116 if ((ErrorPort != NULL) && (!IS_SYSTEM_THREAD(Thread))) {117 Teb = (PTEB)PsGetCurrentThread()->Tcb.Teb;118 try {119 if (Teb->HardErrorMode & RTL_ERRORMODE_FAILCRITICALERRORS) {120 ErrorPort = NULL;121 }122 } except (EXCEPTION_EXECUTE_HANDLER) {123 ;124 }125 }126 127 if (ErrorPort == NULL) {128 *Response = (ULONG)ResponseReturnToCaller;129 return STATUS_SUCCESS;130 }131 //自己给自己发异常?出现问题了132 if (Process == ExpDefaultErrorPortProcess) {133 if (NT_ERROR(ErrorStatus)) {134 ExpSystemErrorHandler (ErrorStatus,135 NumberOfParameters,136 UnicodeStringParameterMask,137 Parameters,138 (BOOLEAN)((PreviousMode != KernelMode) ? TRUE : FALSE));139 }140 *Response = (ULONG)ResponseReturnToCaller;141 Status = STATUS_SUCCESS;142 return Status;143 }144 145 m->h.u1.Length = HARDERROR_API_MSG_LENGTH;146 m->h.u2.ZeroInit = LPC_ERROR_EVENT;147 m->Status = ErrorStatus & ~HARDERROR_OVERRIDE_ERRORMODE;148 m->ValidResponseOptions = ValidResponseOptions;149 m->UnicodeStringParameterMask = UnicodeStringParameterMask;150 m->NumberOfParameters = NumberOfParameters;151 152 if (Parameters != NULL) {153 try {154 RtlCopyMemory (&m->Parameters,155 Parameters,156 sizeof(ULONG_PTR)*NumberOfParameters);157 } except (EXCEPTION_EXECUTE_HANDLER) {158 }159 }160 161 KeQuerySystemTime(&m->ErrorTime);162 //执行发送函数163 Status = LpcRequestWaitReplyPortEx (ErrorPort,164 (PPORT_MESSAGE) m,165 (PPORT_MESSAGE) m);166 167 if (NT_SUCCESS(Status)) {168 switch (m->Response) {169 //作为一个返回值170 case ResponseReturnToCaller :171 case ResponseNotHandled :172 case ResponseAbort :173 case ResponseCancel :174 case ResponseIgnore :175 case ResponseNo :176 case ResponseOk :177 case ResponseRetry :178 case ResponseYes :179 case ResponseTryAgain :180 case ResponseContinue :181 break;182 default:183 m->Response = (ULONG)ResponseReturnToCaller;184 break;185 }186 *Response = m->Response;187 }188 189 return Status;190 }
这个函数把硬错误信息发送到了CSRSS进程的\Windows\ApiPort端口上去。
接下来就是看CSRSS进程是怎么接受并处理这个硬错误消息的。
CSRSS进程中专门启用了一个线程来监听并处理\Windows\ApiPort端口的内容
CsrApiRequestThread就是这个专门用来监听的工作线程
PORT_MESSAGE是发送的LPC端口的数据结构。
CSRSS负责后续的处理工作,并进行用户层面的弹出提示。