From ea26dd7c5eec11c76fca99589bafa20604987c2e Mon Sep 17 00:00:00 2001 From: Dorian Eikenberg Date: Wed, 6 Dec 2023 12:48:12 +0100 Subject: [PATCH] Add inline documentation --- vmicore/src/include/vmicore/filename.h | 3 + vmicore/src/include/vmicore/io/ILogger.h | 7 ++ .../vmicore/os/ActiveProcessInformation.h | 16 +++++ .../vmicore/os/IMemoryRegionExtractor.h | 3 + .../src/include/vmicore/os/IPageProtection.h | 10 +++ vmicore/src/include/vmicore/os/MemoryRegion.h | 18 +++++ vmicore/src/include/vmicore/plugins/IPlugin.h | 17 ++++- .../include/vmicore/plugins/IPluginConfig.h | 20 ++++++ .../include/vmicore/plugins/PluginInterface.h | 65 +++++++++++++++++++ 9 files changed, 157 insertions(+), 2 deletions(-) diff --git a/vmicore/src/include/vmicore/filename.h b/vmicore/src/include/vmicore/filename.h index 93e27a20..088d2e4a 100644 --- a/vmicore/src/include/vmicore/filename.h +++ b/vmicore/src/include/vmicore/filename.h @@ -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 diff --git a/vmicore/src/include/vmicore/io/ILogger.h b/vmicore/src/include/vmicore/io/ILogger.h index c5bd2ffb..86f26b4f 100644 --- a/vmicore/src/include/vmicore/io/ILogger.h +++ b/vmicore/src/include/vmicore/io/ILogger.h @@ -11,12 +11,19 @@ namespace VmiCore { constexpr auto WRITE_TO_FILE_TAG = "writeToFileTag"; + /** + * Structured log field. Use this for providing variable context information. + */ using CxxLogField = std::pair>; 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& fields) = 0; virtual void debug(std::string_view message) const = 0; diff --git a/vmicore/src/include/vmicore/os/ActiveProcessInformation.h b/vmicore/src/include/vmicore/os/ActiveProcessInformation.h index fe646499..1e6aae43 100644 --- a/vmicore/src/include/vmicore/os/ActiveProcessInformation.h +++ b/vmicore/src/include/vmicore/os/ActiveProcessInformation.h @@ -8,17 +8,33 @@ 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 fullName; + /// The file path of the executable this process has been started from, if any. std::unique_ptr 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 memoryRegionExtractor; + /// Indicates whether the process is a 32bit process or a 64bit process. bool is32BitProcess; }; } diff --git a/vmicore/src/include/vmicore/os/IMemoryRegionExtractor.h b/vmicore/src/include/vmicore/os/IMemoryRegionExtractor.h index 47842e3d..29a9b329 100644 --- a/vmicore/src/include/vmicore/os/IMemoryRegionExtractor.h +++ b/vmicore/src/include/vmicore/os/IMemoryRegionExtractor.h @@ -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> extractAllMemoryRegions() const = 0; protected: diff --git a/vmicore/src/include/vmicore/os/IPageProtection.h b/vmicore/src/include/vmicore/os/IPageProtection.h index b3bdfc7a..6bb16293 100644 --- a/vmicore/src/include/vmicore/os/IPageProtection.h +++ b/vmicore/src/include/vmicore/os/IPageProtection.h @@ -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: diff --git a/vmicore/src/include/vmicore/os/MemoryRegion.h b/vmicore/src/include/vmicore/os/MemoryRegion.h index a2c02784..404db7a2 100644 --- a/vmicore/src/include/vmicore/os/MemoryRegion.h +++ b/vmicore/src/include/vmicore/os/MemoryRegion.h @@ -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 + /// IPageProtection.h for details. std::unique_ptr 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; }; } diff --git a/vmicore/src/include/vmicore/plugins/IPlugin.h b/vmicore/src/include/vmicore/plugins/IPlugin.h index cc97a92e..1cf7e6f4 100644 --- a/vmicore/src/include/vmicore/plugins/IPlugin.h +++ b/vmicore/src/include/vmicore/plugins/IPlugin.h @@ -13,7 +13,9 @@ 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; class IPlugin @@ -21,13 +23,24 @@ namespace VmiCore::Plugin 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 IPluginConfig.h 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 init(PluginInterface* pluginInterface, std::shared_ptr config, std::vector args); } diff --git a/vmicore/src/include/vmicore/plugins/IPluginConfig.h b/vmicore/src/include/vmicore/plugins/IPluginConfig.h index 815fa522..0bfe977f 100644 --- a/vmicore/src/include/vmicore/plugins/IPluginConfig.h +++ b/vmicore/src/include/vmicore/plugins/IPluginConfig.h @@ -18,9 +18,18 @@ namespace VmiCore::Plugin public: virtual ~IPluginConfig() = default; + /** + * Returns plugin config as a string. Useful it 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 @@ -30,8 +39,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 configFilePath() const = 0; protected: diff --git a/vmicore/src/include/vmicore/plugins/PluginInterface.h b/vmicore/src/include/vmicore/plugins/PluginInterface.h index 606d2e20..0f85565c 100644 --- a/vmicore/src/include/vmicore/plugins/PluginInterface.h +++ b/vmicore/src/include/vmicore/plugins/PluginInterface.h @@ -21,35 +21,100 @@ namespace VmiCore::Plugin 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> 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>> 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 callback.h + */ virtual void registerProcessStartEvent( const std::function)>& startCallback) = 0; + /** + * Subscribe to process termination 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 callback.h + */ virtual void registerProcessTerminationEvent( const std::function)>& 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 callback.h + * @return Shared pointer to a breakpoint object. Can be used to delete the breakpoint. + */ [[nodiscard]] virtual std::shared_ptr createBreakpoint(uint64_t targetVA, const ActiveProcessInformation& processInformation, const std::function& 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 getResultsDir() const = 0; + /** + * Creates a new logger with a given name. + */ [[nodiscard]] virtual std::unique_ptr 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& 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; + /** + * 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 getIntrospectionAPI() const = 0; protected: