
Una libreria anti-debugging userland C/C++ furtiva e completamente basata su syscall per Windows, progettata per proteggere il software dal reverse engineering
antidbg è una libreria anti-debugging user-mode x64 per Windows, progettata per proteggere il software dal debugging.
Considera la libreria come una base per la tua protezione anti-debugging, non come la tua unica difesa.
La libreria è:
Il modello di minaccia presuppone che un debugger possa intercettare il software protetto da questo sistema di protezione da qualsiasi livello di privilegio, essendo le rilevazioni meno efficaci quanto più alto è il livello di privilegio.
Questo software è rafforzato per aggirare l'intercettazione banale con CPL > 0, tramite:
RAX rilevando e/o sovrascrivendo callback di strumentazione non legittime..text monitorate effettuando un confronto on-disk vs in-memory.VEH, SEH, ) e i breakpoint software.LPTOP_LEVEL_EXCEPTION_FILTERPAGE_GUARD.TLS callbacks dall'aggancio del debugger, eseguendo l'ispezione dell'indirizzo di avvio dei thread.DbgBreakPoint e DbgUiRemoteBreakin, per mandare in crash il processo o ritornare.Se l'unico modo per eseguire un controllo è utilizzare una funzione esportata non richiamabile via syscall, la funzione in questione viene manualmente reverse-engineered e ricostruita per essere eseguita nello spazio di indirizzi del modulo del thread di protezione. Tutte le strutture di memoria user-mode vengono attraversate con introspezione diretta della memoria invece di usare API.
Le routine di protezione lasciano esplicitamente alcuni percorsi di esecuzione senza protezione contro gli hook user-mode, fungendo da honeypot di memoria. Vengono utilizzati per il confronto dello stato e per confondere gli attaccanti.
Le routine di protezione vengono eseguite in modo pseudo-casuale. L'entropia è decisa con puro ASLR basato su hardware, comportamento dello stack e un po' di matematica; senza chiamare il kernel, usando API user-mode, o emettendo istruzioni di uscita condizionali o incondizionali da parte di hypervisor.
Quando viene rilevata una violazione di sicurezza, il sistema di protezione manda in crash il processo corrente invocando INT 29h con STATUS_SXS_EARLY_DEACTIVATION, aggirando tutti gli handler delle eccezioni. In alcuni casi, accoda anche un APC al kernel per terminare il processo corrente.
Trovate nell'entrypoint principale (abdg.c) di questa libreria, spiegate in ordine.
Spiegazioni sintetizzate; una specifica rilevazione può eseguire più controlli extra/sub rispetto a quanto spiegato qui.
Puoi trovare più codice sorgente di altri concetti di rilevazione nella cartella antidebug\archived.
1. Legge il campo BeingDebugged del PEB usando la funzione esportata di kernel32.2. Chiama l'export IsRemoteDebuggerPresent per verificare se il processo target è in fase di debug dall'esterno del proprio contesto.3. Esegue l'interrupt software INT 2D, verificando se il byte successivo a tale istruzione viene saltato e non instradato attraverso l'handler EXCEPTION_BREAKPOINT.4. Esegue l'interrupt di breakpoint INT 3D, poi osserva se l'eccezione viene intercettata o passata normalmente.5. Invoca ICE/0xF1, sollevando EXCEPTION_SINGLE_STEP e verificando se il debugger considererà questa eccezione come l'eccezione normale generata dall'esecuzione dell'istruzione con il bit single step impostato nei registri RFlags6. Sonda i registri del segmento dello stack impostando il flag TF e verificando se il debugger lo cancella da RFLAGS, poiché normalmente i debugger cancellano il trap flag dopo che ogni evento del debugger è stato consegnato.7. Utilizza un caso limite del flusso di istruzioni basato su prefisso e verifica se 0xF3 0x64, che si disassembla come PREFIX REP, forza un salto di 0xF1.8. verifica se dopo aver impostato un trap flag e invocato pushfd mov dword ptr [esp], 0x100 popfd nop, viene raggiunto nop invece di entrare nell'handler EXCEPTION_SINGLE_STEP.9. Solleva un evento DBG_CONTROL_C e un DBG_RIPEXCEPTION per vedere se l'eccezione viene intercettata e non instradata attraverso un SEH.10. Verifica la presenza di un handle di debug object agganciato, interrogando ProcessDebugObjectHandle con NtQueryInformationProcess.11. Interroga la presenza di un kernel debugger usando SystemKernelDebuggerInformation con NtQuerySystemInformation, e leggendo direttamente la pagina di memoria KUSER_SHARED_DATA per il campo KdDebuggerEnabled. Inoltre, verifica se gli ISR del timer del kernel stanno scattando in modo asincrono.12. Legge il flag globale NT per una maschera di FLG_HEAP_ENABLE_TAIL_CHECK (0x10), FLG_HEAP_ENABLE_FREE_CHECK (0x20) e FLG_HEAP_VALIDATE_PARAMETERS (0x40)13. Ispeziona ProcessDebugFlags, per dedurre se il debugging è abilitato o soppresso.14. Duplica gli handle di processo e verifica se un debugger tocca gli handle, eredita gli handle, o li riapre / duplica; Posso creare un handle duplicato protetto, poi duplicarlo di nuovo in modo pulito?15. Esamina la catena dei processi padre per individuare launcher di debugger o ascendenza sospetta come vsjitdebugger, x64dbg, o simili.16. Verifica i campi di debug del PEB senza usare export, leggendo direttamente dalla base (__readgsqword(0x60)) all'offset *(BYTE*)((uintptr_t)peb + 2.17. Interroga ProcessDebugPort per il processo corrente.18. Verifica la presenza di breakpoint hardware ispezionando i registri di debug dei thread (Dr0–Dr7).19. Verifica se la memoria virtuale è stata colpita dai debugger posizionando honeypot e monitorando le modifiche.20. Esegue due test di chiusura di handle non validi con handle di processo e finestra, osserva se ERROR_INVALID_WINDOW_HANDLE e EXCEPTION_INVALID_HANDLE non vengono intercettati.21. Verifica se gli oggetti di debug vengono intercettati dal debugger e se avviene lo stripping degli handle.22. Tenta di aprire un processo in un modo che riveli se l'accesso viene filtrato o reindirizzato dai debugger.23. Verifica se un handle di mutex contrassegnato come HANDLE_FLAG_PROTECT_FROM_CLOSE può essere chiuso direttamente.24. Chiama NtSystemDebugControl con SysDbgGetTriageDump e verifica se un kernel debugger blocca la chiamata o falsifica la chiamata ma non tocca il nostro buffer di memoria.25. Verifica se le letture di memoria del nostro stesso stack vengono strumentate o intercettate.26. Verifica se il processo si trova all'interno di un job object non whitelisted creato da un debugger.27. Utilizza un test di accesso in stile memory-breakpoint, aspettandosi tipicamente un comportamento page-guard o fault se i watchpoint sono attivi.28. Attiva uno scenario di breakpoint con page-exception e ispeziona se la catena di eccezioni consegna correttamente STATUS_GUARD_PAGE_VIOLATION.29. Misura i tempi di esecuzione per rilevare l'overhead introdotto da single-stepping, breakpoint, o strumentazione binaria dinamica/ricompilazione JIT.30. Cerca finestre o artefatti UI del debugger enumerando finestre/classi/titoli associati agli strumenti di debug.31. Verifica se un debugger cancella i bit LBR/BTF precedentemente impostati in DR7 per eseguire il proprio single-stepping, risultando in un array ExceptionInformation vuoto, o se gli indirizzi di branch in kernel-mode vengono rilevati se il debugger decide di lasciare LBR abilitato ma intercetta comunque EXCEPTION_SINGLE_STEP invocato da icebp.32. Attraversa direttamente l'heap e verifica la presenza dei valori magici 0xABABABAB e 0xFEEEFEEE. Effettivamente lo stesso del 12 ma usando API Heap hookabili.33. Verifica se si è verificato un Copy-On-Write nella memoria virtuale controllando se la pagina precedentemente condivisa è stata toccata da un debugger.34. Invia un evento console (CTRL_C_EVENT) e verifica se un debugger lo intercetta e ne cambia la consegna al nostro control handler, o solleva DBG_CONTROL_C.35. Verifica se il processo viene sospeso esternamente per tentativi di injection; rileva qualsiasi chiamata esterna a NtResumeProcess puntata al nostro processo.36. Chiama NtSetDebugFilterState con diversi livelli di privilegio SE_DEBUG_PRIVILEGE e verifica se un kernel debugger gestisce l'accesso in modo errato.37. Analizza i device object, verifica anche se un kernel debugger intercetta la lettura del file.38. Mette i thread in competizione sia contro un kernel debugger che contro il kernel stesso che legge la struttura ContextFlags; verifica se DEBUG_REGISTERS viene rimosso/se Dr0 non è stato impostato.39. Congela alcuni debugger creando e mappando una vista estremamente grande di una sezione virtuale; rileva se le chiamate a NtMapViewOfSection vengono manomesse.40. Verifica la presenza di syscall non implementate (comuni negli emulatori).41. Imposta quattro breakpoint hardware di esecuzione da DR0 a DR3 su quattro NOP consecutivi e conta le consegne risultanti di EXCEPTION_SINGLE_STEP tramite un VEH.42. Testa il coinvolgimento del debugger tramite effetti collaterali di OutputDebugString su Windows XP/2000 legacy e tramite eccezioni DBG_PRINTEXCEPTION_{C,WIDE_C} che un debugger può intercettare.43. Verifica se il primo byte della nostra stessa immagine su disco è 0xCC e sfrutta il comportamento dell'handle di file del loader di Windows dove LoadLibrary può lasciare il file accessibile in modo non esclusivo sotto un debugger.Esempio:
#include "adbg.h"
int main() {
StartDebugProtection();
return 0;
}
Esempio:
#include "adbg.h"
int main() {
if (isProgramBeingDebugged()) {
printf("Debugger detected.\n");
}
else {
printf("No debugger was detected.\n");
}
return 0;
}
Per compilare l'eseguibile test runner, abilita -DBUILD_EXAMPLE=ON. Questo compila example/main.c e lo collega contro antidebug senza inquinare la libreria core con un entry point.
.sln generato).antidebug_runner come progetto di avvio e clicca Build (o premi F5 per eseguire).Dalla root del progetto:
cmake -B build -S . -DBUILD_EXAMPLE=ON
cmake --build build --config Release
L'eseguibile sarà posizionato in:
build/Release/antidebug_runner.exebuild/antidebug_runner.exeNota sulle Build di Debug: Compilare in modalità Debug (
--config Debug) abilita i log diagnostici di console/debugger tramitecore/debug.c. La modalità Release rimuove completamente il logging.
Per impostazione predefinita, CMake produce una libreria statica (antidebug.lib o libantidebug.a). Per compilare una dynamic link library (DLL), passa -DBUILD_SHARED_LIBS=ON.
cl.exe)Usando il generatore 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
Usando Ninja con 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
Avvia la tua shell LLVM-MinGW a 64-bit (x86_64-w64-mingw32-clang nel tuo PATH) e usa 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
Per installare la libreria compilata e gli header in un prefisso locale:
cmake --install build --prefix "C:/local/antidebug"
Questo produce:
C:/local/antidebug/
├── bin/
│ └── antidebug.dll (if BUILD_SHARED_LIBS=ON)
├── lib/
│ └── antidebug.lib (or libantidebug.a)
└── include/
└── antidebug/
├── adbg.h
└── ...
Non sono responsabile né perseguibile per qualsiasi danno tu possa causare attraverso qualsiasi uso malevolo di questo progetto.
La generazione della documentazione BUILD è stata fatta con l'AI, segnala qualsiasi problema.
Licenza: MIT