Windows driver development learning record - one of the fast restart and shutdown computers in the driver

  •   introduction

For fast restart and shutdown of the computer, many software on the Internet call ZW shutdown system (ntshutdown system) under Ring3. Although it is fast, it still goes through at least some processes, such as sending shutdown notice to the device driver. The following is extracted from < < in depth analysis of Windows operating system version 6 (Volume 2) > > p528:

  once Csrss The notification of system shutdown to the system process has been completed, Winlogon Finally, the execution body subsystem function is called. NtShutdownSystem,This ends the shutdown process.
NtShutdownSystem Function called again PoSetSystemPowerState Function to coordinate the device driver and the rest of the actuator subsystem(Plug and play manager, power manager, actuator I/O Manager, configuration manager, and memory manager)Shutdown treatment of.   For example, PoSetSystemPowerState call I/O Manager to send downtime notifications to all device drivers that have requested downtime notifications I/O Bag. This action gives the device driver the opportunity to Windows Perform any necessary special processing before exiting.
The memory stack of the worker thread is replaced, the configuration manager flushes any modified registry data to the disk, and the memory manager writes all modified pages containing file data back to their respective files. If the option "clear page file during shutdown" is turned on,
Then, the memory manager clears the page file at this time. I/O The manager is also called again to tell the file system driver;The system is shutting down. The system shutdown process ends in the power manager. The actions performed by the power manager depend on whether the user specifies to shut down, reboot, or turn off the power.

You can see that there are still a lot of things to deal with when calling ntshutdown system, so I wonder if I can call a lower level implementation to shut down more quickly.

In addition, some programs to prevent shutdown are also Hook ntshutdown system. We try to bypass Hook with a lower level implementation, but it is already driven in the kernel layer. What can't we do.

 

  • Ntshutdown system analysis

The following analysis mainly adopts ReactOS, WindowsXP code and Windbg debugging:

 1 <<ReactOS>>
 2 NTSTATUS
 3 NTAPI
 4 NtShutdownSystem(IN SHUTDOWN_ACTION Action)
 5 {
 6     POWER_ACTION PowerAction;
 7  
 8     /* Convert to power action */
 9     if (Action == ShutdownNoReboot)
10     {
11         PowerAction = PowerActionShutdown;
12     }
13     else if (Action == ShutdownReboot)
14     {
15         PowerAction = PowerActionShutdownReset;
16     }
17     else if (Action == ShutdownPowerOff)
18     {
19         PowerAction = PowerActionShutdownOff;
20     }
21     else
22     {
23         return STATUS_INVALID_PARAMETER;
24     }
25  
26     /* Now call the power manager */
27     DPRINT("Setting state to: %lx\n", PowerAction);
28     return NtSetSystemPowerState(PowerAction,
29                                  PowerSystemSleeping3,
30                                  POWER_ACTION_OVERRIDE_APPS |
31                                  POWER_ACTION_DISABLE_WAKES |
32                                  POWER_ACTION_CRITICAL);
33 }

  

NtSetSystemPowerState is debugged here. Let's take a look at the implementation of NtSetSystemPowerState (intercepting the main content). It can be seen that PopGracefulShutdown is finally called:

 

 1 <<Windows XP>>
 2 NTSYSAPI
 3 NTSTATUS
 4 NTAPI
 5 NtSetSystemPowerState (
 6     IN POWER_ACTION SystemAction,
 7     IN SYSTEM_POWER_STATE LightestSystemState,
 8     IN ULONG Flags                  // POWER_ACTION_xxx flags
 9     )
