
Librería header-only de C++17 para desarrollo evasivo en Windows: hashing de API en tiempo de compilación, desenganchado de DLL, borrado de breakpoints de hardware, eliminación de VEH y supresión de callbacks de notificación de DLL para evadir los hooks de EDR.
Esta es una continuación de mi proyecto original WTSRM y la charla.
Este proyecto fue reescrito para utilizar C++17 y así crear una plantilla que nos permite cambiar la funcionalidad requerida en tiempo de compilación con facilidad. Está implementado como un archivo de solo cabecera (header-only) para que puedas añadirlo fácilmente a tus propios proyectos.
Algunas de las características actualmente implementadas incluyen:
Podemos configurar este comportamiento de la siguiente manera
#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
}
Como utilizamos constexpr, no pagamos por las características que no usamos, ya que son código muerto y se optimizan fuera del programa.
Para importar DLLs, primero debemos crear el hash usando la macro DLL_HASH
DLL_HASH(DBGHELP, L"DBGHELP.DLL")
Luego debemos añadirlo a la tabla module_address_table, que actúa como nuestro IAT interno. La constante hash_ es creada por la macro DLL_HASH. Para DLLs no comunes que no están siempre cargadas, debemos especificar su nombre para poder cargarlas.
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"}
};
Para importar funciones, primero debemos crear el hash del nombre de la función y el typedef usando la macro HASH_FUNC
HASH_FUNC(NtUnmapViewOfSection, NTSTATUS, HANDLE, PVOID)
El primer parámetro es el nombre de la función, el siguiente es el tipo de retorno y luego los argumentos de la función. Luego debemos añadirla a nuestra api_address_table, que se completará con las direcciones actuales de las funciones
inline addr_struct<decltype(HASHING_ALGORITHM(TOKENIZEA(T))), void> api_address_table[] =
{
{hash_GetProcAddress, nullptr},
{hash_NtUnmapViewOfSection, nullptr},
Si no añades esta función a la tabla, el programa no fallará, pero devolverá -1 en un intento de aumentar la estabilidad.
Para usar una API, utiliza la macro API()() como se muestra a continuación
API(NtWaitForSingleObject)(NtCurrentProcess(), FALSE, &timeout);
Para añadir tu propia función de hash de API, debes usar esta plantilla para que funcione con los tipos requeridos.
template <typename T>
constexpr auto hash_string_(const T* buffer)
{
TRIVIAL_TYPE_CHECK()
}
Gracias a estas personas, sin ningún orden en particular, por sus ideas y contribuciones @peterwintrsmith @modexpblog @jonaslyk @JustasMasiulis