
Скрытная библиотека антиотладки для пользовательского режима Windows на C/C++, полностью построенная на системных вызовах и предназначенная для защиты программного обеспечения от обратной разработки
antidbg — это пользовательская (user-mode) библиотека антиотладки для Windows x64, предназначенная для защиты программного обеспечения от отладки.
Рассматривайте библиотеку как основу для вашей антиотладочной защиты, а не как единственную линию обороны.
Библиотека:
Модель угроз предполагает, что отладчик может перехватить программное обеспечение, защищённое этой системой защиты, с любого уровня привилегий, при этом обнаружение становится менее эффективным с повышением уровня привилегий.
Это программное обеспечение усилено для обхода тривиального перехвата на уровне CPL > 0 путём:
RAX путём обнаружения и/или перезаписи нелегитимных callback-ов инструментирования..text путём сравнения on-disk vs in-memory.VEH, SEH, ) и программных точек останова.LPTOP_LEVEL_EXCEPTION_FILTERPAGE_GUARD.TLS callbacks от присоединения отладчика, выполняя проверку стартового адреса потока.DbgBreakPoint и DbgUiRemoteBreakin, для краха процесса или возврата.Если единственный способ выполнить проверку — использовать не вызываемую через системный вызов экспортируемую функцию, эта функция вручную реверс-инжинирится и реконструируется для выполнения в адресном пространстве модуля защитного потока. Все пользовательские структуры памяти обходятся с помощью прямой интроспекции памяти вместо использования API.
Защитные процедуры намеренно оставляют некоторые пути выполнения без защиты от пользовательских хуков, действуя как ловушки памяти. Они используются для сравнения состояний и для запутывания атакующих.
Защитные процедуры выполняются псевдослучайно. Энтропия определяется на основе чистого аппаратного ASLR, поведения стека и небольшой доли математики; без вызова ядра, использования пользовательских API или выполнения условно или безусловно завершающих инструкций гипервизорами.
При обнаружении нарушения безопасности система защиты вызывает крах текущего процесса путём вызова INT 29h с STATUS_SXS_EARLY_DEACTIVATION, обходя все обработчики исключений. В некоторых случаях она также ставит в очередь APC к ядру для завершения текущего процесса.
Находятся в главной точке входа (abdg.c) этой библиотеки, описаны по порядку.
Краткие пояснения; конкретное обнаружение может выполнять больше дополнительных/подпроверок, чем описано здесь.
Больше исходного кода других концепций обнаружения вы можете найти в папке antidebug\archived.
1. Читает поле BeingDebugged из PEB, используя экспортируемую функцию kernel32.2. Вызывает экспорт IsRemoteDebuggerPresent, чтобы проверить, отлаживается ли целевой процесс извне его собственного контекста.3. Выполняет программное прерывание INT 2D, проверяя, пропускается ли байт, следующий за такой инструкцией, и не направляется ли он через обработчик EXCEPTION_BREAKPOINT.4. Выполняет прерывание точки останова INT 3D, затем отслеживает, перехватывается ли исключение или проходит нормально.5. Вызывает ICE/0xF1, возбуждая EXCEPTION_SINGLE_STEP, и проверяет, будет ли отладчик рассматривать это исключение как нормальное исключение, генерируемое выполнением инструкции с установленным битом пошагового выполнения в регистре RFlags.6. Исследует регистры сегмента стека, устанавливая флаг TF и проверяя, сбрасывает ли отладчик его из RFLAGS, так как обычно отладчики сбрасывают флаг ловушки после доставки каждого события отладчика.7. Использует граничный случай потока инструкций на основе префикса и проверяет, заставляет ли 0xF3 0x64, который дизассемблируется как PREFIX REP, пропустить 0xF1.8. Проверяет, достигается ли после установки флага ловушки и вызова pushfd mov dword ptr [esp], 0x100 popfd nop инструкция nop вместо попадания в обработчик EXCEPTION_SINGLE_STEP.9. Возбуждает событие DBG_CONTROL_C и DBG_RIPEXCEPTION, чтобы увидеть, перехватывается ли исключение и не направляется ли оно через SEH.10. Проверяет наличие присоединённого дескриптора объекта отладки, запрашивая ProcessDebugObjectHandle с помощью NtQueryInformationProcess.11. Запрашивает наличие отладчика ядра с помощью SystemKernelDebuggerInformation через NtQuerySystemInformation и напрямую читает страницу памяти KUSER_SHARED_DATA для поля KdDebuggerEnabled. Дополнительно проверяет, тикают ли ISR таймеров ядра асинхронно.12. Читает глобальный флаг NT для маски FLG_HEAP_ENABLE_TAIL_CHECK (0x10), FLG_HEAP_ENABLE_FREE_CHECK (0x20) и FLG_HEAP_VALIDATE_PARAMETERS (0x40)13. Исследует ProcessDebugFlags, чтобы определить, включена или подавлена отладка.14. Дублирует дескрипторы процесса и проверяет, трогает ли отладчик дескрипторы, наследует ли их или повторно открывает / дублирует их; Могу ли я создать защищённый дубликат дескриптора, а затем продублировать его снова чисто?15. Изучает цепочку родительских процессов для выявления лаунчеров отладчиков или подозрительного происхождения, такого как vsjitdebugger, x64dbg или подобных.16. Проверяет поля отладочного PEB без использования экспортов, читая напрямую из базы (__readgsqword(0x60)) по смещению *(BYTE*)((uintptr_t)peb + 2.17. Запрашивает ProcessDebugPort для текущего процесса.18. Проверяет наличие аппаратных точек останова, исследуя отладочные регистры потоков (Dr0–Dr7).19. Проверяет, была ли виртуальная память затронута отладчиками, размещая ловушки и отслеживая изменения.20. Выполняет два теста закрытия недействительного дескриптора с дескрипторами процесса и окна, отслеживает, не перехватываются ли ERROR_INVALID_WINDOW_HANDLE и EXCEPTION_INVALID_HANDLE.21. Проверяет, перехватываются ли объекты отладки отладчиком и происходит ли удаление дескрипторов.22. Пытается открыть процесс способом, который выявляет, фильтруется ли или перенаправляется доступ отладчиками.23. Проверяет, можно ли напрямую закрыть дескриптор мьютекса, помеченный как HANDLE_FLAG_PROTECT_FROM_CLOSE.24. Вызывает NtSystemDebugControl с SysDbgGetTriageDump и проверяет, блокирует ли отладчик ядра вызов или подменяет вызов, но не трогает наш буфер памяти.25. Проверяет, инструментируются ли или перехватываются ли чтения памяти нашего собственного стека.26. Проверяет, находится ли процесс внутри не внесённого в белый список объекта задания, созданного отладчиком.27. Использует тест доступа в стиле точки останова по памяти, обычно ожидая поведение page-guard или сбоя, если активны точки наблюдения.28. Инициирует сценарий точки останова по исключению страницы и исследует, доставляет ли цепочка исключений корректно STATUS_GUARD_PAGE_VIOLATION.29. Измеряет время выполнения для обнаружения накладных расходов, вносимых пошаговым выполнением, точками останова или динамической бинарной инструментацией/JIT-рекомпиляцией.30. Ищет окна отладчика или артефакты UI, перечисляя окна/классы/заголовки, связанные с инструментами отладки.31. Проверяет, сбрасывает ли отладчик ранее установленные биты LBR/BTF в DR7 для выполнения собственного пошагового выполнения, что приводит к пустому массиву ExceptionInformation, или обнаруживаются ли адреса ветвлений режима ядра, если отладчик решает оставить LBR включённым, но всё же перехватывает EXCEPTION_SINGLE_STEP, вызванный icebp.32. Обходит кучу напрямую и проверяет наличие магических значений 0xABABABAB и 0xFEEEFEEE. Фактически то же, что и 12, но с использованием хукабельных Heap API.33. Проверяет, произошёл ли Copy-On-Write в виртуальной памяти, проверяя, была ли ранее разделяемая страница затронута отладчиком.34. Отправляет событие консоли (CTRL_C_EVENT) и проверяет, перехватывает ли его отладчик и меняет ли его доставку на наш обработчик управления, или возбуждает DBG_CONTROL_C.35. Проверяет, приостановлен ли процесс извне для попыток инъекции; обнаруживает любой внешний вызов NtResumeProcess, указывающий на наш процесс.36. Вызывает NtSetDebugFilterState с различными уровнями привилегий SE_DEBUG_PRIVILEGE и проверяет, неправильно ли обрабатывает доступ отладчик ядра.37. Анализирует объекты устройств, также проверяет, перехватывает ли отладчик ядра чтение файла.38. Заставляет потоки соревноваться как с отладчиком ядра, так и с самим ядром при чтении структуры ContextFlags; проверяет, удаляется ли DEBUG_REGISTERS/не был ли установлен Dr0.39. Замораживает некоторые отладчики, создавая и отображая чрезвычайно большое представление виртуальной секции; обнаруживает, подделаны ли вызовы NtMapViewOfSection.40. Проверяет наличие нереализованных системных вызовов (часто встречается в эмуляторах).41. Устанавливает четыре аппаратные точки останова на выполнение с DR0 по DR3 на четырёх последовательных NOP и подсчитывает результирующие доставки EXCEPTION_SINGLE_STEP через VEH.42. Тестирует участие отладчика через побочные эффекты OutputDebugString на устаревших Windows XP/2000 и через исключения DBG_PRINTEXCEPTION_{C,WIDE_C}, которые отладчик может перехватить.43. Проверяет, является ли первый байт нашего собственного образа на диске 0xCC, и эксплуатирует поведение файлового дескриптора загрузчика Windows, при котором LoadLibrary может оставить файл неэксклюзивно доступным под отладчиком.Пример:
#include "adbg.h"
int main() {
StartDebugProtection();
return 0;
}
Пример:
#include "adbg.h"
int main() {
if (isProgramBeingDebugged()) {
printf("Debugger detected.\n");
}
else {
printf("No debugger was detected.\n");
}
return 0;
}
Чтобы собрать исполняемый файл тестового запуска, включите -DBUILD_EXAMPLE=ON. Это скомпилирует example/main.c и слинкует его с antidebug, не загрязняя основную библиотеку точкой входа.
.sln).antidebug_runner как стартовый проект и нажмите Build (или нажмите F5 для запуска).Из корня проекта:
cmake -B build -S . -DBUILD_EXAMPLE=ON
cmake --build build --config Release
Исполняемый файл будет расположен по пути:
build/Release/antidebug_runner.exebuild/antidebug_runner.exeПримечание о Debug-сборках: Компиляция в режиме Debug (
--config Debug) включает диагностические логи консоли/отладчика черезcore/debug.c. Режим Release полностью удаляет логирование.
По умолчанию CMake создаёт статическую библиотеку (antidebug.lib или libantidebug.a). Чтобы собрать динамически подключаемую библиотеку (DLL), передайте -DBUILD_SHARED_LIBS=ON.
cl.exe)Используя генератор Visual Studio:
# Static Library (.lib)
cmake -B build -S . -A x64 -DBUILD_SHARED_LIBS=OFF
cmake --build build --config Release
# Dynamic Library (.dll + import .lib)
cmake -B build -S . -A x64 -DBUILD_SHARED_LIBS=ON
cmake --build build --config Release
Используя Ninja с clang-cl:
# Ensure clang-cl is in your PATH or run from the VS x64 Native Tools Command Prompt
cmake -B build -S . -G Ninja -DCMAKE_C_COMPILER=clang-cl -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF
cmake --build build
Запустите вашу 64-битную оболочку LLVM-MinGW (x86_64-w64-mingw32-clang в вашем PATH) и используйте Ninja:
# Static Library (.a)
cmake -B build -S . -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF
cmake --build build
# Shared Library (.dll)
cmake -B build -S . -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON
cmake --build build
Чтобы установить скомпилированную библиотеку и заголовки в локальный префикс:
cmake --install build --prefix "C:/local/antidebug"
Это создаёт:
C:/local/antidebug/
├── bin/
│ └── antidebug.dll (if BUILD_SHARED_LIBS=ON)
├── lib/
│ └── antidebug.lib (or libantidebug.a)
└── include/
└── antidebug/
├── adbg.h
└── ...
Я не несу ответственности за любой ущерб, причинённый вами в результате любого вредоносного использования этого проекта.
Документация по сборке (BUILD) была сгенерирована с помощью ИИ, сообщайте о любых проблемах.
Лицензия: MIT