10 {
11     KPROCESSOR_MODE         PreviousMode;
12     ......
13     CmSetLazyFlushState(FALSE);
14     ......
15     if (PopAction.Shutdown) {
16  
17         //
18         // Force reacquisition of the dev list. We will be telling Pnp
19         // to unload all possible devices, and therefore Pnp needs us to
20         // release the Pnp Engine Lock.
21         //
22         IoFreePoDeviceNotifyList(&PopAction.DevState->Order);
23         PopAction.DevState->GetNewDeviceList = TRUE;
24  
25         //
26         // We shut down via a system worker thread so that the
27         // current active process will exit cleanly.
28         //
29  
30         if (PsGetCurrentProcess() != PsInitialSystemProcess) {
31  
32             ExInitializeWorkItem(&PopShutdownWorkItem,
33                                  &PopGracefulShutdown,
34                                  NULL);
35  
36             ExQueueWorkItem(&PopShutdownWorkItem,
37                             PO_SHUTDOWN_QUEUE);
38  
39             // Clean up in prep for wait...
40             ASSERT(!PolicyLockOwned);
41  
42             //
43             // If we acquired the timer refresh lock (can happen if we promoted to shutdown)
44             // then we need to release it so that suspend actually suspends.
45             //
46             if (TimerRefreshLockOwned) {
47                 ExReleaseTimeRefreshLock();
48             }
49  
50             // And sleep until we're terminated.
51  
52             // Note that we do NOT clean up the dev state -- it's now
53             // owned by the shutdown worker thread.
54  
55             // Note that we also do not unlock the pagable image
56             // section referred to by ExPageLockHandle -- this keeps
57             // all of our shutdown code in memory.
58  
59             KeSuspendThread(KeGetCurrentThread());
60  
61             return STATUS_SYSTEM_SHUTDOWN;
62         } else {
63             PopGracefulShutdown (NULL);
64         }
65     }
66     ......
67  
68 }

 

Then look at PopGracefulShutdown. You can see that a lot has been done here, such as closing the IO manager, closing the configuration manager, closing the cache manager, etc. The file, registry and other information modified here will be written to disk. Finally, call popshutdown system:

 

 1 <<Windows XP>>
 2 VOID
 3 PopGracefulShutdown (
 4     IN PVOID WorkItemParameter
 5     )
 6 {
 7     PVOID         Context;
 8     ......
 9     if (PoCleanShutdownEnabled()) {
10         //
11         // Terminate all processes.  This will close all the handles and delete
12         // all the address spaces.  Note the system process is kept alive.
13         //
14         PsShutdownSystem ();
15  
16         ......
17  
18     }
19     //
20     // Terminate Plug-N-Play.
21     //
22  
23     PpShutdownSystem (TRUE, 0, &Context);
24  
25     ExShutdownSystem (0);
26  
27     //
28     // Send shutdown IRPs to all drivers that asked for it.
29     //
30  
31     IoShutdownSystem (0);
32     
33     //
34     // Scrub the object directories
35     //
36     if (PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_OB) {
37         ObShutdownSystem (0);
38     }
39  
40     //
41     // Close the registry and the associated handles/file objects.
42     //
43     CmShutdownSystem ();
44     
45     ......
46     
47     MmShutdownSystem (0);
48  
49     //
50     // Inform drivers of the system shutdown state.
51     // This will finish shutting down Io and Mm.
52     // After this is complete,
53     // NO MORE REFERENCES TO PAGABLE CODE OR DATA MAY BE MADE.
54     //
55  
56     // ISSUE-2000/03/14-earhart: shutdown filesystems in dev shutdown
57     IoConfigureCrashDump(CrashDumpDisable);
58     CcWaitForCurrentLazyWriterActivity();
59     ExShutdownSystem(1);
60     IoShutdownSystem(1);
61     MmShutdownSystem(1);
62  
63     ......
64  
65     HalSetWakeEnable(FALSE);
66  
67     ......
68  
69     PpShutdownSystem (TRUE, 1, &Context);
70  
71     ExShutdownSystem (2);
72  
73     if (PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_OB) {
74         ObShutdownSystem (2);
75     }
76  
77     //
78     // Any allocated pool left at this point is a leak.
79     //
80  
81     MmShutdownSystem (2);
82  
83     //
84     // Implement shutdown style action -
85     // N.B. does not return (will bugcheck in preference to returning).
86     //
87  
88     PopShutdownSystem(PopAction.Action);
89 }

  

