1.背景:
a.Windows關機時,将調用NtShutdownSystem Native API來執行關機動作。
b.在WinXp時代,使用者執行"關機"後,将由winlogon.exe調用NtShutdownSystem API;但是到了Win7時代,winlogon.exe不再司此職,而是交由Wininit.exe調用NtShutdownSystem API。
c.進入win10後,雖然Wininit.exe中仍保留WinInitShutdown及PerformSystemRestore函數,但關機時不會由WinInitShutdown調用NtShutdownSystem Native API。是以該任意加載/執行不适用于win10。
2.正文:
在Win7上,給nt!NtShutdownSystem下斷點後,當使用者點選"開始"菜單--"關機"後會得到下列函數調用棧:
#搜尋NtShutdownSystem Native API并下斷點
kd> x nt!NtShutdownSystem
8318c3e6 nt!NtShutdownSystem = <no type information>
kd> bp nt!NtShutdownSystem
#關機後,windbg會中斷在WinInitShutdown函數
kd> g
Breakpoint 0 hit
nt!NtShutdownSystem:
8318c3e6 8bff mov edi,edi
kd> kb
ChildEBP RetAddr Args to Child
95472c28 82ba3173 00000002 0019f8e8 76fea364 nt!NtShutdownSystem
95472c28 76fea364 00000002 0019f8e8 76fea364 nt!KiFastCallEntry+0x163
0019f8c4 76fc1814 00326205 00000002 003220c8 ntdll!KiFastSystemCallRet
0019f9c4 77557646 7ffd7000 0019fa10 76ff0915 ntdll!NtShutdownSystem+0xc
0019f9d0 76ff0915 7ffd7000 88d9a0a3 00000000 kernel32!BaseThreadInitThunk+0xe
0019fa10 76ff0b71 0032f0cd 7ffd7000 00000000 ntdll!__RtlUserThreadStart+0x23
0019fa28 00000000 0032f0cd 7ffd7000 00000000 ntdll!_RtlUserThreadStart+0x1b
上述調用堆棧不是很詳細,切換到wininit程序,檢視更具體的調用堆棧:
#枚舉所有程序
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 87bf19f0 SessionId: 0 Cid: 0180 Peb: 7ffd7000 ParentCid: 0148
DirBase: 3fe820a0 ObjectTable: 99218a60 HandleCount: 88.
Image: wininit.exe
#根據wininit的EPROCESS值,切換到wininit程序
kd> .process /i /p 87bf19f0
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
82b94a9c cc int 3
#顯示wininit程序更詳細的資訊 (包含線程及調用堆棧)
kd> !process 87bf19f0 7
PROCESS 87bf19f0 SessionId: 0 Cid: 0180 Peb: 7ffd7000 ParentCid: 0148
DirBase: 3fe820a0 ObjectTable: 99218a60 HandleCount: 88.
Image: wininit.exe
THREAD 8e49c290 Cid 0180.0184 Teb: 7ffdf000 Win32Thread: ffb53978 WAIT: (Executive) KernelMode Non-Alertable
95472958 SynchronizationEvent
95472930 Semaphore Limit 0x12
Win32 Start Address wininit!WinMainCRTStartup (0x0032f0cd)
Stack Init 95472ed0 Current 95472708 Base 95473000 Limit 95470000 Call 0
Priority 15 BasePriority 15 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
ChildEBP RetAddr Args to Child
95472720 82a6a110 8e49c290 000031d8 00000000 nt!KiSwapContext+0x26 (FPO: [Uses EBP] [0,0,4])
95472758 82a6b715 95472930 8e49c290 8e49c38c nt!KiSwapThread+0x394
95472780 82a5bda6 8e49c290 8e49c350 00000000 nt!KiCommitThreadWait+0x461
954728f8 83203019 00000002 95472974 00000001 nt!KeWaitForMultipleObjects+0x6d2
95472980 83202d32 00000011 9762e350 00000004 nt!PopSleepDeviceList+0xe9
954729d8 83202af3 95472a70 00000001 00000006 nt!PoBroadcastSystemState+0x236
95472a00 83203d98 95472ad4 95472b58 8320398e nt!PopSetDevicesSystemState+0x63
95472ac0 82ba3173 00000006 00000004 c0000004 nt!NtSetSystemPowerState+0x40a
95472ac0 82b92c1d 00000006 00000004 c0000004 nt!KiFastCallEntry+0x163 (FPO: [0,3] TrapFrame @ 95472ad4)
95472b44 83203a70 00000006 00000004 c0000004 nt!ZwSetSystemPowerState+0x11 (FPO: [3,0,0])
95472c14 8318c418 00000006 00000004 c0000004 nt!NtSetSystemPowerState+0xe2
95472c28 82ba3173 00000002 0019f8e8 76fea364 nt!NtShutdownSystem+0x32
95472c28 76fea364 00000002 0019f8e8 76fea364 nt!KiFastCallEntry+0x163 (FPO: [0,3] TrapFrame @ 95472c34)
0019f8c4 76fc1814 00326205 00000002 003220c8 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0019f8c8 00326205 00000002 003220c8 003215a0 ntdll!NtShutdownSystem+0xc (FPO: [1,0,0])
!!----> 0019f8e8 00327f4e 00000002 00336904 00531ceb wininit!WinInitShutdown+0x186 (FPO: [Non-Fpo])
0019f934 0032ef64 00320000 00000000 00531ceb wininit!WinMain+0xcd3 (FPO: [Non-Fpo])
0019f9c4 77557646 7ffd7000 0019fa10 76ff0915 wininit!_initterm_e+0x1a1 (FPO: [Non-Fpo])
0019f9d0 76ff0915 7ffd7000 88d9a0a3 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
0019fa10 76ff0b71 0032f0cd 7ffd7000 00000000 ntdll!__RtlUserThreadStart+0x23 (FPO: [Non-Fpo])
0019fa28 00000000 0032f0cd 7ffd7000 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])
逆向WinInitShutdown實作,發現它會調用内部函數:PerformSystemRestore
逆向該PerformSystemRestore函數,還原得到如下示意代碼(完整代碼見附件wininit.idb):
MACRO_ERROR __stdcall PerformSystemRestore(void (__stdcall *a1)(const unsigned __int16 *))
{
Status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\WinInit", 0, 3u, &phkResult);
Status = RegQueryValueExW(phkResult, L"RestoreInProgress", 0, &Type, Data, &cbData);
if ( Type != 4 )
{
Status = ERROR_INVALID_DATA;
goto LABEL_52;
}
if ( *(_DWORD *)Data )
{
cbData = 522;
Status = RegQueryValueExW(phkResult, L"RestoreModule", 0, &Type, (LPBYTE)v11, &cbData);
if ( Status || Type != 1 || cbData < 2 || *((_WORD *)&Status + (cbData >> 1) + 1) )
{
if ( Status == ERROR_SEVERITY_SUCCESS )
Status = 13;
}
else
{
cbData = 261;
Status = RegQueryValueExA(phkResult, "RestoreFunction", 0, &Type, &v13, &cbData);
if ( Status || Type != 1 || cbData < 1 || *(&v12 + cbData) )
{
if ( Status == ERROR_SEVERITY_SUCCESS )
Status = 13;
}
else
{
v2 = LoadLibraryW(v11);
hLibModule = v2;
if ( v2 )
{
ProcAddr = GetProcAddress(v2, (LPCSTR)&v13);
if ( ProcAddr )
goto LABEL_56;
Status = GetLastError();
if ( Status == ERROR_SEVERITY_SUCCESS )
RtlAssert("ERROR_SUCCESS != dwRet", "d:\\w7rtm\\ds\\security\\umstartup\\wininit\\srestore.cxx", 0xA4u, 0);
}
}
}
}
LABEL_57:
if ( ProcAddr )
{
//調用RestoreFunction指向的函數
((void (__stdcall *)(_DWORD))ProcAddr)(a1);
}
if ( phkResult )
RegCloseKey(phkResult);
if ( hLibModule )
FreeLibrary(hLibModule);
return Status;
}
這段函數流程為:
1.檢查系統資料庫HKLM\SYSTEM\CurrentControlSet\Control\WinInit下是否有DWORD鍵RestoreInProgress;
2.如果RestoreInProgress存在,且值為1。則繼續搜尋是否存在字元串鍵RestoreModule和RestoreFunction;
3.如果RestoreModule和RestoreFunction鍵值存在,則調用LoadLibrary和GetProcAddress獲得RestoreFunction函數位址;
4.以上步驟執行正确,則執行RestoreFunction所指向的函數。
3.利用方式:
發現了PerformSystemRestore函數的執行流程,對應的利用方式可以說是手到擒來:
1.在系統資料庫HKLM\SYSTEM\CurrentControlSet\Control\WinInit下手動建立RestoreInProgress/RestoreModule/RestoreFunction三個鍵,如下:
2.将RestoreModule指向的Dll放到%SystemRoot%/System32路徑下,以下是我做的測試函數及導出函數,導出函數函數名必須與RestoreFunction的鍵值一緻。出于示範目的,往c盤根目錄下建立一檔案:
extern "C" SHUTDOWNRESTORE_API int ShutdownRestore(void);
SHUTDOWNRESTORE_API int ShutdownRestore(void)
{
FILE* flagFile;
char ctmBegin[32] = {0};
time_t tmNow = time(NULL);
tm *ptmNow = localtime(&tmNow);
strftime(ctmBegin, 32, "%Y%m%d%H%M%S", ptmNow);
flagFile = fopen("c:\\loaded.txt","a+");
fwrite(ctmBegin,32, 1, flagFile);
fclose(flagFile);
return 0;
}