Files
2021-01-14 16:24:03 +11:00

1005 lines
37 KiB
C++

#include "sealighter_krabs.h"
#include <iostream>
#include <fstream>
#include <thread>
#include "sealighter_errors.h"
#include "sealighter_util.h"
#include "sealighter_json.h"
#include "sealighter_predicates.h"
#include "sealighter_handler.h"
#include "sealighter_provider.h"
// -------------------------
// GLOBALS - START
// -------------------------
// Add aliases to make code cleaner
namespace kpc = krabs::predicates::comparers;
namespace kpa = krabs::predicates::adapters;
// Holds a KrabsETW User Session Trace if needed
static user_trace* g_user_session = NULL;
// Holds a KrabsETW Kernel Session Trace if needed
static kernel_trace* g_kernel_session = NULL;
// -------------------------
// GLOBALS - END
// -------------------------
// PRIVATE FUNCTIONS - START
// -------------------------
/*
Adds a single property comparer filter to a list
*/
template <
typename ComparerA,
typename ComparerW
>
void add_filter_to_vector_property_compare_item
(
json item,
std::vector<std::shared_ptr<predicates::details::predicate_base>>& list
)
{
if (!item["name"].is_null() && !item["value"].is_null() && !item["type"].is_null()) {
std::wstring name = convert_str_wstr(item["name"].get<std::string>());
std::string type = item["type"].get<std::string>();
if (type == "STRINGA") {
std::string val = item["value"].get<std::string>();
auto pred = std::shared_ptr<
predicates::details::property_view_predicate<
std::string,
kpa::generic_string<char>,
ComparerA
>
>(new predicates::details::property_view_predicate<
std::string,
kpa::generic_string<char>,
ComparerA
>(
name,
val,
kpa::generic_string<char>(),
ComparerA()
));
list.emplace_back(pred);
}
else if (type == "STRINGW") {
std::wstring val = convert_str_wstr(item["value"].get<std::string>());
auto pred = std::shared_ptr<
predicates::details::property_view_predicate<
std::wstring,
kpa::generic_string<wchar_t>,
ComparerW
>
>(new predicates::details::property_view_predicate<
std::wstring,
kpa::generic_string<wchar_t>,
ComparerW
>(
name,
val,
kpa::generic_string<wchar_t>(),
ComparerW()
));
list.emplace_back(pred);
}
else {
// Raise a parse error, type has to be a string
throw nlohmann::detail::exception(
nlohmann::detail::parse_error::create(0, 0,
"The 'type' of a Property Comparer must be 'STRINGA' or 'STRINGW'"));
}
}
else {
// Raise a parse error, properites *must* have all these fields
throw nlohmann::detail::exception(
nlohmann::detail::parse_error::create(
0, 0, "Properties must have a 'name', 'type' AND 'value' keys "));
}
}
/*
Adds a property comparer filter to a list
*/
template <
typename ComparerA,
typename ComparerW
>
void add_filter_to_vector_property_compare
(
json root,
std::string element,
std::vector<std::shared_ptr<predicates::details::predicate_base>>& pred_vector
)
{
std::vector<std::shared_ptr<predicates::details::predicate_base>> list;
if (!root[element].is_null()) {
printf(" %s: %s\n", element.c_str(), convert_json_string(root[element], false).c_str());
if (root[element].is_array()) {
for (json item : root[element]) {
add_filter_to_vector_property_compare_item<ComparerA, ComparerW>(item, list);
}
if (!list.empty()) {
pred_vector.emplace_back(std::shared_ptr<sealighter_any_of>(new sealighter_any_of(list)));
}
}
else {
add_filter_to_vector_property_compare_item<ComparerA, ComparerW>(root[element], pred_vector);
}
}
}
/*
Add a single "property is" filter to a list.
*/
void add_filter_to_vector_property_is_item
(
json item,
std::vector<std::shared_ptr<predicates::details::predicate_base>>& list
)
{
if (!item["name"].is_null() && !item["value"].is_null() && !item["type"].is_null()) {
std::wstring name = convert_str_wstr(item["name"].get<std::string>());
std::string type = item["type"].get<std::string>();
if (type == "STRINGA") {
auto val = item["value"].get<std::string>();
list.emplace_back(std::shared_ptr<sealighter_property_is<std::string>>
(new sealighter_property_is<std::string>(name, val)));
}
else if (type == "STRINGW") {
auto val = convert_str_wstr(item["value"].get<std::string>());
list.emplace_back(std::shared_ptr<sealighter_property_is<std::wstring>>
(new sealighter_property_is<std::wstring>(name, val)));
}
else if (type == "INT8") {
auto val = item["value"].get<std::int8_t>();
list.emplace_back(std::shared_ptr<sealighter_property_is<std::int8_t>>
(new sealighter_property_is<std::int8_t>(name, val)));
}
else if (type == "UINT8") {
auto val = item["value"].get<std::uint8_t>();
list.emplace_back(std::shared_ptr<sealighter_property_is<std::uint8_t>>
(new sealighter_property_is<std::uint8_t>(name, val)));
}
else if (type == "INT16") {
auto val = item["value"].get<std::int16_t>();
list.emplace_back(std::shared_ptr<sealighter_property_is<std::int16_t>>
(new sealighter_property_is<std::int16_t>(name, val)));
}
else if (type == "UINT16") {
auto val = item["value"].get<std::uint16_t>();
list.emplace_back(std::shared_ptr<sealighter_property_is<std::uint16_t>>
(new sealighter_property_is<std::uint16_t>(name, val)));
}
else if (type == "INT32") {
auto val = item["value"].get<std::int32_t>();
list.emplace_back(std::shared_ptr<sealighter_property_is<std::int32_t>>
(new sealighter_property_is<std::int32_t>(name, val)));
}
else if (type == "UINT32") {
auto val = item["value"].get<std::uint32_t>();
list.emplace_back(std::shared_ptr<sealighter_property_is<std::uint32_t>>
(new sealighter_property_is<std::uint32_t>(name, val)));
}
else if (type == "INT64") {
auto val = item["value"].get<std::int64_t>();
list.emplace_back(std::shared_ptr<sealighter_property_is<std::int64_t>>
(new sealighter_property_is<std::int64_t>(name, val)));
}
else if (type == "UINT64") {
auto val = item["value"].get<std::uint64_t>();
list.emplace_back(std::shared_ptr<sealighter_property_is<std::uint64_t>>
(new sealighter_property_is<std::uint64_t>(name, val)));
}
}
else {
// Raise an parse error, properites *must* have all these fields
throw nlohmann::detail::exception(
nlohmann::detail::parse_error::create
(0, 0, "Properties must have a 'name', 'type' AND 'value' keys "));
}
}
/*
Add the "Property Is" filter to a list,
if availible. This is a different type of predicate
to both the basic predicates and the other property comparer ones
*/
void add_filter_to_vector_property_is
(
json root,
std::vector<std::shared_ptr<predicates::details::predicate_base>>& pred_vector
)
{
std::vector<std::shared_ptr<predicates::details::predicate_base>> list;
if (!root.is_null()) {
printf(" Property Is: %s\n", convert_json_string(root, false).c_str());
if (root.is_array()) {
for (json item : root) {
add_filter_to_vector_property_is_item(item, list);
}
if (!list.empty()) {
pred_vector.emplace_back(std::shared_ptr<sealighter_any_of>(new sealighter_any_of(list)));
}
}
else {
add_filter_to_vector_property_is_item(root, pred_vector);
}
}
};
/*
Adds a basic filter with two values
*/
template <
typename TPred,
typename TJson1=std::uint64_t,
typename TJson2=std::uint64_t
>
void add_filter_to_vector_basic_pair
(
json root,
std::string element,
std::string item1_name,
std::string item2_name,
std::vector<std::shared_ptr<predicates::details::predicate_base>>& pred_vector
)
{
std::vector<std::shared_ptr<predicates::details::predicate_base>> list;
if (!root[element].is_null()) {
printf(" %s: %s\n", element.c_str(), convert_json_string(root[element], false).c_str());
if (root[element].is_array()) {
for (json item : root[element]) {
if (!item[item1_name].is_null() && !item[item2_name].is_null()) {
TJson1 item1 = item[item1_name].get<TJson1>();
TJson2 item2 = item[item2_name].get<TJson2>();
list.emplace_back(std::shared_ptr<TPred>(new TPred(item1, item2)));
}
}
if (!list.empty()) {
pred_vector.emplace_back(std::shared_ptr<sealighter_any_of>(new sealighter_any_of(list)));
}
}
else {
if (!root[element][item1_name].is_null() && !root[element][item2_name].is_null()) {
TJson1 item1 = root[element][item1_name].get<TJson1>();
TJson2 item2 = root[element][item2_name].get<TJson2>();
pred_vector.emplace_back(std::shared_ptr<TPred>(new TPred(item1, item2)));
}
}
}
}
/*
Parse JSON to create a KrabsETW filter and add it to a list
JSON can be a single item, or an array of items that we will 'OR'
*/
template <
typename TPred,
typename TJson1=std::uint64_t
>
void add_filter_to_vector_basic
(
json root,
std::string element,
std::vector<std::shared_ptr<predicates::details::predicate_base>>& pred_vector
)
{
std::vector<std::shared_ptr<predicates::details::predicate_base>> list;
if (!root[element].is_null()) {
printf(" %s: %s\n", element.c_str(), convert_json_string(root[element], false).c_str());
// If a list, filter can be any of them
if (root[element].is_array()) {
for (json item : root[element]) {
list.emplace_back(std::shared_ptr<TPred>(new TPred(item.get<TJson1>())));
}
if (!list.empty()) {
pred_vector.emplace_back(std::shared_ptr<sealighter_any_of>(new sealighter_any_of(list)));
}
}
else {
pred_vector.emplace_back(std::shared_ptr<TPred>
(new TPred(root[element].get<TJson1>())));
}
}
}
/*
Parse JSON to add filters to a vector list
*/
int add_filters_to_vector
(
std::vector<std::shared_ptr<predicates::details::predicate_base>>& pred_vector,
json json_list
)
{
int status = ERROR_SUCCESS;
try {
// Add the basic single-value filters
add_filter_to_vector_basic<predicates::id_is>
(json_list, "event_id_is", pred_vector);
add_filter_to_vector_basic<predicates::opcode_is>
(json_list, "opcode_is", pred_vector);
add_filter_to_vector_basic<predicates::process_id_is>
(json_list, "process_id_is", pred_vector);
add_filter_to_vector_basic<predicates::version_is>
(json_list, "version_is", pred_vector);
// Add all the property filters
add_filter_to_vector_property_is(json_list["property_is"], pred_vector);
add_filter_to_vector_property_compare<
kpc::equals<std::equal_to<kpa::generic_string<char>::value_type>>,
kpc::equals<std::equal_to<kpa::generic_string<wchar_t>::value_type>>
>(json_list, "property_equals", pred_vector);
add_filter_to_vector_property_compare<
kpc::equals<iequal_to<kpa::generic_string<char>::value_type>>,
kpc::equals<iequal_to<kpa::generic_string<wchar_t>::value_type>>
>(json_list, "property_iequals", pred_vector);
add_filter_to_vector_property_compare<
kpc::contains<std::equal_to<kpa::generic_string<char>::value_type>>,
kpc::contains<std::equal_to<kpa::generic_string<wchar_t>::value_type>>
>(json_list, "property_contains", pred_vector);
add_filter_to_vector_property_compare<
kpc::contains<iequal_to<kpa::generic_string<char>::value_type>>,
kpc::contains<iequal_to<kpa::generic_string<wchar_t>::value_type>>
>(json_list, "property_icontains", pred_vector);
add_filter_to_vector_property_compare<
kpc::starts_with<std::equal_to<kpa::generic_string<char>::value_type>>,
kpc::starts_with<std::equal_to<kpa::generic_string<wchar_t>::value_type>>
>(json_list, "property_starts_with", pred_vector);
add_filter_to_vector_property_compare<
kpc::starts_with<iequal_to<kpa::generic_string<char>::value_type>>,
kpc::starts_with<iequal_to<kpa::generic_string<wchar_t>::value_type>>
>(json_list, "property_istarts_with", pred_vector);
add_filter_to_vector_property_compare<
kpc::ends_with<std::equal_to<kpa::generic_string<char>::value_type>>,
kpc::ends_with<std::equal_to<kpa::generic_string<wchar_t>::value_type>>
>(json_list, "property_ends_with", pred_vector);
add_filter_to_vector_property_compare<
kpc::ends_with<iequal_to<kpa::generic_string<char>::value_type>>,
kpc::ends_with<iequal_to<kpa::generic_string<wchar_t>::value_type>>
>(json_list, "property_iends_with", pred_vector);
// Add own own created Predicates
add_filter_to_vector_basic<sealighter_max_events_total, std::uint64_t>
(json_list, "max_events_total", pred_vector);
add_filter_to_vector_basic_pair<sealighter_max_events_id>
(json_list, "max_events_id", "id_is", "max_events", pred_vector);
add_filter_to_vector_basic<sealighter_any_field_contains, std::string>
(json_list, "any_field_contains", pred_vector);
add_filter_to_vector_basic<sealighter_process_name_contains, std::string>
(json_list, "process_name_contains", pred_vector);
add_filter_to_vector_basic<sealighter_activity_id_is, std::string>
(json_list, "activity_id_is", pred_vector);
}
catch (const nlohmann::detail::exception& e) {
printf("failed to add filters from config to provider\n");
printf("%s\n", e.what());
status = SEALIGHTER_ERROR_PARSE_FILTER;
}
return status;
}
/*
Add Krabs filters to an ETW provider
*/
template <typename T>
int add_filters
(
details::base_provider<T>* pNew_provider,
std::shared_ptr<struct sealighter_context_t> sealighter_context,
json json_provider
)
{
int status = ERROR_SUCCESS;
if (json_provider["filters"].is_null() ||
(json_provider["filters"]["any_of"].is_null() &&
json_provider["filters"]["all_of"].is_null() &&
json_provider["filters"]["none_of"].is_null()
)
) {
// No filters, log everything
printf(" No event filters\n");
pNew_provider->add_on_event_callback([sealighter_context](const EVENT_RECORD& record, const krabs::trace_context& trace_context) {
handle_event_context(record, trace_context, sealighter_context);
});
}
else {
// Build top-level list
// All 3 options will eventually be ANDed together
std::vector<std::shared_ptr<predicates::details::predicate_base>> top_list;
if (!json_provider["filters"]["any_of"].is_null()) {
printf(" Filtering any of:\n");
std::vector<std::shared_ptr<predicates::details::predicate_base>> list;
status = add_filters_to_vector(list, json_provider["filters"]["any_of"]);
if (ERROR_SUCCESS == status) {
top_list.emplace_back(std::shared_ptr<sealighter_any_of>(new sealighter_any_of(list)));
}
}
if (ERROR_SUCCESS == status && !json_provider["filters"]["all_of"].is_null()) {
printf(" Filtering all of:\n");
std::vector<std::shared_ptr<predicates::details::predicate_base>> list;
status = add_filters_to_vector(list, json_provider["filters"]["all_of"]);
if (ERROR_SUCCESS == status) {
top_list.emplace_back(std::shared_ptr<sealighter_all_of>(new sealighter_all_of(list)));
}
}
if (ERROR_SUCCESS == status && !json_provider["filters"]["none_of"].is_null()) {
printf(" Filtering none of:\n");
std::vector<std::shared_ptr<predicates::details::predicate_base>> list;
status = add_filters_to_vector(list, json_provider["filters"]["none_of"]);
if (ERROR_SUCCESS == status) {
top_list.emplace_back(std::shared_ptr<sealighter_none_of>(new sealighter_none_of(list)));
}
}
// Add top level list to a filter
if (ERROR_SUCCESS == status) {
sealighter_all_of top_pred = sealighter_all_of(top_list);
event_filter filter(top_pred);
filter.add_on_event_callback([sealighter_context](const EVENT_RECORD& record, const krabs::trace_context& trace_context) {
handle_event_context(record, trace_context, sealighter_context);
});
pNew_provider->add_filter(filter);
}
}
return status;
}
/*
Add Kernel Providers and Create Kernel ETW Session
*/
int add_kernel_traces
(
json json_config,
EVENT_TRACE_PROPERTIES session_properties
)
{
int status = ERROR_SUCCESS;
kernel_provider* pNew_provider;
std::string provider_name;
// Initialize Session and props
g_kernel_session = new kernel_trace();
g_kernel_session->set_trace_properties(&session_properties);
// Add any Kernel providers
try {
for (json json_provider : json_config["kernel_traces"]) {
if (json_provider["provider_name"].is_null()) {
printf("Invalid Provider, missing provider name\n");
status = SEALIGHTER_ERROR_PARSE_KERNEL_PROVIDER;
break;
}
provider_name = json_provider["provider_name"].get<std::string>();
if (provider_name == "process") {
pNew_provider = new kernel::process_provider();
}
else if (provider_name == "thread") {
pNew_provider = new kernel::thread_provider();
}
else if (provider_name == "image_load") {
pNew_provider = new kernel::image_load_provider();
}
else if (provider_name == "process_counter") {
pNew_provider = new kernel::process_counter_provider();
}
else if (provider_name == "context_switch") {
pNew_provider = new kernel::context_switch_provider();
}
else if (provider_name == "dpc") {
pNew_provider = new kernel::dpc_provider();
}
else if (provider_name == "debug_print") {
pNew_provider = new kernel::debug_print_provider();
}
else if (provider_name == "interrupt") {
pNew_provider = new kernel::interrupt_provider();
}
else if (provider_name == "system_call") {
pNew_provider = new kernel::system_call_provider();
}
else if (provider_name == "disk_io") {
pNew_provider = new kernel::disk_io_provider();
}
else if (provider_name == "disk_file_io") {
pNew_provider = new kernel::disk_file_io_provider();
}
else if (provider_name == "disk_init_io") {
pNew_provider = new kernel::disk_init_io_provider();
}
else if (provider_name == "thread_dispatch") {
pNew_provider = new kernel::thread_dispatch_provider();
}
else if (provider_name == "memory_page_fault") {
pNew_provider = new kernel::memory_page_fault_provider();
}
else if (provider_name == "memory_hard_fault") {
pNew_provider = new kernel::memory_hard_fault_provider();
}
else if (provider_name == "virtual_alloc") {
pNew_provider = new kernel::virtual_alloc_provider();
}
else if (provider_name == "network_tcpip") {
pNew_provider = new kernel::network_tcpip_provider();
}
else if (provider_name == "registry") {
pNew_provider = new kernel::registry_provider();
}
else if (provider_name == "alpc") {
pNew_provider = new kernel::alpc_provider();
}
else if (provider_name == "split_io") {
pNew_provider = new kernel::split_io_provider();
}
else if (provider_name == "driver") {
pNew_provider = new kernel::driver_provider();
}
else if (provider_name == "profile") {
pNew_provider = new kernel::profile_provider();
}
else if (provider_name == "file_io") {
pNew_provider = new kernel::file_io_provider();
}
else if (provider_name == "file_init_io") {
pNew_provider = new kernel::file_init_io_provider();
}
else if (provider_name == "vamap") {
pNew_provider = new kernel::vamap_provider();
}
else if (provider_name == "object_manager") {
pNew_provider = new kernel::object_manager_provider();
}
else {
printf("Invalid Provider: %s\n", provider_name.c_str());
status = SEALIGHTER_ERROR_PARSE_KERNEL_PROVIDER;
break;
}
// Create context with trace name
if (json_provider["trace_name"].is_null()) {
printf("Invalid Provider, missing trace name\n");
status = SEALIGHTER_ERROR_PARSE_KERNEL_PROVIDER;
break;
}
std::string trace_name = json_provider["trace_name"].get<std::string>();
auto sealighter_context =
std::shared_ptr<struct sealighter_context_t>(new sealighter_context_t(trace_name, false));
for (json json_buffers : json_provider["buffers"]) {
auto event_id = json_buffers["event_id"].get<std::uint32_t>();
auto max = json_buffers["max_before_buffering"].get<std::uint32_t>();
auto buffer_list = event_buffer_list_t(event_id, max);
for (json json_buff_prop: json_buffers["properties_to_match"]) {
buffer_list.properties_to_compare.push_back(json_buff_prop.get<std::string>());
}
add_buffered_list(trace_name, buffer_list);
}
// Add any filters
printf("Kernel Provider: %s\n", provider_name.c_str());
status = add_filters(pNew_provider, sealighter_context, json_provider);
if (ERROR_SUCCESS == status) {
g_kernel_session->enable(*pNew_provider);
}
else {
printf("Failed to add filters to: %s\n", provider_name.c_str());
break;
}
}
}
catch (const nlohmann::detail::exception & e) {
printf("invalid kernel provider in config file\n");
printf("%s\n", e.what());
status = SEALIGHTER_ERROR_PARSE_KERNEL_PROVIDER;
}
return status;
}
/*
Add User providers and create User ETW Session
*/
int add_user_traces
(
json json_config,
EVENT_TRACE_PROPERTIES session_properties,
std::wstring session_name
)
{
int status = ERROR_SUCCESS;
// Initialize Session and props
g_user_session = new user_trace(session_name);
g_user_session->set_trace_properties(&session_properties);
try {
// Parse the Usermode Providers
for (json json_provider : json_config["user_traces"]) {
GUID provider_guid;
provider<>* pNew_provider;
std::wstring provider_name;
std::string trace_name;
if (json_provider["provider_name"].is_null()) {
printf("Invalid Provider\n");
status = SEALIGHTER_ERROR_PARSE_USER_PROVIDER;
break;
}
if (json_provider["trace_name"].is_null()) {
printf("Invalid Provider, missing trace name\n");
status = SEALIGHTER_ERROR_PARSE_KERNEL_PROVIDER;
break;
}
trace_name = json_provider["trace_name"].get<std::string>();
// If provider_name is a GUID, use that
// Otherwise pass it off to Krabs to try to resolve
provider_name = convert_str_wstr(json_provider["provider_name"].get<std::string>());
provider_guid = convert_wstr_guid(provider_name);
if (provider_guid != GUID_NULL) {
pNew_provider = new provider<>(provider_guid);
}
else {
pNew_provider = new provider<>(provider_name);
}
wprintf(L"User Provider: %s\n", provider_name.c_str());
printf(" Trace Name: %s\n", trace_name.c_str());
// If no keywords_all or keywords_any is set
// then set a default 'match anything'
if (json_provider["keywords_all"].is_null() && json_provider["keywords_any"].is_null()) {
printf(" Keywords: All\n");
}
else {
if (!json_provider["keywords_all"].is_null()) {
uint64_t data = json_provider["keywords_all"].get<std::uint64_t>();
printf(" Keywords All: 0x%llx\n", data);
pNew_provider->all(data);
}
if (!json_provider["keywords_any"].is_null()) {
uint64_t data = json_provider["keywords_any"].get<std::uint64_t>();
printf(" Keywords Any: 0x%llx\n", data);
pNew_provider->any(data);
}
}
if (!json_provider["level"].is_null()) {
uint64_t data = json_provider["level"].get<std::uint64_t>();
printf(" Level: 0x%llx\n", data);
pNew_provider->level(data);
}
else {
// Set Max Level
pNew_provider->level(0xff);
}
if (!json_provider["trace_flags"].is_null()) {
uint64_t data = json_provider["trace_flags"].get<std::uint64_t>();
printf(" Trace Flags: 0x%llx\n", data);
pNew_provider->trace_flags(data);
}
// Check if we want a stacktrace
// This is just a helper option, you could also set this in the trace_flags
if (!json_provider["report_stacktrace"].is_null() && json_provider["report_stacktrace"].get<bool>()) {
// Add the stacktrace trace flag
pNew_provider->trace_flags(pNew_provider->trace_flags() | EVENT_ENABLE_PROPERTY_STACK_TRACE);
}
// Create context with trace name
// Check if we're dumping the raw event, or attempting to parse it
bool dump_raw_event = false;
if (!json_provider["dump_raw_event"].is_null()) {
dump_raw_event = json_provider["dump_raw_event"].get<bool>();
if (dump_raw_event) {
printf(" Recording raw events\n");
}
}
auto sealighter_context =
std::shared_ptr<struct sealighter_context_t>(new sealighter_context_t(trace_name, dump_raw_event));
for (json json_buffers : json_provider["buffers"]) {
auto event_id = json_buffers["event_id"].get<std::uint32_t>();
auto max = json_buffers["max_before_buffering"].get<std::uint32_t>();
auto buffer_list = event_buffer_list_t(event_id, max);
for (json json_buff_prop : json_buffers["properties_to_match"]) {
buffer_list.properties_to_compare.push_back(json_buff_prop.get<std::string>());
}
add_buffered_list(trace_name, buffer_list);
}
// Add any filters
status = add_filters(pNew_provider, sealighter_context, json_provider);
if (ERROR_SUCCESS == status) {
g_user_session->enable(*pNew_provider);
}
else {
wprintf(L"Failed to add filters to: %s\n", provider_name.c_str());
break;
}
}
}
catch (const nlohmann::detail::exception & e) {
printf("invalid providers in config file\n");
printf("%s\n", e.what());
status = SEALIGHTER_ERROR_PARSE_USER_PROVIDER;
}
// If everything is good, also add a default handler
if (ERROR_SUCCESS == status) {
g_user_session->set_default_event_callback(handle_event);
}
return status;
}
/*
Parse the Config file, setup
ETW Session and Krabs filters
*/
int parse_config
(
std::string config_path
)
{
int status = ERROR_SUCCESS;
json json_config;
EVENT_TRACE_PROPERTIES session_properties = { 0 };
std::wstring session_name = L"Sealighter-Trace";
if (!file_exists(config_path)) {
printf("Error: Config file doesn't exist\n");
return SEALIGHTER_ERROR_MISSING_CONFIG;
}
try {
std::ifstream config_stream(config_path);
// Read in config file
config_stream >> json_config;
config_stream.close();
// Set defaults
session_properties.BufferSize = 256;
session_properties.MinimumBuffers = 12;
session_properties.MaximumBuffers = 48;
session_properties.FlushTimer = 1;
session_properties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_INDEPENDENT_SESSION_MODE;
// Parse the config json for any custom properties
json json_props = json_config["session_properties"];
if (!json_props.is_null())
{
if (!json_props["session_name"].is_null()) {
session_name = convert_str_wstr(json_props["session_name"].get<std::string>());
wprintf(L"Session Name: %s\n", session_name.c_str());
}
if (!json_props["buffer_size"].is_null()) {
session_properties.BufferSize = json_props["buffer_size"].get<std::uint32_t>();
}
if (!json_props["minimum_buffers"].is_null()) {
session_properties.MinimumBuffers =
json_props["minimum_buffers"].get<std::uint32_t>();
}
if (!json_props["maximum_buffers"].is_null()) {
session_properties.MaximumBuffers =
json_props["maximum_buffers"].get<std::uint32_t>();
}
if (!json_props["flush_timer"].is_null()) {
session_properties.FlushTimer =
json_props["flush_timer"].get<std::uint32_t>();
}
if (!json_props["output_format"].is_null()) {
std::string format = json_props["output_format"].get<std::string>();
if ("stdout" == format) {
set_output_format(Output_format::output_stdout);
}
else if ("event_log" == format) {
set_output_format(Output_format::output_event_log);
}
else if ("file" == format) {
if (json_props["output_filename"].is_null()) {
printf("When output_format == 'file', also set 'output_filename'\n");
status = SEALIGHTER_ERROR_OUTPUT_FILE;
}
else {
//g_output_format = Output_format::output_file;
set_output_format(Output_format::output_file);
status = setup_logger_file(json_props["output_filename"].get<std::string>());
}
}
else {
printf("Invalid output_format\n");
status = SEALIGHTER_ERROR_OUTPUT_FORMAT;
}
printf("Output: %s\n", format.c_str());
}
if (!json_props["buffering_timout_seconds"].is_null()) {
auto timeout = json_props["buffering_timout_seconds"].get<std::uint32_t>();
set_buffer_lists_timeout(timeout);
}
}
}
catch (const nlohmann::detail::exception& e) {
printf("invalid session properties in config file\n");
printf("%s\n", e.what());
status = SEALIGHTER_ERROR_PARSE_CONFIG_PROPS;
}
if (ERROR_SUCCESS == status) {
if (json_config["user_traces"].is_null() && json_config["kernel_traces"].is_null()) {
printf("No User or Kernel providers in config file\n");
status = SEALIGHTER_ERROR_PARSE_NO_PROVIDERS;
}
else {
if (!json_config["user_traces"].is_null()) {
status = add_user_traces(json_config, session_properties, session_name);
}
// Add kernel providers if needed
if (ERROR_SUCCESS == status && !json_config["kernel_traces"].is_null()) {
status = add_kernel_traces(json_config, session_properties);
}
}
}
return status;
}
/*
Run a trace, and ensure we stop if something goes wrong
*/
template <typename T>
void run_trace(trace<T>* trace)
{
if (NULL != trace) {
// Ensure we always stop the trace afterwards
try {
trace->start();
}
catch (...) {
trace->stop();
throw;
}
}
}
/*
Stop any running trace
*/
void stop_traces()
{
if (NULL != g_user_session) {
g_user_session->stop();
}
if (NULL != g_kernel_session) {
g_kernel_session->stop();
}
}
/*
Handler for Ctrl+C cancel events.
Makes sure we stop our ETW Session when shutting down
*/
BOOL WINAPI crl_c_handler
(
DWORD fdwCtrlType
)
{
switch (fdwCtrlType)
{
case CTRL_C_EVENT:
stop_traces();
return TRUE;
}
return FALSE;
}
// -------------------------
// PRIVATE FUNCTIONS - END
// -------------------------
// PUBLIC FUNCTIONS - START
// -------------------------
int run_sealighter
(
std::string config_path
)
{
int status = ERROR_SUCCESS;
// Setup Event Logging
status = EventRegisterSealighter();
if (ERROR_SUCCESS != status) {
printf("Error registering event log: %ul\n", status);
return SEALIGHTER_ERROR_EVENTLOG_REGISTER;
}
// Add ctrl+C handler to make sure we stop the trace
if (!SetConsoleCtrlHandler(crl_c_handler, TRUE)) {
printf("failed to set ctrl-c handler\n");
return SEALIGHTER_ERROR_CTRL_C_REGISTER;
}
// Parse config file
status = parse_config(config_path);
if (ERROR_SUCCESS != status) {
return status;
}
if (NULL == g_user_session && NULL == g_kernel_session) {
printf("Failed to define any ETW Session\n");
return SEALIGHTER_ERROR_NO_SESSION_CREATED;
}
else {
// Setup Buffering thread if needed
start_bufferring();
// Start Trace we've configured
// Don't run multithreaded if we don't have to
if (NULL != g_user_session && NULL == g_kernel_session) {
printf("Starting User Trace...\n");
printf("-----------------------------------------\n");
run_trace(g_user_session);
}
else if (NULL == g_user_session && NULL != g_kernel_session) {
printf("Starting Kernel Trace...\n");
printf("-----------------------------------------\n");
run_trace(g_kernel_session);
}
else {
// Have to multi-thread it
printf("Starting User and Kernel Traces...\n");
printf("-----------------------------------------\n");
std::thread user_thread = std::thread(run_trace<details::ut>, g_user_session);
std::thread kernel_thread = std::thread(run_trace<details::kt>, g_kernel_session);
// Call join, blocking until both have shut down
user_thread.join();
kernel_thread.join();
}
// Teardown and cleanup
stop_bufferring();
teardown_logger_file();
(void)EventUnregisterSealighter();
}
return status;
}