Keep looking   From popshutdown system, we can see that HalReturnToFirmware is finally called:

 

 1 <<Windows XP>>
 2 VOID
 3 PopShutdownSystem (
 4     IN POWER_ACTION SystemAction
 5     )
 6 /*++
 7 Routine Description:
 8     Routine to implement a Shutdown style power actions
 9 Arguments:
10     SystemAction    - Action to implement (must be a valid shutdown type)
11 Return Value:
12     Status
13 --*/
14 {
15  
16     //
17     // Tell the debugger we are shutting down
18     //
19  
20     KD_SYMBOLS_INFO SymbolInfo = {0};
21     SymbolInfo.BaseOfDll = (PVOID)KD_REBOOT;
22     DebugService2(NULL, &SymbolInfo, BREAKPOINT_UNLOAD_SYMBOLS);
23  
24     //
25     // Perform the final shutdown operation
26     //
27  
28     switch (SystemAction) {
29         case PowerActionShutdownReset:
30  
31             //
32             // Reset the system
33             //
34  
35             PopInvokeSystemStateHandler (PowerStateShutdownReset, NULL);
36  
37             //
38             // Didn't do it, go for legacy function
39             //
40  
41             HalReturnToFirmware (HalRebootRoutine);
42             break;
43  
44         case PowerActionShutdownOff:
45         case PowerActionShutdown:
46  
47  
48             //
49             // Power down the system
50             //
51  
52             PopInvokeSystemStateHandler (PowerStateShutdownOff, NULL);
53  
54             //
55             // Didn't do it, go for legacy function
56             //
57  
58             HalReturnToFirmware (HalPowerDownRoutine);
59  
60             //
61             // Due to simulations we can try to power down on systems
62             // which don't support it
63             //
64  
65             PoPrint (PO_ERROR, ("PopShutdownSystem: HalPowerDownRoutine returned\n"));
66             HalReturnToFirmware (HalRebootRoutine);
67             break;
68  
69         default:
70             //
71             // Got some unexpected input...
72             //
73             HalReturnToFirmware (HalRebootRoutine);
74     }
75  
76     KeBugCheckEx (INTERNAL_POWER_ERROR, 5, 0, 0, 0);
77 }

 

 

 

  • Hallreturntofirmware analysis

 1 VOID
 2 HalReturnToFirmware(
 3     IN FIRMWARE_ENTRY Routine
 4     )
 5  
 6  
 7  
 8 {
 9     switch (Routine) {
10         case HalPowerDownRoutine:
11  
12 #if defined(NEC_98)
13  
14             HalpPowerDownFlag = TRUE;
15  
16 #endif // defined(NEC_98)
17  
18         case HalHaltRoutine:
19         case HalRestartRoutine:
20         case HalRebootRoutine:
21  
22             InbvAcquireDisplayOwnership();
23  
24             //
25             // Never returns
26             //
27  
28             HalpReboot();
29             break;
30         default:
31             DbgPrint("HalReturnToFirmware called\n");
32             DbgBreakPoint();
33             break;
34     }
35 }

    

The final call is HalpReboot. On xp, HalpReboot processes some CMOS data and PCI data, so it will not be analyzed in depth.

        Firmware under WinXP_ ENTRY   It is defined as follows:

  

typedef enum _FIRMWARE_REENTRY {
        HalHaltRoutine,
        HalPowerDownRoutine,
        HalRestartRoutine,
        HalRebootRoutine,
        HalInteractiveModeRoutine,
        HalMaximumRoutine
} FIRMWARE_REENTRY, * PFIRMWARE_REENTRY;

 

Hallreturntofirmware is also the lowest interface we can call, because it is exported and can be used directly in the driver.

The results of Windbg disassembly in Win7 x64 environment are as follows, and the jump logic has been marked with color:

