Marylin
19.04.2026, 12:52
В мире низкоуровневого программирования существует категория задач, где требуется выйти за пределы собственного процесса и взаимодействовать с чужим адресным пространством. В этой статье мы разберём, зачем это может понадобиться, какие методы инъекции существуют, и как реализовать базовую DLL для внедрения.
1. Введение
2. Привилегия Debug в токенах пользователей
3. Варианты инъекций DLL
4. Заключение
1. Введение
DLL инъекция - это вполне легальный метод внедрения своего кода в сторонние приложения. Она широко используется в разработке инструментов безопасности, а её тайный замысел зависит исключительно от совести разработчика. Вот возможные сценарии применения этой техники:
1. Мод для расширения функционала ПО. Горячие "Hot-Patch" фиксы позволяют исправлять ошибки в приложениях. Или-же кастомизация интерфейса, как это делает например программа "MacType". Она внедряет свою DLL в процессы Win, чтобы улучшить стандартный механизм сглаживания шрифтов. Аналогичным образом поступает и "PuntoSwitcher", для автоматической смены раскладки клавиатуры Ru/Eng.
2. Антивирусы EDR/XDR - вот кто легальные пользователи этой технологии. В офисах аверов кипит работа по внедрению своих либ, чтобы проверять подозрительные файлы в песочнице "Sandbox" и отслеживать их поведение. Это позволяет шпионить за выделением памяти, вызовами API, и другими действиями малвари в реальном времени. Продукты класса EDR (CrowdStrike, Kaspersky)используют API-Hook внутри системных процессов, для блокировки атак на-лету.
3. В игровой индустрии фишка применяется для внедрения оверлеев в код, чтобы рисовать поверх интерфейса FPS-счётчики и отправлять юзеру всякие уведомления, без модификации файлов самой игры. Это касается и загрузки всевозможных модов, для изменения логики игры.
Да, на лицо вмешательство в работу других приложений, но майки официально предоставляют API
SetWindowsHookEx()
и
CreateRemoteThread()
для этих целей, признавая легитимность данной технологии. Однако если код использует инжект не будучи драйвером безопасности или антивирусом, это вызывает у нас подозрения. Поэтому в корпоративной среде техники подобного рода применяются с осторожностью, чтобы не нарушить системную политику безопасности.
2. ПривилегииDebug и Backupв токенах пользователей
Понятно, что подсистема безопасности Win не позволит кому-попало резвиться в системе на своё усмотрение - для этого как минимум потребуются правами админа, а в большинстве случаях и привилегия отладки SeDebugPrivilege. Но проблема в том, что функция
AdjustTokenPrivileges()
может только вкл/откл привилегии, которые имеются у текущего юзера в токене. Если привилегия изначально не была назначена подсистемой безопасности lsass.exe при входе пользователя в сессию, мы не сможем её включить!
Ознакомиться со списком своих привилегий можно командой
whoami /priv
. Здесь видно, что у смертного юзера нет SeDebug, а потому
AdjustTokenPrivileges()
будет возвращать ошибку ERROR_NOT_ALL_ASSIGNED=0x514. То-есть мы можем включать только те, которые имеются в этом списке:
https://forum.antichat.xyz/attachments/4951622/img_964e9e4b2a.png
А вот лист дефолтных прав админа, и мы можем активировать любую из них, в том числе дарующие нам полную свободу Debug и Backup:
https://forum.antichat.xyz/attachments/4951622/img_987c4f7ab1.png
При таких раскладах, если обычному юзеру потребуется привилегия SeDubug (а для Dll-Inject она необходима), он может через
DuplicateTokenEx()
скоммуниздеть токен админа, после чего включать уже в нём нужные привилегии. Другой вариант - это явное разрешение отладки в системной политике безопасности, правда манипуляции с этими флагами доступны только самому админу, так что круг замыкается. Для этого жмём Win+R и вводим
secpol.msc
, после чего идём по сл.пути:
Локальные политики --> Назначения прав пользователям --> Отладка программ --> Добавить пользователя..
Обычно там только админу прописан мандат, но если админ (за шоколадку) добавит и "Пользователя", то в списке юзера выше под номером(7) появится и SeDebugPrivilege:
https://forum.antichat.xyz/attachments/4951622/img_1fcd3b57f6.png
3. Варианты реализацииDLL-Inject
Классический метод инъекций заключается в цепочке вызовов:
OpenProcess() --> VirtualAllocEx() --> WriteProcessMemory() --> CreateRemoteThread()
Как одноразовый шприц такой приём может и подойдёт, но на долгосрочную перспективу его уже не натянешь. Это наследие прошлого, и начиная с Win7 если и работает, то с большим ограничением. После того как племя служб и сервисов перекочевало в закрытую сессию(0), перечисленная выше триада уже не может до них дотянуться, и нужно искать альтернативные подходы.
Мир открывает столько возможностей, что зацикливаться на одних концепциях просто глупо. Идеи витают в воздухе, пока кто-нибудь не ухватит их, после чего все остальные дружной гурьбой копи-пастят. Но это как грабить уже ограбленный банк, который оцепила кругом полиция. Забавно, что каждое такое ограбление ослабляет охрану остальных объектов, а потому нужно найти наименее охраняемый периметр. Поскольку к каждому сарайчику часового не приставишь, значит простор для творческих манёвров у нас всё-таки есть.
Во-первых, можно поместить свой код в асинхронную очередь потока APC (Asynchronous Procedure Call) - этим занимается функция
QueueUserAPC()
из Kernel32. Например, когда при вызове
CreateFile()
мы указываем FILE_FLAG_OVERLAPTED, возвращаемый дескриптор будет поддерживать асинхронные операции R/W с файлом, т.е. последующие
Read/WriteFile()
будут возвращать управление сразу, не дожидаясь фактического окончания чтения/записи в файл или устройство. При этом система пропишет запрос именно в асинхронную очередь потока, вызвав под катом:
C-подобный:
QueueUserAPC
(
[
in
]
PAPCFUNC pfnAPC
,
;
// Указатель на функцию для исполнения
[
in
]
HANDLE hThread
,
;
// Дескриптор целевого потока
[
in
]
ULONG_PTR dwData
;
// Можно передать параметр в функцию выше
)
;
Она просто помещает запрос в АРС-очередь целевого треда, но не прерывает его принудительно. Для выполнения, в целевом потоке должен быть установлен флаг Аlertable. Это реализуется такими функами как
SleepEx()
и
WaitForSingleObjectEx()
с параметром
bAlertable =1
. В дефолте всегда имеется хоть один поток в процессе, который имеет данный флаг. Обычно перед инжектом ищат подходящих пациентов (разрядность, и флаг Alertable у потока), для чего можно заюзать отладчик WinDbg:
Код:
0: kd> !process 0 3 akelpad.exe
PROCESS fffffa8010c78320
SessionId: 1 Cid: 0a4c Peb: 7efdf000 ParentCid: 0534
Image : AkelPad.exe
THREAD fffffa8010f1bb50 Cid 0a4c.0a50 Teb: 7efdb000 Non-Alertable
fffffa800ffd88c0 SynchronizationEvent
THREAD fffffa8010812b50 Cid 0a4c.0a58 Teb: 7efd8000 Alertable
,
\
,
0
@done
:
mov eax
,
TRUE
ret
endp
;
//--------
section
'.idata'
import data readable writeable
library user32
,
'user32.dll'
include
'api\user32.inc'
;
//--------
section
'.reloc'
fixups data readable discardable
if
$
=
$$
dd
0
,
8
;
// Фиктивная секция релоков для корректной работы
end
if
Таким образом, никто из неё не будет вызывать функции (здесь даже экспорта нет), а пайлоад сработает сам на автомате.
3.1. Обобщение методов DLL-инъекций
CreateRemoteThread() - классический метод.
Поддерживается всеми версиями Win, но легко обнаруживается античитами из-за характерного паттерна: новый поток создаётся в процессе, который его не создавал. Плюсы: простота реализации + высокая надёжность. Минусы: легко детектится, оставляя явные следы.
QueueUserAPC() - асинхронный вызов.
Метод использует существующие потоки целевого процесса. Он добавляет вызов в очередь APC потока, который выполняется, когда поток перейдёт в "Аlertable" состояние. Плюсы: не создаёт новых потоков (сложнее обнаружить). Минусы: требует, чтобы в процессе был поток в alertable состоянии (не все процессы подходят).
NtCreateThreadEx() - метод Native API.
Поскольку многие системы мониторинга проверяют только Win32-API, этот метод может остаться незамеченным. Плюсы: меньше отслеживается античитами, минусы: зависимость от недокументированных структур, а потому может сломаться с обновлениями Win.
SetWindowsHookEx() - перехват сообщений.
Данная техника использует легитимный механизм Win-хуков. При установке, ОС сама загружает DLL с обработчиком в процессы. Плюсы: использует легитимный механизм (не создаёт подозрительных потоков). Минусы: ограничен процессами только с графическим интерфейсом GUI.
Thread Hijacking - перехват потока.
Более сложный метод. Инжектор приостанавливает существующий поток в целевом процессе, сохраняет его контекст (регистры, стек), подменяет
RIP/EIP
на адрес шелла, дожидается выполнения, а затем восстанавливает исходное состояние потока. Плюсы: скрытный метод (не создаёт новых объектов ядра). Минусы: сложность реализации, и риск угробить процесс.
Reflective DLL Injection - ручной маппинг
Этот метод не вызывает
LoadLibrary()
вообще. Вместо этого инжектор загружает DLL в память стороннего процесса полностью вручную: т.е. сам обрабатывает секции, выполняет релоки, резолвит импорты, вызывает точку-входа
DllMain()
, и т.д. Плюсы: не оставляет следов в списке загруженных модулей процесса. Минусы: крайне сложная реализация, т.к. требует глубокого понимания внутреннего формата PE-файла.
4. Выводы
Инжект - мощный инструмент в арсенале разработчика. На fasm эта техника реализуется достаточно прямолинейно, сохраняя всю гибкость и контроль, которые даёт ассемблер. Понимание механизмов инъекции критически важно и для защитников: антивирусы и системы обнаружения вторжений активно мониторят характерные паттерны этих методов. Изучая инъекцию мы не только расширяем свои возможности как разработчика, но и глубже понимаем принципы безопасности Win в целом. Важно помнить, что это палка с двумя концами - при работе всегда нужно соблюдать законодательство и лицензионные соглашения.
Разрешено: Отладка собственных программ, разработка модов к играм с разрешения разработчика, создание инструментов мониторинга для корпоративного использования.
Запрещено: Взлом чужого ПО, создание читов для онлайн-игр (нарушает пользовательское соглашение), разработка вредоносного ПО.
По понятным причинам код готового инжектора не выкладываю, тем более что информации и так достаточно получилось. В сл.части рассмотрим, как можно найти и удалить с любых процессов внедрённые в них описанными выше способами сторонние либы - там и будет полный код анти-инжекта. Всем удачи, пока!
1. Введение
2. Привилегия Debug в токенах пользователей
3. Варианты инъекций DLL
4. Заключение
1. Введение
DLL инъекция - это вполне легальный метод внедрения своего кода в сторонние приложения. Она широко используется в разработке инструментов безопасности, а её тайный замысел зависит исключительно от совести разработчика. Вот возможные сценарии применения этой техники:
1. Мод для расширения функционала ПО. Горячие "Hot-Patch" фиксы позволяют исправлять ошибки в приложениях. Или-же кастомизация интерфейса, как это делает например программа "MacType". Она внедряет свою DLL в процессы Win, чтобы улучшить стандартный механизм сглаживания шрифтов. Аналогичным образом поступает и "PuntoSwitcher", для автоматической смены раскладки клавиатуры Ru/Eng.
2. Антивирусы EDR/XDR - вот кто легальные пользователи этой технологии. В офисах аверов кипит работа по внедрению своих либ, чтобы проверять подозрительные файлы в песочнице "Sandbox" и отслеживать их поведение. Это позволяет шпионить за выделением памяти, вызовами API, и другими действиями малвари в реальном времени. Продукты класса EDR (CrowdStrike, Kaspersky)используют API-Hook внутри системных процессов, для блокировки атак на-лету.
3. В игровой индустрии фишка применяется для внедрения оверлеев в код, чтобы рисовать поверх интерфейса FPS-счётчики и отправлять юзеру всякие уведомления, без модификации файлов самой игры. Это касается и загрузки всевозможных модов, для изменения логики игры.
Да, на лицо вмешательство в работу других приложений, но майки официально предоставляют API
SetWindowsHookEx()
и
CreateRemoteThread()
для этих целей, признавая легитимность данной технологии. Однако если код использует инжект не будучи драйвером безопасности или антивирусом, это вызывает у нас подозрения. Поэтому в корпоративной среде техники подобного рода применяются с осторожностью, чтобы не нарушить системную политику безопасности.
2. ПривилегииDebug и Backupв токенах пользователей
Понятно, что подсистема безопасности Win не позволит кому-попало резвиться в системе на своё усмотрение - для этого как минимум потребуются правами админа, а в большинстве случаях и привилегия отладки SeDebugPrivilege. Но проблема в том, что функция
AdjustTokenPrivileges()
может только вкл/откл привилегии, которые имеются у текущего юзера в токене. Если привилегия изначально не была назначена подсистемой безопасности lsass.exe при входе пользователя в сессию, мы не сможем её включить!
Ознакомиться со списком своих привилегий можно командой
whoami /priv
. Здесь видно, что у смертного юзера нет SeDebug, а потому
AdjustTokenPrivileges()
будет возвращать ошибку ERROR_NOT_ALL_ASSIGNED=0x514. То-есть мы можем включать только те, которые имеются в этом списке:
https://forum.antichat.xyz/attachments/4951622/img_964e9e4b2a.png
А вот лист дефолтных прав админа, и мы можем активировать любую из них, в том числе дарующие нам полную свободу Debug и Backup:
https://forum.antichat.xyz/attachments/4951622/img_987c4f7ab1.png
При таких раскладах, если обычному юзеру потребуется привилегия SeDubug (а для Dll-Inject она необходима), он может через
DuplicateTokenEx()
скоммуниздеть токен админа, после чего включать уже в нём нужные привилегии. Другой вариант - это явное разрешение отладки в системной политике безопасности, правда манипуляции с этими флагами доступны только самому админу, так что круг замыкается. Для этого жмём Win+R и вводим
secpol.msc
, после чего идём по сл.пути:
Локальные политики --> Назначения прав пользователям --> Отладка программ --> Добавить пользователя..
Обычно там только админу прописан мандат, но если админ (за шоколадку) добавит и "Пользователя", то в списке юзера выше под номером(7) появится и SeDebugPrivilege:
https://forum.antichat.xyz/attachments/4951622/img_1fcd3b57f6.png
3. Варианты реализацииDLL-Inject
Классический метод инъекций заключается в цепочке вызовов:
OpenProcess() --> VirtualAllocEx() --> WriteProcessMemory() --> CreateRemoteThread()
Как одноразовый шприц такой приём может и подойдёт, но на долгосрочную перспективу его уже не натянешь. Это наследие прошлого, и начиная с Win7 если и работает, то с большим ограничением. После того как племя служб и сервисов перекочевало в закрытую сессию(0), перечисленная выше триада уже не может до них дотянуться, и нужно искать альтернативные подходы.
Мир открывает столько возможностей, что зацикливаться на одних концепциях просто глупо. Идеи витают в воздухе, пока кто-нибудь не ухватит их, после чего все остальные дружной гурьбой копи-пастят. Но это как грабить уже ограбленный банк, который оцепила кругом полиция. Забавно, что каждое такое ограбление ослабляет охрану остальных объектов, а потому нужно найти наименее охраняемый периметр. Поскольку к каждому сарайчику часового не приставишь, значит простор для творческих манёвров у нас всё-таки есть.
Во-первых, можно поместить свой код в асинхронную очередь потока APC (Asynchronous Procedure Call) - этим занимается функция
QueueUserAPC()
из Kernel32. Например, когда при вызове
CreateFile()
мы указываем FILE_FLAG_OVERLAPTED, возвращаемый дескриптор будет поддерживать асинхронные операции R/W с файлом, т.е. последующие
Read/WriteFile()
будут возвращать управление сразу, не дожидаясь фактического окончания чтения/записи в файл или устройство. При этом система пропишет запрос именно в асинхронную очередь потока, вызвав под катом:
C-подобный:
QueueUserAPC
(
[
in
]
PAPCFUNC pfnAPC
,
;
// Указатель на функцию для исполнения
[
in
]
HANDLE hThread
,
;
// Дескриптор целевого потока
[
in
]
ULONG_PTR dwData
;
// Можно передать параметр в функцию выше
)
;
Она просто помещает запрос в АРС-очередь целевого треда, но не прерывает его принудительно. Для выполнения, в целевом потоке должен быть установлен флаг Аlertable. Это реализуется такими функами как
SleepEx()
и
WaitForSingleObjectEx()
с параметром
bAlertable =1
. В дефолте всегда имеется хоть один поток в процессе, который имеет данный флаг. Обычно перед инжектом ищат подходящих пациентов (разрядность, и флаг Alertable у потока), для чего можно заюзать отладчик WinDbg:
Код:
0: kd> !process 0 3 akelpad.exe
PROCESS fffffa8010c78320
SessionId: 1 Cid: 0a4c Peb: 7efdf000 ParentCid: 0534
Image : AkelPad.exe
THREAD fffffa8010f1bb50 Cid 0a4c.0a50 Teb: 7efdb000 Non-Alertable
fffffa800ffd88c0 SynchronizationEvent
THREAD fffffa8010812b50 Cid 0a4c.0a58 Teb: 7efd8000 Alertable
,
\
,
0
@done
:
mov eax
,
TRUE
ret
endp
;
//--------
section
'.idata'
import data readable writeable
library user32
,
'user32.dll'
include
'api\user32.inc'
;
//--------
section
'.reloc'
fixups data readable discardable
if
$
=
$$
dd
0
,
8
;
// Фиктивная секция релоков для корректной работы
end
if
Таким образом, никто из неё не будет вызывать функции (здесь даже экспорта нет), а пайлоад сработает сам на автомате.
3.1. Обобщение методов DLL-инъекций
CreateRemoteThread() - классический метод.
Поддерживается всеми версиями Win, но легко обнаруживается античитами из-за характерного паттерна: новый поток создаётся в процессе, который его не создавал. Плюсы: простота реализации + высокая надёжность. Минусы: легко детектится, оставляя явные следы.
QueueUserAPC() - асинхронный вызов.
Метод использует существующие потоки целевого процесса. Он добавляет вызов в очередь APC потока, который выполняется, когда поток перейдёт в "Аlertable" состояние. Плюсы: не создаёт новых потоков (сложнее обнаружить). Минусы: требует, чтобы в процессе был поток в alertable состоянии (не все процессы подходят).
NtCreateThreadEx() - метод Native API.
Поскольку многие системы мониторинга проверяют только Win32-API, этот метод может остаться незамеченным. Плюсы: меньше отслеживается античитами, минусы: зависимость от недокументированных структур, а потому может сломаться с обновлениями Win.
SetWindowsHookEx() - перехват сообщений.
Данная техника использует легитимный механизм Win-хуков. При установке, ОС сама загружает DLL с обработчиком в процессы. Плюсы: использует легитимный механизм (не создаёт подозрительных потоков). Минусы: ограничен процессами только с графическим интерфейсом GUI.
Thread Hijacking - перехват потока.
Более сложный метод. Инжектор приостанавливает существующий поток в целевом процессе, сохраняет его контекст (регистры, стек), подменяет
RIP/EIP
на адрес шелла, дожидается выполнения, а затем восстанавливает исходное состояние потока. Плюсы: скрытный метод (не создаёт новых объектов ядра). Минусы: сложность реализации, и риск угробить процесс.
Reflective DLL Injection - ручной маппинг
Этот метод не вызывает
LoadLibrary()
вообще. Вместо этого инжектор загружает DLL в память стороннего процесса полностью вручную: т.е. сам обрабатывает секции, выполняет релоки, резолвит импорты, вызывает точку-входа
DllMain()
, и т.д. Плюсы: не оставляет следов в списке загруженных модулей процесса. Минусы: крайне сложная реализация, т.к. требует глубокого понимания внутреннего формата PE-файла.
4. Выводы
Инжект - мощный инструмент в арсенале разработчика. На fasm эта техника реализуется достаточно прямолинейно, сохраняя всю гибкость и контроль, которые даёт ассемблер. Понимание механизмов инъекции критически важно и для защитников: антивирусы и системы обнаружения вторжений активно мониторят характерные паттерны этих методов. Изучая инъекцию мы не только расширяем свои возможности как разработчика, но и глубже понимаем принципы безопасности Win в целом. Важно помнить, что это палка с двумя концами - при работе всегда нужно соблюдать законодательство и лицензионные соглашения.
Разрешено: Отладка собственных программ, разработка модов к играм с разрешения разработчика, создание инструментов мониторинга для корпоративного использования.
Запрещено: Взлом чужого ПО, создание читов для онлайн-игр (нарушает пользовательское соглашение), разработка вредоносного ПО.
По понятным причинам код готового инжектора не выкладываю, тем более что информации и так достаточно получилось. В сл.части рассмотрим, как можно найти и удалить с любых процессов внедрённые в них описанными выше способами сторонние либы - там и будет полный код анти-инжекта. Всем удачи, пока!