
Header-only-Bibliothek für C++17 zur Entwicklung von Evasion-Techniken unter Windows: Kompilierzeit-API-Hashing, DLL-Unhooking, Löschen von Hardware-Breakpoints, VEH-Entfernung und Unterdrückung von DLL-Benachrichtigungs-Callbacks zur Umgehung von EDR-Hooks.
Dies ist eine Fortsetzung meines ursprünglichen Projekts WTSRM und des zugehörigen Vortrags.
Dieses Projekt wurde neu geschrieben, um C++17 zu nutzen und eine Vorlage zu erstellen, die es uns ermöglicht, die gewünschte Funktionalität zur Compilezeit problemlos zu ändern! Es ist als reine Header-Datei implementiert, sodass du es problemlos zu deinen eigenen Projekten hinzufügen kannst.
Einige der derzeit implementierten Funktionen umfassen:
Wir können dieses Verhalten wie folgt konfigurieren
#define HASHING_ALGORITHM hash_string_fnv1a // constexpr algorithm
namespace config
{
...
//
// configure behaviour
//
inline constexpr unsigned unhooking = system32 | write_process_memory;
inline constexpr auto work_item_load_library = true; // use a work item for LL
//
// configure evasive features
//
inline constexpr auto clear_and_restore_veh = true; // clear and restore veh
inline constexpr auto unregister_dll_notifs = true; // LdrRegisterDllNotification
inline constexpr auto clear_thread_hwbps = true; // clear the hwbps
}
Da wir constexpr verwenden, zahlen wir nicht für die Funktionen, die wir nicht nutzen, da sie toter Code sind und aus dem Programm wegoptimiert werden.
Um DLLs zu importieren, müssen wir zuerst den Hash mithilfe des Makros DLL_HASH erstellen
DLL_HASH(DBGHELP, L"DBGHELP.DLL")
Dann müssen wir ihn zur module_address_table hinzufügen, die als unsere interne IAT fungiert. Die Konstante hash_ wird vom DLL_HASH-Makro erstellt. Für nicht gängige DLLs, die nicht immer geladen sind, müssen wir ihren Namen angeben, damit wir sie laden können.
inline addr_struct<decltype(HASHING_ALGORITHM(TOKENIZEA(T))), wchar_t> module_address_table[] =
{
{hash_NTDLL, nullptr,},
{hash_KERNEL32, nullptr,},
{hash_DBGHELP, nullptr, L"DBGHELP.DLL"}
};
Um Funktionen zu importieren, müssen wir zuerst den Funktionsnamen-Hash und den Typedef mithilfe des Makros HASH_FUNC erstellen
HASH_FUNC(NtUnmapViewOfSection, NTSTATUS, HANDLE, PVOID)
Der erste Parameter ist der Name der Funktion, danach der Rückgabetyp und dann die Argumente der Funktion. Anschließend müssen wir sie zu unserer api_address_table hinzufügen, die mit den aktuellen Funktionsadressen befüllt wird.
inline addr_struct<decltype(HASHING_ALGORITHM(TOKENIZEA(T))), void> api_address_table[] =
{
{hash_GetProcAddress, nullptr},
{hash_NtUnmapViewOfSection, nullptr},
Wenn du diese Funktion nicht zu dieser Tabelle hinzufügst, stürzt das Programm nicht ab, sondern gibt -1 zurück, um die Stabilität zu erhöhen.
Um eine API zu verwenden, nutze das Makro API()() wie gezeigt:
API(NtWaitForSingleObject)(NtCurrentProcess(), FALSE, &timeout);
Um eine eigene API-Hashing-Funktion hinzuzufügen, musst du diese Vorlage verwenden, damit sie mit den erforderlichen Typen funktioniert.
template <typename T>
constexpr auto hash_string_(const T* buffer)
{
TRIVIAL_TYPE_CHECK()
}
Dank an diese Personen in keiner bestimmten Reihenfolge für ihre Ideen und Beiträge @peterwintrsmith @modexpblog @jonaslyk @JustasMasiulis