brief introduction
I recently studied a problem that the interaction between two threads in Winlogon leads to error checking. One thread is the Winlogon thread that initializes GDI. The interesting thing about this scenario is how another thread ends in this process.
What are threads doing?
Here is the user half of the thread stack. The thread attempted to load a 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
This function loads and calls the init dependent DLL
0058fcb4 7c834065 00000000 00080760 0058ff7c ntdll!LdrpLoadDll+0x3cd 0058ff30 77e41bf3 00080760 0058ff7c 0058ff5c ntdll!LdrLoadDll+0x198
0058ff5c is a Unicode string pointer to the DLL name
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
The DLL being loaded depends on other DLLs. When the first DLL is loaded, these DLLs are loaded and initialized. Therefore, if DLL'A' calls DLL'B', the loader loads DLL'B' when DLL'A' is loaded.
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. This is not displayed in any module of the "! peb" output.
This! The PEB extension will display the list of loaded modules and the address range of the process. Not shown here for space reasons. But this address is not in any loaded module.
Let's look at the function:
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] ß The first argument is a pointer to the list of functions. 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 00570200The first argument is a function block. This is what is passed as an initial parameter. What functions are passed? 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
These functions are standard kernel 32 calls. So the question is, why does it do this?
What are threads doing?
Based on the fact that IP is not in any module, IP is page aligned, and the thread is passed the function address as its initial parameter. It seems that this thread is "injected" into the process.
How is the thread injected?
I don't know which process is assigned in another process. This function takes a process handle as input, which can be a different process. The code can then be moved into the process through WriteProcessMemory. The thread can then be created using the starting address of the memory address returned from virtuallallocex.
Are we over?
No - remember the address block? This is required because the loader did not load the module. So the function was not parsed by the linker. So the address of the function outside the moving code is unknown. Because in Windows Server 2003, the functions of some DLL s remain at the same address, they can be passed to another process. Vista and beyond cannot perform this operation, so this method will not work.