簡介
我最近研究了一個問題,Winlogon中兩個線程的交互導致錯誤檢查。一個線程是初始化GDI的Winlogon線程。這個場景的有趣之處在於另一個線程是如何在這個進程中結束的。
線程在干什么?
下面是線程堆棧的用戶一半。線程試圖加載DLL
ChildEBP RetAddr Args to Child 0058eaec 773901ad 773901d9 0058eafc 00240022 ntdll!KiFastSystemCallRet 0058eb0c 775d96f3 775d1808 00000000 77e6f032 USER32!NtUserRegisterWindowMessage+0xc 0058ed24 775e4755 00000000 00000001 7c837512 comctl32!InitGlobalMetrics+0x44 0058ed3c 775e426a 00000031 0058ed68 7763490c comctl32!_ProcessAttach+0x98 0058ed48 7763490c 775d0000 00000001 00000000 comctl32!DllMain+0x21 0058ed68 7c81a352 775d0000 00000001 00000000 comctl32!_DllMainCRTStartup+0x52 0058ed88 7c833465 776348ba 775d0000 00000001 ntdll!LdrpCallInitRoutine+0x14 0058ee90 7c834311 00000000 00000000 7c8e2e58 ntdll!LdrpRunInitializeRoutines+0x367 0058f124 7c834065 00000000 00080e98 0058f3ec ntdll!LdrpLoadDll+0x3cd 0058f3a0 77e41bf3 00080e98 0058f3ec 0058f3cc ntdll!LdrLoadDll+0x198 0058f408 77e5c70b 7c8e2e58 00000000 00000000 kernel32!LoadLibraryExW+0x1b2 0058f41c 7c92a6a1 7c8e2e58 00000000 7c8e2e58 kernel32!LoadLibraryW+0x11 0058f454 7c92a65f 7c8e2e58 7c8d0000 7c9297b6 SHELL32!SHFusionLoadLibrary+0x2a 0058f460 7c9297b6 00000020 00000008 0058f6a8 SHELL32!DelayLoadCC+0x15 0058f694 7c929728 0058f6a8 0000007c 00000001 SHELL32!SHFusionInitializeIDCC+0x92 0058f8b4 7c92966f 7c8d0000 0000007c 00000001 SHELL32!SHFusionInitializeFromModuleID+0x3a 0058f8c8 7c92962c 7c8d0000 00000001 0058f8f8 SHELL32!_ProcessAttach+0x34 0058f8d8 7c92bb63 7c8d0000 00000001 00000000 SHELL32!DllMain+0x27 0058f8f8 7c81a352 7c8d0000 00000001 00000000 SHELL32!_DllMainCRTStartup+0x52 0058f918 7c833465 7c92bb1b 7c8d0000 00000001 ntdll!LdrpCallInitRoutine+0x14 0058fa20 7c834311 00000000 00000000 00000004 ntdll!LdrpRunInitializeRoutines+0x367
這個函數是加載並調用init依賴DLL的函數
0058fcb4 7c834065 00000000 00080760 0058ff7c ntdll!LdrpLoadDll+0x3cd 0058ff30 77e41bf3 00080760 0058ff7c 0058ff5c ntdll!LdrLoadDll+0x198
0058ff5c是Unicode字符串指向DLL名稱的指針
0058ff98 77e5c70b 00570254 00000000 00000000 kernel32!LoadLibraryExW+0x1b2 0058ffac 0057017e 00570254 00000000 00200008 kernel32!LoadLibraryW+0x11 WARNING: Frame IP not in any known module. Following frames may be wrong. 0058fff4 00000000 00570228 00905a4d 00000003 0x57017e
正在加載的DLL依賴於其他DLL。加載第一個DLL時,將加載並初始化這些DLL。因此,如果DLL'A'調用了DLL'B',則加載程序在加載DLL'A'時加載DLL'B'。
What is so unusual about this thread?
1: kd> !thread THREAD 86edd020 Cid 7884.7528 Teb: 7ffdc000 Win32Thread: bc1adb48 RUNNING on processor 1 Not impersonating DeviceMap e10018c0 Owning Process 87c51d88 Image: winlogon.exe Wait Start TickCount 2567944 Ticks: 0 Context Switch Count 4 LargeStack UserTime 00:00:00.015 KernelTime 00:00:00.000 Start Address 0x00570000
Start Address。這不是顯示在"!peb"輸出的任何模塊中。
這個!PEB擴展將顯示加載的模塊列表和進程的地址范圍。由於空間原因,此處未顯示。但是這個地址不在任何加載的模塊中。
我們來看看函數:
00570000 55 push ebp 00570001 8bec mov ebp,esp 00570003 83ec3c sub esp,3Ch 00570006 8365e800 and dword ptr [ebp-18h],0 0057000a 8365ec00 and dword ptr [ebp-14h],0 0057000e 8365f800 and dword ptr [ebp-8],0 00570012 8365dc00 and dword ptr [ebp-24h],0 00570016 8365f000 and dword ptr [ebp-10h],0 1: kd> u 0057001a 8365e000 and dword ptr [ebp-20h],0 0057001e 8365f400 and dword ptr [ebp-0Ch],0 00570022 6a01 push 1 00570024 8b4508 mov eax,dword ptr [ebp+8] ß 第一個參數是指向函數列表的指針. 00570027 ff5004 call dword ptr [eax+4] 0057002a 8945fc mov dword ptr [ebp-4],eax 0057002d 8b4508 mov eax,dword ptr [ebp+8] ß Function block. 00570030 ff5010 call dword ptr [eax+10h] 1: kd> u 00570033 8945e4 mov dword ptr [ebp-1Ch],eax 00570036 837de400 cmp dword ptr [ebp-1Ch],0 0057003a 0f84c0010000 je 00570200第一個參數是一個函數塊。這就是作為初始參數傳遞的。傳遞了哪些函數? 1: kd> dds 570228 l 5 00570228 77e5c6fa kernel32!LoadLibraryW 0057022c 77e6c2dc kernel32!SetErrorMode 00570230 77e70531 kernel32!GetCurrentDirectoryW 00570234 77e70d67 kernel32!SetCurrentDirectoryW 00570238 77e63ec7 kernel32!GetProcessHeap
這些函數是標准的kernel32調用。所以,問題是它為什么要這么做?
線程在干什么?
基於IP不在任何模塊中的事實,IP是頁對齊的,並且線程被傳遞函數地址作為它的初始參數,看起來這個線程被“注入”到這個進程中。
線程是怎么注射的?
不知道在另一個進程中分配了哪個進程。這個函數接受一個進程句柄作為輸入,它可以是一個不同的進程。然后代碼可以通過WriteProcessMemory移動到進程中。然后可以使用從VirtualAllocEx返回的內存地址的起始地址創建線程。
我們結束了嗎?
不——還記得地址塊嗎?這是必需的,因為加載程序沒有加載模塊。所以函數沒有被鏈接器解析。所以移動的代碼之外的函數的地址是未知的。由於在WindowsServer2003中,某些DLL的函數保持在同一地址,因此可以將它們傳遞給另一個進程。Vista和beyond無法執行此操作,因此此方法將不起作用。