Skip to content

Commit

Permalink
Apitracing - separate kernel and user dtb
Browse files Browse the repository at this point in the history
  • Loading branch information
Manuel Bischof committed Aug 24, 2023
1 parent 1de4487 commit e1259eb
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 61 deletions.
16 changes: 10 additions & 6 deletions plugins/apitracing/src/lib/FunctionHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,19 @@ namespace ApiTracing
logger->bind({{VmiCore::WRITE_TO_FILE_TAG, LOG_FILENAME}});
}

void FunctionHook::hookFunction(addr_t moduleBaseAddress, uint64_t processCr3)
void FunctionHook::hookFunction(VmiCore::addr_t moduleBaseAddress,
std::shared_ptr<const VmiCore::ActiveProcessInformation> processInformation)
{
auto processDtb = moduleBaseAddress >= 0xFFFF800000000000 ? processInformation->processKernelDTB
: processInformation->processUserDTB;

auto functionEntrypoint =
introspectionAPI->translateUserlandSymbolToVA(moduleBaseAddress, processCr3, functionName);
introspectionAPI->translateUserlandSymbolToVA(moduleBaseAddress, processDtb, functionName);

interruptEvent = pluginInterface->createBreakpoint(
functionEntrypoint, processCr3, VMICORE_SETUP_SAFE_MEMBER_CALLBACK(hookCallback));
breakpoint = pluginInterface->createBreakpoint(
functionEntrypoint, processInformation, VMICORE_SETUP_SAFE_MEMBER_CALLBACK(hookCallback));

hookedProcesses.emplace_back(processCr3);
hookedProcesses.emplace_back(processDtb);
}

BpResponse FunctionHook::hookCallback(IInterruptEvent& event)
Expand Down Expand Up @@ -79,6 +83,6 @@ namespace ApiTracing

void FunctionHook::teardown() const
{
interruptEvent->remove();
breakpoint->remove();
}
}
5 changes: 3 additions & 2 deletions plugins/apitracing/src/lib/FunctionHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ namespace ApiTracing
std::shared_ptr<std::vector<ParameterInformation>> parameterInformation,
VmiCore::Plugin::PluginInterface* pluginInterface);

void hookFunction(VmiCore::addr_t moduleBaseAddress, uint64_t processCr3);
void hookFunction(VmiCore::addr_t moduleBaseAddress,
std::shared_ptr<const VmiCore::ActiveProcessInformation> processInformation);

VmiCore::BpResponse hookCallback(VmiCore::IInterruptEvent& event);

void teardown() const;

private:
std::shared_ptr<IExtractor> extractor;
std::shared_ptr<VmiCore::IBreakpoint> interruptEvent;
std::shared_ptr<VmiCore::IBreakpoint> breakpoint;
std::shared_ptr<VmiCore::IIntrospectionAPI> introspectionAPI;
std::string functionName;
std::vector<uint64_t> hookedProcesses;
Expand Down
2 changes: 1 addition & 1 deletion plugins/apitracing/src/lib/TracedProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ namespace ApiTracing
auto extractor = std::make_shared<Extractor>(introspectionAPI, addressWidth);
auto functionHook = std::make_shared<FunctionHook>(
moduleHookTarget.name, functionName, extractor, introspectionAPI, definitions, pluginInterface);
functionHook->hookFunction(moduleBaseAddress, processInformation->processCR3);
functionHook->hookFunction(moduleBaseAddress, processInformation);
hookList.push_back(functionHook);
}
catch (const std::exception& e)
Expand Down
52 changes: 40 additions & 12 deletions plugins/apitracing/test/FunctionHook_UnitTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@
#include "mock_Extractor.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <vmicore/vmi/IBreakpoint.h>
#include <vmicore_test/io/mock_Logger.h>
#include <vmicore_test/os/mock_MemoryRegionExtractor.h>
#include <vmicore_test/os/mock_PageProtection.h>
#include <vmicore_test/plugins/mock_PluginInterface.h>
#include <vmicore_test/vmi/mock_InterruptEvent.h>
#include <vmicore_test/vmi/mock_IntrospectionAPI.h>