1: kd> uf hal!HalReturnToFirmware
hal!HalReturnToFirmware:
fffff800`05412d68 48895c2408 mov qword ptr [rsp+8],rbx
fffff800`05412d6d 55 push rbp
fffff800`05412d6e 4883ec20 sub rsp,20h
fffff800`05412d72 bd01000000 mov ebp,1
fffff800`05412d77 85c9 test ecx,ecx
fffff800`05412d79 7422 je hal! Halreturntofirmware + 0x35 (fff800 ` 05412d9d) / / the parameter passed in is 0

hal!HalReturnToFirmware+0x13:
fffff800`05412d7b 3bcd cmp ecx,ebp
fffff800`05412d7d 7419 je hal! Hallreturntofirmware + 0x30 (fff800 ` 05412d98) / / the parameter passed in is 1. Jump to hal!HalpShutdown

hal!HalReturnToFirmware+0x17:
fffff800`05412d7f 7e05 jle hal! Halreturntofirmware + 0x1E (fff800 ` 05412d86) / / pass in a value less than 0, illegal

hal!HalReturnToFirmware+0x19:
fffff800`05412d81 83f903 cmp ecx,3
fffff800`05412d84 7e17 jle hal! Hallreturntofirmware + 0x35 (fff800 ` 05412d9d) / / when the parameter passed in is less than or equal to 3, that is, when 2 and 3

hal!HalReturnToFirmware+0x1e:
fffff800`05412d86 488d0d13400100 lea rcx,[hal! ?? ::FNODOBFM::`string' (fffff800`05426da0)]
fffff800`05412d8d e84e350100 call hal!DbgPrint (fffff800`054262e0)
fffff800`05412d92 cc int 3
fffff800`05412d93 e9ca000000 jmp hal!HalReturnToFirmware+0xfa (fffff800`05412e62)

hal!HalReturnToFirmware+0x30:
fffff800`05412d98 e8cb010000 call hal!HalpShutdown (fffff800`05412f68)

hal!HalReturnToFirmware+0x35:
fffff800`05412d9d ff1555550100 call qword ptr [hal!_imp_InbvAcquireDisplayOwnership (fffff800`054282f8)]
fffff800`05412da3 4533c0 xor r8d,r8d
fffff800`05412da6 8bd5 mov edx,ebp
fffff800`05412da8 33c9 xor ecx,ecx
fffff800`05412daa e895230000 call hal!HalpMapPhysicalMemory64 (fffff800`05415144)
fffff800`05412daf 4885c0 test rax,rax
fffff800`05412db2 740c je hal!HalReturnToFirmware+0x58 (fffff800`05412dc0)

hal!HalReturnToFirmware+0x4c:
fffff800`05412db4 b934120000 mov ecx,1234h
fffff800`05412db9 66898872040000 mov word ptr [rax+472h],cx

hal!HalReturnToFirmware+0x58:
fffff800`05412dc0 b9e8030000 mov ecx,3E8h
fffff800`05412dc5 e8867bffff call hal!HalpAcquireCmosSpinLockEx (fffff800`0540a950)
fffff800`05412dca fa cli
fffff800`05412dcb 803d33f6010000 cmp byte ptr [hal!HalpTimeSourceInitializationComplete (fffff800`05432405)],0
fffff800`05412dd2 750d jne hal!HalReturnToFirmware+0x79 (fffff800`05412de1)

hal!HalReturnToFirmware+0x6c:
fffff800`05412dd4 ba64000000 mov edx,64h
fffff800`05412dd9 b0fe mov al,0FEh
fffff800`05412ddb ee out dx,al
fffff800`05412ddc e981000000 jmp hal!HalReturnToFirmware+0xfa (fffff800`05412e62)

hal!HalReturnToFirmware+0x79:
fffff800`05412de1 ba70000000 mov edx,70h
fffff800`05412de6 b00b mov al,0Bh
fffff800`05412de8 ee out dx,al
fffff800`05412de9 8bcd mov ecx,ebp
fffff800`05412deb e8085b0000 call hal!KeStallExecutionProcessor (fffff800`054188f8)
fffff800`05412df0 ba71000000 mov edx,71h
fffff800`05412df5 ec in al,dx
fffff800`05412df6 8ad8 mov bl,al
fffff800`05412df8 8bcd mov ecx,ebp
fffff800`05412dfa e8f95a0000 call hal!KeStallExecutionProcessor (fffff800`054188f8)
fffff800`05412dff 80e3bf and bl,0BFh
fffff800`05412e02 ba71000000 mov edx,71h
fffff800`05412e07 8ac3 mov al,bl
fffff800`05412e09 ee out dx,al
fffff800`05412e0a 8bcd mov ecx,ebp
fffff800`05412e0c e8e75a0000 call hal!KeStallExecutionProcessor (fffff800`054188f8)
fffff800`05412e11 ba70000000 mov edx,70h
fffff800`05412e16 b00a mov al,0Ah
fffff800`05412e18 ee out dx,al
fffff800`05412e19 8bcd mov ecx,ebp
fffff800`05412e1b e8d85a0000 call hal!KeStallExecutionProcessor (fffff800`054188f8)
fffff800`05412e20 ba71000000 mov edx,71h
fffff800`05412e25 ec in al,dx
fffff800`05412e26 8ad8 mov bl,al
fffff800`05412e28 8bcd mov ecx,ebp
fffff800`05412e2a e8c95a0000 call hal!KeStallExecutionProcessor (fffff800`054188f8)
fffff800`05412e2f 80e3f6 and bl,0F6h
fffff800`05412e32 ba71000000 mov edx,71h
fffff800`05412e37 80cb06 or bl,6
fffff800`05412e3a 8ac3 mov al,bl
fffff800`05412e3c ee out dx,al
fffff800`05412e3d 8bcd mov ecx,ebp
fffff800`05412e3f e8b45a0000 call hal!KeStallExecutionProcessor (fffff800`054188f8)
fffff800`05412e44 ba70000000 mov edx,70h
fffff800`05412e49 b015 mov al,15h
fffff800`05412e4b ee out dx,al
fffff800`05412e4c 8bcd mov ecx,ebp
fffff800`05412e4e e8a55a0000 call hal!KeStallExecutionProcessor (fffff800`054188f8)
fffff800`05412e53 e880a30000 call hal!HalpResetAllProcessors (fffff800`0541d1d8)
fffff800`05412e58 e8d3010000 call hal!HalpWriteResetCommand (fffff800`05413030)
fffff800`05412e5d e84e390100 call hal!HalpHalt (fffff800`054267b0)

hal!HalReturnToFirmware+0xfa:
fffff800`05412e62 488b5c2430 mov rbx,qword ptr [rsp+30h]
fffff800`05412e67 4883c420 add rsp,20h
fffff800`05412e6b 5d pop rbp
fffff800`05412e6c c3 ret

Win7 x64 bit logic has changed from hal!HalReturnToFirmware+0x79   In fact, it was the specific implementation of HalpReboot under XP at the beginning.

 

  • code implementation

To sum up, the code implementation is relatively simple:

Header file definition

 1 #pragma once
 2  
 3 typedef enum _FIRMWARE_REENTRY {
 4         HalHaltRoutine,
 5         HalPowerDownRoutine,
 6         HalRestartRoutine,
 7         HalRebootRoutine,
 8         HalInteractiveModeRoutine,
 9         HalMaximumRoutine
10 } FIRMWARE_REENTRY, * PFIRMWARE_REENTRY;
11  
12 //Redefine export function HalReturnToFirmware
13 EXTERN_C NTKERNELAPI VOID NTAPI HalReturnToFirmware(
14         LONG lReturnType
15 );

Implementation:

1 VOID ComputerPowerOffByHal()
2 {
3         HalReturnToFirmware(HalPowerDownRoutine);
4 }
5  
6 VOID ComputerResetByHal()
7 {
8         HalReturnToFirmware(HalRebootRoutine);
9 }

 

Posted by Hell Toupee on Thu, 25 Nov 2021 16:59:32 -0800