• 《软件调试》读书笔记:第13章 硬错误和蓝屏


    会话管理器进程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 override
    101             // status.
    102             //
    103 
    104             if (ErrorStatus & HARDERROR_OVERRIDE_ERRORMODE) {
    105                 ErrorPort = ExpDefaultErrorPort;
    106             }
    107         }
    108     }
    109     //虽然上面比较的那么热闹。。但是ExpDefaultErrorPort和ExeceptionPort的值其实是一样的
    110     //都是CSRSS进程的WindowsApiPort端口。这是一个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进程的WindowsApiPort端口上去。

    接下来就是看CSRSS进程是怎么接受并处理这个硬错误消息的。

    CSRSS进程中专门启用了一个线程来监听并处理WindowsApiPort端口的内容

    CsrApiRequestThread就是这个专门用来监听的工作线程

    PORT_MESSAGE是发送的LPC端口的数据结构。

    CSRSS负责后续的处理工作,并进行用户层面的弹出提示。

  • 相关阅读:
    关于学习netty的两个完整服务器客户端范例
    android-betterpickers
    ValueBar
    CircleDisplay
    JellyViewPager
    十天学习PHP之第二天
    android-測试so动态库(九)
    实习题
    android 编程小技巧(持续中)
    Codeforces Round #253 (Div. 2)——Borya and Hanabi
  • 原文地址:https://www.cnblogs.com/Ox9A82/p/5383818.html
Copyright © 2020-2023  润新知