Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add inline documentation #138

Merged
merged 2 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions vmicore/src/include/vmicore/filename.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ constexpr ::std::string_view filenameStem(const ::std::source_location& sourceLo
return {fileName, lastDotAt(fileName)};
}

/**
* Retrieves the name of the current source file without the extension at compile time.
*/
#define FILENAME_STEM filenameStem(::std::source_location::current())

#endif // VMICORE_FILENAME_H
11 changes: 10 additions & 1 deletion vmicore/src/include/vmicore/io/ILogger.h
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
#ifndef VMICORE_ILOGGER_H
#define VMICORE_ILOGGER_H

#include <memory>
#include <cstdint>
#include <initializer_list>
#include <string_view>
#include <utility>
#include <variant>

namespace VmiCore
{
constexpr auto WRITE_TO_FILE_TAG = "writeToFileTag";

/**
* Structured log field. Use this for providing variable context information.
*/
using CxxLogField = std::pair<std::string_view, std::variant<std::string_view, bool, int64_t, uint64_t, double>>;

class ILogger
{
public:
virtual ~ILogger() = default;

/**
* Bind additional default context information. Will be attached to every log call.
*/
virtual void bind(const std::initializer_list<CxxLogField>& fields) = 0;

virtual void debug(std::string_view message) const = 0;
Expand Down
17 changes: 16 additions & 1 deletion vmicore/src/include/vmicore/os/ActiveProcessInformation.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,36 @@
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

namespace VmiCore
{
/// OS-agnostic representation of a process.
struct ActiveProcessInformation
{
/// The base address of the process struct in the kernel.
uint64_t base;
/// The pointer to the top level paging structure.
uint64_t processDtb;
/// The pointer to the user top level paging structure. Will be the same value as processDtb if either there is
/// no support for kernel page table isolation or it is turned off.
uint64_t processUserDtb;
/// The process ID.
pid_t pid;
/// The Process ID of the parent process. Note that a value of zero could mean that either the parent has got a
/// PID of zero or there is no parent at all.
pid_t parentPid;
/// The name of the process. Possibly truncated to a fixed amount of characters which is usually a restriction
/// of the process struct memory layout.
std::string name;
/// The full name of the process without any length restrictions. Extracted from a location other than the
/// process struct.
std::unique_ptr<std::string> fullName;
/// The file path of the executable this process has been started from, if any.
std::unique_ptr<std::string> processPath;
/// An object that provides on-demand extraction of memory region descriptors. Parses kernel structures used for
/// tracking memory allocations of processes.
std::unique_ptr<IMemoryRegionExtractor> memoryRegionExtractor;
/// Indicates whether the process is a 32bit process or a 64bit process.
bool is32BitProcess;
};
}
Expand Down
3 changes: 3 additions & 0 deletions vmicore/src/include/vmicore/os/IMemoryRegionExtractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ namespace VmiCore
public:
virtual ~IMemoryRegionExtractor() = default;

/**
* Provides a representation of all memory regions for a specific process. Does not guarantee any ordering.
*/
[[nodiscard]] virtual std::unique_ptr<std::vector<MemoryRegion>> extractAllMemoryRegions() const = 0;

protected:
Expand Down
10 changes: 10 additions & 0 deletions vmicore/src/include/vmicore/os/IPageProtection.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,20 @@ namespace VmiCore
public:
virtual ~IPageProtection() = default;

/**
* Get an OS-agnostic representation of protection values.
*/
[[nodiscard]] virtual ProtectionValues get() const = 0;

/**
* Get protection values in exactly the same representation as they have been extracted from memory. Not
* OS-agnostic, but may provide more information.
*/
[[nodiscard]] virtual uint64_t getRaw() const = 0;

/**
* Returns a string representation of the protection values.
*/
[[nodiscard]] virtual std::string toString() const = 0;

protected:
Expand Down
18 changes: 18 additions & 0 deletions vmicore/src/include/vmicore/os/MemoryRegion.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,30 @@ namespace VmiCore
{
}

/// The start address of the memory region.
addr_t base;
/// The size of the memory region in bytes.
std::size_t size;
/**
* If the memory region is file-backed, contains the path of the file on disk. If an exception occurred during
* extraction, will contain the special string "unknownFilename". Will be an empty string otherwise.
*
* @note For linux guests, this string might be empty or partially extracted if an error is encountered instead
* of being set to "unknownFilename". This behavior might be adjusted in the future for increased consistency.
*/
std::string moduleName;
/// An object representing the protection values for the memory region. See
/// <a href=./IPageProtection.h>IPageProtection.h</a> for details.
std::unique_ptr<IPageProtection> protection;
/// Indicates whether this memory region can be shared between processes.
bool isSharedMemory;
/// Indicates whether this memory is marked for deletion. Always false for linux guests.
bool isBeingDeleted;
/**
* Indicates whether this memory region represents the executable the process has been initiated from.
*
* @note Currently always false for linux guests.
*/
bool isProcessBaseImage;
};
}
Expand Down
20 changes: 18 additions & 2 deletions vmicore/src/include/vmicore/plugins/IPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,37 @@

