Skip to content

Commit

Permalink
apimon: optimize plugin by adding no-retval dll hook modifier (#1757)
Browse files Browse the repository at this point in the history
* if we don't need return status or output parameter values, we need install return hook for this hook entry
* for such entrues we may add no-retval modificator, i.e. 'name.dll,FuncName,no-retval,log,type1,type2'
* add some unit-tests for parse_entry function
  • Loading branch information
disaykin authored Jan 6, 2024
1 parent 30fa531 commit 5c4a5d3
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 241 deletions.
2 changes: 1 addition & 1 deletion src/libusermode/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ printers_check_LDADD = $(CHECK_LIBS) $(ZLIB_LIBS) printers/utils.lo
check_PROGRAMS += userhook_check
userhook_check_SOURCES = check.cpp
userhook_check_CFLAGS = $(CHECK_CFLAGS) $(ZLIB_CFLAGS)
userhook_check_LDADD = $(CHECK_LIBS) $(ZLIB_LIBS) utils.lo
userhook_check_LDADD = $(CHECK_LIBS) $(ZLIB_LIBS) libusermode.la

TESTS = $(check_PROGRAMS)

Expand Down
82 changes: 80 additions & 2 deletions src/libusermode/check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,14 @@
* *
***************************************************************************/

#include <check.h>

#include "utils.hpp"
#include "printers/printers.hpp"
#include "userhook.hpp"

#include <sstream>
#include <string>

#include <check.h>

START_TEST(test_match_dll_name)
{
Expand Down Expand Up @@ -133,6 +138,78 @@ static Suite* dll_matching_suite(void)
return s;
}

static plugin_target_config_entry_t test_parse_dll_entry(const std::string& entry)
{
PrinterConfig config;
std::stringstream ss(entry);
return parse_entry(ss, config);
}

START_TEST(test_parse_dll_hook)
{
auto entry_str = "combase.dll,CoCreateInstance,log,rclsid:refclsid,punkOuter:lpvoid,dwClsContext:dword,riid:refiid,ppv:void**";
auto entry = test_parse_dll_entry(entry_str);

ck_assert(entry.dll_name == "combase.dll");
ck_assert(entry.function_name == "CoCreateInstance");
ck_assert(entry.type == HOOK_BY_NAME);
ck_assert(entry.clsid.empty());
ck_assert(entry.offset == 0);
ck_assert(!entry.no_retval);
ck_assert(entry.actions.log && !entry.actions.stack);
ck_assert(entry.argument_printers.size() == 5);
}
END_TEST

START_TEST(test_parse_dll_hook_with_offset)
{
auto entry_str = "taskschd.dll,ITaskFolder::RegisterTaskDefinition,clsid,0F87369F-A4E5-4CFC-BD3E-73E6154572DD,13cd3,log,lpvoid,bstr";
auto entry = test_parse_dll_entry(entry_str);

ck_assert(entry.dll_name == "taskschd.dll");
ck_assert(entry.function_name == "ITaskFolder::RegisterTaskDefinition");
ck_assert(entry.type == HOOK_BY_OFFSET);
ck_assert(entry.clsid == "0F87369F-A4E5-4CFC-BD3E-73E6154572DD");
ck_assert(entry.offset == 0x13cd3);
ck_assert(!entry.no_retval);
ck_assert(entry.actions.log && !entry.actions.stack);
ck_assert(entry.argument_printers.size() == 2);
}
END_TEST

START_TEST(test_parse_dll_hook_with_empty_args)
{
auto entry_str = "combase.dll,CoCreateInstance,log";
auto entry = test_parse_dll_entry(entry_str);

ck_assert(entry.dll_name == "combase.dll");
ck_assert(entry.function_name == "CoCreateInstance");
ck_assert(entry.type == HOOK_BY_NAME);
ck_assert(entry.clsid.empty());
ck_assert(entry.offset == 0);
ck_assert(!entry.no_retval);
ck_assert(entry.actions.log && !entry.actions.stack);
ck_assert(entry.argument_printers.empty());
}
END_TEST

static Suite* dll_hooks_parsing_suite(void)
{
Suite* s;
TCase* tc_core;

s = suite_create("Parse DLL hooks");

tc_core = tcase_create("Core");

tcase_add_test(tc_core, test_parse_dll_hook);
tcase_add_test(tc_core, test_parse_dll_hook_with_offset);
tcase_add_test(tc_core, test_parse_dll_hook_with_empty_args);
suite_add_tcase(s, tc_core);

return s;
}

int main(void)
{
int number_failed;
Expand All @@ -141,6 +218,7 @@ int main(void)

s = dll_matching_suite();
sr = srunner_create(s);
srunner_add_suite(sr, dll_hooks_parsing_suite());

srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
Expand Down
164 changes: 2 additions & 162 deletions src/libusermode/userhook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -771,9 +771,9 @@ void userhook::request_usermode_hook(drakvuf_t drakvuf, const dll_view_t* dll, c
dll_t* p_dll = reinterpret_cast<dll_t*>(const_cast<dll_view_t*>(dll));

if (target->type == HOOK_BY_NAME)
p_dll->targets.emplace_back(target->function_name, target->clsid, callback, target->argument_printers, extra);
p_dll->targets.emplace_back(target->function_name, target->clsid, target->no_retval, callback, target->argument_printers, extra);
else // HOOK_BY_OFFSET
p_dll->targets.emplace_back(target->function_name, target->clsid, target->offset, callback, target->argument_printers, extra);
p_dll->targets.emplace_back(target->function_name, target->clsid, target->offset, target->no_retval, callback, target->argument_printers, extra);
}

void userhook::register_plugin(drakvuf_t drakvuf, usermode_cb_registration reg)
Expand Down Expand Up @@ -927,166 +927,6 @@ bool drakvuf_stop_userhooks(drakvuf_t drakvuf)
return !userhook::is_supported(drakvuf) || userhook::get_instance(drakvuf).stop();
}

std::optional<HookActions> get_hook_actions(const std::string& str)
{
if (str == "log")
{
return HookActions::empty().set_log();
}
else if (str == "log+stack")
{
return HookActions::empty().set_log().set_stack();
}

return std::nullopt;
}

namespace
{
std::optional<std::string> try_parse_token(std::stringstream& ss)
{
const char SEPARATOR = ',';
std::string result;
if (!std::getline(ss, result, SEPARATOR) || result.empty())
{
return std::nullopt;
}
return result;
}

std::string parse_token(std::stringstream& ss)
{
auto maybe_token = try_parse_token(ss);
if (!maybe_token)
{
throw std::runtime_error{"Expected a token"};
}
return *maybe_token;
}

std::unique_ptr<ArgumentPrinter> make_arg_printer(
const PrinterConfig& config,
const std::string& type,
const std::string& name)
{
if (type == "lpstr" || type == "lpcstr" || type == "lpctstr")
{
return std::make_unique<AsciiPrinter>(name, config);
}
else if (type == "lpcwstr" || type == "lpwstr" || type == "bstr")
{
return std::make_unique<WideStringPrinter>(name, config);
}
else if (type == "punicode_string")
{
return std::make_unique<UnicodePrinter>(name, config);
}
else if (type == "pulong")
{
return std::make_unique<UlongPrinter>(name, config);
}
else if (type == "pulonglong")
{
return std::make_unique<UlongLongPrinter>(name, config);
}
else if (type == "lpvoid*")
{
return std::make_unique<PointerToPointerPrinter>(name, config);
}
else if (type == "refclsid" || type == "refiid")
{
return std::make_unique<GuidPrinter>(name, config);
}
else if (type == "binary16")
{
return std::make_unique<Binary16StringPrinter>(name, config);
}

return std::make_unique<ArgumentPrinter>(name, config);
}


std::vector<std::unique_ptr<ArgumentPrinter>> parse_arguments(
const PrinterConfig& config,
std::stringstream& ss)
{
std::vector<std::unique_ptr<ArgumentPrinter>> argument_printers;

for (size_t arg_idx = 0; ; arg_idx++)
{
auto maybe_arg = try_parse_token(ss);
if (!maybe_arg) break;

const std::string arg = *maybe_arg;
std::string arg_name;
std::string arg_type;
const auto pos = arg.find_first_of(':');

if (pos == std::string::npos)
{
arg_name = std::string("Arg") + std::to_string(arg_idx);
arg_type = arg;
}
else
{
arg_name = arg.substr(0, pos);
arg_type = arg.substr(pos + 1);
}

argument_printers.emplace_back(make_arg_printer(config, arg_type, arg_name));
}
return argument_printers;
}

plugin_target_config_entry_t parse_entry(
std::stringstream& ss,
PrinterConfig& config)
{
plugin_target_config_entry_t entry{};

entry.dll_name = parse_token(ss);
entry.function_name = parse_token(ss);
entry.type = HOOK_BY_NAME;

std::string log_strategy_or_offset;
std::string token = parse_token(ss);
if (token == "clsid")
{
entry.clsid = parse_token(ss);
log_strategy_or_offset = parse_token(ss);
}
else
{
log_strategy_or_offset = token;
}

std::optional<HookActions> actions = get_hook_actions(log_strategy_or_offset);
if (!actions)
{
entry.type = HOOK_BY_OFFSET;
try
{
entry.offset = std::stoull(log_strategy_or_offset, 0, 16);
}
catch (const std::logic_error& exc)
{
throw std::runtime_error{"Invalid offset"};
}

std::string strategy_name = parse_token(ss);
actions = get_hook_actions(strategy_name);
if (!actions)
throw std::runtime_error{"Invalid hook action"};
}

entry.actions = *actions;
entry.argument_printers = parse_arguments(config, ss);

return entry;
}
} // namespace


void drakvuf_load_dll_hook_config(drakvuf_t drakvuf, const char* dll_hooks_list_path, bool print_no_addr, const hook_filter_t& hook_filter, wanted_hooks_t& wanted_hooks)
{
PrinterConfig config{};
Expand Down
Loading

0 comments on commit 5c4a5d3

Please sign in to comment.