Skip to content
Merged
2 changes: 2 additions & 0 deletions osquery/events/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function(generateOsqueryEvents)
list(APPEND source_files
windows/etw/etw_user_session.cpp
windows/etw/etw_publisher_processes.cpp
windows/etw/etw_publisher_dns.cpp
windows/etw/etw_provider_config.cpp
windows/etw/etw_post_processing_pipeline.cpp
windows/etw/etw_kernel_session.cpp
Expand Down Expand Up @@ -183,6 +184,7 @@ function(generateOsqueryEvents)
if(OSQUERY_BUILD_ETW)
list(APPEND platform_public_header_files
windows/etw/etw_user_session.h
windows/etw/etw_publisher_dns.h
windows/etw/etw_publisher_processes.h
windows/etw/etw_publisher.h
windows/etw/etw_provider_config.h
Expand Down
45 changes: 41 additions & 4 deletions osquery/events/windows/etw/etw_data_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,26 +107,63 @@ struct EtwProcessStopData final {

using EtwProcStopDataRef = std::shared_ptr<EtwProcessStopData>;

/**
* @brief DNS request event payload
*/
struct EtwDnsRequestData final {
/// Process ID
std::uint32_t ProcessId{0};

// Process image path
std::string ProcessImagePath;

/// Query Name
std::string QueryName;

/// Query Type
std::uint32_t QueryType{0};

// Query Type as a string
std::string QueryTypeString;

/// Query Status Code
std::uint32_t QueryStatus{0};

/// Query Results
std::string QueryResults;

/// User SID
std::string UserSid;

/// User Name
std::string UserName;
};

using EtwDnsRequestDataRef = std::shared_ptr<EtwDnsRequestData>;

/**
* @brief ETW Event Payload
*/
using EtwPayloadVariant =
std::variant<std::monostate, EtwProcStartDataRef, EtwProcStopDataRef>;
using EtwPayloadVariant = std::variant<std::monostate,
EtwProcStartDataRef,
EtwProcStopDataRef,
EtwDnsRequestDataRef>;

/**
* @brief Event types
* The event type is used to tag an ETW event to an specific data type that will
* be used to dispatch events to different provider post processors
*/
enum class EtwEventType { Invalid, ProcessStart, ProcessStop };
enum class EtwEventType { Invalid, ProcessStart, ProcessStop, DnsRequest };

/**
* @brief Event Type string representation
*/
const auto kEtwEventTypeStrings = std::unordered_map<EtwEventType, std::string>{
{EtwEventType::Invalid, "Invalid"},
{EtwEventType::ProcessStart, "ProcessStart"},
{EtwEventType::ProcessStop, "ProcessStop"}};
{EtwEventType::ProcessStop, "ProcessStop"},
{EtwEventType::DnsRequest, "DnsRequests"}};

/**
* @brief ETW Event Header
Expand Down
77 changes: 77 additions & 0 deletions osquery/events/windows/etw/etw_publisher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace osquery {

EtwPublisherBase::EtwPublisherBase(const std::string& name) {
name_ = name;
initializeHardVolumeConversions();
}

EtwController& EtwPublisherBase::EtwEngine() {
Expand All @@ -34,4 +35,80 @@ EtwPublisherBase::getPostProcessorCallback() {
};
}

void EtwPublisherBase::updateUserInfo(const std::string& userSid,
std::string& username) {
// Updating user information using gathered user SIDs as input
auto usernameIt = usernamesBySIDs_.find(userSid);
if (usernameIt != usernamesBySIDs_.end()) {
auto cachedUsername = usernameIt->second;
username.assign(cachedUsername);
} else {
PSID pSid = nullptr;

if (!ConvertStringSidToSidA(userSid.c_str(), &pSid) || pSid == nullptr) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use unicode version

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressing this in a separate PR as it will affect both the existing process events table and this one.

// Inserting empty username to avoid the lookup logic to be called again
usernamesBySIDs_.insert({userSid, ""});
return;
}

std::vector<char> domainNameStr(MAX_PATH - 1, 0x0);
std::vector<char> userNameStr(MAX_PATH - 1, 0x0);
DWORD domainNameSize = MAX_PATH;
DWORD userNameSize = MAX_PATH;
SID_NAME_USE sidType = SID_NAME_USE::SidTypeInvalid;

if (!LookupAccountSidA(NULL,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use unicode version

pSid,
userNameStr.data(),
&userNameSize,
domainNameStr.data(),
&domainNameSize,
&sidType) ||
strlen(domainNameStr.data()) == 0 ||
strlen(domainNameStr.data()) >= MAX_PATH ||
strlen(userNameStr.data()) == 0 ||
strlen(userNameStr.data()) >= MAX_PATH ||
sidType == SID_NAME_USE::SidTypeInvalid) {
// Inserting empty username to avoid the lookup logic to be called again
usernamesBySIDs_.insert({userSid, ""});
LocalFree(pSid);
return;
}

LocalFree(pSid);

username.append(domainNameStr.data());
username.append("\\");
username.append(userNameStr.data());

usernamesBySIDs_.insert({userSid, username});
}
}

void EtwPublisherBase::initializeHardVolumeConversions() {
const auto& validDriveLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

for (const auto& driveLetter : validDriveLetters) {
std::string queryPath;
queryPath.push_back(driveLetter);
queryPath.push_back(':');

char hardVolume[MAX_PATH + 1] = {0};
if (QueryDosDeviceA(queryPath.c_str(), hardVolume, MAX_PATH)) {
hardVolumeDrives_.insert({hardVolume, queryPath});
}
}
}

void EtwPublisherBase::updateHardVolumeWithLogicalDrive(std::string& path) {
// Updating the hardvolume entries with logical volume data
for (const auto& [hardVolume, logicalDrive] : hardVolumeDrives_) {
size_t pos = 0;
if ((pos = path.find(hardVolume, pos)) != std::string::npos) {
path.replace(pos, hardVolume.length(), logicalDrive);
break;
}
}
}

} // namespace osquery
23 changes: 23 additions & 0 deletions osquery/events/windows/etw/etw_publisher.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ class EtwPublisherBase {
*/
std::function<void(const EtwEventDataRef&)> getPostProcessorCallback();

protected:
/**
* @brief Given the userSid, update the username using a cached value or
* looking up the value if no cached value exists.
*/
void updateUserInfo(const std::string& userSid, std::string& username);

/**
* @brief Given an image file (device) path, update to use the logical (eg.
* C:) drive
*/
void updateHardVolumeWithLogicalDrive(std::string& path);

private:
/**
* @brief Abstract method that has to be implemented in the ETW publisher
Expand All @@ -79,6 +92,16 @@ class EtwPublisherBase {
* @brief Provider name
*/
std::string name_;

// Cache for hard volume conversions
void initializeHardVolumeConversions();
using HardVolumeDriveCollection =
std::unordered_map<std::string, std::string>;
HardVolumeDriveCollection hardVolumeDrives_;

// Cache for updateUserInfo
using UsernameBySIDCollection = std::unordered_map<std::string, std::string>;
UsernameBySIDCollection usernamesBySIDs_;
};

} // namespace osquery
Loading
Loading