namespace VmiCore::Plugin
{
// Will be verified by VMICore in order to ensure ABI compatibility.
/**
* Will be verified by VMICore in order to ensure ABI compatibility. Has to be an exact match.
*/
extern const uint8_t API_VERSION;

/**
* An abstract class that has to be implemented by the main plugin class.
*/
class IPlugin
{
public:
virtual ~IPlugin() = default;

// Called right before shutting down the application. Is allowed to throw.
/**
* Called right before shutting down the application. Is allowed to throw.
*/
virtual void unload() = 0;

protected:
IPlugin() = default;
};

/**
* Entry point for plugin initialization. Will be called by the plugin host (VMICore).
*
* @param pluginInterface An object containing all API functions that are exposed to plugins.
* @param config The plugin specific configuration. See <a href=./IPluginConfig.h>IPluginConfig.h</a href> for
* details.
* @param args Commandline arguments passed to this specific plugin. Elements are whitespace separated.
* @return An instance of the plugin. Has to implement the IPlugin interface. Lifetime will be managed by VMICore.
*/
extern std::unique_ptr<IPlugin>
init(PluginInterface* pluginInterface, std::shared_ptr<IPluginConfig> config, std::vector<std::string> args);
}
Expand Down
25 changes: 25 additions & 0 deletions vmicore/src/include/vmicore/plugins/IPluginConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,28 @@

