diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bc8aa44..0751dc32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # ChangeLog +## [10.1.5] - 2023-01-17 +### Added +- Print configured archive and command timeouts in parameters summary +- Outcome: add output file size in command_set.command.output.size + +### Changed +- Improve location exclusion to support path such as 'c:\vss.dd' +- Location exclusion option does not support anymore ',' as separator + +### Fixed +- Ntfs: fix parsing for very fragmented volumes +- Authenticode: fix possible incorrect AuthenticodeStatus (thanks Roger) +- Ntfs: add check to avoid infinite loop for corrupted MFT +- Ntfs: fix error handling for nested records possiblity leading to an handled exception +- NtfsInfo: improve performances by reducing IO + + ## [10.1.4] - 2022-10-24 ### Added - ToolEmbed: add configurations checks for compressed xml settings @@ -14,6 +31,7 @@ - Volume Shadow Copy: workaround for MS behavior https://github.com/DFIR-ORC/readshadow (expect slower performances with VSS) - Outcome: fix missing command name when job was interrupted + ## [10.1.3] - 2022-09-26 ### Added - Outline, Outcome: add execution id @@ -31,6 +49,7 @@ - Bits: upload when on 'single' mode (only 'overwrite' was working) - Guid: wide string conversion of Guid + ## [10.1.2] - 2022-07-20 ### Added - ToolEmbed: add multiple checks to detect bad configurations before deployment @@ -42,6 +61,7 @@ - FastFind/GetThis: fix missing error handling (ntfs_find, Yara...) - Terminate any child processes on WolfLauncher unexpected exit using jobs + ## [10.1.1] - 2022-06-20 ### Added - Toolembed: display a message for missing/broken resource because of bad configuration diff --git a/cmake/Orc.cmake b/cmake/Orc.cmake index fcb9c8c1..dc25762e 100644 --- a/cmake/Orc.cmake +++ b/cmake/Orc.cmake @@ -165,4 +165,7 @@ foreach(OPTION IN ITEMS ${LINK_OPTIONS_RELEASE}) add_link_options($<$:${OPTION}>) endforeach() +set(OPTION "/INCREMENTAL:NO") +add_link_options($<$:${OPTION}>) + endmacro() diff --git a/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Run.cpp b/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Run.cpp index ae480c6c..938f537f 100644 --- a/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Run.cpp +++ b/src/OrcCommand/Command/NTFSInfo/NTFSInfo_Run.cpp @@ -109,6 +109,8 @@ HRESULT Main::RunThroughUSNJournal() } }; + bool hasSomeFailure = false; + for (const auto& loc : locations) { if (loc->GetType() != Location::Type::MountedVolume) @@ -142,14 +144,16 @@ HRESULT Main::RunThroughUSNJournal() } else { - Log::Warn(L"Failed to init walk for '{}' [{}]", loc->GetLocation(), SystemError(hr)); + Log::Critical(L"Failed to init walk for '{}' [{}]", loc->GetLocation(), SystemError(hr)); + hasSomeFailure = true; } } else { if (FAILED(hr = walk.EnumJournal(callbacks))) { - Log::Error(L"Failed to walk volume '{}' [{}]", loc->GetLocation(), SystemError(hr)); + Log::Critical(L"Failed to walk volume '{}' [{}]", loc->GetLocation(), SystemError(hr)); + hasSomeFailure = true; } else { @@ -164,6 +168,11 @@ HRESULT Main::RunThroughUSNJournal() } } + if (hasSomeFailure) + { + return E_FAIL; + } + return S_OK; } @@ -675,6 +684,8 @@ HRESULT Main::RunThroughMFT() auto timelineIterator = begin(m_TimeLineOutput.Outputs()); auto secdescrIterator = begin(m_SecDescrOutput.Outputs()); + bool hasSomeFailure = false; + for (auto& loc : locations) { BOOST_SCOPE_EXIT( @@ -784,7 +795,8 @@ HRESULT Main::RunThroughMFT() } else { - Log::Error(L"Failed to init walk for '{}' [{}]", loc->GetLocation(), SystemError(hr)); + hasSomeFailure = true; + Log::Critical(L"Failed to init walk for '{}' [{}]", loc->GetLocation(), SystemError(hr)); } } else @@ -792,7 +804,8 @@ HRESULT Main::RunThroughMFT() m_FullNameBuilder = walker.GetFullNameBuilder(); if (FAILED(hr = walker.Walk(callBacks))) { - Log::Error(L"Failed to walk volume '{}' [{}]", loc->GetLocation(), SystemError(hr)); + hasSomeFailure = true; + Log::Critical(L"Failed to walk volume '{}' [{}]", loc->GetLocation(), SystemError(hr)); } else { @@ -802,6 +815,11 @@ HRESULT Main::RunThroughMFT() } } + if (hasSomeFailure) + { + return E_FAIL; + } + return S_OK; } diff --git a/src/OrcCommand/Command/USNInfo/USNInfo_Run.cpp b/src/OrcCommand/Command/USNInfo/USNInfo_Run.cpp index 17ce2773..09bb9b4b 100644 --- a/src/OrcCommand/Command/USNInfo/USNInfo_Run.cpp +++ b/src/OrcCommand/Command/USNInfo/USNInfo_Run.cpp @@ -196,7 +196,7 @@ HRESULT Main::Run() return S_OK; } - Log::Error( + Log::Critical( L"Failed to init walk for volume '{}' [{}]", dir.first.m_pLoc->GetLocation(), SystemError(hr)); return hr; } diff --git a/src/OrcCommand/Command/WolfLauncher/Outcome.cpp b/src/OrcCommand/Command/WolfLauncher/Outcome.cpp index 169ea7ed..e0a31960 100644 --- a/src/OrcCommand/Command/WolfLauncher/Outcome.cpp +++ b/src/OrcCommand/Command/WolfLauncher/Outcome.cpp @@ -55,6 +55,12 @@ void Write( writer->WriteNamed(L"name", item.GetName()); writer->WriteNamed(L"type", ToString(item.GetType())); + + const auto& size = item.GetSize(); + if (size.has_value()) + { + writer->WriteNamed(L"size", *size); + } } }; diff --git a/src/OrcCommand/Command/WolfLauncher/Outcome.h b/src/OrcCommand/Command/WolfLauncher/Outcome.h index a2e19d71..368e310d 100644 --- a/src/OrcCommand/Command/WolfLauncher/Outcome.h +++ b/src/OrcCommand/Command/WolfLauncher/Outcome.h @@ -77,6 +77,8 @@ class Archive class Command { public: + using FileSize = Traits::ByteQuantity; + class Origin { public: @@ -188,9 +190,14 @@ class Command Type GetType() const { return m_type; } void SetType(Type type) { m_type = type; } + const std::optional& GetSize() const { return m_size; } + void SetSize(const uint64_t size) { m_size = size; } + void SetSize(const std::optional& size) { m_size = size; } + private: std::wstring m_name; Type m_type; + std::optional m_size; }; const std::vector& GetOutput() const { return m_output; } @@ -263,6 +270,28 @@ class CommandSet return it->second; } + Command* GetCommandByOutputFileName(const std::wstring& name) + { + auto commandIt = std::find_if(std::begin(m_commands), std::end(m_commands), [&name](const auto& item) { + const auto& command = item.second; + const auto& output = command.GetOutput(); + + auto outputIt = + std::find_if(std::cbegin(output), std::cend(output), [&name](const Command::Output& output) { + return name == output.GetName(); + }); + + return outputIt != std::cend(output); + }); + + if (commandIt == std::cend(m_commands)) + { + return nullptr; + } + + return &commandIt->second; + } + Archive& GetArchive() { return m_archive; } const Archive& GetArchive() const { return m_archive; } diff --git a/src/OrcCommand/Command/WolfLauncher/WolfExecution_Config.cpp b/src/OrcCommand/Command/WolfLauncher/WolfExecution_Config.cpp index ff9ab5e5..6ac9648a 100644 --- a/src/OrcCommand/Command/WolfLauncher/WolfExecution_Config.cpp +++ b/src/OrcCommand/Command/WolfLauncher/WolfExecution_Config.cpp @@ -27,6 +27,7 @@ #include #include +#include "Utils/WinApi.h" using namespace std; @@ -120,20 +121,23 @@ WolfExecution::GetExecutableToRun(const ConfigItem& item, wstring& strExeToRun, } else { - wstring strExeFile; - hr = ExpandFilePath(strExeRef.c_str(), strExeFile); - if (FAILED(hr)) + std::error_code ec; + std::wstring strExeFile = ExpandEnvironmentStringsApi(strExeRef.c_str(), ec); + if (ec) { - Log::Error(L"Executable file '{}' does not exist or is not a file [{}]", strExeRef, SystemError(hr)); - return E_FAIL; + Log::Error("Failed to expand environment variables in uri [{}]", ec); + return ToHRESULT(ec); } - hr = VerifyFileIsBinary(strExeFile.c_str()); - if (FAILED(hr)) + if (std::filesystem::exists(strExeFile, ec)) { + hr = VerifyFileIsBinary(strExeFile.c_str()); + if (FAILED(hr)) + { - Log::Error(L"Executable file '{}' is not a compatible binary [{}]", strExeFile, SystemError(hr)); - return E_FAIL; + Log::Error(L"Executable file '{}' is not a compatible binary [{}]", strExeFile, SystemError(hr)); + return E_FAIL; + } } strExeToRun = strExeFile; diff --git a/src/OrcCommand/Command/WolfLauncher/WolfExecution_Execute.cpp b/src/OrcCommand/Command/WolfLauncher/WolfExecution_Execute.cpp index 006bd468..1107b8d3 100644 --- a/src/OrcCommand/Command/WolfLauncher/WolfExecution_Execute.cpp +++ b/src/OrcCommand/Command/WolfLauncher/WolfExecution_Execute.cpp @@ -237,7 +237,8 @@ void WolfExecution::ArchiveNotificationHandler(const ArchiveNotification::Notifi } auto&& lock = m_outcome.Lock(); - auto& outcomeArchive = m_outcome.GetCommandSet(m_commandSet).GetArchive(); + auto& commandSet = m_outcome.GetCommandSet(m_commandSet); + auto& outcomeArchive = commandSet.GetArchive(); switch (notification->GetType()) { @@ -251,10 +252,26 @@ void WolfExecution::ArchiveNotificationHandler(const ArchiveNotification::Notifi m_journal.Print( m_commandSet, operation, L"Add file: {} ({})", notification->Keyword(), notification->FileSize()); + const auto& fileName = notification->Keyword(); + const auto& fileSize = notification->FileSize(); + Outcome::Archive::Item item; - item.SetName(notification->Keyword()); - item.SetSize(notification->FileSize()); + item.SetName(fileName); + item.SetSize(fileSize); outcomeArchive.Add(item); + + // BEWARE: this should not be set in archive notifications but there is no other places + auto command = commandSet.GetCommandByOutputFileName(fileName); + if (command) + { + for (auto& item : command->GetOutput()) + { + if (item.GetName() == fileName) + { + item.SetSize(fileSize); + } + } + } break; } case ArchiveNotification::DirectoryAddition: { diff --git a/src/OrcCommand/Command/WolfLauncher/WolfLauncher_Output.cpp b/src/OrcCommand/Command/WolfLauncher/WolfLauncher_Output.cpp index c003f320..15bf530e 100644 --- a/src/OrcCommand/Command/WolfLauncher/WolfLauncher_Output.cpp +++ b/src/OrcCommand/Command/WolfLauncher/WolfLauncher_Output.cpp @@ -191,6 +191,9 @@ void Main::PrintParameters() PrintValue(node, L"Explicit key selection", keySelection.empty() ? Text::kNoneW : keySelection); PrintValues(node, L"Enable keys", config.EnableKeywords); PrintValues(node, L"Disable keys", config.DisableKeywords); + PrintValue( + node, L"Command timeout", std::chrono::duration_cast(config.msCommandTerminationTimeOut)); + PrintValue(node, L"Archive timeout", std::chrono::duration_cast(config.msArchiveTimeOut)); const auto kNoLimits = L"No limits"; if (config.NoLimitsKeywords.empty()) diff --git a/src/OrcCommand/UtilitiesMain.cpp b/src/OrcCommand/UtilitiesMain.cpp index 78428bce..af3cafd4 100644 --- a/src/OrcCommand/UtilitiesMain.cpp +++ b/src/OrcCommand/UtilitiesMain.cpp @@ -813,7 +813,7 @@ void UtilitiesMain::ParseLocationExcludes( } std::vector splits; - boost::split(splits, rawExcludes, boost::is_any_of(L",;|")); + boost::split(splits, rawExcludes, boost::is_any_of(L";|")); for (auto& exclude : splits) { diff --git a/src/OrcCommand/UtilitiesMain.h b/src/OrcCommand/UtilitiesMain.h index f8848f5d..fc79e547 100644 --- a/src/OrcCommand/UtilitiesMain.h +++ b/src/OrcCommand/UtilitiesMain.h @@ -844,7 +844,7 @@ class UtilitiesMain Log::Critical("Exception during execution. Type: {}, Reason: {}", typeid(e).name(), e.what()); #ifdef ORC_BUILD_BOOST_STACKTRACE - boost::stacktrace::stacktrace(); + std::cerr << boost::stacktrace::stacktrace(); #endif return E_ABORT; } @@ -854,7 +854,7 @@ class UtilitiesMain Log::Critical("Exception during during command execution."); #ifdef ORC_BUILD_BOOST_STACKTRACE - boost::stacktrace::stacktrace(); + std::cerr << boost::stacktrace::stacktrace(); #endif return E_ABORT; } diff --git a/src/OrcLib/Authenticode.cpp b/src/OrcLib/Authenticode.cpp index 107e9c25..eabefac5 100644 --- a/src/OrcLib/Authenticode.cpp +++ b/src/OrcLib/Authenticode.cpp @@ -377,7 +377,6 @@ Authenticode::VerifySignatureWithCatalogs( WintrustCatalogStructure.pbCalculatedFileHash = hash.GetData(); WintrustCatalogStructure.pcwszMemberTag = MemberTag.c_str(); WintrustCatalogStructure.pcwszMemberFilePath = nullptr; - WintrustCatalogStructure.hMemberFile = 0L; // If we get here, we have catalog info! Verify it. WINTRUST_DATA WintrustStructure; @@ -387,23 +386,14 @@ Authenticode::VerifySignatureWithCatalogs( WintrustStructure.fdwRevocationChecks = WTD_REVOKE_NONE; WintrustStructure.dwUnionChoice = WTD_CHOICE_CATALOG; WintrustStructure.pCatalog = &WintrustCatalogStructure; - WintrustStructure.dwStateAction = WTD_STATEACTION_VERIFY; + WintrustStructure.dwStateAction = WTD_STATEACTION_IGNORE; // Ignore hWVTStateData WintrustStructure.dwProvFlags = WTD_REVOCATION_CHECK_NONE; WintrustStructure.dwUIContext = 0L; - std::wstring strCatalog(InfoStruct.wszCatalogFile); - auto iter = m_StateMap.find(strCatalog); - if (iter != m_StateMap.end()) - WintrustStructure.hWVTStateData = iter->second; - else - WintrustStructure.hWVTStateData = NULL; - // WinVerifyTrust verifies signatures as specified by the GUID // and Wintrust_Data. LONG lStatus = m_wintrust.WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WVTPolicyGUID, &WintrustStructure); - m_StateMap[std::move(strCatalog)] = WintrustStructure.hWVTStateData; - if (FAILED(ExtractCatalogSigners(InfoStruct.wszCatalogFile, data.Signers, data.SignersCAs, data.CertStores))) { Log::Debug(L"Failed to extract signer information from catalog '{}'", InfoStruct.wszCatalogFile); @@ -694,7 +684,7 @@ HRESULT Orc::Authenticode::ExtractSignatureSize(const CBinaryBuffer& signature, NULL)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error(L"Failed CryptQueryObject [{}]", SystemError(hr)); + Log::Debug("Failed CryptQueryObject [{}]", SystemError(hr)); return hr; } BOOST_SCOPE_EXIT((&hMsg)) { CryptMsgClose(hMsg); } @@ -707,7 +697,7 @@ HRESULT Orc::Authenticode::ExtractSignatureSize(const CBinaryBuffer& signature, if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, &dwBytes)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error(L"Failed to query authenticode content info [{}]", SystemError(hr)); + Log::Debug(L"Failed to query authenticode content info [{}]", SystemError(hr)); return hr; } cbSize = dwBytes; @@ -742,7 +732,7 @@ HRESULT Authenticode::ExtractSignatureHash(const CBinaryBuffer& signature, Authe NULL)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed CryptQueryObject [{}]", SystemError(hr)); + Log::Debug("Failed CryptQueryObject [{}]", SystemError(hr)); return hr; } BOOST_SCOPE_EXIT((&hMsg)) { CryptMsgClose(hMsg); } @@ -755,7 +745,7 @@ HRESULT Authenticode::ExtractSignatureHash(const CBinaryBuffer& signature, Authe if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, &dwBytes)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed to query authenticode content info [{}]", SystemError(hr)); + Log::Debug("Failed to query authenticode content info [{}]", SystemError(hr)); return hr; } CBinaryBuffer msgContent; @@ -764,7 +754,7 @@ HRESULT Authenticode::ExtractSignatureHash(const CBinaryBuffer& signature, Authe if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, msgContent.GetData(), &dwBytes)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed to get authenticode content info [{}]", SystemError(hr)); + Log::Debug("Failed to get authenticode content info [{}]", SystemError(hr)); return hr; } @@ -772,7 +762,7 @@ HRESULT Authenticode::ExtractSignatureHash(const CBinaryBuffer& signature, Authe if (!CryptMsgGetParam(hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, NULL, &dwBytes)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed to query authenticode content type info [{}]", SystemError(hr)); + Log::Debug("Failed to query authenticode content type info [{}]", SystemError(hr)); return hr; } @@ -787,7 +777,7 @@ HRESULT Authenticode::ExtractSignatureHash(const CBinaryBuffer& signature, Authe &dwBytes)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed to decode authenticode SPC_INDIRECT_DATA info length [{}]", SystemError(hr)); + Log::Debug("Failed to decode authenticode SPC_INDIRECT_DATA info length [{}]", SystemError(hr)); return hr; } @@ -805,7 +795,7 @@ HRESULT Authenticode::ExtractSignatureHash(const CBinaryBuffer& signature, Authe &dwBytes)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed to decode authenticode SPC_INDIRECT_DATA info [{}]", SystemError(hr)); + Log::Debug("Failed to decode authenticode SPC_INDIRECT_DATA info [{}]", SystemError(hr)); return hr; } PSPC_INDIRECT_DATA_CONTENT pIndirectData = (PSPC_INDIRECT_DATA_CONTENT)decodedIndirectData.GetData(); @@ -853,7 +843,7 @@ HRESULT Authenticode::ExtractSignatureTimeStamp(const CBinaryBuffer& signature, NULL)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed CryptQueryObject [{}]", SystemError(hr)); + Log::Debug("Failed CryptQueryObject [{}]", SystemError(hr)); return hr; } BOOST_SCOPE_EXIT((&hMsg)) { CryptMsgClose(hMsg); } @@ -866,7 +856,7 @@ HRESULT Authenticode::ExtractSignatureTimeStamp(const CBinaryBuffer& signature, if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwBytes)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed to query authenticode content info [{}]", SystemError(hr)); + Log::Debug("Failed to query authenticode content info [{}]", SystemError(hr)); return hr; } CBinaryBuffer msgContent; @@ -875,7 +865,7 @@ HRESULT Authenticode::ExtractSignatureTimeStamp(const CBinaryBuffer& signature, if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, msgContent.GetData(), &dwBytes)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed to get authenticode content info [{}]", SystemError(hr)); + Log::Debug("Failed to get authenticode content info [{}]", SystemError(hr)); return hr; } @@ -943,7 +933,7 @@ HRESULT Authenticode::ExtractSignatureTimeStamp(const CBinaryBuffer& signature, &dwData)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed CryptDecodeObject [{}]", SystemError(hr)); + Log::Debug("Failed CryptDecodeObject [{}]", SystemError(hr)); break; } break; // Break from for loop. @@ -1088,7 +1078,7 @@ HRESULT Authenticode::ExtractSignatureSigners( NULL)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed CryptDecodeObject [{}]", SystemError(hr)); + Log::Debug("Failed CryptDecodeObject [{}]", SystemError(hr)); return hr; } @@ -1113,7 +1103,7 @@ HRESULT Authenticode::ExtractSignatureSigners( if (GetLastError() != CRYPT_E_INVALID_INDEX) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed to extract signer information from blob [{}]", SystemError(hr)); + Log::Debug("Failed to extract signer information from blob [{}]", SystemError(hr)); return hr; } @@ -1143,7 +1133,7 @@ HRESULT Authenticode::ExtractSignatureSigners( &pChain)) { hr = HRESULT_FROM_WIN32(GetLastError()); - Log::Error("Failed to obtain certificate chain [{}]", SystemError(hr)); + Log::Debug("Failed to obtain certificate chain [{}]", SystemError(hr)); break; } BOOST_SCOPE_EXIT(pChain) { CertFreeCertificateChain(pChain); } @@ -1399,7 +1389,7 @@ HRESULT Orc::Authenticode::SignatureSize(LPCWSTR szFileName, const CBinaryBuffer DWORD dwSignatureSize = 0L; if (FAILED(hr = ExtractSignatureSize(signature, dwSignatureSize))) { - spdlog::error( + Log::Error( L"Failed to extract hash from signature in the security directory of '{}' [{}]", szFileName, SystemError(hr)); @@ -1419,37 +1409,8 @@ HRESULT Orc::Authenticode::SignatureSize(LPCWSTR szFileName, const CBinaryBuffer return S_OK; } -HRESULT Authenticode::CloseCatalogState() -{ - WINTRUST_DATA sWTD; - WINTRUST_CATALOG_INFO sWTCI; - - memset(&sWTD, 0x00, sizeof(WINTRUST_DATA)); - sWTD.cbStruct = sizeof(WINTRUST_DATA); - sWTD.dwUIChoice = WTD_UI_NONE; - sWTD.dwUnionChoice = WTD_CHOICE_CATALOG; - sWTD.pCatalog = &sWTCI; - sWTD.dwStateAction = WTD_STATEACTION_CLOSE; - - memset(&sWTCI, 0x00, sizeof(WINTRUST_CATALOG_INFO)); - sWTCI.cbStruct = sizeof(WINTRUST_CATALOG_INFO); - - std::for_each( - m_StateMap.begin(), m_StateMap.end(), [&sWTD, this](const std::pair pair) { - if (pair.second) - { - sWTD.hWVTStateData = pair.second; - - m_wintrust.WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &WVTPolicyGUID, &sWTD); - } - }); - m_StateMap.clear(); - return S_OK; -} - Authenticode::~Authenticode() { - CloseCatalogState(); if (m_hContext != INVALID_HANDLE_VALUE) m_wintrust.CryptCATAdminReleaseContext(m_hContext, 0); if (m_hMachineStore != INVALID_HANDLE_VALUE) diff --git a/src/OrcLib/Authenticode.h b/src/OrcLib/Authenticode.h index 107cc281..22ac3ff0 100644 --- a/src/OrcLib/Authenticode.h +++ b/src/OrcLib/Authenticode.h @@ -99,7 +99,6 @@ class Authenticode private: HCERTSTORE m_hMachineStore = INVALID_HANDLE_VALUE; HANDLE m_hContext = INVALID_HANDLE_VALUE; - std::map m_StateMap; WinTrustExtension m_wintrust; @@ -162,8 +161,6 @@ class Authenticode HRESULT Verify(LPCWSTR szFileName, const CBinaryBuffer& secdir, const PE_Hashs& hashs, AuthenticodeData& data); HRESULT SignatureSize(LPCWSTR szFileName, const CBinaryBuffer& secdir, DWORD& cbSize); - HRESULT CloseCatalogState(); - ~Authenticode(); }; diff --git a/src/OrcLib/CacheStream.cpp b/src/OrcLib/CacheStream.cpp index 17dee6f1..f8a5f6c7 100644 --- a/src/OrcLib/CacheStream.cpp +++ b/src/OrcLib/CacheStream.cpp @@ -14,15 +14,17 @@ using namespace std::string_view_literals; namespace Orc { -CacheStream::CacheStream(std::shared_ptr stream) +CacheStream::CacheStream(std::shared_ptr stream, size_t cacheSize) : ByteStream() , m_stream(std::move(stream)) , m_streamOffset(0) - , m_cache() - , m_cacheSize(0) , m_offset(0) - , m_cacheLoadOffset(0) + , m_cache() + , m_cacheUse(0) + , m_cacheSize(cacheSize) + , m_cacheOffset(0) { + m_cache.resize(cacheSize); } STDMETHODIMP Orc::CacheStream::Clone(std::shared_ptr& clone) @@ -32,13 +34,26 @@ STDMETHODIMP Orc::CacheStream::Clone(std::shared_ptr& clone) HRESULT CacheStream::Close() { - m_stream->Close(); + HRESULT hr = m_stream->Close(); + if (FAILED(hr)) + { + Log::Debug("Failed to close underlying cached stream [{}]", SystemError(hr)); + } + + m_cache.resize(0); + m_cache.shrink_to_fit(); + m_cacheUse = 0; return S_OK; } HRESULT CacheStream::Duplicate(const CacheStream& other) { m_stream = other.m_stream; + m_streamOffset = other.m_streamOffset; + m_cacheOffset = other.m_cacheOffset; + m_cacheUse = other.m_cacheUse; + m_cacheSize = other.m_cacheSize; + m_cache = other.m_cache; return S_OK; } @@ -51,33 +66,8 @@ HRESULT CacheStream::Open() return E_FAIL; } - if (m_stream->CanSeek() == S_OK) - { - ULONGLONG currPointer; - hr = m_stream->SetFilePointer(0, FILE_BEGIN, &currPointer); - if (FAILED(hr)) - { - Log::Debug("Failed to open CacheStream: underlying stream seek failed [{}]", SystemError(hr)); - return E_FAIL; - } - } - else - { - Log::Trace("CacheStream cannot seek underlying stream [{}]", SystemError(hr)); - } - - ULONGLONG written = 0; - hr = m_stream->Read(m_cache.data(), m_cache.size(), &written); - if (FAILED(hr)) - { - Log::Debug("Failed to open CacheStream: underlying stream failed to copy [{}]", SystemError(hr)); - return hr; - } - - m_streamOffset += written; - m_offset = 0; - m_cacheLoadOffset = 0; - m_cacheSize = written; + m_cache.resize(m_cacheSize); + m_cacheUse = 0; return S_OK; } @@ -88,7 +78,8 @@ HRESULT CacheStream::Read_( { HRESULT hr = E_FAIL; - if (m_cacheSize == 0) + // Lazy allocation + if (m_cache.size() == 0) { hr = Open(); if (FAILED(hr)) @@ -100,11 +91,11 @@ HRESULT CacheStream::Read_( ULONGLONG totalRead = 0; while (totalRead != cbBytes) { - if (m_offset >= m_cacheLoadOffset && m_offset < m_cacheLoadOffset + m_cacheSize) + if (m_offset >= m_cacheOffset && m_offset < m_cacheOffset + m_cacheUse) { - const auto offset = m_offset - m_cacheLoadOffset; + const auto offset = m_offset - m_cacheOffset; - ULONGLONG cacheRead = std::min(cbBytes - totalRead, m_cacheSize - offset); + ULONGLONG cacheRead = std::min(cbBytes - totalRead, m_cacheUse - offset); std::copy( std::cbegin(m_cache) + offset, std::cbegin(m_cache) + offset + cacheRead, @@ -127,8 +118,8 @@ HRESULT CacheStream::Read_( break; } - m_cacheLoadOffset = m_streamOffset; - m_cacheSize = streamRead; + m_cacheOffset = m_streamOffset; + m_cacheUse = streamRead; m_streamOffset += streamRead; } } @@ -152,6 +143,12 @@ HRESULT CacheStream::Write_( HRESULT CacheStream::SetFilePointer(__in LONGLONG DistanceToMove, __in DWORD dwMoveMethod, __out_opt PULONG64 pCurrPointer) { + ULONG64 newOffset = 0; + if (!pCurrPointer) + { + pCurrPointer = &newOffset; + } + HRESULT hr = m_stream->SetFilePointer(DistanceToMove, dwMoveMethod, pCurrPointer); if (FAILED(hr)) { diff --git a/src/OrcLib/CacheStream.h b/src/OrcLib/CacheStream.h index 23a6a7eb..b1918351 100644 --- a/src/OrcLib/CacheStream.h +++ b/src/OrcLib/CacheStream.h @@ -21,9 +21,7 @@ class CBinaryBuffer; class CacheStream : public ByteStream { public: - using CacheBuffer = std::array; - - CacheStream(std::shared_ptr stream); + CacheStream(std::shared_ptr stream, size_t cacheSize = 1048576); ~CacheStream(); HRESULT Open(); @@ -62,10 +60,11 @@ class CacheStream : public ByteStream private: std::shared_ptr m_stream; uint64_t m_streamOffset; - CacheBuffer m_cache; - size_t m_cacheSize; uint64_t m_offset; - uint64_t m_cacheLoadOffset; + std::vector m_cache; + size_t m_cacheUse; + size_t m_cacheSize; + uint64_t m_cacheOffset; }; } // namespace Orc diff --git a/src/OrcLib/CompleteVolumeReader.cpp b/src/OrcLib/CompleteVolumeReader.cpp index 1aaefeb4..9135e085 100644 --- a/src/OrcLib/CompleteVolumeReader.cpp +++ b/src/OrcLib/CompleteVolumeReader.cpp @@ -24,8 +24,6 @@ CompleteVolumeReader::CompleteVolumeReader(const WCHAR* szLocation) HRESULT CompleteVolumeReader::Seek(ULONGLONG offset) { - if (offset > m_Extents[0].GetLength()) - return E_INVALIDARG; if (m_BytesPerSector && offset % m_BytesPerSector) { diff --git a/src/OrcLib/DiskExtent.cpp b/src/OrcLib/DiskExtent.cpp index 34591f9e..6e78d536 100644 --- a/src/OrcLib/DiskExtent.cpp +++ b/src/OrcLib/DiskExtent.cpp @@ -180,10 +180,6 @@ HRESULT CDiskExtent::Read(__in_bcount(dwCount) PVOID lpBuf, DWORD dwCount, PDWOR _ASSERT(INVALID_HANDLE_VALUE != m_hFile); _ASSERT(pdwBytesRead != nullptr); - // This is needed because volsnap.sys (shadow copy) can return sucessfully without modifying the buffer when trying - // to read unused/free blocks. With <= v10.1.2 the buffer was always zeroed. - SecureZeroMemory(lpBuf, dwCount); - DWORD dwBytesRead = 0; Log::Trace(L"CDiskExtent: Reading {} bytes", dwCount); if (!ReadFile(m_hFile, lpBuf, dwCount, &dwBytesRead, NULL)) diff --git a/src/OrcLib/FileFind.cpp b/src/OrcLib/FileFind.cpp index 634a3929..c2e50189 100644 --- a/src/OrcLib/FileFind.cpp +++ b/src/OrcLib/FileFind.cpp @@ -4171,6 +4171,8 @@ HRESULT FileFind::Find(const LocationSet& locations, FileFind::FoundMatchCallbac if (FAILED(hr = InitializeYara())) return hr; + bool hasSomeFailure = false; + for (const auto& aLoc : locs) { HRESULT hr = E_FAIL; @@ -4189,7 +4191,8 @@ HRESULT FileFind::Find(const LocationSet& locations, FileFind::FoundMatchCallbac } else { - Log::Debug(L"Failed to init walk for volume '{}' [{}]", aLoc->GetLocation(), SystemError(hr)); + Log::Critical(L"Failed to init walk for volume '{}' [{}]", aLoc->GetLocation(), SystemError(hr)); + hasSomeFailure = true; } } else @@ -4270,6 +4273,11 @@ HRESULT FileFind::Find(const LocationSet& locations, FileFind::FoundMatchCallbac } } + if (hasSomeFailure) + { + return E_FAIL; + } + return S_OK; } diff --git a/src/OrcLib/LocationSet.cpp b/src/OrcLib/LocationSet.cpp index 87ac5c2a..0ee98a3d 100644 --- a/src/OrcLib/LocationSet.cpp +++ b/src/OrcLib/LocationSet.cpp @@ -209,6 +209,17 @@ void GetExcludedVolumeLocations( break; } } + + for (const auto& location : volume.Locations) + { + if (excludedPaths.find(location->GetLocation()) != std::cend(excludedPaths)) + { + std::copy( + std::cbegin(volume.Locations), std::cend(volume.Locations), std::back_inserter(excludedLocations)); + Log::Info(L"Exclude: '{}'", location->GetLocation()); + break; + } + } } bool NotContain(const Location::Ptr& location, const std::vector& locations) diff --git a/src/OrcLib/Log/FileSink.h b/src/OrcLib/Log/FileSink.h index 2b8bb60c..6d9ff7f3 100644 --- a/src/OrcLib/Log/FileSink.h +++ b/src/OrcLib/Log/FileSink.h @@ -38,7 +38,7 @@ class FileSink final : public spdlog::sinks::base_sink using SpdlogFileSink = spdlog::sinks::basic_file_sink_st; using MemorySink = MemorySink, spdlog::details::null_mutex>; - const size_t kMemorySinkSize = 16384; + const size_t kMemorySinkSize = 128000; FileSink() : m_fileSink() diff --git a/src/OrcLib/MFTOnline.cpp b/src/OrcLib/MFTOnline.cpp index 82336065..315365df 100644 --- a/src/OrcLib/MFTOnline.cpp +++ b/src/OrcLib/MFTOnline.cpp @@ -9,6 +9,8 @@ #include "MFTOnline.h" +#include + #include "VolumeReader.h" #include "Log/Log.h" @@ -39,15 +41,14 @@ HRESULT MFTOnline::Initialize() if (FAILED(hr = m_pVolReader->LoadDiskProperties())) return hr; - // Get all the extents (runs) associated with the MFT itself. - if (FAILED(hr = GetMFTExtents(m_pVolReader->GetBootSector()))) - break; - m_pFetchReader = m_pVolReader->ReOpen( FILE_READ_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS); + // Get all the extents (runs) associated with the MFT itself. + if (FAILED(hr = GetMFTExtents(m_pVolReader->GetBootSector()))) + break; } while (false); } catch (...) @@ -58,6 +59,104 @@ HRESULT MFTOnline::Initialize() return S_OK; } +HRESULT MFTOnline::GetMFTChildsRecordExtents( + const std::shared_ptr& volume, + MFTRecord& mftRecord, + MFTUtils::NonResidentDataAttrInfo& extentsInfo) +{ + struct Child + { + MFTUtils::SafeMFTSegmentNumber segment; + VCN lowerVCN; + }; + + std::vector childs; + for (const auto& attributeListEntry : mftRecord.GetAttributeList()) + { + if (attributeListEntry.TypeCode() != $DATA) + { + continue; + } + + if (attributeListEntry.LowestVCN() == 0) + { + continue; + } + + childs.emplace_back(Child {attributeListEntry.HostRecordSegmentNumber(), attributeListEntry.LowestVCN()}); + } + + std::sort(std::begin(childs), std::end(childs), [](const auto& lhs, const auto& rhs) { + return lhs.lowerVCN < rhs.lowerVCN; + }); + + for (auto it = std::cbegin(childs); it != std::cend(childs); ++it) + { + bool hasFetchedRecord = false; + + MFT_SEGMENT_REFERENCE frn; + frn.SegmentNumberLowPart = it->segment & 0xFFFFFFFF; + frn.SegmentNumberHighPart = (it->segment >> 32) & 0x0000FFFF; + frn.SequenceNumber = (it->segment >> 48); + + HRESULT hr = FetchMFTRecord( + frn, + [&](MFTUtils::SafeMFTSegmentNumber& ulRecordIndex, CBinaryBuffer& childRecordBuffer) -> HRESULT { + MFTRecord childRecord; + HRESULT hr = childRecord.ParseRecord( + volume, + reinterpret_cast(childRecordBuffer.GetData()), + childRecordBuffer.GetCount(), + &mftRecord); + + bool hasFoundDataAttribute = false; + for (auto& attribute : childRecord.GetAttributeList()) + { + if (attribute.TypeCode() != $DATA) + { + continue; + } + + if (hasFoundDataAttribute) + { + Log::Error("Found $MFT child record with multiple attributes $DATA"); + } + + hasFoundDataAttribute = true; + + hr = MFTUtils::GetAttributeNRExtents(attribute.Attribute()->Header(), extentsInfo, volume); + if (FAILED(hr)) + { + Log::Error(L"Failed to parse extent from $MFT child record [{}]", SystemError(hr)); + return hr; + } + } + + if (!hasFoundDataAttribute) + { + Log::Error("$MFT child record has no $DATA attribute ({:#x})", it->segment); + } + + return S_OK; + }, + hasFetchedRecord); + + if (FAILED(hr)) + { + Log::Error(L"Failed to parse $MFT child record (frn: {:#x}) [{}]", it->segment, SystemError(hr)); + continue; // Get as many extent as possible + } + + if (!hasFetchedRecord) + { + Log::Error(L"Failed to fetch $MFT child record (frn: {:#x}) [{}]", it->segment, SystemError(hr)); + continue; + } + } + + return S_OK; +} + // Retrieves the extents of MFT itself. HRESULT MFTOnline::GetMFTExtents(const CBinaryBuffer& buffer) { @@ -125,7 +224,7 @@ HRESULT MFTOnline::GetMFTExtents(const CBinaryBuffer& buffer) if (FAILED(hr = m_pVolReader->Read(m_MftOffset, record, ulBytesPerFRS, ullBytesRead))) { - Log::Error(L"Failed to read MFT record!"); + Log::Error(L"Failed to read $MFT record!"); return hr; } @@ -134,90 +233,33 @@ HRESULT MFTOnline::GetMFTExtents(const CBinaryBuffer& buffer) mftRecord.ParseRecord( m_pVolReader, reinterpret_cast(record.GetData()), record.GetCount(), NULL); - PFILE_RECORD_SEGMENT_HEADER pData = (PFILE_RECORD_SEGMENT_HEADER)record.GetData(); - - if (!(pData->Flags & FILE_RECORD_SEGMENT_IN_USE)) + auto dataAttribute = mftRecord.GetDataAttribute(L""); + if (!dataAttribute) { - Log::Error("MFT record marked as 'not in use'"); - return E_UNEXPECTED; + Log::Error(L"Failed to read $MFT:$DATA"); + return E_FAIL; } - realLength = (ULONG)record.GetCount(); - - if (pData->FirstAttributeOffset > realLength) + hr = MFTUtils::GetAttributeNRExtents(dataAttribute->Header(), m_MFT0Info, m_pVolReader); + if (FAILED(hr)) { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA), - Log::Error( - L"MFT Record length is smaller than First attribute Offset ({}/{})", - realLength, - pData->FirstAttributeOffset); + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + Log::Error("Failed to read extents from $MFT base record"); return hr; } - PATTRIBUTE_RECORD_HEADER pAttrData = (PATTRIBUTE_RECORD_HEADER)((LPBYTE)pData + pData->FirstAttributeOffset); - ULONG nAttrLength = realLength - pData->FirstAttributeOffset; + m_MFT0Info.DataSize = dataAttribute->GetNonResidentInformation(m_pVolReader)->ValidDataSize; - // - // Go through each attribute and locate the one we are interested in. - // - hr = E_FAIL; - while (nAttrLength >= sizeof(ATTRIBUTE_RECORD_HEADER)) + if (!mftRecord.GetChildRecords().empty()) { - if (pAttrData->TypeCode == $END) - { - break; - } - if (pAttrData->RecordLength == 0) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA), - Log::Error(L"MFT - an attribute's (Attribute TypeCode: {:#x}) Record length is zero", pAttrData->TypeCode); - } - - if ($DATA == pAttrData->TypeCode) - { - if (pAttrData->FormCode == RESIDENT_FORM) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA), - Log::Error(L"MFT - not expecting data attribute to be in resident form"); - break; - } - else // non-resident form - { - m_MFT0Info.DataSize = pAttrData->Form.Nonresident.ValidDataLength; - if (FAILED(hr = MFTUtils::GetAttributeNRExtents(pAttrData, m_MFT0Info, m_pVolReader))) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - Log::Error("MFT - failed to get non resident extents"); - break; - } - hr = S_OK; - break; - } - } - - if (pAttrData->RecordLength > nAttrLength) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA), - Log::Error( - L"MFT - an attribute's (Attribute TypeCode = {:#x}) Record length ({}) is greater than the attribute " - L"length ({}).", - pAttrData->TypeCode, - pAttrData->RecordLength, - nAttrLength); - break; - } - else + hr = GetMFTChildsRecordExtents(m_pVolReader, mftRecord, m_MFT0Info); + if (FAILED(hr)) { - PATTRIBUTE_RECORD_HEADER pNextAttrData = - (PATTRIBUTE_RECORD_HEADER)((LPBYTE)pAttrData + pAttrData->RecordLength); // ready for next record - nAttrLength -= pAttrData->RecordLength; - pAttrData = pNextAttrData; + Log::Error("Failed to read some $MFT child records [{}]", SystemError(hr)); + hr = S_OK; // continue: get as many data as possible } } - if (FAILED(hr)) - return hr; - { ULONGLONG Offset = m_MftOffset + (5 * ulBytesPerFRS); CBinaryBuffer RootRecordBuffer; @@ -239,6 +281,7 @@ HRESULT MFTOnline::GetMFTExtents(const CBinaryBuffer& buffer) m_RootUSN = NtfsFullSegmentNumber(&RootReference); } + return hr; } @@ -295,9 +338,8 @@ HRESULT MFTOnline::EnumMFTRecord(MFTUtils::EnumMFTRecordCall pCallBack) ULONGLONG position = 0LL; - for (auto iter = m_MFT0Info.ExtentsVector.begin(); iter != m_MFT0Info.ExtentsVector.end(); ++iter) + for (auto& NRAE : m_MFT0Info.ExtentsVector) { - MFTUtils::NonResidentAttributeExtent& NRAE = *iter; if (NRAE.bZero) continue; @@ -328,8 +370,8 @@ HRESULT MFTOnline::EnumMFTRecord(MFTUtils::EnumMFTRecordCall pCallBack) { Log::Error( L"Failed to read {} bytes from at position {} [{}]", - extent_position, ulBytesPerFRS * ullFRSToRead, + extent_position, SystemError(hr)); return hr; } @@ -366,10 +408,6 @@ HRESULT MFTOnline::EnumMFTRecord(MFTUtils::EnumMFTRecordCall pCallBack) Log::Warn("Add Record Callback failed [{}]", SystemError(hr)); } - if (ullCurrentIndex != ullCurrentFRNIndex) - { - Log::Debug("Current index does not match current FRN index"); - } ullCurrentFRNIndex++; ullCurrentIndex++; } @@ -381,6 +419,19 @@ HRESULT MFTOnline::EnumMFTRecord(MFTUtils::EnumMFTRecordCall pCallBack) return S_OK; } +HRESULT +MFTOnline::FetchMFTRecord(MFT_SEGMENT_REFERENCE& frn, MFTUtils::EnumMFTRecordCall pCallBack, bool& hasFoundRecord) +{ + hasFoundRecord = false; + + auto callback = [&](MFTUtils::SafeMFTSegmentNumber& ulRecordIndex, CBinaryBuffer& data) -> HRESULT { + hasFoundRecord = true; + return pCallBack(ulRecordIndex, data); + }; + + return FetchMFTRecord(std::vector {frn}, callback); +} + HRESULT MFTOnline::FetchMFTRecord(std::vector& frn, MFTUtils::EnumMFTRecordCall pCallBack) { HRESULT hr = E_FAIL; @@ -498,7 +549,8 @@ HRESULT MFTOnline::FetchMFTRecord(std::vector& frn, MFTUt return hr; } - Log::Warn("Add Record Callback failed [{}]", SystemError(hr)); + Log::Error("Add Record Callback failed [{}]", SystemError(hr)); + return hr; } frnIdx++; if (frnIdx >= frn.size()) diff --git a/src/OrcLib/MFTOnline.h b/src/OrcLib/MFTOnline.h index 6368d81c..05ffc0c3 100644 --- a/src/OrcLib/MFTOnline.h +++ b/src/OrcLib/MFTOnline.h @@ -15,6 +15,8 @@ namespace Orc { +class MFTRecord; + class MFTOnline : public IMFT { public: @@ -39,6 +41,14 @@ class MFTOnline : public IMFT HRESULT GetMFTExtents(const CBinaryBuffer& buffer); + HRESULT GetMFTChildsRecordExtents( + const std::shared_ptr& volume, + MFTRecord& mftRecord, + MFTUtils::NonResidentDataAttrInfo& extentsInfo); + + HRESULT + FetchMFTRecord(MFT_SEGMENT_REFERENCE& frn, MFTUtils::EnumMFTRecordCall pCallBack, bool& hasFoundRecord); + ULONG64 m_MftOffset; MFTUtils::NonResidentDataAttrInfo m_MFT0Info; diff --git a/src/OrcLib/MFTRecord.h b/src/OrcLib/MFTRecord.h index 838510d5..007e4d73 100644 --- a/src/OrcLib/MFTRecord.h +++ b/src/OrcLib/MFTRecord.h @@ -42,6 +42,8 @@ class MFTRecord return m_ChildRecords; }; + std::vector>& GetChildRecords() { return m_ChildRecords; }; + bool IsDirectory() const { return m_bIsDirectory; }; bool HasReparsePoint() const { return m_bHasReparsePoint; }; bool IsJunction() const { return m_bIsJunction; } diff --git a/src/OrcLib/MFTWalker.cpp b/src/OrcLib/MFTWalker.cpp index ab3780df..d43f53ee 100644 --- a/src/OrcLib/MFTWalker.cpp +++ b/src/OrcLib/MFTWalker.cpp @@ -163,7 +163,8 @@ HRESULT MFTWalker::ExtendNameBuffer(WCHAR** pCurrent) HRESULT MFTWalker::UpdateAttributeList(MFTRecord* pRecord) { HRESULT hr = E_FAIL; - if (pRecord->m_pAttributeList->IsPresent()) + + if (pRecord->m_pAttributeList && pRecord->m_pAttributeList->IsPresent()) { for (auto& attr : pRecord->m_pAttributeList->m_AttList) { @@ -1457,7 +1458,11 @@ MFTWalker::AddRecord(MFTUtils::SafeMFTSegmentNumber& ullRecordIndex, CBinaryBuff NtfsFullSegmentNumber(&pRecord->m_FileReferenceNumber), SystemError(hr)); } + DeleteRecord(pRecord); + + // BEWARE: was returning S_OK but it is dangerous as pRecord is deleted and all childs record + return hr; } } else @@ -1571,7 +1576,7 @@ HRESULT MFTWalker::AddRecordCallback(MFTUtils::SafeMFTSegmentNumber& ullRecordIn if (FAILED(hr) || pRecord == nullptr) { Log::Debug("Fetched record {} is incomplete", ullRecordIndex); - return S_OK; + return hr; } if (hr != S_FALSE) diff --git a/src/OrcLib/MftRecordAttribute.cpp b/src/OrcLib/MftRecordAttribute.cpp index c64f13aa..66f00c76 100644 --- a/src/OrcLib/MftRecordAttribute.cpp +++ b/src/OrcLib/MftRecordAttribute.cpp @@ -774,8 +774,14 @@ HRESULT ExtendedAttribute::Parse(const std::shared_ptr& VolReader) wstring(szName), CBinaryBuffer(((BYTE*)pEAInfo->EaName) + pEAInfo->EaNameLength + 1, pEAInfo->EaValueLength))); + if (pEAInfo->NextEntryOffset == 0) + { + break; + } + pEAInfo = (PFILE_FULL_EA_INFORMATION)((BYTE*)pEAInfo + pEAInfo->NextEntryOffset); } + m_bParsed = true; return S_OK; } diff --git a/src/OrcLib/PEInfo.cpp b/src/OrcLib/PEInfo.cpp index c88840e5..c058ccb4 100644 --- a/src/OrcLib/PEInfo.cpp +++ b/src/OrcLib/PEInfo.cpp @@ -299,7 +299,14 @@ HRESULT PEInfo::OpenVersionInformation() CBinaryBuffer rsrcBuf; if (!rsrcBuf.SetCount(1024)) return E_OUTOFMEMORY; - std::shared_ptr stream = m_FileInfo.GetDetails()->GetDataStream(); + std::shared_ptr directStream = m_FileInfo.GetDetails()->GetDataStream(); + if (!directStream) + { + return E_POINTER; + } + + auto stream = std::make_shared(directStream, 4096); + ULONGLONG ullBytesRead; size_t rsrc_rsrc_offset = 0; diff --git a/src/OrcLib/Robustness.cpp b/src/OrcLib/Robustness.cpp index 8c7137c7..1fff602c 100644 --- a/src/OrcLib/Robustness.cpp +++ b/src/OrcLib/Robustness.cpp @@ -151,13 +151,13 @@ int Robustness::handle_program_memory_depletion(size_t attempted) const auto div = 1048576; wprintf( - L"Memory: physical: %I64/%I64 MB, paged: %I64/%I64 MB, virtual: %I64/%I64 MB\n", - memory.ullTotalPhys / div, + L"Memory: physical: %I64u/%I64u MB, paged: %I64u/%I64u MB, virtual: %I64u/%I64u MB\n", memory.ullAvailPhys / div, - memory.ullTotalPageFile / div, + memory.ullTotalPhys / div, memory.ullAvailPageFile / div, - memory.ullTotalVirtual / div, - memory.ullAvailVirtual / div); + memory.ullTotalPageFile / div, + memory.ullAvailVirtual / div, + memory.ullTotalVirtual / div); } } throw MemoryException(attempted); diff --git a/src/OrcLib/SnapshotVolumeReader.cpp b/src/OrcLib/SnapshotVolumeReader.cpp index f9b08ca4..912ae6e3 100644 --- a/src/OrcLib/SnapshotVolumeReader.cpp +++ b/src/OrcLib/SnapshotVolumeReader.cpp @@ -95,6 +95,13 @@ SnapshotVolumeReader::Read(ULONGLONG offset, CBinaryBuffer& buffer, ULONGLONG ul return E_OUTOFMEMORY; } + // + // BEWARE + // + // Always zero the buffer ! As VirtualAlloc is being use it is not required here. But because volsnap.sys (shadow + // copy) can return sucessfully without modifying the provided buffer when trying to read unused/free cow blocks, it + // must be zeroed. + // CBinaryBuffer localBuffer(true); localBuffer.SetCount(bytesPerCowBlock); diff --git a/src/OrcLib/TaskTracker.cpp b/src/OrcLib/TaskTracker.cpp index 9950494a..ebe38eda 100644 --- a/src/OrcLib/TaskTracker.cpp +++ b/src/OrcLib/TaskTracker.cpp @@ -932,7 +932,6 @@ HRESULT TaskTracker::CheckSamplesSignature() AuthenticodeStatus status; CheckSampleSignatureStatus(item.second, status); }); - m_authenticode.CloseCatalogState(); return S_OK; } diff --git a/tools/ci/test.psm1 b/tools/ci/test.psm1 index 7233df0e..c14c1e49 100644 --- a/tools/ci/test.psm1 +++ b/tools/ci/test.psm1 @@ -1170,7 +1170,7 @@ function Test-OrcOutcome { $PassThru ) - $OutcomeList = Get-OrcOutcome $Path + $OutcomeList = Get-OrcOutcome -Exclude:$Exclude $Path foreach ($Outcome in $OutcomeList) { foreach ($Failure in $Outcome["Failures"])