**הזרקת DLL** (באנגלית: **DLL injection**) היא טכניקה המשמשת להפעלת קוד בתוך מרחב הכתובות (אנ') של תהליך אחר על ידי אילוץ התהליך האחר לטעון ספריית קישור דינמי (DLL). השימוש בהזרקת DLL נעשה לעיתים קרובות במטרה להשפיע על התנהגותה של תוכנה כלשהי באופן שמחבריה לא חזו או התכוונו. לדוגמה, הקוד המוזרק יכול לבצע יירוט קריאה לשגרה או הודעה המועברת בין תוכניות (Hooking).

שיטות להזרקת DLL
----------------

### Microsoft Windows

ישנן מספר דרכים לבצע הזרקת קוד במערכת ההפעלה Microsoft Windows:

* קובצי DLL המפורטים בערך ה-Registry `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs` נטענים בכל תהליך הטוען את User32.dll.
* קובצי DLL הרשומים תחת מפתח הרישום `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs` נטענים בכל תהליך הקורא לפונקציות ה-Win32 API הבאות: CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, CreateProcessWithTokenW ו-WinExex.
* פונקציות המבצעות פעולות על תהליכים כמו CreateRemoteThread מאפשרות להזריק DLL לתוכנה בזמן ריצה. הזרקת DLL מסוג זה נעשית במספר שלבים:

1. פתיחת Handle (אנ') לתהליך מרוחק. ניתן לעשות זאת על ידי יצירת תהליך חדש או על ידי שימוש בתוצרים של תהליך קיים: למשל, חלון בעל שם ידוע שנוצר במסגרת התהליך או השגת רשימת של התהליכים הרצים בזמן מסוים ואיתור התהליך הרצוי על פי השם של קובץ ההרצה שלו.
2. כדי לשמור את המחרוזת של התהליך שמתארת את הנתיב של ה-DLL בזיכרון של התהליך המרוחק, יש להקצות זיכרון בתהליך המרוחק ולהעתיק אליו את המחרוזת עם הנתיב של ה-DLL (שיועבר לפונקציה LoadLibrary).
3. יצירת ת'רד בתהליך המרוחק. כאשר פותחים ת'רד חדש, מעבירים לו כתובת אשר ממנה יש להתחיל את הריצה. על מנת לבצע הזרקת DLL, יש להעביר את הכתובת של הפונקציה LoadLibrary, יחד עם הכתובת של הנתיב של ה-DLL המוזרק בתוך התהליך המרוחק, אשר נשמרה בזיכרון של התהליך המרוחק בשלב השני.

קוד לדוגמה
----------

### העתקת DLL שנטען ב-LoadLibrary לתהליך המרוחק

על מנת לבצע הזרקת DLL, יש לדעת מה הכתובת של הפונקציה LoadLibrary, שממנה יש להתחיל את הריצה. כמו כן, יש לשמור בזיכרון של התהליך המרוחק מחרוזת שמתארת את הנתיב של ה-DLL אותו רוצים להזריק. לכן יש לבדוק מה הכתובת LoadLibrary בתהליך ממנו מבוצעת ההזרקה (הכתובת תהיה זהה לכתובת של LoadLibrary בתהליך המרוחק). כדי לשמור את המחרוזת של התהליך שמתארת את הנתיב של ה-DLL בזיכרון של התהליך המרוחק, יש להקצות זיכרון בתהליך המרוחק ולהעתיק אליו את המחרוזת עם הנתיב של ה-DLL (שיועבר לפונקציה LoadLibrary).

```
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <memory>
#include <system\_error>
#include <charconv>
#include <vector>
#include <cassert>

#if defined(\_MSC\_VER)
 #pragma warning(disable: 6387)
#endif

using namespace std;

using XHANDLE = unique\_ptr<void, decltype([]( void \*h ) { h && h != INVALID\_HANDLE\_VALUE && CloseHandle( (HANDLE)h ); })>;
using XHMODULE = unique\_ptr<remove\_reference\_t<decltype(\*HMODULE())>, decltype([]( HMODULE hm ) { hm && FreeLibrary( hm); })>;

MODULEENTRY32W getModuleDescription( HMODULE hmModule );
size\_t maxReadableRange( void \*pRegion );
string getAbsolutePathA( char const \*fileName, char const \*err );
DWORD dumbParseDWORD( wchar\_t const \*str );
wstring getAbsolutePath( wchar\_t const \*makeAbsolute, char const \*errStr );
[[noreturn]]
void throwSysErr( char const \*str );

constexpr wchar\_t const \*LOADER\_DLL\_NAME = L"loaderDll.dll";
constexpr char const \*LOADER\_THREAD\_PROC = "loadLibraryThread";

int wmain( int argc, wchar\_t \*\*argv )
{
 try
 {
 if( argc < 3 )
 return EXIT\_FAILURE;
 wchar\_t const
 \*processId = argv[1],
 \*remoteLoadedDll = argv[2],
 \*initData = argc >= 4 ? argv[3] : L"";
 DWORD dwProcessId = dumbParseDWORD( processId );
 XHANDLE xhProcess( OpenProcess( PROCESS\_ALL\_ACCESS, FALSE, dwProcessId ) );
 if( !xhProcess.get() )
 throwSysErr( "can't open remote process with unlimited access" );
 XHMODULE xhmLocalLoader;
 MODULEENTRY32W meLocalLoader;
 for( ; ; )
 {
 xhmLocalLoader.reset( LoadLibraryW( LOADER\_DLL\_NAME ) );
 if( !xhmLocalLoader.get() )
 throwSysErr( "can't locally load loader DLL" );
 // get module starting address and size
 meLocalLoader = getModuleDescription( (HMODULE)xhmLocalLoader.get() );
 // try to allocate memory range in the foreign process with the same size the DLL in our process occupies
 if( VirtualAllocEx( xhProcess.get(), meLocalLoader.modBaseAddr, meLocalLoader.modBaseSize, MEM\_RESERVE | MEM\_COMMIT, PAGE\_EXECUTE\_READWRITE ) )
 break;
 // allocation failed, free library
 xhmLocalLoader.reset( nullptr );
 // try to reserve address range which the library occupied before to prevent
 // recycling of that address range with the next LoadLibrary() call.
 if( !VirtualAlloc( meLocalLoader.modBaseAddr, meLocalLoader.modBaseSize, MEM\_RESERVE, PAGE\_NOACCESS ) )
 throwSysErr( "can't reserve address range of previously mapped DLL" );
 }
 LPTHREAD\_START\_ROUTINE loaderThreadProc = (LPTHREAD\_START\_ROUTINE)GetProcAddress( (HMODULE)xhmLocalLoader.get(), ::LOADER\_THREAD\_PROC );
 if( !loaderThreadProc )
 throwSysErr( "can't get procedure entry point" );
 // coppy all readable DLL-contents to the destination process
 if( SIZE\_T copied; !WriteProcessMemory( xhProcess.get(), meLocalLoader.modBaseAddr, meLocalLoader.modBaseAddr, meLocalLoader.modBaseSize, &copied ) && GetLastError() != ERROR\_PARTIAL\_COPY )
 throwSysErr( "can't copy loader DLL to remote process" );
 // create two concatenated C strings that contain the DLL to load as well as the parameter
 // given to the remotely loaded DLL
 wstring data( getAbsolutePath( remoteLoadedDll, "can't get absolute path to DLL to be remotely loaded" ) );
 data += L'\0';
 data += initData;
 data += L'\0';
 size\_t dataSize = data.size() \* sizeof(wchar\_t);
 auto initStrErr = []() { throwSysErr( "failed to copy initialization data to loader DLL" ); };
 void \*remoteData;
 // remotely allocate memory large enough to hold at least our both strings
 if( !(remoteData = VirtualAllocEx( xhProcess.get(), nullptr, dataSize, MEM\_RESERVE | MEM\_COMMIT, PAGE\_READWRITE)) )
 initStrErr();
 // write our both strings to remote memory
 if( SIZE\_T copied; !WriteProcessMemory( xhProcess.get(), remoteData, data.data(), dataSize, &copied )
 || copied != dataSize )
 initStrErr();
 // create a remote DLL loader thread; the given entry point has the same address in our process as well as the remote address
 // tive this thread the address of our both remotely copied strings
 XHANDLE xhRemoteInitThread( CreateRemoteThread( xhProcess.get(), nullptr, 0, loaderThreadProc, remoteData, 0, nullptr ) );
 if( !xhRemoteInitThread.get() )
 throwSysErr( "failed to create remote initializaton thread" );
 // wait on our remote loader thread to finish
 // it should that very soon as its only task is to copy the strings for the remotely loaded DLL and load this DLL itself
 if( WaitForSingleObject( xhRemoteInitThread.get(), INFINITE ) == WAIT\_FAILED )
 throwSysErr( "can't wait for remote initialization thread" );
 DWORD dwInitThreadExitCode;
 if( !GetExitCodeThread( xhRemoteInitThread.get(), &dwInitThreadExitCode ) )
 throwSysErr( "can't get initialization thread's success code" );
 // check for remote loader's exit-code, it should be NO\_ERROR (0)
 if( dwInitThreadExitCode != NO\_ERROR )
 throw system\_error( (int)dwInitThreadExitCode, system\_category(), "LoadLibary() error in remote loader dll" );
 }
 catch( exception const &se )
 {
 cout << se.what() << endl;
 }
}

MODULEENTRY32W getModuleDescription( HMODULE hmModule )
{
 // returns the absolute path to for a given module handle
 auto getModulePath = []( HMODULE hm, char const \*err ) -> wstring
 {
 wchar\_t modulePath[MAX\_PATH];
 if( DWORD dwRet = GetModuleFileNameW( hm, modulePath, MAX\_PATH ); !dwRet || dwRet >= MAX\_PATH )
 throwSysErr( err );
 return modulePath;
 };
 // local DLL's module path
 wstring moduleAbsolute( getModulePath( hmModule, "can't get absolute path for local loader DLL" ) );
 XHANDLE xhToolHelp( CreateToolhelp32Snapshot( TH32CS\_SNAPMODULE, GetCurrentProcessId() ) );
 auto toolHelpErr = []() { throwSysErr( "can't list modules in injecting process" ); };
 if( xhToolHelp.get() == INVALID\_HANDLE\_VALUE )
 toolHelpErr();
 MODULEENTRY32W me;
 me.dwSize = sizeof me;
 if( !Module32FirstW( xhToolHelp.get(), &me ) )
 toolHelpErr();
 for( ; ; )
 {
 // has the current image in the snapshot the same path like the DLL which is given by the module handle
 // no need to compare case insensitive because we got both paths from the kernel so that they should exactly match
 if( getModulePath( me.hModule, "can't get absolute path for toolhelp-enumerated DLL name" ) == moduleAbsolute )
 return me;
 me.dwSize = sizeof me;
 if( !Module32NextW( xhToolHelp.get(), &me ) )
 toolHelpErr();
 }
}

[[noreturn]]
void throwSysErr( char const \*str )
{
 throw system\_error( (int)GetLastError(), system\_category(), str );
}

DWORD dumbParseDWORD( wchar\_t const \*str )
{
 // idiot's from\_chars because there's no from\_chars for unicode characters
 DWORD dwRet = 0;
 while( \*str )
 dwRet = dwRet \* 10 + (unsigned char)(\*str++ - L'0');
 return dwRet;
}

wstring getAbsolutePath( wchar\_t const \*makeAbsolute, char const \*errStr )
{
 // get absolute path of a given relative path
 wstring path( MAX\_PATH, L'\0' );
 DWORD dwLength;
 if( !(dwLength = GetFullPathNameW( makeAbsolute, MAX\_PATH, path.data(), nullptr )) )
 throwSysErr( errStr );
 // if deRet == MAX\_PATH we might miss a zero-termination character, treat this as an error
 else if( dwLength >= MAX\_PATH )
 throw invalid\_argument( errStr );
 path.resize( dwLength );
 return path;
}

```

לקריאה נוספת
------------

* אורי חדד, המדריך למזריק (DLL) המתחיל, **Digital Whisper** גיליון 118, עמ' 1–31
* אוראל ארד, Code Injection, **Digital Whisper** גיליון 13, עמ' 1–16

הערות שוליים
------------

1. ↑  "CreateProcess". *Platform SDK for Windows XP SP2*. Microsoft. נבדק ב-31 באוגוסט 2008. `{{cite web}}`:  (עזרה)
2. ↑  "GetWindowThreadProcessId Function". *Platform SDK for Windows XP SP2*. Microsoft. נבדק ב-31 באוגוסט 2008. `{{cite web}}`:  (עזרה)
3. ↑  "EnumProcesses". *Platform SDK for Windows XP SP2*. Microsoft. נבדק ב-31 באוגוסט 2008. `{{cite web}}`:  (עזרה)
4. ↑  "GetModuleBaseName". *Platform SDK for Windows XP SP2*. Microsoft. נבדק ב-31 באוגוסט 2008. `{{cite web}}`:  (עזרה)
5. ↑  "VirtualAllocEx". *Platform SDK for Windows XP SP2*. Microsoft. נבדק ב-31 באוגוסט 2008. `{{cite web}}`:  (עזרה)
6. ↑  "WriteProcessMemory". *Platform SDK for Windows XP SP2*. Microsoft. נבדק ב-31 באוגוסט 2008. `{{cite web}}`:  (עזרה)
7. ↑  "CreateRemoteThread". *Platform SDK for Windows XP SP2*. Microsoft. נבדק ב-31 באוגוסט 2008. `{{cite web}}`:  (עזרה)
8. ↑  "LoadLibrary". *Platform SDK for Windows XP SP2*. Microsoft. נבדק ב-31 באוגוסט 2008. `{{cite web}}`:  (עזרה)