namespace VmiCore::Plugin
{
/**
* Defines methods for retrieving plugin specific configuration options. See the
* <a href=../../../../../plugins/Readme.md>plugin readme</a> for details on how to pass configuration options to a
* plugin.
*/
class IPluginConfig
{
public:
virtual ~IPluginConfig() = default;

/**
* Returns plugin config as a string. Useful if the config should be parsed by the plugin separately.
*/
[[nodiscard]] virtual std::string asString() const = 0;

#ifdef YAML_CPP_SUPPORT
/**
* Retrieve the plugin config as a yaml-cpp node. VMICore and the plugin should have the same yaml-cpp version,
* otherwise the behavior is undefined. Currently, there is no way to verify matching versions at run time.
*
* @return A reference to a yaml node that represents the root of the plugin config.
*/
[[nodiscard]] virtual const YAML::Node& rootNode() const = 0;
#else
// Keep vtable ordinals consistent
Expand All @@ -30,8 +44,19 @@ namespace VmiCore::Plugin
}
#endif

/**
* Returns the path of the main config file used by VMICore.
*/
[[nodiscard]] virtual std::filesystem::path mainConfigFileLocation() const = 0;

/**
* If the plugin has a separate config file instead of an inline config, the path to it can be obtained via
* this function.
*
* @return An optional file path. The key "config_file" has to defined in the VMICore config file for the
* specific plugin. Will return std::nullopt otherwise. If the path is absolute it will be returned as is, if it
* is relative it will be interpreted relative to the location of the main plugin file.
*/
[[nodiscard]] virtual std::optional<std::filesystem::path> configFilePath() const = 0;

protected:
Expand Down
72 changes: 70 additions & 2 deletions vmicore/src/include/vmicore/plugins/PluginInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,122 @@
#include "../io/ILogger.h"
#include "../os/ActiveProcessInformation.h"
#include "../types.h"
#include "../vmi/BpResponse.h"
#include "../vmi/IBreakpoint.h"
#include "../vmi/IIntrospectionAPI.h"
#include "IPluginConfig.h"
#include "../vmi/events/IInterruptEvent.h"
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

namespace VmiCore::Plugin
{
/**
* Contains all functionality that is exposed to plugins.
*/
class PluginInterface
{
public:
constexpr static uint8_t API_VERSION = 15;

virtual ~PluginInterface() = default;

/**
* Reads a region of contiguous virtual memory from a process. The starting offset as well as the size must be
* 4kb page aligned.
*
* @return A unique pointer to a byte vector containing the memory content. Subregions that could not be
* extracted (e.g. because they are paged out) will be replaced by a single all zero padding page.
*/
[[nodiscard]] virtual std::unique_ptr<std::vector<uint8_t>>
readProcessMemoryRegion(pid_t pid, addr_t address, size_t numberOfBytes) const = 0;

/**
* Obtain a vector containing an OS-agnostic representation of all currently running processes.
* The vector is a snapshot of the current state, it won't receive any updates.
*/
[[nodiscard]] virtual std::unique_ptr<std::vector<std::shared_ptr<const ActiveProcessInformation>>>
getRunningProcesses() const = 0;

/**
* Subscribe to process start events. The supplied lambda function will be called once the event occurs.
*
* @param startCallback It is recommended to create the lambda with the help of VMICORE_SETUP_MEMBER_CALLBACK
* from <a href="file:../callback.h">callback.h</a>
*/
virtual void registerProcessStartEvent(
const std::function<void(std::shared_ptr<const ActiveProcessInformation>)>& startCallback) = 0;

/**
* Subscribe to process termination events. The supplied lambda function will be called once the event occurs.
*
* @param terminationCallback It is recommended to create the lambda with the help of
* VMICORE_SETUP_MEMBER_CALLBACK from <a href="file:../callback.h">callback.h</a>
*/
virtual void registerProcessTerminationEvent(
const std::function<void(std::shared_ptr<const ActiveProcessInformation>)>& terminationCallback) = 0;

/**
* Create a software breakpoint at the given virtual address. The breakpoint will be protected, so that
* it won't be visible to the guest through reading the memory. Multiple breakpoints per address are allowed and
* will not interfere with each other. If the breakpoint is hit, the supplied callback will be called.
*
* @param targetVA Target address to place the breakpoint on.
* @param processInformation The process information for the target process. Can be obtained via
* getRunningProcesses().
* @param callbackFunction It is recommended to create the lambda with the help of VMICORE_SETUP_MEMBER_CALLBACK
* from <a href="file:../callback.h">callback.h</a>
* @return Shared pointer to a breakpoint object. Can be used to delete the breakpoint.
*/
[[nodiscard]] virtual std::shared_ptr<IBreakpoint>
createBreakpoint(uint64_t targetVA,
const ActiveProcessInformation& processInformation,
const std::function<BpResponse(IInterruptEvent&)>& callbackFunction) = 0;

/**
* Retrieves the path to the directory where plugins are supposed to store any files that are generated
* throughout the course of a run. However, it is generally discouraged to store files directly. Instead,
* the writeToFile or the logging APIs should be used.
*/
[[nodiscard]] virtual std::unique_ptr<std::string> getResultsDir() const = 0;

/**
* Creates a new logger with a given name.
*/
[[nodiscard]] virtual std::unique_ptr<ILogger> newNamedLogger(std::string_view name) const = 0;

/**
* Saves content to a file with the given name. Does not append. Saving the same file more than once is
* undefined behavior. Use the other overload for raw data.
*/
virtual void writeToFile(const std::string& filename, const std::string& message) const = 0;

/**
* Saves content to a file with the given name. Does not append. Saving the same file more than once is
* undefined behavior. Use the other overload for strings.
*/
virtual void writeToFile(const std::string& filename, const std::vector<uint8_t>& data) const = 0;

/**
* Only useful if using a gRPC connection, does nothing otherwise. Will send an error event via a separate
* channel which indicates that the run is not successful.
*/
virtual void sendErrorEvent(std::string_view message) const = 0;

/**
* Only useful if using a gRPC connection, does nothing otherwise. Will send an inmemory scanner detection event
* via a gRPC channel.
*/
virtual void sendInMemDetectionEvent(std::string_view message) const = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Not part of this PR, but I think we should replace this with a more plugin agnostic version.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, you could create an issue for that.


/**
* Gives access to low level introspection API. Interface is limited to a subset of calls that are deemed
* non-invasive in order to avoid interfering with other plugins. Note that any call made through the
* introspection API object will acquire an API-wide lock because the underlying implementation is not
* considered thread safe.
*/
[[nodiscard]] virtual std::shared_ptr<IIntrospectionAPI> getIntrospectionAPI() const = 0;

protected:
Expand Down
4 changes: 4 additions & 0 deletions vmicore/src/include/vmicore/vmi/BpResponse.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@

namespace VmiCore
{
/// An enum containing possible responses for breakpoint event user callbacks.
enum class BpResponse
{
/// Continue with regular workflow.
Continue,
/// Remove the breakpoint after handling the current event. Similar to calling <tt>breakpoint.remove()</tt>.
/// However, <tt>breakpoint.remove()</tt> should not be used in event callbacks.
Deactivate,
};
}
Expand Down
Loading