Skip to content

Commit

Permalink
Add backing parameter logging
Browse files Browse the repository at this point in the history
  • Loading branch information
Manuel Bischof committed Oct 4, 2023
1 parent 8d3df4a commit 2a07ea0
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 17 deletions.
88 changes: 83 additions & 5 deletions plugins/apitracing/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,18 @@ Modules:
ReturnValue: BOOL
Structures:
LPPROCESS_INFORMATION:
hProcess: HANDLE
hThread: HANDLE
dwProcessId: DWORD
dwThreadId: DWORD
hProcess:
Type: HANDLE
Offset: 0
hThread:
Type: HANDLE
Offset: 8
dwProcessId:
Type: DWORD
Offset: 16
dwThreadId:
Type: DWORD
Offset: 20
HighLevelParameterTypes:
AddressWidth32Bit:
DWORD: unsigned long
Expand Down Expand Up @@ -88,4 +96,74 @@ type *LPPROCESS_INFORMATION*.
For example the parameter *hProcess* is of the type *HANDLE*. Depending on the address width of the process it gets
resolved
to either *unsigned int* or *unsigned _int64*. Those are *BackingParameterTypes* so either 8 or 4 byte are read when
extracting this structure from the heap.
extracting this structure from the heap.
## Extraction Format
Function call traces are logged in an unindented json format which is illustrated indented below for better visibility.
The parameter extraction logs are identified by the *key* ```Parameterlist:```.
Its *value* is a list of all function call parameters, whereby the parameter names form the *keys* of the contained *key:value* pairs.
Each *value* is either the value of the parameter as integer or string value, or it is a list containing
the *key:value* pairs of backing parameters if it's a pointer to a struct.


```json
{
"Parameterlist": [
{"FileHandle":[
{"HANDLE":45}]},
{"DesiredAccess":1180063},
{"ObjectAttributes":[
{"Length":48},
{"RootDirectory":0},
{"ObjectName":"\\Device\\ConDrv\\Server"},
{"Attributes":66},
{"SecurityDescriptor":0},
{"SecurityQualityOfService":0}]},
{"IoStatusBlock":958106952720},
{"AllocationSize":0},
{"FileAttributes":0},
{"ShareAccess":7},
{"CreateDisposition":2},
{"CreateOptions":0},
{"EaBuffer":0},
{"EaLength":0}]
}
```

The log example above is a function call of *NtCreateFile* with these parameters:

```c
__kernel_entry NTSTATUS NtCreateFile(
[out] PHANDLE FileHandle,
[in] ACCESS_MASK DesiredAccess,
[in] POBJECT_ATTRIBUTES ObjectAttributes,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[in, optional] PLARGE_INTEGER AllocationSize,
[in] ULONG FileAttributes,
[in] ULONG ShareAccess,
[in] ULONG CreateDisposition,
[in] ULONG CreateOptions,
[in] PVOID EaBuffer,
[in] ULONG EaLength
);
```

*FileHandle, DesiredAccess, ObjectAttributues, IOStatusBlocck,AllocationSize, FileAttributes,ShareAccess, CreateDisposition,
CreateOptions, EaBuffer* and *EaLength* are members of the list in parameterlist *value*.

```c
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES;
```

*ObjectAttributes* is a pointer to a struct, so its value is a list containing the backing parameters.
The parameter *ObjectName* is a pointer to a unicode and forms an exception since it is extracted directly.
Both *PVOID* at the end are structs, that are currently not covered by our definitions.
You can find a list under the struct section in the [function definitions file](configuration/functiondefinitions/functionDefinitions.yaml).
15 changes: 15 additions & 0 deletions plugins/apitracing/src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,22 @@ set_property(TARGET yaml-cpp PROPERTY POSITION_INDEPENDENT_CODE TRUE)
target_compile_definitions(apitracing-obj PUBLIC YAML_CPP_SUPPORT)
target_link_libraries(apitracing-obj PUBLIC yaml-cpp)

# Setup json-cpp

FetchContent_Declare(
jsoncpp
GIT_REPOSITORY https://github.com/open-source-parsers/jsoncpp.git
GIT_TAG 1.9.5
)
option(JSONCPP_WITH_TESTS "" OFF)
option(JSONCPP_WITH_POST_BUILD_UNITTEST "" OFF)
option(JSONCPP_WITH_EXAMPLE "" OFF)
FetchContent_MakeAvailable(jsoncpp)
set_property(TARGET jsoncpp_static PROPERTY POSITION_INDEPENDENT_CODE TRUE)
target_link_libraries(apitracing-obj PUBLIC jsoncpp_static)

