From 6ebe2853037ba5eea59df5c7b5e561006f5f440a Mon Sep 17 00:00:00 2001 From: florinDNL Date: Sat, 21 Jun 2025 19:00:46 +0300 Subject: [PATCH 1/5] Implement CSV serialization --- ProcMonX/CSVEventDataSerializer.cpp | 53 +++++++++++++++++++++++++++-- ProcMonX/CSVEventDataSerializer.h | 5 ++- ProcMonX/View.cpp | 2 ++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/ProcMonX/CSVEventDataSerializer.cpp b/ProcMonX/CSVEventDataSerializer.cpp index 6fb8882..4c02dc4 100644 --- a/ProcMonX/CSVEventDataSerializer.cpp +++ b/ProcMonX/CSVEventDataSerializer.cpp @@ -1,16 +1,63 @@ #include "pch.h" #include "CSVEventDataSerializer.h" #include +#include +#include "FormatHelper.h" + + +// Needed for processes like msedge which contain commans in their command line, messing up the csv formatting +std::wstring CSVEventDataSerializer::EscapeCommandLine(std::wstring cmd) +{ + std::wstring escapedCmd = L""; + for (wchar_t c : cmd) + { + if (c == '\"') + { + escapedCmd += c; + } + escapedCmd += c; + } + + return std::format(L"\"{}\"", escapedCmd); +} + +std::wstring CSVEventDataSerializer::SerializeEvent(std::shared_ptr& event) +{ + std::wstring ts = (std::wstring)FormatHelper::FormatTime(event->GetTimeStamp()); + std::wstring name = event->GetEventName(); + std::wstring cmd, status, parentId = L""; + CString img = L""; + + if (name == L"Process/Start" || name == L"Process/End") + { + cmd = (std::wstring)(event->GetProperty(L"CommandLine")->GetUnicodeString()); + img = event->GetProperty(L"ImageFileName")->GetAnsiString(); + status = std::to_wstring(event->GetProperty(L"ExitStatus")->GetValue()); + parentId = std::to_wstring(event->GetProperty(L"ParentId")->GetValue()); + } + + std::wstring serial = std::format(L"{},{},{},{},{},{},{},{},{},{}", event->GetIndex(), ts, name, + event->GetProcessName(), event->GetProcessId(), event->GetThreadId(), + EscapeCommandLine(cmd), (std::wstring)img, status, parentId); + + return serial; +} bool CSVEventDataSerializer::Save(const std::vector>& events, const EventDataSerializerOptions& options, PCWSTR path) { - std::ofstream out; + std::wofstream out; out.open(path); if(out.fail()) return false; - for (auto& evt : events) { + out << L"Id,Time Stamp,Event Name,Process Name,PID,TID,Command Line,Image Name,Exit Status,Parent ID" << std::endl; + + for (std::shared_ptr evt : events) { + out << SerializeEvent(evt) << std::endl; } - return false; + + out.close(); + + return true; } std::vector> CSVEventDataSerializer::Load(PCWSTR path) { diff --git a/ProcMonX/CSVEventDataSerializer.h b/ProcMonX/CSVEventDataSerializer.h index c2750e0..0d001e9 100644 --- a/ProcMonX/CSVEventDataSerializer.h +++ b/ProcMonX/CSVEventDataSerializer.h @@ -2,8 +2,11 @@ #include "IEventDataSerializer.h" class CSVEventDataSerializer : public IEventDataSerializer { +private: + std::wstring SerializeEvent(std::shared_ptr& event); + std::wstring EscapeCommandLine(std::wstring cmd); public: virtual bool Save(const std::vector>& events, const EventDataSerializerOptions& options, PCWSTR path) override; - virtual std::vector> Load(PCWSTR path) override; + virtual std::vector> Load(PCWSTR path) override; }; diff --git a/ProcMonX/View.cpp b/ProcMonX/View.cpp index 366706a..6df5d22 100644 --- a/ProcMonX/View.cpp +++ b/ProcMonX/View.cpp @@ -489,6 +489,8 @@ LRESULT CView::OnSave(WORD, WORD, HWND, BOOL&) { auto eventsCopy = m_OrgEvents; if (!serializer->Save(eventsCopy, options, dlg.m_szFileName)) AtlMessageBox(*this, L"Failed to save data", IDS_TITLE, MB_ICONERROR); + else + AtlMessageBox(*this, L"CSV Data saved successfully", IDS_TITLE, MB_ICONINFORMATION); return 0; } } From 83fc4c621a095f1f9fd98a99aeac8f951679848a Mon Sep 17 00:00:00 2001 From: florinDNL Date: Sat, 21 Jun 2025 19:11:58 +0300 Subject: [PATCH 2/5] fix typo, remove trailing whitespace --- ProcMonX/CSVEventDataSerializer.cpp | 2 +- ProcMonX/CSVEventDataSerializer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ProcMonX/CSVEventDataSerializer.cpp b/ProcMonX/CSVEventDataSerializer.cpp index 4c02dc4..eb97347 100644 --- a/ProcMonX/CSVEventDataSerializer.cpp +++ b/ProcMonX/CSVEventDataSerializer.cpp @@ -5,7 +5,7 @@ #include "FormatHelper.h" -// Needed for processes like msedge which contain commans in their command line, messing up the csv formatting +// Needed for processes like msedge which contain commas in their command line, messing up the csv formatting std::wstring CSVEventDataSerializer::EscapeCommandLine(std::wstring cmd) { std::wstring escapedCmd = L""; diff --git a/ProcMonX/CSVEventDataSerializer.h b/ProcMonX/CSVEventDataSerializer.h index 0d001e9..857f232 100644 --- a/ProcMonX/CSVEventDataSerializer.h +++ b/ProcMonX/CSVEventDataSerializer.h @@ -7,6 +7,6 @@ class CSVEventDataSerializer : public IEventDataSerializer { std::wstring EscapeCommandLine(std::wstring cmd); public: virtual bool Save(const std::vector>& events, const EventDataSerializerOptions& options, PCWSTR path) override; - virtual std::vector> Load(PCWSTR path) override; + virtual std::vector> Load(PCWSTR path) override; }; From 359eef1742d27847964ef365379923841fb045e3 Mon Sep 17 00:00:00 2001 From: florinDNL Date: Sun, 22 Jun 2025 21:11:16 +0300 Subject: [PATCH 3/5] Account for all event types, move properties into Details tab --- ProcMonX/CSVEventDataSerializer.cpp | 63 +++++++++++++++++------------ ProcMonX/CSVEventDataSerializer.h | 2 +- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/ProcMonX/CSVEventDataSerializer.cpp b/ProcMonX/CSVEventDataSerializer.cpp index eb97347..97dd631 100644 --- a/ProcMonX/CSVEventDataSerializer.cpp +++ b/ProcMonX/CSVEventDataSerializer.cpp @@ -3,44 +3,58 @@ #include #include #include "FormatHelper.h" - +#include // Needed for processes like msedge which contain commas in their command line, messing up the csv formatting -std::wstring CSVEventDataSerializer::EscapeCommandLine(std::wstring cmd) +void CSVEventDataSerializer::EscapeCsvString(std::wstring& str) { - std::wstring escapedCmd = L""; - for (wchar_t c : cmd) - { - if (c == '\"') - { - escapedCmd += c; + if (str.empty()) + return; + + std::wstring escapedStr = L""; + for (wchar_t c : str) { + if (c == '\"') { + escapedStr += c; } - escapedCmd += c; + escapedStr += c; } - return std::format(L"\"{}\"", escapedCmd); + str = std::format(L"\"{}\"", escapedStr); } std::wstring CSVEventDataSerializer::SerializeEvent(std::shared_ptr& event) { + uint32_t index = event->GetIndex(); + DWORD tPid = event->GetProcessId(); + DWORD tTid = event->GetThreadId(); + std::wstring ts = (std::wstring)FormatHelper::FormatTime(event->GetTimeStamp()); std::wstring name = event->GetEventName(); - std::wstring cmd, status, parentId = L""; - CString img = L""; + std::wstring pName = event->GetProcessName(); + std::wstring pid = (tPid == (DWORD) - 1) ? L"" : std::to_wstring(tPid); + std::wstring tid = (tTid == (DWORD) - 1) ? L"" : std::to_wstring(tTid); + std::wstring details = L""; - if (name == L"Process/Start" || name == L"Process/End") - { - cmd = (std::wstring)(event->GetProperty(L"CommandLine")->GetUnicodeString()); - img = event->GetProperty(L"ImageFileName")->GetAnsiString(); - status = std::to_wstring(event->GetProperty(L"ExitStatus")->GetValue()); - parentId = std::to_wstring(event->GetProperty(L"ParentId")->GetValue()); - } + std::vector props = event->GetProperties(); - std::wstring serial = std::format(L"{},{},{},{},{},{},{},{},{},{}", event->GetIndex(), ts, name, - event->GetProcessName(), event->GetProcessId(), event->GetThreadId(), - EscapeCommandLine(cmd), (std::wstring)img, status, parentId); + if (props.size() > 0) { + bool needsEscaping = false; + for (EventProperty& p : props) { + std::wstring pValue = event->FormatProperty(p); + if (!pValue.empty()) { + details += p.Name + L": " + pValue + L"; "; + if (p.Name == L"CommandLine") { + needsEscaping = true; + } + } + } + + if (needsEscaping) { + EscapeCsvString(details); + } + } - return serial; + return std::format(L"{},{},{},{},{},{},{}", index, ts, name, pName, pid, tid, details); } bool CSVEventDataSerializer::Save(const std::vector>& events, const EventDataSerializerOptions& options, PCWSTR path) { @@ -49,14 +63,13 @@ bool CSVEventDataSerializer::Save(const std::vector>& if(out.fail()) return false; - out << L"Id,Time Stamp,Event Name,Process Name,PID,TID,Command Line,Image Name,Exit Status,Parent ID" << std::endl; + out << L"Id,Time Stamp,Event Name,Process Name,PID,TID,Details" << std::endl; for (std::shared_ptr evt : events) { out << SerializeEvent(evt) << std::endl; } out.close(); - return true; } diff --git a/ProcMonX/CSVEventDataSerializer.h b/ProcMonX/CSVEventDataSerializer.h index 857f232..fa79d51 100644 --- a/ProcMonX/CSVEventDataSerializer.h +++ b/ProcMonX/CSVEventDataSerializer.h @@ -3,8 +3,8 @@ class CSVEventDataSerializer : public IEventDataSerializer { private: + void EscapeCsvString(std::wstring& str); std::wstring SerializeEvent(std::shared_ptr& event); - std::wstring EscapeCommandLine(std::wstring cmd); public: virtual bool Save(const std::vector>& events, const EventDataSerializerOptions& options, PCWSTR path) override; virtual std::vector> Load(PCWSTR path) override; From ac135f2c2939fe88debb5b180a938c910addacf6 Mon Sep 17 00:00:00 2001 From: florinDNL Date: Sun, 22 Jun 2025 21:22:31 +0300 Subject: [PATCH 4/5] cleanup --- ProcMonX/CSVEventDataSerializer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ProcMonX/CSVEventDataSerializer.cpp b/ProcMonX/CSVEventDataSerializer.cpp index 97dd631..a05048c 100644 --- a/ProcMonX/CSVEventDataSerializer.cpp +++ b/ProcMonX/CSVEventDataSerializer.cpp @@ -1,9 +1,8 @@ #include "pch.h" #include "CSVEventDataSerializer.h" +#include "FormatHelper.h" #include #include -#include "FormatHelper.h" -#include // Needed for processes like msedge which contain commas in their command line, messing up the csv formatting void CSVEventDataSerializer::EscapeCsvString(std::wstring& str) From 9e4db223b39a2ab137e935690b74ebfa68d10821 Mon Sep 17 00:00:00 2001 From: FlorinD <79944491+florinDNL@users.noreply.github.com> Date: Sun, 22 Jun 2025 21:31:07 +0300 Subject: [PATCH 5/5] fix file formatting --- ProcMonX/CSVEventDataSerializer.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/ProcMonX/CSVEventDataSerializer.cpp b/ProcMonX/CSVEventDataSerializer.cpp index a05048c..eb33f8a 100644 --- a/ProcMonX/CSVEventDataSerializer.cpp +++ b/ProcMonX/CSVEventDataSerializer.cpp @@ -7,9 +7,10 @@ // Needed for processes like msedge which contain commas in their command line, messing up the csv formatting void CSVEventDataSerializer::EscapeCsvString(std::wstring& str) { - if (str.empty()) - return; - + if (str.empty()) { + return; + } + std::wstring escapedStr = L""; for (wchar_t c : str) { if (c == '\"') { @@ -23,18 +24,18 @@ void CSVEventDataSerializer::EscapeCsvString(std::wstring& str) std::wstring CSVEventDataSerializer::SerializeEvent(std::shared_ptr& event) { - uint32_t index = event->GetIndex(); + uint32_t index = event->GetIndex(); DWORD tPid = event->GetProcessId(); DWORD tTid = event->GetThreadId(); std::wstring ts = (std::wstring)FormatHelper::FormatTime(event->GetTimeStamp()); std::wstring name = event->GetEventName(); - std::wstring pName = event->GetProcessName(); + std::wstring pName = event->GetProcessName(); std::wstring pid = (tPid == (DWORD) - 1) ? L"" : std::to_wstring(tPid); std::wstring tid = (tTid == (DWORD) - 1) ? L"" : std::to_wstring(tTid); std::wstring details = L""; - std::vector props = event->GetProperties(); + std::vector props = event->GetProperties(); if (props.size() > 0) { bool needsEscaping = false; @@ -45,14 +46,14 @@ std::wstring CSVEventDataSerializer::SerializeEvent(std::shared_ptr& if (p.Name == L"CommandLine") { needsEscaping = true; } - } + } } - if (needsEscaping) { - EscapeCsvString(details); - } - } - + if (needsEscaping) { + EscapeCsvString(details); + } + } + return std::format(L"{},{},{},{},{},{},{}", index, ts, name, pName, pid, tid, details); }