diff --git a/external/Catch2 b/external/Catch2 index fb51116d5b..8ac8190e49 160000 --- a/external/Catch2 +++ b/external/Catch2 @@ -1 +1 @@ -Subproject commit fb51116d5b20ea3fef6c5a89267e7d91856a60cf +Subproject commit 8ac8190e494a381072c89f5e161b92a08d98b37b diff --git a/include/ebpf_nethooks.h b/include/ebpf_nethooks.h index 3f1ba7e83c..ba2dc86262 100644 --- a/include/ebpf_nethooks.h +++ b/include/ebpf_nethooks.h @@ -273,8 +273,8 @@ typedef enum _process_operation typedef struct _process_md { - uint8_t* command_start; ///< Pointer to start of the command line. - uint8_t* command_end; ///< Pointer to end of the command line. + uint8_t* command_start; ///< Pointer to start of the command line as UTF-8 string. + uint8_t* command_end; ///< Pointer to end of the command line as UTF-8 string. uint64_t process_id; ///< Process ID. uint64_t parent_process_id; ///< Parent process ID. uint64_t creating_process_id; ///< Creating process ID. @@ -298,6 +298,33 @@ typedef struct _process_md typedef int process_hook_t(process_md_t* context); +// Process helper functions. +#define PROCESS_EXT_HELPER_FN_BASE 0xFFFF + +#ifndef __doxygen +#define EBPF_HELPER(return_type, name, args) typedef return_type(*name##_t) args +#endif + +typedef enum +{ + BPF_FUNC_process_get_image_path = PROCESS_EXT_HELPER_FN_BASE + 1, +} ebpf_process_helper_id_t; + +/** + * @brief Get the image path of the process. + * + * @param[in] context Process metadata. + * @param[out] path Buffer to store the image path. + * @param[in] path_length Length of the buffer. + * + * @retval >=0 The length of the image path. + * @retval <0 A failure occurred. + */ +EBPF_HELPER(int, bpf_process_get_image_path, (process_md_t * ctx, uint8_t* path, uint32_t path_length)); +#ifndef __doxygen +#define bpf_process_get_image_path ((bpf_process_get_image_path_t)BPF_FUNC_process_get_image_path) +#endif + #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/netebpfext/net_ebpf_ext_process.c b/netebpfext/net_ebpf_ext_process.c index 57f0553978..0601fddf6d 100644 --- a/netebpfext/net_ebpf_ext_process.c +++ b/netebpfext/net_ebpf_ext_process.c @@ -9,6 +9,8 @@ #include "ebpf_shared_framework.h" #include "net_ebpf_ext_process.h" +#include + static ebpf_result_t _ebpf_process_context_create( _In_reads_bytes_opt_(data_size_in) const uint8_t* data_in, @@ -29,11 +31,20 @@ void _ebpf_process_create_process_notify_routine_ex( _Inout_ PEPROCESS process, _In_ HANDLE process_id, _Inout_opt_ PPS_CREATE_NOTIFY_INFO create_info); +static int32_t +_ebpf_process_get_image_path(_In_ process_md_t* process_md, _Out_ uint8_t* path, uint32_t path_length); + +static const void* _ebpf_process_helper_functions[] = {(void*)&_ebpf_process_get_image_path}; + +static ebpf_helper_function_addresses_t _ebpf_process_helper_function_address_table = { + EBPF_COUNT_OF(_ebpf_process_helper_functions), (uint64_t*)_ebpf_process_helper_functions}; + // // Process Program Information NPI Provider. // static ebpf_program_data_t _ebpf_process_program_data = { .program_info = &_ebpf_process_program_info, + .program_type_specific_helper_function_addresses = &_ebpf_process_helper_function_address_table, .context_create = _ebpf_process_context_create, .context_destroy = _ebpf_process_context_destroy, .required_irql = PASSIVE_LEVEL, @@ -304,6 +315,8 @@ typedef struct _process_notify_context process_md_t process_md; PEPROCESS process; PPS_CREATE_NOTIFY_INFO create_info; + UTF8_STRING command_line_utf8; + UTF8_STRING image_file_name_utf8; } process_notify_context_t; void @@ -315,22 +328,27 @@ _ebpf_process_create_process_notify_routine_ex( NET_EBPF_EXT_LOG_ENTRY(); - if (create_info != NULL && create_info->CommandLine != NULL) { - process_notify_context.process_md.command_start = (uint8_t*)create_info->CommandLine->Buffer; + if (create_info != NULL) { + if (create_info->CommandLine != NULL) { + RtlUnicodeStringToUTF8String(&process_notify_context.command_line_utf8, create_info->CommandLine, TRUE); + } + if (create_info->ImageFileName != NULL) { + RtlUnicodeStringToUTF8String( + &process_notify_context.image_file_name_utf8, create_info->ImageFileName, TRUE); + } + process_notify_context.process_md.operation = PROCESS_OPERATION_CREATE; + process_notify_context.process_md.process_id = (uint64_t)process_id; + process_notify_context.process_md.parent_process_id = (uint64_t)create_info->ParentProcessId; + process_notify_context.process_md.creating_process_id = (uint64_t)create_info->CreatingThreadId.UniqueProcess; + process_notify_context.process_md.creating_thread_id = (uint64_t)create_info->CreatingThreadId.UniqueThread; + process_notify_context.process_md.command_start = (uint8_t*)process_notify_context.command_line_utf8.Buffer; process_notify_context.process_md.command_end = - (uint8_t*)create_info->CommandLine->Buffer + create_info->CommandLine->Length; + (uint8_t*)process_notify_context.command_line_utf8.Buffer + process_notify_context.command_line_utf8.Length; + } else { + process_notify_context.process_md.operation = PROCESS_OPERATION_DELETE; + process_notify_context.process_md.process_id = (uint64_t)process_id; } - process_notify_context.process_md.operation = - (create_info != NULL) ? PROCESS_OPERATION_CREATE : PROCESS_OPERATION_DELETE; - process_notify_context.process_md.process_id = (uint64_t)process_id; - process_notify_context.process_md.parent_process_id = - (create_info != NULL) ? (uint64_t)create_info->ParentProcessId : 0; - process_notify_context.process_md.creating_process_id = - (create_info != NULL) ? (uint64_t)create_info->CreatingThreadId.UniqueProcess : 0; - process_notify_context.process_md.creating_thread_id = - (create_info != NULL) ? (uint64_t)create_info->CreatingThreadId.UniqueThread : 0; - // For each attached client call the process hook. ebpf_result_t result; net_ebpf_extension_hook_client_t* client_context = @@ -363,5 +381,33 @@ _ebpf_process_create_process_notify_routine_ex( net_ebpf_extension_hook_get_next_attached_client(_ebpf_process_hook_provider_context, client_context); } + if (process_notify_context.command_line_utf8.Buffer != NULL) { + RtlFreeUTF8String(&process_notify_context.command_line_utf8); + } + + if (process_notify_context.image_file_name_utf8.Buffer != NULL) { + RtlFreeUTF8String(&process_notify_context.image_file_name_utf8); + } + NET_EBPF_EXT_LOG_EXIT(); } + +static int32_t +_ebpf_process_get_image_path(_In_ process_md_t* process_md, _Out_ uint8_t* path, uint32_t path_length) +{ + process_notify_context_t* process_notify_context = (process_notify_context_t*)process_md; + int32_t result = 0; + if (process_notify_context->image_file_name_utf8.Length > path_length) { + return -EINVAL; + } + if (process_notify_context->image_file_name_utf8.Buffer != NULL) { + if (path_length >= process_notify_context->image_file_name_utf8.Length) { + memcpy( + path, + process_notify_context->image_file_name_utf8.Buffer, + process_notify_context->image_file_name_utf8.Length); + result = process_notify_context->image_file_name_utf8.Length; + } + } + return result; +} diff --git a/netebpfext/net_ebpf_ext_program_info.h b/netebpfext/net_ebpf_ext_program_info.h index b26b1c125e..7ab913dc0d 100644 --- a/netebpfext/net_ebpf_ext_program_info.h +++ b/netebpfext/net_ebpf_ext_program_info.h @@ -122,15 +122,27 @@ static const ebpf_program_section_info_t _ebpf_sock_ops_section_info[] = { BPF_PROG_TYPE_SOCK_OPS, BPF_CGROUP_SOCK_OPS}}; +// Process program information. +static const ebpf_helper_function_prototype_t _process_ebpf_extension_helper_function_prototype[] = { + {XDP_EXT_HELPER_FUNCTION_START + 1, + "bpf_process_get_image_path", + EBPF_RETURN_TYPE_INTEGER, + {EBPF_ARGUMENT_TYPE_PTR_TO_CTX, EBPF_ARGUMENT_TYPE_PTR_TO_WRITABLE_MEM, EBPF_ARGUMENT_TYPE_CONST_SIZE}}, +}; + static const ebpf_context_descriptor_t _ebpf_process_context_descriptor = { - sizeof(process_md_t), EBPF_OFFSET_OF(process_md_t, command_start), EBPF_OFFSET_OF(process_md_t, command_end), -1}; + sizeof(process_md_t), + EBPF_OFFSET_OF(process_md_t, command_start), + EBPF_OFFSET_OF(process_md_t, command_end), + -1, +}; static const ebpf_program_info_t _ebpf_process_program_info = { - {"process", &_ebpf_process_context_descriptor, EBPF_PROGRAM_TYPE_PROCESS_GUID, BPF_PROG_TYPE_PROCESS}, 0, NULL}; + {"process", &_ebpf_process_context_descriptor, EBPF_PROGRAM_TYPE_PROCESS_GUID, BPF_PROG_TYPE_PROCESS}, + EBPF_COUNT_OF(_process_ebpf_extension_helper_function_prototype), + _process_ebpf_extension_helper_function_prototype, +}; static const ebpf_program_section_info_t _ebpf_process_section_info[] = { - {L"process", - &EBPF_PROGRAM_TYPE_PROCESS, - &EBPF_ATTACH_TYPE_PROCESS, - BPF_PROG_TYPE_PROCESS, - BPF_ATTACH_TYPE_PROCESS}}; + {L"process", &EBPF_PROGRAM_TYPE_PROCESS, &EBPF_ATTACH_TYPE_PROCESS, BPF_PROG_TYPE_PROCESS, BPF_ATTACH_TYPE_PROCESS}, +}; diff --git a/tests/sample/process_monitor.c b/tests/sample/process_monitor.c index b5c8564acd..4c06bba215 100644 --- a/tests/sample/process_monitor.c +++ b/tests/sample/process_monitor.c @@ -19,33 +19,37 @@ #include "bpf_helpers.h" #include "ebpf_nethooks.h" +// The non variable fields from the process_md_t struct. typedef struct { + uint64_t process_id; uint64_t parent_process_id; - uint8_t command_line[256]; -} proces_entry_t; + uint64_t creating_process_id; + uint64_t creating_thread_id; + uint64_t operation; +} process_info_t; -typedef struct -{ - uint64_t process_id; - proces_entry_t entry; -} process_create_event_t; +#define MAX_PATH (496 - sizeof(process_info_t)) -typedef struct +// LRU hash for storing the image path of a process. +struct { - uint64_t process_id; -} process_delete_event_t; + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, uint64_t); + __type(value, char[MAX_PATH]); + __uint(max_entries, 1024); +} process_map SEC(".maps"); -// Map for running processes. +// LRU hash for storing the command line of a process. struct { - __uint(type, BPF_MAP_TYPE_HASH); + __uint(type, BPF_MAP_TYPE_LRU_HASH); __type(key, uint64_t); - __type(value, proces_entry_t); + __type(value, char[MAX_PATH]); __uint(max_entries, 1024); -} process_map SEC(".maps"); +} command_map SEC(".maps"); -// Ringbuffer for process events. +// Ring-buffer for process_info_t. struct { __uint(type, BPF_MAP_TYPE_RINGBUF); @@ -61,26 +65,29 @@ SEC("process") int ProcessMonitor(process_md_t* ctx) { + process_info_t process_info = { + .process_id = ctx->process_id, + .parent_process_id = ctx->parent_process_id, + .creating_process_id = ctx->creating_process_id, + .creating_thread_id = ctx->creating_thread_id, + .operation = ctx->operation, + }; + if (ctx->operation == PROCESS_OPERATION_CREATE) { - process_create_event_t create_event; - __builtin_memset(&create_event, 0, sizeof(create_event)); - create_event.entry.parent_process_id = ctx->parent_process_id; - create_event.process_id = ctx->process_id; - uint64_t process_id = ctx->process_id; + uint8_t buffer[MAX_PATH]; + + memset(buffer, sizeof(buffer), 0); + + memcpy_s(buffer, sizeof(buffer), ctx->command_start, ctx->command_end - ctx->command_start); + bpf_map_update_elem(&command_map, &process_info.process_id, buffer, BPF_ANY); - memcpy_s( - create_event.entry.command_line, - sizeof(create_event.entry.command_line), - ctx->command_start, - (uint32_t)(ctx->command_end - ctx->command_start)); + // Reset the buffer. + memset(buffer, sizeof(buffer), 0); - bpf_map_update_elem(&process_map, &process_id, &create_event.entry, BPF_ANY); - bpf_ringbuf_output(&process_ringbuf, &create_event, sizeof(create_event), 0); - } else if (ctx->operation == PROCESS_OPERATION_DELETE) { - process_delete_event_t delete_event = {.process_id = ctx->process_id}; - uint64_t process_id = ctx->process_id; - bpf_map_delete_elem(&process_map, &process_id); - bpf_ringbuf_output(&process_ringbuf, &delete_event, sizeof(delete_event), 0); + // Copy image path into the LRU hash. + bpf_process_get_image_path(ctx, buffer, sizeof(buffer)); + bpf_map_update_elem(&process_map, &process_info.process_id, buffer, BPF_ANY); } + bpf_ringbuf_output(&process_ringbuf, &process_info, sizeof(process_info), 0); return 0; } diff --git a/tests/unit/libbpf_test.cpp b/tests/unit/libbpf_test.cpp index f7822b40f1..9998d18b9f 100644 --- a/tests/unit/libbpf_test.cpp +++ b/tests/unit/libbpf_test.cpp @@ -3424,7 +3424,9 @@ _process_hook_test(ebpf_execution_type_t execution_type) single_instance_hook_t hook(EBPF_PROGRAM_TYPE_PROCESS, EBPF_ATTACH_TYPE_PROCESS); REQUIRE(hook.initialize() == EBPF_SUCCESS); program_info_provider_t sample_program_info; - REQUIRE(sample_program_info.initialize(EBPF_PROGRAM_TYPE_PROCESS) == EBPF_SUCCESS); + REQUIRE( + sample_program_info.initialize(EBPF_PROGRAM_TYPE_PROCESS, &_test_process_program_info_provider_data) == + EBPF_SUCCESS); const char* file_name = (execution_type == EBPF_EXECUTION_NATIVE ? dll_name : obj_name); struct bpf_object* process_object = bpf_object__open(file_name); @@ -3439,6 +3441,9 @@ _process_hook_test(ebpf_execution_type_t execution_type) struct bpf_map* process_map = bpf_object__find_map_by_name(process_object, "process_map"); REQUIRE(process_map != nullptr); + struct bpf_map* command_map = bpf_object__find_map_by_name(process_object, "command_map"); + REQUIRE(command_map != nullptr); + bpf_link_ptr link(bpf_program__attach(caller)); REQUIRE(link != nullptr); @@ -3447,7 +3452,7 @@ _process_hook_test(ebpf_execution_type_t execution_type) ctx.process_id = 1234; ctx.operation = PROCESS_OPERATION_CREATE; ctx.parent_process_id = 5678; - ctx.command_start = (uint8_t*)"test_process"; + ctx.command_start = (uint8_t*)"notepad foo.bar.txt"; ctx.command_end = ctx.command_start + strlen((char*)ctx.command_start); uint32_t result; @@ -3455,19 +3460,21 @@ _process_hook_test(ebpf_execution_type_t execution_type) // Check if the entry was added to the map. uint64_t key = 1234; - proces_entry_t value; - REQUIRE(bpf_map_lookup_elem(bpf_map__fd(process_map), &key, &value) == 0); + uint8_t value[512] = {0}; + bpf_map_lookup_elem(bpf_map__fd(command_map), &key, &value); - // Verify the entry. - REQUIRE(value.parent_process_id == 5678); - REQUIRE(strcmp((char*)value.command_line, "test_process") == 0); + std::string returned_command = (char*)value; - // Test process termination. - ctx.operation = PROCESS_OPERATION_DELETE; - REQUIRE(hook.fire(&ctx, &result) == EBPF_SUCCESS); + // Verify the result. + REQUIRE(returned_command == "notepad foo.bar.txt"); - // Check if the entry was removed from the map. - REQUIRE(bpf_map_lookup_elem(bpf_map__fd(process_map), &key, &value) != 0); + // Get the image path. + bpf_map_lookup_elem(bpf_map__fd(process_map), &key, &value); + + returned_command = (char*)value; + + // Verify the result. + REQUIRE(returned_command == "C:\\Windows\\System32\\notepad.exe"); result = bpf_link__destroy(link.release()); REQUIRE(result == 0); diff --git a/tools/process_monitor/process_monitor.cpp b/tools/process_monitor/process_monitor.cpp index ffb95dce04..97be602225 100644 --- a/tools/process_monitor/process_monitor.cpp +++ b/tools/process_monitor/process_monitor.cpp @@ -18,43 +18,56 @@ extern "C" int process_monitor_history_callback(void* ctx, void* data, size_t size); } - -typedef struct +typedef enum _process_operation { - uint64_t parent_process_id; - uint8_t command_line[256]; -} proces_entry_t; + PROCESS_OPERATION_CREATE, ///< Process creation. + PROCESS_OPERATION_DELETE, ///< Process deletion. +} process_operation_t; typedef struct { uint64_t process_id; - proces_entry_t entry; -} process_create_event_t; + uint64_t parent_process_id; + uint64_t creating_process_id; + uint64_t creating_thread_id; + uint64_t operation; +} process_info_t; -typedef struct -{ - uint64_t process_id; -} process_delete_event_t; +struct bpf_map* process_map; +struct bpf_map* command_map; int process_monitor_history_callback(void* ctx, void* data, size_t size) { UNREFERENCED_PARAMETER(ctx); + if (size != sizeof(process_info_t)) { + return 0; + } + process_info_t* event = (process_info_t*)data; + std::string file_name; + std::string command_line; + + file_name.resize(1024); + command_line.resize(1024); - switch (size) { - case sizeof(process_create_event_t): { - process_create_event_t* event = (process_create_event_t*)data; - std::wcout << L"Process created: " << event->process_id << L" " - << reinterpret_cast(event->entry.command_line) << std::endl; + // Get the process name. + bpf_map_lookup_elem(bpf_map__fd(process_map), &event->process_id, file_name.data()); + bpf_map_lookup_elem(bpf_map__fd(command_map), &event->process_id, command_line.data()); + + // Trim the strings. + file_name.resize(strlen(file_name.c_str())); + command_line.resize(strlen(command_line.c_str())); + + switch (event->operation) { + case PROCESS_OPERATION_CREATE: { + std::cout << "Process created: " << event->process_id << "\n" << file_name << "\n" << command_line << std::endl; break; } - case sizeof(process_delete_event_t): { - process_delete_event_t* event = (process_delete_event_t*)data; - std::wcout << L"Process deleted: " << event->process_id << std::endl; + case PROCESS_OPERATION_DELETE: { + std::cout << "Process deleted: " << event->process_id << "\n" << file_name << std::endl; break; } default: - std::wcout << L"Unknown event size: " << size << std::endl; break; } @@ -87,6 +100,9 @@ main(int argc, char** argv) return 1; } + // Command line and image path are UTF8. + SetConsoleOutputCP(CP_UTF8); + std::cerr << "Press Ctrl-C to shutdown" << std::endl; // Load process_monitor.sys BPF program. @@ -101,6 +117,18 @@ main(int argc, char** argv) return 1; } + process_map = bpf_object__find_map_by_name(object, "process_map"); + if (!process_map) { + std::cerr << "bpf_object__find_map_by_name for \"process_map\" failed: " << errno << std::endl; + return 1; + } + + command_map = bpf_object__find_map_by_name(object, "command_map"); + if (!command_map) { + std::cerr << "bpf_object__find_map_by_name for \"command_map\" failed: " << errno << std::endl; + return 1; + } + auto process_monitor = bpf_object__find_program_by_name(object, "ProcessMonitor"); if (!process_monitor) { std::cerr << "bpf_object__find_program_by_name for \"connection_tracker\" failed: " << errno << std::endl;