# Add public vmicore headers

add_subdirectory("${VMICORE_DIRECTORY_ROOT}/src/include" "${CMAKE_CURRENT_BINARY_DIR}/vmicore-public-headers")
target_link_libraries(apitracing-obj PUBLIC vmicore-public-headers)

38 changes: 27 additions & 11 deletions plugins/apitracing/src/lib/FunctionHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace ApiTracing
logger(this->pluginInterface->newNamedLogger(APITRACING_LOGGER_NAME))
{
logger->bind({{VmiCore::WRITE_TO_FILE_TAG, LOG_FILENAME}});
builder["indentation"] = "";
}

void FunctionHook::hookFunction(VmiCore::addr_t moduleBaseAddress,
Expand All @@ -52,25 +53,40 @@ namespace ApiTracing
}

auto extractedParameters = extractor->extractParameters(event, parameterInformation);
logParameterList(extractedParameters);
auto json = getParameterListAsJson(extractedParameters);
std::string unformattedTraces = Json::writeString(builder, json);

logger->info("Monitored function called",
{{"FunctionName", functionName},
{"ModuleName", moduleName},
{"ProcessDtb", fmt::format("{:x}", event.getCr3())},
{"ProcessTeb", fmt::format("{:x}", event.getGs())},
{"Parameterlist", unformattedTraces}});

return BpResponse::Continue;
}

void FunctionHook::logParameterList(const std::vector<ExtractedParameterInformation>& extractedParameters)
Json::Value
FunctionHook::getParameterListAsJson(const std::vector<ExtractedParameterInformation>& extractedParameters)
{
Json::Value parameterList;

for (const auto& extractedParameter : extractedParameters)
{
std::visit(
[&extractedParameter = extractedParameter, &logger = logger](auto&& arg)
{
logger->info("Parameter",
{{"Name", extractedParameter.name},
{"Value", std::forward<std::remove_reference_t<decltype(arg)>>(arg)}});
},
extractedParameter.data);
// TODO: Log backing parameters
Json::Value parameter;
if (!extractedParameter.backingParameters.empty())
{
parameter[extractedParameter.name] = getParameterListAsJson(extractedParameter.backingParameters);
}
else
{
std::visit([&parameter = parameter, &extractedParameter = extractedParameter]<typename T>(T&& arg)
{ parameter[extractedParameter.name] = std::forward<T>(arg); },
extractedParameter.data);
}
parameterList.append(parameter);
}
return parameterList;
}

void FunctionHook::teardown() const
Expand Down
5 changes: 4 additions & 1 deletion plugins/apitracing/src/lib/FunctionHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "ConstantDefinitions.h"
#include "config/FunctionDefinitions.h"
#include "os/Extractor.h"
#include <json/value.h>
#include <json/writer.h>
#include <vmicore/io/ILogger.h>
#include <vmicore/plugins/PluginInterface.h>
#include <vmicore/vmi/IBreakpoint.h>
Expand Down Expand Up @@ -36,8 +38,9 @@ namespace ApiTracing
std::shared_ptr<std::vector<ParameterInformation>> parameterInformation;
VmiCore::Plugin::PluginInterface* pluginInterface;
std::unique_ptr<VmiCore::ILogger> logger;
Json::StreamWriterBuilder builder;

void logParameterList(const std::vector<ExtractedParameterInformation>& extractedParameters);
Json::Value getParameterListAsJson(const std::vector<ExtractedParameterInformation>& extractedParameters);
};
}
#endif // APITRACING_FUNCTIONHOOK_H
5 changes: 5 additions & 0 deletions plugins/apitracing/test/FunctionHook_UnitTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ 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::vector<ExtractedParameterInformation> extractedParameterInformation{
{.name = "TestInt0", .data = 0},
{.name = "TestInt1",
.data = 3,
.backingParameters = {{.name = "TestInt2", .data = 4}, {.name = "TestString1", .data = "A"}}}};

void SetUp() override
{
Expand Down

0 comments on commit 2a07ea0

Please sign in to comment.