MINIFICPP-1147 Implemented.
Signed-off-by: Arpad Boda <aboda@apache.org>
This closes #728
diff --git a/extensions/windows-event-log/ConsumeWindowsEventLog.cpp b/extensions/windows-event-log/ConsumeWindowsEventLog.cpp
index d066f8f..a6832a9 100644
--- a/extensions/windows-event-log/ConsumeWindowsEventLog.cpp
+++ b/extensions/windows-event-log/ConsumeWindowsEventLog.cpp
@@ -164,6 +164,9 @@
}
ConsumeWindowsEventLog::~ConsumeWindowsEventLog() {
+ if (hMsobjsDll_) {
+ FreeLibrary(hMsobjsDll_);
+ }
}
void ConsumeWindowsEventLog::initialize() {
@@ -226,6 +229,25 @@
}
}
+ std::string mode;
+ context->getProperty(OutputFormat.getName(), mode);
+
+ writeXML_ = (mode == Both || mode == XML);
+
+ writePlainText_ = (mode == Both || mode == Plaintext);
+
+ if (writeXML_ && !hMsobjsDll_) {
+ char systemDir[MAX_PATH];
+ if (GetSystemDirectory(systemDir, sizeof(systemDir))) {
+ hMsobjsDll_ = LoadLibrary((systemDir + std::string("\\msobjs.dll")).c_str());
+ if (!hMsobjsDll_) {
+ logger_->log_error("!LoadLibrary error %x", GetLastError());
+ }
+ } else {
+ logger_->log_error("!GetSystemDirectory error %x", GetLastError());
+ }
+ }
+
if (subscriptionHandle_) {
logger_->log_error("Processor already subscribed to Event Log, expected cleanup to unsubscribe.");
} else {
@@ -233,13 +255,6 @@
subscribe(context);
}
-
- std::string mode;
- context->getProperty(OutputFormat.getName(), mode);
-
- writeXML_ = (mode == Both || mode == XML);
-
- writePlainText_ = (mode == Both || mode == Plaintext);
}
void ConsumeWindowsEventLog::onTrigger(const std::shared_ptr<core::ProcessContext> &context, const std::shared_ptr<core::ProcessSession> &session) {
@@ -286,6 +301,96 @@
return providers_[name];
}
+
+// !!! Used a non-documented approach to resolve `%%` in XML via C:\Windows\System32\MsObjs.dll.
+// Links which mention this approach:
+// https://social.technet.microsoft.com/Forums/Windows/en-US/340632d1-60f0-4cc5-ad6f-f8c841107d0d/translate-value-1833quot-on-impersonationlevel-and-similar-values?forum=winservergen
+// https://github.com/libyal/libevtx/blob/master/documentation/Windows%20XML%20Event%20Log%20(EVTX).asciidoc
+// https://stackoverflow.com/questions/33498244/marshaling-a-message-table-resource
+//
+// Traverse xml and check each node, if it starts with '%%' and contains only digits, use it as key to lookup value in C:\Windows\System32\MsObjs.dll.
+void ConsumeWindowsEventLog::substituteXMLPercentageItems(pugi::xml_document& doc) {
+ if (!hMsobjsDll_) {
+ return;
+ }
+
+ struct TreeWalker: public pugi::xml_tree_walker {
+ TreeWalker(HMODULE hMsobjsDll, std::unordered_map<std::string, std::string>& xmlPercentageItemsResolutions, std::shared_ptr<logging::Logger> logger)
+ : hMsobjsDll_(hMsobjsDll), xmlPercentageItemsResolutions_(xmlPercentageItemsResolutions), logger_(logger) {
+ }
+
+ bool for_each(pugi::xml_node& node) override {
+ static const std::string percentages = "%%";
+
+ bool percentagesReplaced = false;
+
+ std::string nodeText = node.text().get();
+
+ for (size_t numberPos = 0; std::string::npos != (numberPos = nodeText.find(percentages, numberPos));) {
+ numberPos += percentages.size();
+
+ auto number = 0u;
+ try {
+ // Assumption - first character is not '0', otherwise not all digits will be replaced by 'value'.
+ number = std::stoul(&nodeText[numberPos]);
+ } catch (std::invalid_argument& e) {
+ continue;
+ }
+
+ const std::string key = std::to_string(number);
+
+ std::string value;
+ const auto it = xmlPercentageItemsResolutions_.find(key);
+ if (it == xmlPercentageItemsResolutions_.end()) {
+ LPTSTR pBuffer{};
+ if (FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
+ hMsobjsDll_,
+ number,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&pBuffer,
+ 1024,
+ 0
+ )) {
+ value = pBuffer;
+ LocalFree(pBuffer);
+
+ value = utils::StringUtils::trimRight(value);
+
+ xmlPercentageItemsResolutions_.insert({key, value});
+ } else {
+ // Add "" to xmlPercentageItemsResolutions_ - don't need to call FormaMessage for this 'key' again.
+ xmlPercentageItemsResolutions_.insert({key, ""});
+
+ logger_->log_error("!FormatMessage error: %d. '%s' is not found in msobjs.dll.", GetLastError(), key.c_str());
+ }
+ } else {
+ value = it->second;
+ }
+
+ if (!value.empty()) {
+ nodeText.replace(numberPos - percentages.size(), key.size() + percentages.size(), value);
+
+ percentagesReplaced = true;
+ }
+ }
+
+ if (percentagesReplaced) {
+ node.text().set(nodeText.c_str());
+ }
+
+ return true;
+ }
+
+ private:
+ HMODULE hMsobjsDll_;
+ std::unordered_map<std::string, std::string>& xmlPercentageItemsResolutions_;
+ std::shared_ptr<logging::Logger> logger_;
+ } treeWalker(hMsobjsDll_, xmlPercentageItemsResolutions_, logger_);
+
+ doc.traverse(treeWalker);
+}
+
void ConsumeWindowsEventLog::processEvent(EVT_HANDLE hEvent) {
DWORD size = 0;
DWORD used = 0;
@@ -343,6 +448,8 @@
}
if (writeXML_) {
+ substituteXMLPercentageItems(doc);
+
if (resolve_as_attributes_) {
renderedData.matched_fields_ = walker.getFieldValues();
}
diff --git a/extensions/windows-event-log/ConsumeWindowsEventLog.h b/extensions/windows-event-log/ConsumeWindowsEventLog.h
index 31ab521..2314611 100644
--- a/extensions/windows-event-log/ConsumeWindowsEventLog.h
+++ b/extensions/windows-event-log/ConsumeWindowsEventLog.h
@@ -34,6 +34,7 @@
#include "utils/OsUtils.h"
#include <Objbase.h>
#include <mutex>
+#include <unordered_map>
namespace org {
namespace apache {
@@ -108,13 +109,13 @@
void LogWindowsError();
void processEvent(EVT_HANDLE eventHandle);
bool processEventsAfterBookmark(EVT_HANDLE hEventResults, const std::wstring& channel, const std::wstring& query);
+ void substituteXMLPercentageItems(pugi::xml_document& doc);
static constexpr const char * const XML = "XML";
static constexpr const char * const Both = "Both";
static constexpr const char * const Plaintext = "Plaintext";
private:
-
// Logger
wel::METADATA_NAMES header_names_;
std::string header_delimiter_;
@@ -140,6 +141,8 @@
bool writePlainText_;
std::unique_ptr<Bookmark> pBookmark_;
std::mutex onTriggerMutex_;
+ std::unordered_map<std::string, std::string> xmlPercentageItemsResolutions_;
+ HMODULE hMsobjsDll_{};
};
REGISTER_RESOURCE(ConsumeWindowsEventLog, "Windows Event Log Subscribe Callback to receive FlowFiles from Events on Windows.");