diff --git a/plugins/apitracing/src/lib/os/Extractor.cpp b/plugins/apitracing/src/lib/os/Extractor.cpp index 9963bee5..8c1b69c3 100644 --- a/plugins/apitracing/src/lib/os/Extractor.cpp +++ b/plugins/apitracing/src/lib/os/Extractor.cpp @@ -1,6 +1,8 @@ #include "Extractor.h" #include "../ConstantDefinitions.h" +#include "../Filenames.h" #include +#include using VmiCore::addr_t; using VmiCore::IInterruptEvent; @@ -8,8 +10,13 @@ using VmiCore::IIntrospectionAPI; namespace ApiTracing { - Extractor::Extractor(std::shared_ptr introspectionApi, uint8_t addressWidth) - : addressWidth(addressWidth), introspectionAPI(std::move(introspectionApi)) + Extractor::Extractor(std::shared_ptr introspectionApi, + VmiCore::Plugin::PluginInterface* pluginInterface, + uint8_t addressWidth) + : addressWidth(addressWidth), + introspectionAPI(std::move(introspectionApi)), + pluginInterface(pluginInterface), + logger(this->pluginInterface->newNamedLogger(APITRACING_LOGGER_NAME)) { } @@ -74,23 +81,41 @@ namespace ApiTracing for (const auto& parameterInfo : *parameterInformation) { - if (!parameterInfo.basicType.empty()) + if (parameterInfo.basicType.empty()) { - extractedParameterInformation.emplace_back( - extractSingleParameter(shallowParameters.at(parameterIndex), cr3, parameterInfo)); + throw std::runtime_error("Malformed parameter information ! Aborting"); } - else if (!parameterInfo.backingParameters.empty()) + + if (!parameterInfo.backingParameters.empty()) { - addr_t structVA = dereferencePointer(shallowParameters.at(parameterIndex), cr3); + auto pointerAddress = shallowParameters.at(parameterIndex); + // skip optional parameters (e.g. AllocationSize in NtCreateFile) + if (pointerAddress == 0) + { + extractedParameterInformation.emplace_back( + extractSingleParameter(shallowParameters.at(parameterIndex), cr3, parameterInfo)); + parameterIndex++; + continue; + } + ExtractedParameterInformation paramStruct{ .name = parameterInfo.name, .data = {}, .backingParameters = {}}; - paramStruct.backingParameters = - extractBackingParameters({parameterInfo.backingParameters}, structVA, cr3); + try + { + paramStruct.backingParameters = extractBackingParameters( + {parameterInfo.backingParameters}, shallowParameters.at(parameterIndex), cr3); + } + catch (const std::exception& e) + { + logger->debug("Could not deep extract parameter", + {{"name", parameterInfo.name}, {"exception", e.what()}}); + } extractedParameterInformation.emplace_back(paramStruct); } else { - throw std::runtime_error("Malformed parameter information ! Aborting"); + extractedParameterInformation.emplace_back( + extractSingleParameter(shallowParameters.at(parameterIndex), cr3, parameterInfo)); } parameterIndex++; } @@ -103,8 +128,17 @@ namespace ApiTracing { ExtractedParameterInformation result{}; result.name = parameterInfo.name; - - switch (basicTypeStringToEnum.at(parameterInfo.basicType)) + BasicTypes parameterType; + try + { + parameterType = basicTypeStringToEnum.at(parameterInfo.basicType); + } + catch (...) + { + std::throw_with_nested( + std::invalid_argument(fmt::format("Parameter not defined: {}", parameterInfo.basicType))); + } + switch (parameterType) { using enum BasicTypes; @@ -123,11 +157,19 @@ namespace ApiTracing case UNICODE_WSTR_32: case UNICODE_WSTR_64: { - result.data = extractUnicodeString(shallowParameter, cr3); + try + { + result.data = extractUnicodeString(shallowParameter, cr3); + } + catch (std::exception& e) + { + result.data = ""; + } break; } case __PTR32: case __PTR64: + case UNSIGNED___INT32: case UNSIGNED___INT64: case UNSIGNED_LONG: case UNSIGNED_INT: @@ -145,7 +187,7 @@ namespace ApiTracing } default: { - break; + throw std::invalid_argument(fmt::format("Parameter not defined: {}", parameterInfo.basicType)); } } return result; @@ -213,8 +255,16 @@ namespace ApiTracing // to the nexţ iteration addr_t structPointer = dereferencePointer(address + parameter.offset, cr3); ExtractedParameterInformation extraction{.name = parameter.name, .data = {}, .backingParameters = {}}; - extraction.backingParameters = - extractBackingParameters(parameter.backingParameters, structPointer, cr3); + try + { + extraction.backingParameters = + extractBackingParameters(parameter.backingParameters, structPointer, cr3); + } + catch (const std::exception& e) + { + logger->debug("Could not extract backing parameter", + {{"name", parameter.name}, {"exception", e.what()}}); + } extractedBackingParameters.emplace_back(extraction); } } diff --git a/plugins/apitracing/src/lib/os/Extractor.h b/plugins/apitracing/src/lib/os/Extractor.h index d8e337cb..22a99318 100644 --- a/plugins/apitracing/src/lib/os/Extractor.h +++ b/plugins/apitracing/src/lib/os/Extractor.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ namespace ApiTracing INT, LONG, __INT64, + UNSIGNED___INT32, UNSIGNED___INT64, UNSIGNED_LONG, UNSIGNED_INT, @@ -86,7 +88,9 @@ namespace ApiTracing class Extractor : public IExtractor { public: - Extractor(std::shared_ptr introspectionApi, uint8_t addressWidth); + Extractor(std::shared_ptr introspectionApi, + VmiCore::Plugin::PluginInterface* pluginInterface, + uint8_t addressWidth); [[nodiscard]] std::vector extractParameters(VmiCore::IInterruptEvent& event, @@ -105,6 +109,8 @@ namespace ApiTracing uint8_t addressWidth; std::variant extractedParameters; std::shared_ptr introspectionAPI; + VmiCore::Plugin::PluginInterface* pluginInterface; + std::unique_ptr logger; std::map> basicTypeStringToEnum{ {"LPSTR_32", BasicTypes::LPSTR_32}, {"LPSTR_64", BasicTypes::LPSTR_64}, @@ -117,6 +123,7 @@ namespace ApiTracing {"int", BasicTypes::INT}, {"long", BasicTypes::LONG}, {"__int64", BasicTypes::__INT64}, + {"unsigned __int32", BasicTypes::UNSIGNED___INT32}, {"unsigned __int64", BasicTypes::UNSIGNED___INT64}, {"unsigned long", BasicTypes::UNSIGNED_LONG}, {"unsigned int", BasicTypes::UNSIGNED_INT}, @@ -135,6 +142,8 @@ namespace ApiTracing [[nodiscard]] std::vector extractBackingParameters( const std::vector& backingParameters, uint64_t address, uint64_t cr3); + [[nodiscard]] size_t getMissingAlignment(uint64_t currentAddress, uint64_t nextParameterSize) const; + [[nodiscard]] uint64_t readParameter(uint64_t stackPointerVA, uint8_t parameterSize, uint64_t cr3) const; [[nodiscard]] uint64_t zeroGarbageBytes(uint64_t parameter, uint8_t parameterSize) const; diff --git a/plugins/apitracing/test/Extractor_Unittest.cpp b/plugins/apitracing/test/Extractor_Unittest.cpp index b60d31f1..ee7f451b 100644 --- a/plugins/apitracing/test/Extractor_Unittest.cpp +++ b/plugins/apitracing/test/Extractor_Unittest.cpp @@ -3,13 +3,18 @@ #include "TestConstantDefinitions.h" #include #include +#include +#include #include #include +using testing::_; // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) using testing::ContainerEq; +using testing::NiceMock; using testing::Return; using VmiCore::MockInterruptEvent; using VmiCore::MockIntrospectionAPI; +using VmiCore::Plugin::MockPluginInterface; namespace ApiTracing { @@ -61,7 +66,6 @@ namespace ApiTracing // clang-format off // @formatter:off const auto testParams32 = std::vector{ - {.parameterInformation{.name = "param1", .size = TestConstantDefinitions::fourBytes, .backingParameters{}}, .expectedValue = param1Value}, {.parameterInformation{.name = "param2", .size = TestConstantDefinitions::fourBytes, .backingParameters{}}, @@ -73,42 +77,57 @@ namespace ApiTracing {.parameterInformation{.name = "param5", .size = TestConstantDefinitions::fourBytes, .backingParameters{}}, .expectedValue = param5Value}, }; + + const auto testParamUnknownType = std::vector{ + {.parameterInformation{.basicType = "__unknown int96", .name = "UnknownType", .size = TestConstantDefinitions::fourBytes, .backingParameters{}}, + .expectedValue = param1Value}, + +}; // @formatter:on // clang-format on - const auto objectAttributesBackingParametersLevelTwo = - std::vector{{.parameterInformation{.basicType = "unsigned long", - .name = "ObjectAttributesTwoContentOne", - .size = TestConstantDefinitions::fourBytes, - .offset = ObjectAttributesTwoContentOneOffset, - .backingParameters{}}, - .expectedValue = ObjectAttributesTwoContentOneValue}, - {.parameterInformation{.basicType = "LPSTR_64", - .name = "ObjectAttributesTwoContentTwo", - .size = TestConstantDefinitions::eightBytes, - .offset = ObjectAttributesTwoContentTwoOffset, - .backingParameters{}}, - .expectedValue = ObjectAttributesTwoContentTwoValue}}; + const auto objectAttributesBackingParametersLevelTwo = std::vector{ + // resides on ObjectAttributesTwoValue, value is ObjectAttributesTwoContentOneValue + {.parameterInformation{.basicType = "unsigned long", + .name = "ObjectAttributesTwoContentOne", + .size = TestConstantDefinitions::fourBytes, + .offset = ObjectAttributesTwoContentOneOffset, + .backingParameters{}}, + .expectedValue = ObjectAttributesTwoContentOneValue}, + // resides on ObjectAttributesTwoValue + ObjectAttributesTwoContentTwoOffset, + // value is ObjectAttributesTwoContentTwoValue which is the address to a string + {.parameterInformation{.basicType = "LPSTR_64", + .name = "ObjectAttributesTwoContentTwo", + .size = TestConstantDefinitions::eightBytes, + .offset = ObjectAttributesTwoContentTwoOffset, + .backingParameters{}}, + .expectedValue = ObjectAttributesTwoContentTwoValue}}; const auto objectAttributesBackingParametersLevelOne = std::vector{ + // resides on param2Value, value is ObjectAttributesTwoValue {.parameterInformation{ + .basicType = "LPSTR_64", .name = "ObjectAttributesTwo", .size = TestConstantDefinitions::eightBytes, .backingParameters{{objectAttributesBackingParametersLevelTwo.at(0).parameterInformation}, {objectAttributesBackingParametersLevelTwo.at(1).parameterInformation}}}, .expectedValue = ObjectAttributesTwoValue}}; + // Two function parameters in rcx and rdx const auto testNestedStruct = std::vector{ + // resides in rcx, value is param1Value {.parameterInformation{.basicType = "unsigned __int64", .name = "FileHandle", .size = TestConstantDefinitions::eightBytes, .backingParameters{}}, .expectedValue = param1Value}, + // resides in rdx, value is param2Value {.parameterInformation{ + .basicType = "LPSTR_64", .name = "ObjectAttributesOne", .size = TestConstantDefinitions::eightBytes, .backingParameters{{objectAttributesBackingParametersLevelOne.at(0).parameterInformation}}}, - .expectedValue = ObjectAttributesOneValue}}; + .expectedValue = param2Value}}; } class ExtractorFixture : public testing::Test @@ -116,10 +135,14 @@ namespace ApiTracing protected: std::shared_ptr introspectionAPI = std::make_shared(); std::shared_ptr interruptEvent = std::make_shared(); + std::unique_ptr pluginInterface = std::make_unique>(); std::shared_ptr> paramInformation; void SetUp() override { + ON_CALL(*pluginInterface, newNamedLogger(_)) + .WillByDefault([]() { return std::make_unique>(); }); + ON_CALL(*interruptEvent, getCr3()).WillByDefault(Return(testDtb)); ON_CALL(*interruptEvent, getRcx).WillByDefault(Return(testParams64[0].expectedValue)); ON_CALL(*interruptEvent, getRdx).WillByDefault(Return(testParams64[1].expectedValue)); @@ -139,7 +162,7 @@ namespace ApiTracing void SetupNestedStructPointerReads() { - ON_CALL(*introspectionAPI, read64VA(param2Value, testDtb)).WillByDefault(Return(ObjectAttributesOneValue)); + ON_CALL(*introspectionAPI, read64VA(param2Value, testDtb)).WillByDefault(Return(ObjectAttributesTwoValue)); ON_CALL(*introspectionAPI, read64VA(ObjectAttributesOneValue, testDtb)) .WillByDefault(Return(ObjectAttributesTwoValue)); @@ -230,7 +253,8 @@ namespace ApiTracing TEST_F(ExtractorFixture, getShallowExtractedParams_64Bit0ParametersFunction_CorrectParametersExtracted) { - auto extractor = std::make_shared(introspectionAPI, ConstantDefinitions::x64AddressWidth); + auto extractor = + std::make_shared(introspectionAPI, pluginInterface.get(), ConstantDefinitions::x64AddressWidth); auto expectedExtractedParameters = SetupParametersAndStack({}, ConstantDefinitions::x64AddressWidth); auto extractedParameters = extractor->getShallowExtractedParams(*interruptEvent, paramInformation); @@ -240,7 +264,8 @@ namespace ApiTracing TEST_F(ExtractorFixture, getShallowExtractedParams_32Bit0ParametersFunction_CorrectParametersExtracted) { - auto extractor = std::make_shared(introspectionAPI, ConstantDefinitions::x86AddressWidth); + auto extractor = + std::make_shared(introspectionAPI, pluginInterface.get(), ConstantDefinitions::x86AddressWidth); auto expectedExtractedParameters = SetupParametersAndStack({}, ConstantDefinitions::x86AddressWidth); auto extractedParameters = extractor->getShallowExtractedParams(*interruptEvent, paramInformation); @@ -250,7 +275,8 @@ namespace ApiTracing TEST_F(ExtractorFixture, getShallowExtractedParams_32Bit4ParametersFunction_CorrectParametersExtracted) { - auto extractor = std::make_shared(introspectionAPI, ConstantDefinitions::x86AddressWidth); + auto extractor = + std::make_shared(introspectionAPI, pluginInterface.get(), ConstantDefinitions::x86AddressWidth); auto expectedExtractedParameters = SetupParametersAndStack(testParams32, ConstantDefinitions::x86AddressWidth); auto extractedParameters = extractor->getShallowExtractedParams(*interruptEvent, paramInformation); @@ -260,7 +286,8 @@ namespace ApiTracing TEST_F(ExtractorFixture, getShallowExtractedParams_64Bit6ParametersFunction_CorrectParametersExtracted) { - auto extractor = std::make_shared(introspectionAPI, ConstantDefinitions::x64AddressWidth); + auto extractor = + std::make_shared(introspectionAPI, pluginInterface.get(), ConstantDefinitions::x64AddressWidth); auto expectedExtractedParameters = SetupParametersAndStack(testParams64, ConstantDefinitions::x64AddressWidth); auto extractedParameters = extractor->getShallowExtractedParams(*interruptEvent, paramInformation); @@ -270,7 +297,8 @@ namespace ApiTracing TEST_F(ExtractorFixture, extractParameters_64Bit6ParametersFunction_CorrectParameters) { - auto extractor = std::make_shared(introspectionAPI, ConstantDefinitions::x64AddressWidth); + auto extractor = + std::make_shared(introspectionAPI, pluginInterface.get(), ConstantDefinitions::x64AddressWidth); auto expectedExtractedParameters = SetupExpectedNestedParameters(); auto relevantParameters = std::vector(testNestedStruct); SetupParameterInformation(relevantParameters); @@ -282,4 +310,17 @@ namespace ApiTracing ASSERT_EQ(actualParameters.size(), expectedExtractedParameters.size()); EXPECT_THAT(expectedExtractedParameters, ContainerEq(actualParameters)); } + + TEST_F(ExtractorFixture, extractParameters_UnknownParameterType_NestedExceptionInvalidArgument) + { + auto extractor = + std::make_shared(introspectionAPI, pluginInterface.get(), ConstantDefinitions::x64AddressWidth); + SetupParameterInformation(std::vector(testParamUnknownType)); + + auto shallowParameters = extractor->getShallowExtractedParams(*interruptEvent, paramInformation); + + EXPECT_THROW(auto extraction = + extractor->getDeepExtractParameters(shallowParameters, paramInformation, testDtb), + std::invalid_argument); + } }