How it Works: DLL Injection




Hi everyone, this is Bob again.  I recently worked on an issue where the interaction of two threads in Winlogon led to a bugcheck.  One thread was a Winlogon thread initializing GDI.  The interesting thing about this scenario is how the other thread ended up in this process.




What was the thread doing?


Below 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 is

      loading and calling init

      functions of dependent DLL’s

0058fcb4 7c834065 00000000 00080760 0058ff7c ntdll!LdrpLoadDll+0x3cd

0058ff30 77e41bf3 00080760 0058ff7c 0058ff5c ntdll!LdrLoadDll+0x198        ß 0058ff5c is the Unicode string

   pointer to 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.  These DLLs are loaded and initialized when the first DLL is loaded.  So if DLL ‘A’ has a call to DLL ‘B’, then DLL ‘B’ is loaded by the loader when DLL ‘A’ is loaded.



What is so unusual about this thread?

If you examine the IP for that start address taken from !thread and where it is calling LoadLibraryW, the IP is not in a range of any loaded module.


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   ß Starting address.  This is not in any module displayed by “!peb”


The !PEB extension will display the loaded module list and address range for the process.  It is not shown here because of space.  However 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 1st argument is a pointer to a 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      00570200


The first argument is a block of functions.  That is what was passed as the initial parameter.  What functions were 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 kernel32 calls.  So, the question is why is it doing that?



What is this thread doing?

Based on the fact that the IP is not in any module, the IP is page aligned, and the thread was passed function addresses as it initial parameter, it looks like this thread was “injected” into this process. 



How was this thread injected?

Another process, (I do not know which one) allocated a block of memory in the Winlogon process via VirtualAllocEx.  This function takes a process handle as input and it can be a different process.  Then code can be moved into the process via WriteProcessMemory.  Then a thread can be created using the starting address of the memory address returned from VirtualAllocEx.



Are we done?

Nope – remember the block of addresses?  This is needed because the module was not loaded by the loader.  So the functions did not get resolved by the linker.  So the addresses of functions outside of the code moved are unknown.  Since in Windows Server 2003 the functions of certain DLLs stay at the same address, they can be passed to another process.  Vista and beyond does not do this, so this method will not work.




Hope you found this interesting.  More on DLL injection can be found here: and here:


AppInit_DLLs is another method of getting a DLL to load (in all process that rely on user32.dll), documented in KB 197571.  This can lead to problems though, as Raymond Chen discussed here.


Comments (7)

  1. molotov says:

    Good write-up, Bob – Thanks!

    Do you suspect the injection was related to malware?

    >>  I recently worked on an issue where the interaction of two threads in Winlogon led to a bugcheck.  One thread was a Winlogon thread initializing GDI.  The interesting thing about this scenario is how the other thread ended up in this process.  <<

    Wonder if you might care to indicate what the interaction was that led to the bugcheck?

  2. Hello,

    I suppose things get wrong because of "Address Space Randomization" feature introduced in Vista to prevent successful execution of "shell code" in case of stack/heap corruptions (addresses of API functions usually are static in shell code).

  3. "Wonder if you might care to indicate what the interaction was that led to the bugcheck?"

    To "molotov": calling function on improper address usually results in access violations or simular errors. This leads to termination of process in which the dll was loaded. Since the process is winlogon.exe, the system goes into reboot, because it is critical process.

  4. Ask the Directory Services Team : Custom Certificate Request in Windows Vista Microsoft Security Development

  5. molotov says:

    @Volodymyr – Yes, thanks – I am aware of that.  I was curious for more details as to the specific interaction between the threads that caused the problem (one thread causing an access violation etc. would be enough to bring down winlogon and the system).  The first thread is mentioned in the sentence "One thread was a Winlogon thread initializing GDI.", and then is not mentioned again.  The rest of the analysis revolves around the injected thread.

    Maybe I’m missing something…

  6. calin_iaru says:


    Starting address.  This is not in any module displayed by “!peb”

    should be


Skip to main content