using testing::_; // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
using testing::NiceMock;
using testing::Return;
using VmiCore::ActiveProcessInformation;
using VmiCore::addr_t;
using VmiCore::MemoryRegion;
using VmiCore::MockInterruptEvent;
using VmiCore::MockIntrospectionAPI;
using VmiCore::Plugin::MockPluginInterface;
Expand All @@ -23,35 +29,57 @@ namespace ApiTracing
std::shared_ptr<MockIntrospectionAPI> introspectionAPI = std::make_shared<NiceMock<MockIntrospectionAPI>>();
std::shared_ptr<MockExtractor> extractor = std::make_shared<NiceMock<MockExtractor>>();
std::shared_ptr<MockInterruptEvent> interruptEvent = std::make_shared<NiceMock<MockInterruptEvent>>();
std::shared_ptr<ActiveProcessInformation> tracedProcessInformation;

static constexpr VmiCore::addr_t testModuleBase = 0x420;
static constexpr VmiCore::addr_t testDtb = 0x1337;
const uint64_t kernelspaceLowerBoundary = 0xFFFF800000000000;
const addr_t tracedProcessKernelDtb = 0x1338;
const addr_t tracedProcessUserDtb = 0x1337;
const addr_t kernelDllBase = 0x1234000 + kernelspaceLowerBoundary;
const addr_t kernelDllFunctionAddress = 0x1234567;
const std::string_view kernelDllName = "KernelBase.dll";
const std::string_view kernelDllFunctionName = "kernelDllFunction";

void SetUp() override
{
ON_CALL(*interruptEvent, getCr3()).WillByDefault(Return(testDtb));
ON_CALL(*interruptEvent, getCr3()).WillByDefault(Return(tracedProcessKernelDtb));
ON_CALL(*pluginInterface, newNamedLogger(_))
.WillByDefault([]() { return std::make_unique<NiceMock<VmiCore::MockLogger>>(); });
ON_CALL(
*introspectionAPI,
translateUserlandSymbolToVA(kernelDllBase, tracedProcessKernelDtb, std::string(kernelDllFunctionName)))
.WillByDefault(Return(kernelDllFunctionAddress));

tracedProcessInformation =
std::make_shared<ActiveProcessInformation>(0,
tracedProcessKernelDtb,
tracedProcessUserDtb,
0,
0,
std::string(""),
std::make_unique<std::string>(""),
std::make_unique<std::string>(""),
std::make_unique<VmiCore::MockMemoryRegionExtractor>(),
true);
}
};

