Windows驅動開發學習記錄-驅動中快速重啟關閉計算機之一
-
引言
關於快速重啟和關閉計算機,網上有不少軟體在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 elseif (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 }