From 368499959519b750f12c754756509ccad260f06a Mon Sep 17 00:00:00 2001 From: Victorien Blanchard Date: Thu, 21 Nov 2024 11:19:57 +0100 Subject: [PATCH] Modernize usermode hooking using libusermode Applies the recommandation of @psrok1 at https://github.com/tklengyel/drakvuf/issues/1613#issuecomment-2248031590 of implementing usermode hooking for socketmon plugin using libusermode. --- src/plugins/socketmon/socketmon.cpp | 168 +++++++++++----------------- src/plugins/socketmon/socketmon.h | 2 + 2 files changed, 69 insertions(+), 101 deletions(-) diff --git a/src/plugins/socketmon/socketmon.cpp b/src/plugins/socketmon/socketmon.cpp index 51edc7022..0bc1fb88b 100644 --- a/src/plugins/socketmon/socketmon.cpp +++ b/src/plugins/socketmon/socketmon.cpp @@ -666,7 +666,8 @@ static void print_dns_info(drakvuf_t drakvuf, drakvuf_trap_info_t* info, socketm static event_response_t trap_DnsQuery_A_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { - socketmon* sm = (socketmon*)info->trap->data; + hook_target_entry_t* target = (hook_target_entry_t*)info->trap->data; + socketmon* sm = (socketmon*)target->plugin; addr_t domain_name_addr = drakvuf_get_function_argument(drakvuf, info, 1); @@ -678,10 +679,10 @@ static event_response_t trap_DnsQuery_A_cb(drakvuf_t drakvuf, drakvuf_trap_info_ return 0; } - static event_response_t trap_DnsQuery_W_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { - socketmon* sm = (socketmon*)info->trap->data; + hook_target_entry_t* target = (hook_target_entry_t*)info->trap->data; + socketmon* sm = (socketmon*)target->plugin; addr_t domain_name_addr = drakvuf_get_function_argument(drakvuf, info, 1); unicode_string_t* domain_name_us = nullptr; @@ -714,7 +715,8 @@ template static event_response_t trap_DnsQueryExW_impl(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { unicode_string_t* domain_name_us = nullptr; - socketmon* sm = (socketmon*)info->trap->data; + hook_target_entry_t* target = (hook_target_entry_t*)info->trap->data; + socketmon* sm = (socketmon*)target->plugin; addr_t domain_name_addr = drakvuf_get_function_argument(drakvuf, info, 1); @@ -767,7 +769,8 @@ static event_response_t trap_DnsQueryExW_x86_cb(drakvuf_t drakvuf, drakvuf_trap_ // Works on Windows 7 static event_response_t trap_DnsQueryExA_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { - socketmon* sm = (socketmon*)info->trap->data; + hook_target_entry_t* target = (hook_target_entry_t*)info->trap->data; + socketmon* sm = (socketmon*)target->plugin; addr_t domain_name_addr = drakvuf_get_function_argument(drakvuf, info, 1); @@ -782,7 +785,8 @@ static event_response_t trap_DnsQueryExA_cb(drakvuf_t drakvuf, drakvuf_trap_info // Works on Windows 8+ static event_response_t trap_DnsQueryEx_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) { - socketmon* sm = (socketmon*)info->trap->data; + hook_target_entry_t* target = (hook_target_entry_t*)info->trap->data; + socketmon* sm = (socketmon*)target->plugin; unicode_string_t* domain_name_us = nullptr; addr_t query_request_addr = drakvuf_get_function_argument(drakvuf, info, 1); @@ -833,106 +837,74 @@ static void register_tcpip_trap( drakvuf_t drakvuf, json_object* tcpip_profile_j if ( ! drakvuf_add_trap( drakvuf, trap ) ) throw -1; } -namespace -{ - -struct module_trap_context_t +static event_response_t usermode_hook_cb(drakvuf_t drakvuf, drakvuf_trap_info* info) { - bool is_wow; - const char* module_name; - const char* function_name; - drakvuf_trap_t* trap; - event_response_t(*hook_cb)(drakvuf_t drakvuf, drakvuf_trap_info_t* info); -}; - -} - -static bool module_trap_visitor(drakvuf_t drakvuf, const module_info_t* module_info, void* visitor_ctx ) -{ - module_trap_context_t const* data = reinterpret_cast(visitor_ctx); - status_t ret ; - vmi_instance_t vmi; - addr_t exec_func ; - ACCESS_CONTEXT(ctx, - .translate_mechanism = VMI_TM_PROCESS_DTB, - .dtb = module_info->dtb, - .addr = module_info->base_addr - ); - - PRINT_DEBUG("[SOCKETMON] trap_visitor: CR3[0x%lX] pid[0x%X %d] is_wow_process[%d] is_wow_module[%d] base_name[%s] load_address[0x%lX] full_name[%s]\n", - module_info->dtb, module_info->pid, module_info->pid, module_info->is_wow_process, module_info->is_wow, module_info->base_name->contents, module_info->base_addr, module_info->full_name->contents ); + hook_target_entry_t* target = (hook_target_entry_t*)info->trap->data; - if (data->is_wow && !module_info->is_wow) - return false; + if (target->pid != info->attached_proc_data.pid) + return VMI_EVENT_RESPONSE_NONE; - vmi = drakvuf_lock_and_get_vmi( drakvuf ); + socketmon* plugin = (socketmon*) target->plugin; - ret = vmi_translate_sym2v( vmi, &ctx, data->function_name, &exec_func ); + if (target->target_name == "DnsQuery_W") + { + trap_DnsQuery_W_cb(drakvuf, info); + } - drakvuf_release_vmi( drakvuf ); + else if (target->target_name == "DnsQuery_A" || target->target_name == "DnsQuery_UTF8") + { + trap_DnsQuery_A_cb(drakvuf, info); + } - if ( ret == VMI_FAILURE ) + else if (target->target_name == "DnsQueryExA") { - PRINT_DEBUG("[SOCKETMON] Failed to get address of %s!%s\n", data->module_name, data->function_name); - return false; + trap_DnsQueryExA_cb(drakvuf, info); } - PRINT_DEBUG("[SOCKETMON] Address of %s!%s is 0x%lx\n", data->module_name, data->function_name, exec_func); + else if (target->target_name == "DnsQueryEx") + { + trap_DnsQueryEx_cb(drakvuf, info); + } - data->trap->breakpoint.module = data->module_name; - data->trap->breakpoint.pid = module_info->pid; - data->trap->breakpoint.addr = exec_func; + else if (target->target_name == "DnsQueryExW") + { + if (plugin->pm == VMI_PM_IA32E) + trap_DnsQueryExW_x64_cb(drakvuf, info); - data->trap->name = data->function_name; - data->trap->cb = data->hook_cb; + trap_DnsQueryExW_x86_cb(drakvuf, info); + } - return drakvuf_add_trap(drakvuf, data->trap); + return VMI_EVENT_RESPONSE_NONE; } -static void register_module_trap( drakvuf_t drakvuf, drakvuf_trap_t* trap, - const char* module_name, const char* function_name, - event_response_t(*hook_cb)( drakvuf_t drakvuf, drakvuf_trap_info_t* info ), - event_response_t(*wow_hook_cb)( drakvuf_t drakvuf, drakvuf_trap_info_t* info ) = nullptr ) +static void on_dll_discovered(drakvuf_t drakvuf, const std::string& dll_name, const dll_view_t* dll, void* extra) { - struct module_trap_context_t visitor_ctx = {}; - visitor_ctx.is_wow = false; - visitor_ctx.module_name = module_name; - visitor_ctx.function_name = function_name; - visitor_ctx.trap = trap; - visitor_ctx.hook_cb = hook_cb; - - if (!drakvuf_enumerate_processes_with_module(drakvuf, module_name, module_trap_visitor, &visitor_ctx)) - { - PRINT_DEBUG("[SOCKETMON] Failed to trap function %s!%s\n", module_name, function_name); - throw -1; - } + socketmon* plugin = (socketmon*)extra; - if (drakvuf_get_page_mode(drakvuf) == VMI_PM_IA32E) + plugin->wanted_hooks.visit_hooks_for(dll_name, [&](const auto& e) { - visitor_ctx.is_wow = true; - if (wow_hook_cb) - visitor_ctx.hook_cb = wow_hook_cb; - if (!drakvuf_enumerate_processes_with_module(drakvuf, module_name, module_trap_visitor, &visitor_ctx)) + if (!drakvuf_request_usermode_hook(drakvuf, dll, &e, usermode_hook_cb, plugin)) { - PRINT_DEBUG("[SOCKETMON] Failed to trap function SysWOW64 %s!%s\n", module_name, function_name); - throw -1; + PRINT_DEBUG("Could not set hook on DNS userland function from dnsapi.dll\n"); } - } + }); } -static void register_dnsapi_trap( drakvuf_t drakvuf, drakvuf_trap_t* trap, - const char* function_name, - event_response_t(*hook_cb)( drakvuf_t drakvuf, drakvuf_trap_info_t* info ), - event_response_t(*wow_hook_cb)( drakvuf_t drakvuf, drakvuf_trap_info_t* info ) = nullptr ) +static void on_dll_hooked(drakvuf_t drakvuf, const dll_view_t* dll, const std::vector& targets, void* extra) { - trap->ttl = drakvuf_get_limited_traps_ttl(drakvuf); - register_module_trap(drakvuf, trap, "dnsapi.dll", function_name, hook_cb, wow_hook_cb); + PRINT_DEBUG("[SOCKETMON] DLL hooked - done\n"); } socketmon::socketmon(drakvuf_t drakvuf_, const socketmon_config* c, output_format_t output) : format{output} , drakvuf{drakvuf_} { + if (!drakvuf_are_userhooks_supported(drakvuf)) + { + PRINT_DEBUG("[SOCKETMON] Usermode hooking not supported.\n"); + return; + } + this->pm = drakvuf_get_page_mode(drakvuf); { vmi_lock_guard vmi(drakvuf); @@ -952,25 +924,31 @@ socketmon::socketmon(drakvuf_t drakvuf_, const socketmon_config* c, output_forma throw -1; } - assert(sizeof(dnsapi_traps) / sizeof(dnsapi_traps[0]) > 5); - register_dnsapi_trap(drakvuf, &this->dnsapi_traps[0], "DnsQuery_W", trap_DnsQuery_W_cb); - register_dnsapi_trap(drakvuf, &this->dnsapi_traps[1], "DnsQuery_A", trap_DnsQuery_A_cb); - register_dnsapi_trap(drakvuf, &this->dnsapi_traps[2], "DnsQuery_UTF8", trap_DnsQuery_A_cb); // intentionally trap_DnsQuery_A_cb + const auto log = HookActions::empty(); + + wanted_hooks.add_hook(plugin_target_config_entry_t ("dnsapi.dll", "DnsQuery_W", log, std::vector>())); + wanted_hooks.add_hook(plugin_target_config_entry_t ("dnsapi.dll", "DnsQuery_A", log, std::vector>())); + wanted_hooks.add_hook(plugin_target_config_entry_t ("dnsapi.dll", "DnsQuery_UTF8", log, std::vector>())); if (this->build.version == VMI_OS_WINDOWS_7) { - if (pm == VMI_PM_IA32E) - register_dnsapi_trap(drakvuf, &this->dnsapi_traps[3], "DnsQueryExW", trap_DnsQueryExW_x64_cb, trap_DnsQueryExW_x86_cb); - else - register_dnsapi_trap(drakvuf, &this->dnsapi_traps[3], "DnsQueryExW", trap_DnsQueryExW_x86_cb); - register_dnsapi_trap(drakvuf, &this->dnsapi_traps[4], "DnsQueryExA", trap_DnsQueryExA_cb); + wanted_hooks.add_hook(plugin_target_config_entry_t("dnsapi.dll", "DnsQueryExW", log, std::vector>())); + wanted_hooks.add_hook(plugin_target_config_entry_t("dnsapi.dll", "DnsQueryA", log, std::vector>())); } if (this->build.version >= VMI_OS_WINDOWS_8) { - register_dnsapi_trap(drakvuf, &this->dnsapi_traps[5], "DnsQueryEx", trap_DnsQueryEx_cb); + wanted_hooks.add_hook(plugin_target_config_entry_t("dnsapi.dll", "DnsQueryEx", log, std::vector>())); } + usermode_cb_registration reg = + { + .pre_cb = on_dll_discovered, + .post_cb = on_dll_hooked, + .extra = (void*)this + }; + drakvuf_register_usermode_callback(drakvuf, ®); + json_object* tcpip_profile_json = json_object_from_file(c->tcpip_profile); if (!tcpip_profile_json) { @@ -1024,17 +1002,5 @@ bool socketmon::stop_impl() { drakvuf_remove_trap(drakvuf, &this->tcpip_trap[0], nullptr); drakvuf_remove_trap(drakvuf, &this->tcpip_trap[1], nullptr); - - drakvuf_remove_trap(drakvuf, &this->dnsapi_traps[0], nullptr); - drakvuf_remove_trap(drakvuf, &this->dnsapi_traps[1], nullptr); - drakvuf_remove_trap(drakvuf, &this->dnsapi_traps[2], nullptr); - if (this->build.version == VMI_OS_WINDOWS_7) - { - drakvuf_remove_trap(drakvuf, &this->dnsapi_traps[3], nullptr); - drakvuf_remove_trap(drakvuf, &this->dnsapi_traps[4], nullptr); - } - if (this->build.version >= VMI_OS_WINDOWS_8) - drakvuf_remove_trap(drakvuf, &this->dnsapi_traps[5], nullptr); - - return true; + return drakvuf_stop_userhooks(drakvuf); } diff --git a/src/plugins/socketmon/socketmon.h b/src/plugins/socketmon/socketmon.h index a15f6faa3..edfcb1ccc 100644 --- a/src/plugins/socketmon/socketmon.h +++ b/src/plugins/socketmon/socketmon.h @@ -105,6 +105,7 @@ #ifndef SOCKETMON_H #define SOCKETMON_H +#include #include "plugins/plugins.h" #include "private.h" @@ -120,6 +121,7 @@ class socketmon: public plugin output_format_t format; win_build_info_t build; drakvuf_t drakvuf; + wanted_hooks_t wanted_hooks; drakvuf_trap_t tcpip_trap[2] = {