
Eine stealthy, vollständig syscall-basierte C/C++-Userland-Anti-Debugging-Bibliothek für Windows, die Software vor Reverse Engineering schützen soll
antidbg ist eine x64-Benutzermodus-Anti-Debugging-Bibliothek für Windows, die dazu entwickelt wurde, Software vor Debugging zu schützen.
Betrachten Sie die Bibliothek als Grundlage für Ihren Anti-Debugging-Schutz, nicht als Ihre einzige Verteidigung.
Die Bibliothek ist:
Das Bedrohungsmodell geht davon aus, dass ein Debugger Software, die durch dieses Schutzsystem abgesichert ist, von jeder Art von Privilegienstufe aus abfangen kann, wobei die Erkennungen umso weniger wirksam sind, je höher die Privilegienstufe ist.
Diese Software ist gehärtet, um triviale CPL > 0-Abfangen zu umgehen, indem sie:
RAX verhindert, indem nicht-legitime Instrumentierungs-Callbacks erkannt und/oder überschrieben werden..text-Abschnitten erkennt, indem ein On-Disk-vs-In-Memory-Vergleich durchgeführt wird.VEH, , ) und Software-Breakpoints analysiert.SEHLPTOP_LEVEL_EXCEPTION_FILTERPAGE_GUARD-Umleitungsverhalten testet.TLS callbacks vor Debugger-Anhängen schützt und eine Inspektion der Thread-Startadresse durchführt.DbgBreakPoint und DbgUiRemoteBreakin erstellt, um den Prozess zum Absturz zu bringen oder zurückzukehren.Wenn die einzige Möglichkeit, eine Prüfung durchzuführen, darin besteht, eine nicht syscall-fähige exportierte Funktion zu verwenden, wird die betreffende Funktion manuell reverse-engineered und rekonstruiert, um im Modul-Adressraum des Schutz-Threads zu laufen. Alle Benutzermodus-Speicherstrukturen werden mit direkter Speicherintrospektion statt mit APIs durchlaufen.
Die Schutzroutinen lassen explizit einige Ausführungspfade ohne Schutz gegen Benutzermodus-Hooks, die als Speicher-Honeypots fungieren. Sie werden für Zustandsvergleiche und zur Verwirrung von Angreifern verwendet.
Schutzroutinen laufen pseudo-zufällig. Die Entropie wird durch reines hardwarebasiertes ASLR, Stack-Verhalten und ein wenig Mathematik bestimmt; ohne den Kernel aufzurufen, Benutzermodus-APIs zu verwenden oder bedingt oder unbedingt beendende Anweisungen durch Hypervisoren auszugeben.
Wenn eine Sicherheitsverletzung erkannt wird, bringt das Schutzsystem den aktuellen Prozess zum Absturz, indem es INT 29h mit STATUS_SXS_EARLY_DEACTIVATION aufruft und dabei alle Ausnahmehandler umgeht. In einigen Fällen stellt es auch einen APC an den Kernel in die Warteschlange, um den aktuellen Prozess zu beenden.
Zu finden im Haupteinstiegspunkt (abdg.c) dieser Bibliothek, in der Reihenfolge erklärt.
Zusammengefasste Erklärungen; eine bestimmte Erkennung kann mehr zusätzliche/Unterprüfungen durchführen als hier erklärt.
Weiteren Quellcode anderer Erkennungskonzepte finden Sie im Ordner antidebug\archived.
1. Liest das BeingDebugged-Feld der PEB unter Verwendung der Exportfunktion von kernel32.2. Ruft den Export IsRemoteDebuggerPresent auf, um zu sehen, ob der Zielprozess von außerhalb seines eigenen Kontexts debuggt wird.3. Führt den Software-Interrupt INT 2D aus und prüft, ob das auf diese Anweisung folgende Byte übersprungen und nicht über den EXCEPTION_BREAKPOINT-Handler geleitet wird.4. Führt den Breakpoint-Interrupt INT 3D aus und beobachtet dann, ob die Ausnahme abgefangen oder normal durchgelassen wird.5. Ruft ICE/0xF1 auf, löst EXCEPTION_SINGLE_STEP aus und prüft, ob der Debugger diese Ausnahme als die normale Ausnahme betrachtet, die durch Ausführen der Anweisung mit gesetztem Single-Step-Bit in den RFlags-Registern erzeugt wird.6. Testet Stack-Segment-Register, indem das TF-Flag gesetzt und geprüft wird, ob der Debugger es aus RFLAGS löscht, da Debugger normalerweise das Trap-Flag nach jedem ausgelieferten Debugger-Ereignis löschen.7. Verwendet einen präfixbasierten Instruktionsfluss-Sonderfall und prüft, ob 0xF3 0x64, das als PREFIX REP disassembliert wird, einen 0xF1-Sprung erzwingt.8. Prüft, ob nach dem Setzen eines Trap-Flags und dem Aufruf von pushfd mov dword ptr [esp], 0x100 popfd nop das nop erreicht wird, anstatt in den EXCEPTION_SINGLE_STEP-Handler zu gelangen.9. Löst ein DBG_CONTROL_C- und ein DBG_RIPEXCEPTION-Ereignis aus, um zu sehen, ob die Ausnahme abgefangen und nicht über einen SEH geleitet wird.10. Prüft auf ein angehängtes Debug-Objekt-Handle, indem ProcessDebugObjectHandle mit NtQueryInformationProcess abgefragt wird.11. Fragt das Vorhandensein eines Kernel-Debuggers mit SystemKernelDebuggerInformation über NtQuerySystemInformation ab und liest direkt die Speicherseite KUSER_SHARED_DATA für das Feld KdDebuggerEnabled. Zusätzlich wird geprüft, ob Kernel-Timer-ISRs asynchron ticken.12. Liest das NT-Global-Flag für eine Maske aus FLG_HEAP_ENABLE_TAIL_CHECK (0x10), FLG_HEAP_ENABLE_FREE_CHECK (0x20) und FLG_HEAP_VALIDATE_PARAMETERS (0x40)13. Untersucht ProcessDebugFlags, um abzuleiten, ob Debugging aktiviert oder unterdrückt ist.14. Dupliziert Prozess-Handles und prüft, ob ein Debugger Handles anfasst, Handles erbt oder sie neu öffnet/dupliziert; Kann ich ein geschütztes Duplikat-Handle erstellen und es dann erneut sauber duplizieren?15. Untersucht die Elternprozesskette, um Debugger-Starter oder verdächtige Abstammung wie vsjitdebugger, x64dbg oder ähnliches zu erkennen.16. Prüft die Debug-PEB-Felder ohne Verwendung von Exporten, indem direkt von der Basis (__readgsqword(0x60)) bei Offset *(BYTE*)((uintptr_t)peb + 2 gelesen wird.17. Fragt den ProcessDebugPort für den aktuellen Prozess ab.18. Prüft auf Hardware-Breakpoints durch Untersuchen der Thread-Debug-Register (Dr0–Dr7).19. Prüft, ob virtueller Speicher von Debuggern getroffen wurde, indem Honeypots platziert und auf Änderungen überwacht werden.20. Führt zwei Ungültig-Handle-Schließen-Tests mit Prozess- und Fenster-Handles durch und beobachtet, ob ERROR_INVALID_WINDOW_HANDLE und EXCEPTION_INVALID_HANDLE nicht abgefangen werden.21. Prüft, ob Debug-Objekte vom Debugger abgefangen werden und ob Handle-Stripping auftritt.22. Versucht, einen Prozess auf eine Weise zu öffnen, die offenlegt, ob der Zugriff von Debuggern gefiltert oder umgeleitet wird.23. Prüft, ob ein Mutex-Handle, das als HANDLE_FLAG_PROTECT_FROM_CLOSE markiert ist, direkt geschlossen werden kann.24. Ruft NtSystemDebugControl mit SysDbgGetTriageDump auf und prüft, ob ein Kernel-Debugger den Aufruf blockiert oder den Aufruf spoofed, aber unseren Speicherpuffer nicht anfasst.25. Prüft, ob Speicherlesevorgänge unseres eigenen Stacks instrumentiert oder abgefangen werden.26. Prüft, ob sich der Prozess in einem nicht-whitelisted Job-Objekt befindet, das von einem Debugger erstellt wurde.27. Verwendet einen Speicher-Breakpoint-artigen Zugriffstest, der normalerweise Page-Guard- oder Fault-Verhalten erwartet, wenn Watchpoints aktiv sind.28. Löst ein Page-Exception-Breakpoint-Szenario aus und untersucht, ob die Ausnahmekette korrekt STATUS_GUARD_PAGE_VIOLATION liefert.29. Misst die Ausführungszeit, um den durch Single-Stepping, Breakpoints oder dynamische Binärinstrumentierung/JIT-Rekompilierung eingeführten Overhead zu erkennen.30. Sucht nach Debugger-Fenstern oder UI-Artefakten durch Aufzählen von Fenstern/Klassen/Titeln, die mit Debugger-Tools verbunden sind.31. Prüft, ob ein Debugger zuvor gesetzte LBR/BTF-Bits in DR7 löscht, um sein eigenes Single-Stepping durchzuführen, was zu einem leeren ExceptionInformation-Array führt, oder ob Kernel-Modus-Branch-Adressen erkannt werden, wenn der Debugger beschließt, LBR aktiviert zu lassen, aber dennoch EXCEPTION_SINGLE_STEP abzufangen, das von icebp aufgerufen wird.32. Durchläuft den Heap direkt und prüft auf die Magic-Werte 0xABABABAB und 0xFEEEFEEE. Effektiv dasselbe wie 12, aber unter Verwendung von hookbaren Heap-APIs.33. Prüft, ob ein Copy-On-Write im virtuellen Speicher aufgetreten ist, indem geprüft wird, ob die zuvor gemeinsam genutzte Seite von einem Debugger angefasst wurde.34. Sendet ein Konsolenereignis (CTRL_C_EVENT) und prüft, ob ein Debugger es abfängt und seine Auslieferung an unseren Control-Handler ändert oder DBG_CONTROL_C auslöst.35. Prüft, ob der Prozess extern für Injektionsversuche suspendiert wird; erkennt jeden externen Aufruf von NtResumeProcess, der auf unseren Prozess zeigt.36. Ruft NtSetDebugFilterState mit verschiedenen SE_DEBUG_PRIVILEGE-Privilegienstufen auf und prüft, ob ein Kernel-Debugger den Zugriff falsch handhabt.37. Analysiert Geräteobjekte und prüft auch, ob ein Kernel-Debugger das Lesen der Datei abfängt.38. Lässt Threads gegen sowohl einen Kernel-Debugger als auch den Kernel selbst um das Lesen der ContextFlags-Struktur wetteifern; prüft, ob DEBUG_REGISTERS entfernt wird/ob Dr0 nicht gesetzt wurde.39. Friert einige Debugger ein, indem eine extrem große Ansicht eines virtuellen Abschnitts erstellt und gemappt wird; erkennt, ob Aufrufe von NtMapViewOfSection manipuliert werden.40. Prüft auf nicht implementierte Syscalls (häufig in Emulatoren).41. Setzt vier Hardware-Ausführungs-Breakpoints von DR0 bis DR3 auf vier aufeinanderfolgende NOPs und zählt die resultierenden EXCEPTION_SINGLE_STEP-Auslieferungen über einen VEH.42. Testet Debugger-Beteiligung über OutputDebugString-Nebeneffekte auf älteren Windows XP/2000 und über DBG_PRINTEXCEPTION_{C,WIDE_C}-Ausnahmen, die ein Debugger abfangen kann.43. Prüft, ob das erste Byte unseres eigenen On-Disk-Images 0xCC ist, und nutzt das Windows-Loader-Datei-Handle-Verhalten aus, bei dem LoadLibrary die Datei unter einem Debugger nicht-exklusiv zugänglich lassen kann.Beispiel:
#include "adbg.h"
int main() {
StartDebugProtection();
return 0;
}
Beispiel:
#include "adbg.h"
int main() {
if (isProgramBeingDebugged()) {
printf("Debugger detected.\n");
}
else {
printf("No debugger was detected.\n");
}
return 0;
}
Um die Test-Runner-Executable zu bauen, aktivieren Sie -DBUILD_EXAMPLE=ON. Dies kompiliert example/main.c und linkt es gegen antidebug, ohne die Kernbibliothek mit einem Einstiegspunkt zu verunreinigen.
.sln-Datei).antidebug_runner als Startprojekt fest und klicken Sie auf Build (oder drücken Sie F5 zum Ausführen).Vom Projektstammverzeichnis aus:
cmake -B build -S . -DBUILD_EXAMPLE=ON
cmake --build build --config Release
Die Executable befindet sich unter:
build/Release/antidebug_runner.exebuild/antidebug_runner.exeHinweis zu Debug-Builds: Das Kompilieren im Debug-Modus (
--config Debug) aktiviert Konsolen-/Debugger-Diagnoseprotokolle übercore/debug.c. Der Release-Modus entfernt die Protokollierung vollständig.
Standardmäßig erzeugt CMake eine statische Bibliothek (antidebug.lib oder libantidebug.a). Um eine Dynamic Link Library (DLL) zu bauen, übergeben Sie -DBUILD_SHARED_LIBS=ON.
cl.exe)Verwendung des Visual Studio-Generators:
# 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
Verwendung von Ninja mit 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
Starten Sie Ihre 64-Bit-LLVM-MinGW-Shell (x86_64-w64-mingw32-clang in Ihrem PATH) und verwenden Sie 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
Um die kompilierte Bibliothek und Header in ein lokales Präfix zu installieren:
cmake --install build --prefix "C:/local/antidebug"
Dies erzeugt:
C:/local/antidebug/
├── bin/
│ └── antidebug.dll (if BUILD_SHARED_LIBS=ON)
├── lib/
│ └── antidebug.lib (or libantidebug.a)
└── include/
└── antidebug/
├── adbg.h
└── ...
Ich bin nicht verantwortlich oder haftbar für Schäden, die Sie durch jegliche bösartige Nutzung dieses Projekts verursachen.
Die Generierung der BUILD-Dokumentation wurde mit KI erstellt; melden Sie etwaige Probleme.
Lizenz: MIT