DLL-инъекция
DLL-инъекция (англ. DLL injection) — техника, применяемая в программировании, которая позволяет запускать код в адресное пространство другого процесса путём принудительной загрузки им динамически подключаемой библиотеки (DLL)[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] Начиная с Windows Vista AppInit_DLLs по умолчанию отключены[10]. В Windows 7 инфраструктура AppInit_DLL поддерживает подписывание кода. Начиная с Windows 8, функциональность AppInit_DLL полностью отключается при включённой опции Secure Boot, независимо от подписи или настроек реестра[11]. - DLL, перечисленные в разделе
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs, загружаются в каждый процесс, который вызывает функции Win32 API:
CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW, CreateProcessWithTokenW и WinExec. Это рекомендованный и «законный» способ DLL-инъекции в современных версиях Windows (до Windows 10 включительно); DLL должна быть подписана действительным сертификатом.
- Использование функций манипулирования процессом, таких как
CreateRemoteThread, или техник, подобных AtomBombing[12], для внедрения DLL в уже запущенный процесс[5][6][13][14][15][16].- Открытие дескриптора к целевому процессу, что может быть реализовано как запуск процесса[17][18] или привязка к элементу, создаваемому этим процессом и известному заранее, например, окно с предсказуемым заголовком[19], или получение списка процессов[20] и поиск по имени исполняемого файла[21].
- Выделение памяти в целевом процессе[22], в которую записывается имя DLL для внедрения[13][23].
- Этот шаг можно пропустить, если имя подходящей DLL уже присутствует в целевом процессе: например, если процесс ссылается на User32.dll, GDI32.dll, Kernel32.dll или другую библиотеку с окончанием
32.dll, возможно загрузить библиотеку с именем32.dll[24].
- Этот шаг можно пропустить, если имя подходящей DLL уже присутствует в целевом процессе: например, если процесс ссылается на User32.dll, GDI32.dll, Kernel32.dll или другую библиотеку с окончанием
- Создание нового потока в целевом процессе[25], стартовая функция которого —
LoadLibrary, а аргументом является адрес только что загруженной строки[13][26].- Вместо передачи имени DLL и запуска потока в
LoadLibraryможно записать непосредственно код для исполнения и запустить поток с указанием адреса этого кода[6].
- Вместо передачи имени DLL и запуска потока в
- Операционная система вызывает инициализационную функцию внедрённой DLL[13][27].
- Без специальных мер, такой подход может быть обнаружен целевым процессом по уведомлениям
DLL_THREAD_ATTACH, отправляемым каждому загруженному модулю при старте потока[27].
- Без специальных мер, такой подход может быть обнаружен целевым процессом по уведомлениям
- Использование функций-хуков Windows, например
SetWindowsHookEx[2][5][6][28][29][30]. - Использование функций
SuspendThreadилиNtSuspendThreadдля приостановки всех потоков, и затемSetThreadContextлибоNtSetContextThreadдля изменения контекста потока таким образом, чтобы выполнить внедряемый код, который, в свою очередь, может загрузить DLL[4][31][32]. - Эксплуатация изъянов в дизайне Windows и приложения, когда функции
LoadLibraryилиLoadLibraryExвызываются без указания полного пути к DLL[33][34][35]. - Системные шимы операционной системы.
- Замена специфической для приложения DLL на вредоносную, реализующую те же экспортируемые функции, что и оригинальная DLL[36].
- Рефлективные DLL или DLL, способные загружать себя самостоятельно. Такая DLL копируется в пространство памяти целевого процесса, после чего управление передаётся её точке входа и она загружает себя из памяти[37].
Подходы в Unix-подобных системах
В Unix-подобных системах с динамическим компоновщиком ld.so (на BSD) и ld-linux.so (на Linux), произвольные библиотеки могут быть подлинкованы к новому процессу через указание пути к библиотеке в переменной окружения LD_PRELOAD, которую можно задавать глобально или для отдельного процесса[38].
Например, в Linux следующая команда запускает программу «prog» с подключением к ней общей библиотеки из файла «test.so» при старте:
LD_PRELOAD="./test.so" prog
Такую библиотеку можно скомпилировать аналогично другим shared object. С помощью GCC это достигается компиляцией исходника с опцией -fpic или -fPIC[39], и компоновкой с опцией -shared[40]. Такая библиотека будет иметь доступ к внешним символам программы, как и любая другая библиотека.
В macOS для аналогичной задачи используют команду:
DYLD_INSERT_LIBRARIES="./test.dylib" DYLD_FORCE_FLAT_NAMESPACE=1 prog
Библиотека из файла «test.dylib» будет подключена к процессу «prog» при запуске[41].
Также в Unix-подобных системах возможно использование техник на основе отладчиков[42].
Пример кода
Поскольку вызова LoadLibrary() для загрузки DLL в другой процесс не существует, нужно скопировать локально загруженную DLL в отведённую память чужого процесса. Далее приведён комментированный пример кода на C++:
#include <Windows.h>
/* ... далее весь приведённый выше C++-код, описывающий процесс копирования и загрузки DLL и создания потока в процессе ... */
Главная задача здесь — обеспечить, чтобы копируемая DLL заняла в удалённом процессе те же адреса, что и в процессе внедрения, что достигается резервированием той же области памяти. Особенность подхода: если не удаётся выделить нужную область, DLL локально выгружается, прежний диапазон адресов резервируется, и LoadLibrary() пробуют снова. Это предотвращает повторное использование старого адресного диапазона новым вызовом LoadLibrary().
Недостаток подхода — зависимые библиотеки не подгружаются, и указатели/функции DLL могут не корректно работать в чужом пространстве. Однако обычно ядро Windows поддерживает предпочтительные адреса для системных DLL, таких как kernel32.dll, что позволяет корректно использовать часть их функций. Путь к загружаемой DLL и любые дополнительные параметры передаются через память и доступны внедряемому коду.
Далее — пример исходного кода DLL-загрузчика, копируемого с помощью вышеуказанного механизма:
#include <Windows.h>
/* ... и весь приведённый выше код основной экспортируемой функции загрузчика DLL ... */
Также приведён пример DLL, которая загружается загрузчиком (печатает параметры в файл):
#include <Windows.h>
/* ... соответствующий пример на C++ ... */
Важное замечание: экспортируется только переменная данных, приемник параметров (initData), все действия по инициализации происходят в DllMain. Поток, созданный из DllMain, не будет запущен, пока все функции DLL_THREAD_ATTACH не завершат выполнение успешно — синхронизацию из DllMain с потоком выполнять нельзя.
Примечания
- ↑ 1 2 James Shewmaker. Анализ DLL-инъекции (англ.). GSM Presentation. Bluenotch (2006). Дата обращения: 31 августа 2008. Архивировано 3 декабря 2008 года.
- ↑ 1 2 Iczelion. Учебник 24: Windows Hooks (англ.). Iczelion's Win32 Assembly Homepage (август 2002). Дата обращения: 31 августа 2008. Архивировано 1 августа 2008 года.
- ↑ Rocky Pulley. Расширение диспетчера задач с помощью DLL-инъекции (англ.). CodeProject (19 мая 2005). Дата обращения: 1 сентября 2008. Архивировано 6 февраля 2009 года.
- ↑ 1 2 Nasser R. Rowhani. DLL-инъекция и перехват функций: руководство (англ.). CodeProject (23 октября 2003). Дата обращения: 31 августа 2008. Архивировано 15 апреля 2018 года.
- ↑ 1 2 3 Ivo Ivanov. Реализация перехвата API (англ.). CodeProject (2 декабря 2002). Дата обращения: 31 августа 2008. Архивировано 14 октября 2008 года.
- ↑ 1 2 3 4 Robert Kuster. Три способа внедрить код в другой процесс (англ.). CodeProject (20 августа 2003). Дата обращения: 31 августа 2008. Архивировано 20 июля 2008 года.
- ↑ Работа со значением реестра AppInit_DLLs (англ.). Microsoft Help and Support. Microsoft (21 ноября 2006). Дата обращения: 31 августа 2008.
- ↑ Raymond Chen. AppInit_DLLs стоит переименовать в Deadlock_Or_Crash_Randomly_DLLs (англ.). The Old New Thing. Microsoft (13 декабря 2007). Дата обращения: 31 августа 2008.
- ↑ dllmain.c (англ.). ReactOS. ReactOS Foundation (8 июля 2008). Дата обращения: 31 августа 2008.
- ↑ AppInit_DLLs в Windows 7 и Windows Server 2008 R2 (англ.). Дата обращения: 28 февраля 2024.
- ↑ AppInit DLLs and Secure Boot (англ.). MSDN. Дата обращения: 29 марта 2016.
- ↑ 'AtomBombing' в Microsoft Windows через внедрение кода (англ.), Dark Reading. Дата обращения: 20 апреля 2017.
- ↑ 1 2 3 4 Trent Waddington. InjectDLL (англ.). Дата обращения: 31 августа 2008. Архивировано 30 декабря 2019 года.
- ↑ DLL Injection (англ.). DreamInCode.net. MediaGroup1 (4 мая 2006). Дата обращения: 31 августа 2008. Архивировано 2 сентября 2008 года.
- ↑ Greg Jenkins. DLL Injection Framework (англ.). Ring3 Circus. WordPress (ноябрь 2007). Дата обращения: 31 августа 2008. Архивировано 28 июня 2020 года.
- ↑ Drew Benton. Более полное решение DLL-инъекции с использованием CreateRemoteThread (англ.). CodeProject (17 августа 2007). Дата обращения: 1 сентября 2008.
- ↑ CreateProcess (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ PROCESS_INFORMATION (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ GetWindowThreadProcessId Function (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ EnumProcesses (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ GetModuleBaseName (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ VirtualAllocEx (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ WriteProcessMemory (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ Обход защиты Outpost через продвинутую DLL-инъекцию с кражею handle (англ.). Matousec (1 декабря 2006). Дата обращения: 31 августа 2008. Архивировано 6 февраля 2009 года.
- ↑ CreateRemoteThread (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ LoadLibrary (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ 1 2 DllMain (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ SetWindowsHookEx Function (англ.). Platform SDK for Windows XP SP2. Microsoft. Дата обращения: 31 августа 2008.
- ↑ AppInit_DLLs Registry Value and Windows 95 (англ.). Microsoft Help and Support. Microsoft (1 марта 2005). Дата обращения: 31 августа 2008.
- ↑ DLL-инъекция через SetWindowsHookEx() (англ.). Game Reversal (3 апреля 2008). Дата обращения: 1 сентября 2008. Архивировано 4 апреля 2016 года.
- ↑ SetThreadContext DLL Injection (англ.) (16 января 2007). Дата обращения: 1 сентября 2008. Архивировано 23 августа 2011 года.
- ↑ Ben Botto. DLL Injector (англ.) (6 сентября 2008). Дата обращения: 1 сентября 2008. Архивировано 7 февраля 2009 года.
- ↑ Небезопасная загрузка библиотек может позволить удалённое выполнение кода (англ.). Microsoft (10 июня 2011). Дата обращения: 20 апреля 2016.
- ↑ Безопасная загрузка библиотек для предотвращения атак через DLL preloading (англ.). Microsoft (10 июня 2011). Дата обращения: 8 августа 2012.
- ↑ Microsoft Security Advisory: небезопасная загрузка библиотек может позволить удалённое выполнение кода (англ.). Microsoft (10 июня 2011). Дата обращения: 20 апреля 2016.
- ↑ Nicolas Falliere. Заражение проектов Step 7 через Stuxnet (англ.). Symantec (26 сентября 2010).
- ↑ Team, Microsoft Defender Security Research Обнаружение рефлективной загрузки DLL с помощью Windows Defender ATP (англ.). Microsoft Security Blog (13 ноября 2017). Дата обращения: 26 марта 2025.
- ↑ Linus Torvalds. ld.so/ld-linux.so — динамический компоновщик/загрузчик (англ.). UNIX man pages (14 марта 1998). Дата обращения: 31 августа 2008. Архивировано 6 февраля 2009 года.
- ↑ Опции генерации кода (англ.). Using the GNU Compiler Collection (GCC). Free Software Foundation. — «
-fpicГенерирует позиционно-независимый код (PIC) для использования в разделяемых библиотеках.» Дата обращения: 31 августа 2008. - ↑ Опции линковки (англ.). Using the GNU Compiler Collection (GCC). Free Software Foundation. — «
-sharedФормирует разделяемый объект для последующего связывания в исполняемый файл.» Дата обращения: 31 августа 2008. - ↑ Трюк с LD_PRELOAD (англ.). Peter Goldsborough. Дата обращения: 17 мая 2017.
- ↑ Gregory Shpitalnik. Внедрение кода в работающее Linux-приложение (англ.). Code Project (12 февраля 2009). Дата обращения: 18 ноября 2010. Архивировано 12 июня 2010 года.