
Une bibliothèque anti-débogage en espace utilisateur C/C++ furtive et entièrement basée sur des appels système pour Windows, conçue pour protéger les logiciels contre l'ingénierie inverse
antidbg est une bibliothèque anti-débogage en mode utilisateur x64 pour Windows, conçue pour protéger les logiciels contre le débogage.
Considérez cette bibliothèque comme une base pour votre protection anti-débogage, et non comme votre seule défense.
La bibliothèque est :
Le modèle de menace suppose qu'un débogueur peut intercepter un logiciel protégé par ce système de protection depuis n'importe quel niveau de privilège, les détections étant moins efficaces à mesure que le niveau de privilège augmente.
Ce logiciel est renforcé pour contourner l'interception triviale avec CPL > 0, en :
RAX en détectant et/ou en écrasant les callbacks d'instrumentation non légitimes..text surveillées en effectuant une comparaison sur disque vs en mémoire.VEH, , ) et les points d'arrêt logiciels.SEHLPTOP_LEVEL_EXCEPTION_FILTERPAGE_GUARD.TLS callbacks contre l'attachement d'un débogueur, en effectuant une inspection de l'adresse de démarrage des threads.DbgBreakPoint et DbgUiRemoteBreakin, pour faire planter le processus ou retourner.Si le seul moyen d'effectuer une vérification est d'utiliser une fonction exportée non appelable par syscall, la fonction en question est manuellement rétro-ingénierée et reconstruite pour s'exécuter dans l'espace d'adressage du module du thread de protection. Toutes les structures mémoire en mode utilisateur sont parcourues par introspection directe de la mémoire au lieu d'utiliser des API.
Les routines de protection laissent explicitement certains chemins d'exécution sans protection contre les hooks en mode utilisateur, agissant comme des pots de miel mémoire. Ils sont utilisés pour la comparaison d'état et pour confondre les attaquants.
Les routines de protection s'exécutent de manière pseudo-aléatoire. L'entropie est décidée avec un pur ASLR basé sur le matériel, le comportement de la pile et un peu de mathématiques ; sans appeler le noyau, sans utiliser d'API en mode utilisateur, ni émettre d'instructions de sortie conditionnelles ou inconditionnelles par des hyperviseurs.
Lorsqu'une violation de sécurité est détectée, le système de protection fait planter le processus courant en invoquant INT 29h avec STATUS_SXS_EARLY_DEACTIVATION, contournant tous les gestionnaires d'exceptions. Dans certains cas, il met également en file d'attente un APC vers le noyau afin de terminer le processus courant.
Se trouvent dans le point d'entrée principal (abdg.c) de cette bibliothèque, expliquées dans l'ordre.
Explications résumées ; une détection spécifique peut effectuer plus de vérifications supplémentaires/sous-vérifications que ce qui est expliqué ici.
Vous pouvez trouver plus de code source d'autres concepts de détection dans le dossier antidebug\archived.
1. Lit le champ BeingDebugged du PEB en utilisant la fonction exportée de kernel32.2. Appelle l'export IsRemoteDebuggerPresent pour voir si le processus cible est débogué depuis l'extérieur de son propre contexte.3. Exécute l'interruption logicielle INT 2D, en vérifiant si l'octet suivant cette instruction est ignoré et non routé via le gestionnaire EXCEPTION_BREAKPOINT.4. Exécute l'interruption de point d'arrêt INT 3D, puis observe si l'exception est interceptée ou transmise normalement.5. Invoque ICE/0xF1, déclenchant EXCEPTION_SINGLE_STEP et vérifiant si le débogueur considérera cette exception comme l'exception normale générée par l'exécution de l'instruction avec le bit de pas à pas activé dans les registres RFlags.6. Sonde les registres de segment de pile en définissant le flag TF et en vérifiant si le débogueur l'efface de RFLAGS, car normalement les débogueurs effacent le flag de piège après chaque événement de débogueur livré.7. Utilise un cas limite de flux d'instructions basé sur un préfixe et vérifie si 0xF3 0x64, qui se désassemble en PREFIX REP, force un saut de 0xF1.8. Vérifie si après avoir défini un flag de piège et invoqué pushfd mov dword ptr [esp], 0x100 popfd nop, le nop est atteint au lieu d'entrer dans le gestionnaire EXCEPTION_SINGLE_STEP.9. Déclenche un événement DBG_CONTROL_C et DBG_RIPEXCEPTION pour voir si l'exception est interceptée et non routée via un SEH.10. Vérifie la présence d'un handle d'objet de débogage attaché, en interrogeant ProcessDebugObjectHandle avec NtQueryInformationProcess.11. Interroge la présence d'un débogueur noyau en utilisant SystemKernelDebuggerInformation avec NtQuerySystemInformation, et en lisant directement la page mémoire KUSER_SHARED_DATA pour le champ KdDebuggerEnabled. De plus, vérifie si les ISR du timer noyau avancent de manière asynchrone.12. Lit le flag global NT pour un masque de FLG_HEAP_ENABLE_TAIL_CHECK (0x10), FLG_HEAP_ENABLE_FREE_CHECK (0x20) et FLG_HEAP_VALIDATE_PARAMETERS (0x40)13. Inspecte ProcessDebugFlags, pour déduire si le débogage est activé ou supprimé.14. Duplique les handles de processus et vérifie si un débogueur touche les handles, hérite des handles, ou les rouvre / duplique ; Puis-je créer un handle dupliqué protégé, puis le dupliquer à nouveau proprement ?15. Examine la chaîne de processus parents pour repérer les lanceurs de débogueurs ou une ascendance suspecte comme vsjitdebugger, x64dbg, ou similaire.16. Vérifie les champs de débogage du PEB sans utiliser d'exports, en lisant directement depuis la base (__readgsqword(0x60)) à l'offset *(BYTE*)((uintptr_t)peb + 2.17. Interroge ProcessDebugPort pour le processus courant.18. Vérifie la présence de points d'arrêt matériels en inspectant les registres de débogage des threads (Dr0–Dr7).19. Vérifie si la mémoire virtuelle a été touchée par des débogueurs en plaçant des pots de miel et en surveillant les changements.20. Effectue deux tests de fermeture de handle invalide avec des handles de processus et de fenêtre, observe si ERROR_INVALID_WINDOW_HANDLE et EXCEPTION_INVALID_HANDLE ne sont pas interceptés.21. Vérifie si les objets de débogage sont interceptés par le débogueur et si un dépouillement de handle se produit.22. Tente d'ouvrir un processus d'une manière qui révèle si l'accès est filtré ou redirigé par des débogueurs.23. Vérifie si un handle de mutex marqué comme HANDLE_FLAG_PROTECT_FROM_CLOSE peut être fermé directement.24. Appelle NtSystemDebugControl avec SysDbgGetTriageDump et vérifie si un débogueur noyau bloque l'appel ou usurpe l'appel mais ne touche pas notre tampon mémoire.25. Vérifie si les lectures mémoire de notre propre pile sont instrumentées ou interceptées.26. Vérifie si le processus se trouve dans un objet job non whitelisté créé par un débogueur.27. Utilise un test d'accès de style point d'arrêt mémoire, s'attendant généralement à un comportement de page-guard ou de faute si des watchpoints sont actifs.28. Déclenche un scénario de point d'arrêt d'exception de page et inspecte si la chaîne d'exceptions délivre correctement STATUS_GUARD_PAGE_VIOLATION.29. Mesure le timing d'exécution pour détecter la surcharge introduite par le pas à pas, les points d'arrêt, ou l'instrumentation binaire dynamique/recompilation JIT.30. Recherche les fenêtres de débogueur ou les artefacts d'interface utilisateur en énumérant les fenêtres/classes/titres associés aux outils de débogage.31. Vérifie si un débogueur efface les bits LBR/BTF précédemment définis dans DR7 pour effectuer son propre pas à pas, résultant en un tableau ExceptionInformation vide, ou si des adresses de branche en mode noyau sont détectées si le débogueur décide de laisser LBR activé mais intercepte quand même EXCEPTION_SINGLE_STEP invoqué par icebp.32. Parcourt le tas directement et vérifie les valeurs magiques 0xABABABAB et 0xFEEEFEEE. Effectivement identique à 12 mais en utilisant des API Heap hookables.33. Vérifie si un Copy-On-Write s'est produit dans la mémoire virtuelle en vérifiant si la page précédemment partagée a été touchée par un débogueur.34. Envoie un événement console (CTRL_C_EVENT) et vérifie si un débogueur l'intercepte et change sa livraison vers notre gestionnaire de contrôle, ou déclenche DBG_CONTROL_C.35. Vérifie si le processus est suspendu de manière externe pour des tentatives d'injection ; détecte tout appel externe à NtResumeProcess pointant vers notre processus.36. Appelle NtSetDebugFilterState avec différents niveaux de privilège SE_DEBUG_PRIVILEGE et vérifie si un débogueur noyau gère incorrectement l'accès.37. Analyse les objets de périphérique, vérifie également si un débogueur noyau intercepte la lecture de fichier.38. Met des threads en course contre à la fois un débogueur noyau et le noyau lui-même lisant la structure ContextFlags ; vérifie si DEBUG_REGISTERS est dépouillé/si Dr0 n'a pas été défini.39. Gèle certains débogueurs en créant et mappant une vue extrêmement grande d'une section virtuelle ; détecte si les appels à NtMapViewOfSection sont altérés.40. Vérifie les appels système non implémentés (courants dans les émulateurs).41. Définit quatre points d'arrêt d'exécution matériels de DR0 à DR3 sur quatre NOPs consécutifs et compte les livraisons EXCEPTION_SINGLE_STEP résultantes via un VEH.42. Teste l'implication du débogueur via les effets secondaires de OutputDebugString sur les anciens Windows XP/2000 et via les exceptions DBG_PRINTEXCEPTION_{C,WIDE_C} qu'un débogueur peut intercepter.43. Vérifie si le premier octet de notre propre image sur disque est 0xCC et exploite le comportement du handle de fichier du chargeur Windows où LoadLibrary peut laisser le fichier accessible de manière non exclusive sous un débogueur.Exemple :
#include "adbg.h"
int main() {
StartDebugProtection();
return 0;
}
Exemple :
#include "adbg.h"
int main() {
if (isProgramBeingDebugged()) {
printf("Debugger detected.\n");
}
else {
printf("No debugger was detected.\n");
}
return 0;
}
Pour compiler l'exécutable de test, activez -DBUILD_EXAMPLE=ON. Cela compile example/main.c et le lie contre antidebug sans polluer la bibliothèque principale avec un point d'entrée.
.sln généré).antidebug_runner comme projet de démarrage et cliquez sur Build (ou appuyez sur F5 pour exécuter).Depuis la racine du projet :
cmake -B build -S . -DBUILD_EXAMPLE=ON
cmake --build build --config Release
L'exécutable sera situé à :
build/Release/antidebug_runner.exebuild/antidebug_runner.exeNote sur les builds Debug : La compilation en mode Debug (
--config Debug) active les logs de diagnostic console/débogueur viacore/debug.c. Le mode Release supprime complètement la journalisation.
Par défaut, CMake produit une bibliothèque statique (antidebug.lib ou libantidebug.a). Pour compiler une bibliothèque de liens dynamiques (DLL), passez -DBUILD_SHARED_LIBS=ON.
cl.exe)En utilisant le générateur 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
En utilisant Ninja avec 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
Lancez votre shell LLVM-MinGW 64 bits (x86_64-w64-mingw32-clang dans votre PATH) et utilisez 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
Pour installer la bibliothèque compilée et les en-têtes dans un préfixe local :
cmake --install build --prefix "C:/local/antidebug"
Cela produit :
C:/local/antidebug/
├── bin/
│ └── antidebug.dll (if BUILD_SHARED_LIBS=ON)
├── lib/
│ └── antidebug.lib (or libantidebug.a)
└── include/
└── antidebug/
├── adbg.h
└── ...
Je ne suis pas responsable ni redevable de tout dommage que vous causez par une utilisation malveillante de ce projet.
La génération de la documentation BUILD a été réalisée avec l'IA, signalez tout problème.
Licence : MIT