DLL注入工作原理


簡介

我最近研究了一個問題,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?

如果你用!thread檢查IP的起始地址,當它調用LoadLibraryW時,IP不在任何加載模塊的范圍內。
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無法執行此操作,因此此方法將不起作用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM