Skip to content

Commit

Permalink
Modernize usermode hooking using libusermode
Browse files Browse the repository at this point in the history
Applies the recommandation of @psrok1 at tklengyel#1613 (comment) of implementing usermode hooking for socketmon plugin using libusermode.
  • Loading branch information
Victorien Blanchard committed Nov 21, 2024
1 parent 414c4e3 commit 3684999
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 101 deletions.
168 changes: 67 additions & 101 deletions src/plugins/socketmon/socketmon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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;
Expand Down Expand Up @@ -714,7 +715,8 @@ template <typename dns_query_ex_w_string_t>
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);

Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand Down Expand Up @@ -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<module_trap_context_t*>(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<hook_target_view_t>& 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);
Expand All @@ -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<std::unique_ptr<ArgumentPrinter>>()));
wanted_hooks.add_hook(plugin_target_config_entry_t ("dnsapi.dll", "DnsQuery_A", log, std::vector<std::unique_ptr<ArgumentPrinter>>()));
wanted_hooks.add_hook(plugin_target_config_entry_t ("dnsapi.dll", "DnsQuery_UTF8", log, std::vector<std::unique_ptr<ArgumentPrinter>>()));

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<std::unique_ptr<ArgumentPrinter>>()));
wanted_hooks.add_hook(plugin_target_config_entry_t("dnsapi.dll", "DnsQueryA", log, std::vector<std::unique_ptr<ArgumentPrinter>>()));
}

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<std::unique_ptr<ArgumentPrinter>>()));
}

usermode_cb_registration reg =
{
.pre_cb = on_dll_discovered,
.post_cb = on_dll_hooked,
.extra = (void*)this
};
drakvuf_register_usermode_callback(drakvuf, &reg);

json_object* tcpip_profile_json = json_object_from_file(c->tcpip_profile);
if (!tcpip_profile_json)
{
Expand Down Expand Up @@ -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);
}
2 changes: 2 additions & 0 deletions src/plugins/socketmon/socketmon.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
#ifndef SOCKETMON_H
#define SOCKETMON_H

#include <libusermode/userhook.hpp>
#include "plugins/plugins.h"
#include "private.h"

Expand All @@ -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] =
{
Expand Down

0 comments on commit 3684999

Please sign in to comment.