
Libreria header-only C++17 per lo sviluppo evasivo su Windows: hashing delle API in fase di compilazione, unhooking delle DLL, cancellazione dei breakpoint hardware, rimozione dei VEH e soppressione delle callback di notifica delle DLL per eludere gli hook degli EDR.
Questo è un seguito del mio progetto originale WTSRM e del relativo talk.
Questo progetto è stato riscritto per utilizzare C++17 al fine di creare un template che ci permetta di cambiare facilmente le funzionalità richieste in fase di compilazione! È implementato come un file header-only, così da poterlo aggiungere facilmente ai propri progetti.
Alcune delle funzionalità attualmente implementate includono:
Possiamo configurare questo comportamento come segue
#define HASHING_ALGORITHM hash_string_fnv1a // algoritmo constexpr
namespace config
{
...
//
// configura il comportamento
//
inline constexpr unsigned unhooking = system32 | write_process_memory;
inline constexpr auto work_item_load_library = true; // usa un work item per LL
//
// configura le funzionalità evasive
//
inline constexpr auto clear_and_restore_veh = true; // pulisci e ripristina i veh
inline constexpr auto unregister_dll_notifs = true; // LdrRegisterDllNotification
inline constexpr auto clear_thread_hwbps = true; // pulisci gli hwbps
}
Poiché utilizziamo constexpr, non paghiamo per le funzionalità che non usiamo, dato che sono codice morto e vengono ottimizzate fuori dal programma.
Per importare le DLL, prima dobbiamo creare l'hash usando la macro DLL_HASH
DLL_HASH(DBGHELP, L"DBGHELP.DLL")
Poi dobbiamo aggiungerla a module_address_table che funge da nostro IAT interno. La costante hash_ viene creata dalla macro DLL_HASH. Per le DLL non comuni che non sono sempre caricate, dobbiamo specificare il loro nome in modo da poterle caricare.
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"}
};
Per importare funzioni, prima dobbiamo creare l'hash del nome della funzione e il typedef usando la macro HASH_FUNC
HASH_FUNC(NtUnmapViewOfSection, NTSTATUS, HANDLE, PVOID)
Il primo parametro è il nome della funzione, il secondo è il tipo di ritorno, e poi gli argomenti della funzione. Siamo quindi tenuti ad aggiungerla alla nostra api_address_table che verrà popolata con gli indirizzi correnti delle funzioni
inline addr_struct<decltype(HASHING_ALGORITHM(TOKENIZEA(T))), void> api_address_table[] =
{
{hash_GetProcAddress, nullptr},
{hash_NtUnmapViewOfSection, nullptr},
Se non si aggiunge questa funzione alla tabella, il programma non andrà in crash, ma restituirà -1 nel tentativo di aumentare la stabilità.
Per usare un'API, si usa la macro API()() come dimostrato
API(NtWaitForSingleObject)(NtCurrentProcess(), FALSE, &timeout);
Per aggiungere la propria funzione di API hashing, bisogna usare questo template per poter lavorare con i tipi richiesti.
template <typename T>
constexpr auto hash_string_(const T* buffer)
{
TRIVIAL_TYPE_CHECK()
}
Grazie a queste persone, in nessun ordine particolare, per le loro idee e i loro contributi @peterwintrsmith @modexpblog @jonaslyk @JustasMasiulis