TEST_F(FunctionHookTestFixture, hookFunction_validFunction_noThrow)
{
FunctionHook functionHook{"TestModule",
"TestFunction",
FunctionHook functionHook{std::string(kernelDllName),
std::string(kernelDllFunctionName),
extractor,
introspectionAPI,
std::make_shared<std::vector<ParameterInformation>>(
std::vector<ParameterInformation>{{.name = "TestParameter"}}),
pluginInterface.get()};

ASSERT_NO_THROW(functionHook.hookFunction(testModuleBase, testDtb));
ASSERT_NO_THROW(functionHook.hookFunction(kernelDllBase, tracedProcessInformation));
}

TEST_F(FunctionHookTestFixture, hookFunction_validFunction_createsHook)
{
FunctionHook functionHook{"TestModule",
"TestFunction",
FunctionHook functionHook{std::string(kernelDllName),
std::string(kernelDllFunctionName),
extractor,
introspectionAPI,
std::make_shared<std::vector<ParameterInformation>>(
Expand All @@ -60,20 +88,20 @@ namespace ApiTracing
EXPECT_CALL(*introspectionAPI, translateUserlandSymbolToVA).Times(1);
EXPECT_CALL(*pluginInterface, createBreakpoint).Times(1);

functionHook.hookFunction(testModuleBase, testDtb);
functionHook.hookFunction(kernelDllBase, tracedProcessInformation);
}

TEST_F(FunctionHookTestFixture, hookCallBack_functionHookWithParameters_extractsParameters)
{
FunctionHook functionHook{"TestModule",
"TestFunction",
FunctionHook functionHook{std::string(kernelDllName),
std::string(kernelDllFunctionName),
extractor,
introspectionAPI,
std::make_shared<std::vector<ParameterInformation>>(
std::vector<ParameterInformation>{{.name = "TestParameter"}}),
pluginInterface.get()};

functionHook.hookFunction(testModuleBase, testDtb);
functionHook.hookFunction(kernelDllBase, tracedProcessInformation);
EXPECT_CALL(*extractor, extractParameters).Times(1);

functionHook.hookCallback(*interruptEvent);
Expand Down
91 changes: 51 additions & 40 deletions plugins/apitracing/test/TracedProcess_UnitTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,32 @@ using VmiCore::Plugin::MockPluginInterface;

namespace ApiTracing
{
namespace
{
constexpr std::string_view targetProcessName = "TraceMeNow.exe";
constexpr pid_t tracedProcessPid = 420;
constexpr addr_t tracedProcessCr3 = 0x1337;

constexpr size_t size = 0x666;
constexpr addr_t kernelDllBase = 0x1234000;
constexpr addr_t ntdllBase = 0x1235000;
constexpr addr_t NonDllBase = 0x1236000;
constexpr addr_t kernelDllFunctionAddress = 0x1234567;
constexpr addr_t ntdllFunctionAddress = 0x9876543210;

constexpr std::string_view kernelDllName = "KernelBase.dll";
constexpr std::string_view kernellDllFunctionName = "kernelDllFunction";
constexpr std::string_view ntdllDllName = "ntdll.dll";
constexpr std::string_view ntdllFunctionName = "ntdllFunction";
constexpr std::string_view nonDllName = "KernelBase";
}

class TracedProcessTestFixture : public testing::Test
{
protected:
std::unique_ptr<MockPluginInterface> mockPluginInterface = std::make_unique<MockPluginInterface>();
std::shared_ptr<MockIntrospectionAPI> mockIntrospectionAPI = std::make_shared<MockIntrospectionAPI>();
std::shared_ptr<const ActiveProcessInformation> tracedProcessInformation;

const uint64_t kernelspaceLowerBoundary = 0xFFFF800000000000;

const std::string_view targetProcessName = "TraceMeNow.exe";
const pid_t tracedProcessPid = 420;
const addr_t tracedProcessKernelDtb = 0x1337;
const addr_t tracedProcessUserDtb = 0x1337;

const size_t size = 0x666;
const addr_t kernelDllBase = 0x1234000 + kernelspaceLowerBoundary;
const addr_t ntdllBase = 0x1235000;
const addr_t NonDllBase = 0x1236000;
const addr_t kernelDllFunctionAddress = 0x1234567;
const addr_t ntdllFunctionAddress = 0x9876543210;

const std::string_view kernelDllName = "KernelBase.dll";
const std::string_view kernellDllFunctionName = "kernelDllFunction";
const std::string_view ntdllDllName = "ntdll.dll";
const std::string_view ntdllFunctionName = "ntdllFunction";
const std::string_view nonDllName = "KernelBase";

void SetUp() override
{
Expand All @@ -67,39 +68,45 @@ namespace ApiTracing
false};
}

TracedProcess createDefaultTracedProcess()
void setupProcessInformation()
{
auto mockMemoryRegionExtractor = std::make_unique<VmiCore::MockMemoryRegionExtractor>();
ON_CALL(*mockMemoryRegionExtractor, extractAllMemoryRegions())
.WillByDefault(
[]()
[this]()
{
auto memoryRegions = std::make_unique<std::list<MemoryRegion>>();
memoryRegions->push_back(createMemoryRegionDescriptor(kernelDllBase, size, kernelDllName));
memoryRegions->push_back(createMemoryRegionDescriptor(ntdllBase, size, ntdllDllName));
memoryRegions->push_back(createMemoryRegionDescriptor(NonDllBase, size, nonDllName));
return memoryRegions;
});

tracedProcessInformation =
std::make_shared<const ActiveProcessInformation>(0,
tracedProcessKernelDtb,
tracedProcessUserDtb,
tracedProcessPid,
0,
std::string(targetProcessName),
std::make_unique<std::string>(targetProcessName),
std::make_unique<std::string>(""),
std::move(mockMemoryRegionExtractor),
true);
};

TracedProcess createDefaultTracedProcess()
{
std::vector<ModuleInformation> defaultModuleTracingInformation = {
{std::string(kernelDllName), {{std::string(kernellDllFunctionName)}}},
{std::string(ntdllDllName), {{std::string(ntdllFunctionName)}}}};

ON_CALL(*mockIntrospectionAPI,
translateUserlandSymbolToVA(kernelDllBase, _, std::string(kernellDllFunctionName)))
.WillByDefault(Return(kernelDllFunctionAddress));
ON_CALL(*mockIntrospectionAPI, translateUserlandSymbolToVA(ntdllBase, _, std::string(ntdllFunctionName)))
.WillByDefault(Return(ntdllFunctionAddress));

auto tracedProcessInformation =
std::make_shared<ActiveProcessInformation>(0,
tracedProcessCr3,
tracedProcessPid,
0,
std::string(targetProcessName),
std::make_unique<std::string>(targetProcessName),
std::make_unique<std::string>(""),
std::move(mockMemoryRegionExtractor),
true);
std::vector<ModuleInformation> defaultModuleTracingInformation = {
{std::string(kernelDllName), {{std::string(kernellDllFunctionName)}}},
{std::string(ntdllDllName), {{std::string(ntdllFunctionName)}}}};

return {mockPluginInterface.get(),
std::make_shared<NiceMock<MockFunctionDefinitions>>(),
std::make_shared<Windows::Library>(),
Expand All @@ -110,21 +117,25 @@ namespace ApiTracing

TEST_F(TracedProcessTestFixture, constructor_defaultTracedProcess_correctBreakpointsInjected)
{
EXPECT_CALL(*mockPluginInterface, createBreakpoint(kernelDllFunctionAddress, tracedProcessCr3, _))
setupProcessInformation();

EXPECT_CALL(*mockPluginInterface, createBreakpoint(kernelDllFunctionAddress, tracedProcessInformation, _))
.WillOnce(Return(std::make_shared<NiceMock<MockBreakpoint>>()));
EXPECT_CALL(*mockPluginInterface, createBreakpoint(ntdllFunctionAddress, tracedProcessCr3, _))
EXPECT_CALL(*mockPluginInterface, createBreakpoint(ntdllFunctionAddress, tracedProcessInformation, _))
.WillOnce(Return(std::make_shared<NiceMock<MockBreakpoint>>()));

EXPECT_NO_THROW(createDefaultTracedProcess());
}

TEST_F(TracedProcessTestFixture, destructor_defaultTracedProcess_breakpointsRemoved)
{
setupProcessInformation();

auto kernelDllFunctionBreakpoint = std::make_shared<MockBreakpoint>();
EXPECT_CALL(*mockPluginInterface, createBreakpoint(kernelDllFunctionAddress, tracedProcessCr3, _))
EXPECT_CALL(*mockPluginInterface, createBreakpoint(kernelDllFunctionAddress, tracedProcessInformation, _))
.WillOnce(Return(kernelDllFunctionBreakpoint));
auto ntdllFunctionBreakpoint = std::make_shared<MockBreakpoint>();
EXPECT_CALL(*mockPluginInterface, createBreakpoint(ntdllFunctionAddress, tracedProcessCr3, _))
EXPECT_CALL(*mockPluginInterface, createBreakpoint(ntdllFunctionAddress, tracedProcessInformation, _))
.WillOnce(Return(ntdllFunctionBreakpoint));

EXPECT_CALL(*kernelDllFunctionBreakpoint, remove());
Expand Down
1 change: 1 addition & 0 deletions plugins/apitracing/test/Tracer_UnitTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ namespace ApiTracing
createActiveProcessInformation(std::string_view processName, pid_t processPid, pid_t parentPid)
{
return std::make_shared<VmiCore::ActiveProcessInformation>(
0,
0,
0,
processPid,
Expand Down

0 comments on commit e1259eb

Please sign in to comment.