Русская Википедия:DLL-инъекция
DLL-инъекция (Шаблон:Lang-en) — в программировании, метод, используемый для запуска кода в адресном пространстве другого процесса, заставляя его загружать динамически подключаемую библиотеку[1]. DLL-инъекции часто используются внешними программами, чтобы повлиять на поведение другой программы так, как её авторы не задумывали и не предполагали[1][2][3]. Например, внедрённый код может перехватывать системные вызовы функций[4][5] или прочитать содержимое текстовых полей пароля, что невозможно сделать обычным способом[6]. Программа, используемая для внедрения произвольного кода в произвольные процессы, называется DLL-инжектором.
Microsoft Windows
На Microsoft Windows имеется множество способов заставить процесс загрузить код в DLL-библиотеке против воли автора приложения:
- DLL-файлы, указанные в списке системного реестра по ключу
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
, будут загружаться в каждом процессе, загружающем библиотеку User32.dll при её начальном вызове.[7][8][9] - DLL-файлы по ключу
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs
будут загружаться в каждом процессе, которые вызывают функции Windows API CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, CreateProcessWithTokenW и WinExec. Это один из законных методов DLL-инъекции на Windows 10 при условии, что DLL-файл подписан корректным сертификатом. - Функции манипуляции процессами, такие как CreateRemoteThread, либо технологии внедрения кода, например AtomBombing[10], которых возможно использовать для внедрения DLL в программу после её запуска.[5][6][11][12][13][14]
- Перехватывающие вызовы Windows, например SetWindowsHookEx.[2][5][6][15][16][17]
- Применение функций SuspendThread или NtSuspendThread, чтобы приостановить все потоки, а также использование функций SetThreadContext или NtSetContextThread, чтобы модифицировать контекст существующих потоков в приложении с целью запустить внедряемый код, который сможет загрузить DLL.[4][18][19]
- Эксплуатация ограничений Windows и приложений, вызывающих LoadLibrary или LoadLibraryEx без указания пути к загружаемой DLL.[20][21][22]
- Оперирование прослойками системного уровня.
- Подмена одного из зависимых DLL-файлов приложения на поддельный, который содержит те же экспортированные объекты, что и оригинал.[23]
Unix-подобные операционные системы
На Unix-подобных операционных системах, с помощью динамического линковщика, основанном на ld.so (на BSD) и на ld-linux.so (на Linux), можно подгружать произвольные библиотеки в новый процесс, указав путь к библиотеке с помощью переменной среды LD_PRELOAD
, которую можно как назначить глобально, так и назначить конкретному процессу индивидуально.[24]
Например, на Linux-системе, данная команда запускает процесс "prog" вместе с разделяемой библиотекой "test.so", сопоставленной в него во время запуска:
LD_PRELOAD="./test.so" prog
Такие библиотеки создаются тем же способом, что и разделяемые объекты. Библиотека имеет доступ к внешним символам, указанным в программе, как и любая другая библиотека. На macOS, данная команда запускает процесс "prog" вместе с разделяемой библиотекой "test.dylib", сопоставленной в него во время запуска:[25]
DYLD_INSERT_LIBRARIES="./test.dylib" DYLD_FORCE_FLAT_NAMESPACE=1 prog
В Unix-подобных системах также возможно использовать методы, основанные на отладчиках.[26]
Пример кода
Использование API-функции LoadLibrary
Пример функции, представленный ниже, использует метод DLL-инъекции, который эксплуатирует тот факт, что kernel32.dll сопоставлен с тем же адресом, что и почти все процессы. Поэтому, LoadLibrary (которая является функцией из kernel32.dll) также сопоставлена с тем же адресом. LoadLibrary также подходит для процедуры запуска потока, необходимой для CreateRemoteThread.
#include <windows.h>
HANDLE inject_DLL(const char* file_name, int PID)
{
HANDLE h_process, h_rThread;
char fullDLLPath[_MAX_PATH];
LPVOID DLLPath_addr, LoadLib_addr;
DWORD exit_code;
/* Извлечение handle-идентификатора целевого процесса */
h_process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
/* Получение полного пути к DLL-файлу */
GetFullPathName(file_name, _MAX_PATH, fullDLLPath, NULL);
/* Выделение памяти в целевом процессе */
DLLPath_addr = VirtualAllocEx(h_process, NULL, _MAX_PATH,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
/* Запись пути к DLL-файлу в недавно созданный блок памяти */
WriteProcessMemory(h_process, DLLPath_addr, fullDLLPath,
strlen(fullDLLPath), NULL);
/* Получение адреса LoadLibraryA (такой же для всех процессов), чтобы начать его запуск */
LoadLib_addr = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");
/* Запуск удалённого потока в LoadLibraryA, и проброс пути к DLL в качестве аргумента */
h_rThread = CreateRemoteThread(h_process, NULL, 0,
(LPTHREAD_START_ROUTINE)LoadLib_addr, DLLPath_addr, 0, NULL);
/* Ожидание его завершения */
WaitForSingleObject(h_rThread, INFINITE);
/* Получение кода завершения (то есть, значение handle, возвращённый вызовом LoadLibraryA */
GetExitCodeThread(h_rThread, &exit_code);
/* Освобождение носителя внедрённого потока. */
CloseHandle(h_rThread);
/* А также память, выделенная для пути к DLL */
VirtualFreeEx(h_process, DLLPath_addr, 0, MEM_RELEASE);
/* А также handle-иднетификатор целевого процесса */
CloseHandle(h_process);
return (HANDLE)exit_code;
}
Примечания
- ↑ 1,0 1,1 Шаблон:Cite web
- ↑ 2,0 2,1 Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ 4,0 4,1 Шаблон:Cite web
- ↑ 5,0 5,1 5,2 Шаблон:Cite web
- ↑ 6,0 6,1 6,2 Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite news
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web