-
引言
关于快速重启和关闭计算机,网上有不少软件在Ring3下调用ZwShutdownSystem (NtShutdownSystem)来实现,虽然速度很快,但还至少经历一些流程,比如向设备驱动发送停机通知等。以下内容摘自<<深入解析Windows操作系统 第6版(下册)>> P528:
一旦Csrss已经完成了向系统进程传达系统停机的通知,Winlogon最后调用执行体子系统函数 NtShutdownSystem,从而结束停机过程。
NtShutdownSystem 函数又调用 PoSetSystemPowerState函数,来协调设备驱动程序和执行体子系统其余部分(即插即用管理器、电源管理器、执行体、I/O管理器、配置管理器和内存管理器)的停机处理。 例如,PoSetSystemPowerState调用I/O管理器,以便给那些已经请求过停机通知的所有设备驱动程序发送停机I/O包。这一动作使得设备驱动程序有机会在Windows退出以前执行任何必要的特殊处理。
工作者线程的内存栈被换入,配置管理器将任何修改过的注册表数据刷新到磁盘上,内存管理器将所有己修改过的且包含了文件数据的页面写回到它们各自的文件中。如果“在停机时清除页面文件”的选项已被打开,
那么,内存管理器在这时候清除页面文件。I/O管理器还会被再次调用,以便告诉文件系统驱动程序;系统正在进行停机。系统停机过程最终在电源管理器中结束。电源管理器所执行的动作取决于用户指定的是停机、重新引导,还是关闭电源。
可以看到调用NtShutdownSystem还有不少处理的东西,所以我在想能不能调用更底层的实现来更快速的关机。
再者有些防止关机的程序也是Hook了NtShutdownSystem,我们想办法用更底层的实现可以绕过Hook,不过话说已经在内核层驱动了,有啥不能干的呢^_^。
-
NtShutdownSystem分析
下面的分析主要采用ReactOS、WindowsXP的代码以及Windbg调试:
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,再来看看NtSetSystemPowerState的实现(截取主要内容),可以看出最终调用PopGracefulShutdown:
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 }
接着再看PopGracefulShutdown,可以看到这里也做了很多,IO管理器的关闭,配置管理器的关闭,缓存管理器的关闭等。在这里修改的文件、注册表等信息将被写到磁盘。最终调用PopShutdownSystem:
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 }
接着看 PopShutdownSystem,可以看出最终调用的是HalReturnToFirmware:
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 }
-
HalReturnToFirmware分析
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 }
最终调用的是HalpReboot,在xp上,HalpReboot处理一些CMOS数据和PCI数据,就不再深入分析了。
WinXP下FIRMWARE_ENTRY 定义如下:
typedef enum _FIRMWARE_REENTRY { HalHaltRoutine, HalPowerDownRoutine, HalRestartRoutine, HalRebootRoutine, HalInteractiveModeRoutine, HalMaximumRoutine } FIRMWARE_REENTRY, * PFIRMWARE_REENTRY;
HalReturnToFirmware也是我们可调用的最底层接口,因为它是导出的,可以在驱动中直接使用。
在Win7 x64环境下Windbg反汇编的结果如下,跳转逻辑已用颜色标记出来:
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 (fffff800`05412d9d) //传入的参数为0hal!HalReturnToFirmware+0x13:
fffff800`05412d7b 3bcd cmp ecx,ebp
fffff800`05412d7d 7419 je hal!HalReturnToFirmware+0x30 (fffff800`05412d98) //传入的参数为1,跳转到hal!HalpShutdownhal!HalReturnToFirmware+0x17:
fffff800`05412d7f 7e05 jle hal!HalReturnToFirmware+0x1e (fffff800`05412d86) //传入小于0的数值,非法hal!HalReturnToFirmware+0x19:
fffff800`05412d81 83f903 cmp ecx,3
fffff800`05412d84 7e17 jle hal!HalReturnToFirmware+0x35 (fffff800`05412d9d) //传入的参数小于等于3,也就是2和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],cxhal!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位下逻辑有所改变,从hal!HalReturnToFirmware+0x79 开始其实就为XP下HalpReboot的具体实现。
-
代码实现
综上所述,代码实现就比较简单了:
头文件定义
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 //再定义导出函数 HalReturnToFirmware 13 EXTERN_C NTKERNELAPI VOID NTAPI HalReturnToFirmware( 14 LONG lReturnType 15 );
实现:
1 VOID ComputerPowerOffByHal() 2 { 3 HalReturnToFirmware(HalPowerDownRoutine); 4 } 5 6 VOID ComputerResetByHal() 7 { 8 HalReturnToFirmware(HalRebootRoutine); 9 }