Implement eBPF extension and provider loading functionality (#90)

* Add support for invoking NMR to register client and providers.
Load global helper functions as extensions.
Create user-mode mocks to test extension loading.

Resolves: #80

Signed-off-by: Alan Jowett <alanjo@microsoft.com>
This commit is contained in:
Alan Jowett 2021-04-26 16:37:12 -06:00 коммит произвёл GitHub
Родитель 68cc22c090
Коммит 0b0eaca37c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
19 изменённых файлов: 834 добавлений и 71 удалений

Просмотреть файл

@ -266,7 +266,7 @@ extern "C"
ebpf_api_unpin_map(const uint8_t* name, uint32_t name_length);
/**
* @brief Find a map given it's associated name.
* @brief Find a map given its associated name.
* @param[in] name Name to find.
*/
uint32_t

Просмотреть файл

@ -14,6 +14,8 @@ extern "C"
#endif
#include "ebpf_protocol.h"
extern GUID ebpf_global_helper_function_interface_id;
typedef uint32_t(__stdcall* ebpf_hook_function)(uint8_t*);
/**
@ -82,23 +84,6 @@ extern "C"
ebpf_core_get_protocol_handler_properties(
ebpf_operation_id_t operation_id, _Out_ size_t* minimum_request_size, _Out_ size_t* minimum_reply_size);
/**
* @brief Get the count of global helper functions.
*
* @return Count of global helper functions.
*/
size_t
ebpf_core_get_global_helper_count();
/**
* @brief Get the address of a global helper function.
*
* @param[in] helper_id Id of the global helper function.
* @return Address of the global helper function.
*/
const void*
ebpf_core_get_global_helper(size_t helper_id);
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -42,13 +42,22 @@ extern "C"
typedef struct _epbf_non_preemptible_work_item epbf_non_preemptible_work_item_t;
typedef struct _ebpf_timer_work_item ebpf_timer_work_item_t;
typedef struct _ebpf_extension_client ebpf_extension_client_t;
typedef struct _ebpf_extension_provider ebpf_extension_provider_t;
typedef ebpf_error_code_t (*_ebpf_extension_dispatch_function)();
typedef struct _ebpf_extension_dispatch_table
{
size_t size;
uint16_t version;
uint16_t size;
_ebpf_extension_dispatch_function function[1];
} ebpf_extension_dispatch_table_t;
typedef struct _ebpf_extension_data
{
uint16_t version;
uint16_t size;
uint8_t data[1];
} ebpf_extension_data_t;
#define EBPF_LOCK_SIZE sizeof(uint64_t)
#define EBPF_LOCK_STATE_SIZE sizeof(uint64_t)
typedef uint8_t ebpf_lock_t[EBPF_LOCK_SIZE];
@ -425,18 +434,18 @@ extern "C"
ebpf_interlocked_compare_exchange_int32(volatile int32_t* destination, int32_t exchange, int32_t comperand);
/**
* @brief Load an extension and get it's dispatch table.
* @brief Load an extension and get its dispatch table.
*
* @param[out] client_context Context used to unload the extension.
* @param[in] client_id GUID representing the identity of the client.
* @param[in] interface_id GUID representing the identity of the interface.
* @param[in] client_binding_context Opaque per-instance pointer passed to the extension.
* @param[in] client_data Opaque client data passed to the extension.
* @param[in] client_data_length Length of the client data.
* @param[in] client_dispatch_table Table of function pointers the client
* exposes.
* @param[in] provider_id GUID representing the extension to load.
* @param[out] provider_data Opaque provider data.
* @param[out] provider_data_length Length of the provider data.
* @param[out] provider_dispatch_table Table of function pointer the
* @param[out] provider_dispatch_table Table of function pointers the
* provider exposes.
* @retval EBPF_ERROR_SUCCESS The operation was successful.
* @retval EBPF_ERROR_NOT_FOUND The provider was not found.
@ -446,13 +455,12 @@ extern "C"
ebpf_error_code_t
ebpf_extension_load(
ebpf_extension_client_t** client_context,
const GUID* client_id,
const uint8_t* client_data,
size_t client_data_length,
const GUID* interface_id,
void* client_binding_context,
const ebpf_extension_data_t* client_data,
const ebpf_extension_dispatch_table_t* client_dispatch_table,
const GUID* provider_id,
uint8_t** provider_data,
size_t* provider_data_length,
void** provider_binding_context,
ebpf_extension_data_t** provider_data,
ebpf_extension_dispatch_table_t** provider_dispatch_table);
/**
@ -463,6 +471,51 @@ extern "C"
void
ebpf_extension_unload(ebpf_extension_client_t* client_context);
typedef ebpf_error_code_t (*ebpf_provider_client_attach_callback_t)(
const GUID* client_id,
void* client_binding_context,
const ebpf_extension_data_t* client_data,
const ebpf_extension_dispatch_table_t* client_dispatch_table);
typedef ebpf_error_code_t (*ebpf_provider_client_detach_callback_t)(const GUID* client_id);
/**
* @brief Register as an extension provider.
*
* @param[out] provider_context Context used to unload the provider.
* @param[in] interface_id GUID representing the identity of the interface.
* @param[in] provider_data Opaque provider data.
* @param[in] provider_dispatch_table Table of function pointers the
* provider exposes.
* @param[in] client_attach_callback Function invoked when a client attaches.
* @param[in] client_detach_callback Function invoked when a client detaches.
* @retval EBPF_ERROR_SUCCESS The operation was successful.
* @retval EBPF_ERROR_EXTENSION_FAILED_TO_LOAD The provider was unable to
* load.
* @retval EBPF_ERROR_OUT_OF_RESOURCES Unable to allocate resources for this
* operation.
*/
ebpf_error_code_t
ebpf_provider_load(
ebpf_extension_provider_t** provider_context,
const GUID* interface_id,
void* provider_binding_context,
const ebpf_extension_data_t* provider_data,
const ebpf_extension_dispatch_table_t* provider_dispatch_table,
ebpf_provider_client_attach_callback_t client_attach_callback,
ebpf_provider_client_detach_callback_t client_detach_callback);
/**
* @brief Unload a provider.
*
* @param[in] provider_context Provider to unload.
*/
void
epbf_provider_unload(ebpf_extension_provider_t* provider_context);
ebpf_error_code_t
ebpf_guid_create(GUID* new_guid);
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -40,6 +40,7 @@ typedef enum _ebpf_error_code
EBPF_ERROR_NOT_SUPPORTED,
EBPF_ERROR_DUPLICATE_NAME,
EBPF_ERROR_ARITHMETIC_OVERFLOW,
EBPF_ERROR_EXTENSION_FAILED_TO_LOAD,
} ebpf_error_code_t;
typedef struct _ebpf_map_definition

Просмотреть файл

@ -6,6 +6,7 @@
#include <ntstatus.h>
#include <ntintsafe.h>
#include <ntddk.h>
#include <netioddk.h>
#define uint8_t UINT8
#define uint16_t UINT16
#define uint32_t UINT32

Просмотреть файл

@ -12,9 +12,12 @@
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <rpc.h>
#include <windows.h>
#include <winioctl.h>
#pragma comment(lib, "rpcrt4")
#define ebpf_assert(x) assert(x)
#if !defined(UNREFERENCED_PARAMETER)

Просмотреть файл

@ -11,6 +11,15 @@
#include "ebpf_pinning_table.h"
#include "ebpf_program.h"
GUID ebpf_global_helper_function_interface_id = {/* 8d2a1d3f-9ce6-473d-b48e-17aa5c5581fe */
0x8d2a1d3f,
0x9ce6,
0x473d,
{0xb4, 0x8e, 0x17, 0xaa, 0x5c, 0x55, 0x81, 0xfe}};
static ebpf_extension_dispatch_table_t* _ebpf_global_helper_function_dispatch_table = NULL;
static ebpf_extension_provider_t* _ebpf_global_helper_function_provider_context = NULL;
static ebpf_pinning_table_t* _ebpf_core_map_pinning_table = NULL;
static ebpf_lock_t _ebpf_core_hook_table_lock = {0};
@ -35,18 +44,6 @@ static const void* _ebpf_program_helpers[] = {
(void*)&_ebpf_core_map_update_element,
(void*)&_ebpf_core_map_delete_element};
size_t
ebpf_core_get_global_helper_count()
{
return EBPF_COUNT_OF(_ebpf_program_helpers);
}
const void*
ebpf_core_get_global_helper(size_t helper_id)
{
return _ebpf_program_helpers[helper_id];
}
static ebpf_error_code_t
_ebpf_core_set_hook_entry(ebpf_program_t* code, ebpf_program_type_t program_type)
{
@ -101,13 +98,38 @@ ebpf_core_initiate()
ebpf_lock_create(&_ebpf_core_hook_table_lock);
_ebpf_global_helper_function_dispatch_table = ebpf_allocate(
EBPF_OFFSET_OF(ebpf_extension_dispatch_table_t, function) + sizeof(_ebpf_program_helpers),
EBPF_MEMORY_NO_EXECUTE);
if (!_ebpf_global_helper_function_dispatch_table) {
return_value = EBPF_ERROR_OUT_OF_RESOURCES;
goto Done;
}
_ebpf_global_helper_function_dispatch_table->version = 0;
_ebpf_global_helper_function_dispatch_table->size =
EBPF_OFFSET_OF(ebpf_extension_dispatch_table_t, function) + sizeof(_ebpf_program_helpers);
memcpy(_ebpf_global_helper_function_dispatch_table->function, _ebpf_program_helpers, sizeof(_ebpf_program_helpers));
return_value = ebpf_provider_load(
&_ebpf_global_helper_function_provider_context,
&ebpf_global_helper_function_interface_id,
NULL,
NULL,
_ebpf_global_helper_function_dispatch_table,
NULL,
NULL);
if (return_value != EBPF_ERROR_SUCCESS) {
goto Done;
}
return_value = ebpf_get_code_integrity_state(&_ebpf_core_code_integrity_state);
Done:
if (return_value != EBPF_ERROR_SUCCESS) {
ebpf_epoch_terminate();
ebpf_platform_terminate();
ebpf_handle_table_terminate();
ebpf_core_terminate();
}
return return_value;
}
@ -115,6 +137,12 @@ Done:
void
ebpf_core_terminate()
{
epbf_provider_unload(_ebpf_global_helper_function_provider_context);
_ebpf_global_helper_function_provider_context = NULL;
ebpf_free(_ebpf_global_helper_function_dispatch_table);
_ebpf_global_helper_function_dispatch_table = NULL;
ebpf_handle_table_terminate();
ebpf_pinning_table_free(_ebpf_core_map_pinning_table);

Просмотреть файл

@ -17,17 +17,14 @@ typedef struct _ebpf_hook_instance
ebpf_program_entry_point_t program_entry_point;
ebpf_attach_type_t attach_type;
ebpf_extension_data_t* client_data;
ebpf_extension_client_t* extension_client_context;
uint8_t* hook_properties;
size_t hook_properties_length;
void* provider_binding_context;
ebpf_extension_data_t* provider_data;
ebpf_extension_dispatch_table_t* provider_dispatch_table;
} ebpf_hook_instance_t;
// TODO: Get the actual GUID for the hook client.
static const GUID _ebpf_hook_client_id = {0};
ebpf_error_code_t
_ebpf_hook_instance_invoke(const ebpf_hook_instance_t* hook, void* program_context);
@ -43,6 +40,7 @@ _ebpf_hook_free(ebpf_object_t* object)
ebpf_hook_instance_t* hook = (ebpf_hook_instance_t*)object;
ebpf_hook_instance_detach_program(hook);
ebpf_extension_unload(hook->extension_client_context);
ebpf_free(hook->client_data);
ebpf_epoch_free(hook);
}
@ -64,16 +62,26 @@ ebpf_hook_instance_initialize(
ebpf_hook_instance_t* hook, ebpf_attach_type_t attach_type, const uint8_t* context_data, size_t context_data_length)
{
ebpf_error_code_t return_value;
size_t client_data_length;
ebpf_safe_size_t_add(sizeof(ebpf_extension_data_t), context_data_length, &client_data_length);
hook->client_data = ebpf_allocate(client_data_length, EBPF_MEMORY_NO_EXECUTE);
if (!hook->client_data)
return EBPF_ERROR_OUT_OF_RESOURCES;
hook->client_data->version = 0;
hook->client_data->size = (uint16_t)client_data_length;
memcpy(hook->client_data->data, context_data, context_data_length);
return_value = ebpf_extension_load(
&(hook->extension_client_context),
&_ebpf_hook_client_id,
context_data,
context_data_length,
(ebpf_extension_dispatch_table_t*)&_ebpf_hook_dispatch_table,
&attach_type,
&(hook->hook_properties),
&(hook->hook_properties_length),
hook,
hook->client_data,
(ebpf_extension_dispatch_table_t*)&_ebpf_hook_dispatch_table,
&(hook->provider_binding_context),
&(hook->provider_data),
&(hook->provider_dispatch_table));
return return_value;
@ -82,11 +90,11 @@ ebpf_hook_instance_initialize(
ebpf_error_code_t
ebpf_hook_instance_get_properties(ebpf_hook_instance_t* hook, uint8_t** hook_properties, size_t* hook_properties_length)
{
if (!hook->hook_properties)
if (!hook->provider_data)
return EBPF_ERROR_INVALID_PARAMETER;
*hook_properties = hook->hook_properties;
*hook_properties_length = hook->hook_properties_length;
*hook_properties = hook->provider_data->data;
*hook_properties_length = hook->provider_data->size;
return EBPF_ERROR_SUCCESS;
}

Просмотреть файл

@ -26,6 +26,11 @@ typedef struct _ebpf_program
// EBPF_CODE_EBPF
struct ubpf_vm* vm;
} code_or_vm;
ebpf_extension_client_t* global_helper_extension_client;
ebpf_extension_data_t* global_helper_provider_data;
ebpf_extension_dispatch_table_t* global_helper_provider_dispatch_table;
} ebpf_program_t;
static void
@ -35,6 +40,10 @@ _ebpf_program_free(ebpf_object_t* object)
if (!program)
return;
if (program->global_helper_extension_client) {
ebpf_extension_unload(program->global_helper_extension_client);
}
if (program->parameters.code_type == EBPF_CODE_NATIVE) {
ebpf_epoch_free(program->code_or_vm.code);
} else {
@ -66,6 +75,20 @@ ebpf_program_initialize(ebpf_program_t* program, const ebpf_program_parameters_t
ebpf_error_code_t return_value;
ebpf_utf8_string_t local_program_name = {NULL, 0};
ebpf_utf8_string_t local_section_name = {NULL, 0};
void* provider_binding_context;
return_value = ebpf_extension_load(
&program->global_helper_extension_client,
&ebpf_global_helper_function_interface_id,
NULL,
NULL,
NULL,
&provider_binding_context,
&program->global_helper_provider_data,
&program->global_helper_provider_dispatch_table);
if (return_value != EBPF_ERROR_SUCCESS)
goto Done;
return_value = ebpf_duplicate_utf8_string(&local_program_name, &program_parameters->program_name);
if (return_value != EBPF_ERROR_SUCCESS)
@ -127,17 +150,19 @@ Done:
}
static ebpf_error_code_t
_ebpf_program_register_helpers(struct ubpf_vm* vm)
_ebpf_program_register_helpers(ebpf_program_t* program)
{
size_t index = 0;
size_t count = ebpf_core_get_global_helper_count();
size_t count = (program->global_helper_provider_dispatch_table->size -
EBPF_OFFSET_OF(ebpf_extension_dispatch_table_t, function)) /
sizeof(program->global_helper_provider_dispatch_table->function);
for (index = 0; index < count; index++) {
const void* helper = ebpf_core_get_global_helper(index);
const void* helper = (void*)program->global_helper_provider_dispatch_table->function[index];
if (helper == NULL)
continue;
if (ubpf_register(vm, (unsigned int)index, NULL, (void*)helper) < 0)
if (ubpf_register(program->code_or_vm.vm, (unsigned int)index, NULL, (void*)helper) < 0)
return EBPF_ERROR_INVALID_PARAMETER;
}
return EBPF_ERROR_SUCCESS;
@ -160,7 +185,7 @@ ebpf_program_load_byte_code(ebpf_program_t* program, ebpf_instuction_t* instruct
// failing.
toggle_bounds_check(program->code_or_vm.vm, false);
return_value = _ebpf_program_register_helpers(program->code_or_vm.vm);
return_value = _ebpf_program_register_helpers(program);
if (return_value != EBPF_ERROR_SUCCESS)
goto Done;

Просмотреть файл

@ -0,0 +1,343 @@
/*
* Copyright (c) Microsoft Corporation
* SPDX-License-Identifier: MIT
*/
#include "ebpf_platform.h"
typedef struct _ebpf_extension_client
{
NPIID npi_id;
NPI_CLIENT_CHARACTERISTICS client_characteristics;
NPI_MODULEID client_id;
void* client_binding_context;
const ebpf_extension_data_t* client_data;
const ebpf_extension_dispatch_table_t* client_dispatch_table;
NPI_MODULEID provider_id;
void* provider_binding_context;
ebpf_extension_data_t* provider_data;
ebpf_extension_dispatch_table_t* provider_dispatch_table;
HANDLE nmr_client_handle;
bool provider_is_attached;
} ebpf_extension_client_t;
typedef struct _ebpf_extension_provider
{
NPIID npi_id;
NPI_PROVIDER_CHARACTERISTICS provider_characteristics;
NPI_MODULEID provider_id;
void* provider_binding_context;
const ebpf_extension_data_t* provider_data;
const ebpf_extension_dispatch_table_t* provider_dispatch_table;
HANDLE nmr_provider_handle;
ebpf_provider_client_attach_callback_t client_attach_callback;
ebpf_provider_client_detach_callback_t client_detach_callback;
} ebpf_extension_provider_t;
typedef struct _ebpf_extension_provider_binding_context
{
GUID client_id;
ebpf_provider_client_detach_callback_t client_detach_callback;
} ebpf_extension_provider_binding_context;
NTSTATUS
_ebpf_extension_client_attach_provider(
HANDLE nmr_binding_handle, void* client_context, const NPI_REGISTRATION_INSTANCE* provider_registration_instance)
{
NTSTATUS status;
ebpf_extension_client_t* local_client_context = (ebpf_extension_client_t*)client_context;
// Only permit one provider to attach.
if (local_client_context->provider_data != NULL) {
return STATUS_NOINTERFACE;
}
// Check that the interface matches.
if (memcmp(
provider_registration_instance->NpiId,
&local_client_context->npi_id,
sizeof(local_client_context->npi_id)) != 0) {
return STATUS_NOINTERFACE;
}
local_client_context->provider_id = *provider_registration_instance->ModuleId;
local_client_context->provider_data =
(ebpf_extension_data_t*)provider_registration_instance->NpiSpecificCharacteristics;
status = NmrClientAttachProvider(
nmr_binding_handle,
local_client_context->client_binding_context,
local_client_context->client_dispatch_table,
&local_client_context->provider_binding_context,
&local_client_context->provider_dispatch_table);
local_client_context->provider_is_attached = NT_SUCCESS(status);
return status;
}
NTSTATUS
_ebpf_extension_client_detach_provider(void* client_binding_context)
{
UNREFERENCED_PARAMETER(client_binding_context);
return STATUS_SUCCESS;
}
void
_ebpf_extension_client_cleanup_binding_context(void* client_binding_context)
{
UNREFERENCED_PARAMETER(client_binding_context);
}
ebpf_error_code_t
ebpf_extension_load(
ebpf_extension_client_t** client_context,
const GUID* interface_id,
void* client_binding_context,
const ebpf_extension_data_t* client_data,
const ebpf_extension_dispatch_table_t* client_dispatch_table,
void** provider_binding_context,
ebpf_extension_data_t** provider_data,
ebpf_extension_dispatch_table_t** provider_dispatch_table)
{
ebpf_error_code_t return_value;
ebpf_extension_client_t* local_client_context;
NPI_CLIENT_CHARACTERISTICS* client_characteristics;
NPI_REGISTRATION_INSTANCE* client_registration_instance;
NTSTATUS status;
local_client_context = ebpf_allocate(sizeof(ebpf_extension_client_t), EBPF_MEMORY_NO_EXECUTE);
if (!local_client_context) {
return_value = EBPF_ERROR_OUT_OF_RESOURCES;
goto Done;
}
memset(local_client_context, 0, sizeof(ebpf_extension_client_t));
local_client_context->client_data = client_data;
local_client_context->npi_id = *interface_id;
local_client_context->client_binding_context = client_binding_context;
local_client_context->client_id.Length = sizeof(local_client_context->client_id);
local_client_context->client_id.Type = MIT_GUID;
local_client_context->client_dispatch_table = client_dispatch_table;
status = ExUuidCreate(&local_client_context->client_id.Guid);
if (!NT_SUCCESS(status)) {
return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD;
goto Done;
}
client_characteristics = &(local_client_context->client_characteristics);
client_registration_instance = &(client_characteristics->ClientRegistrationInstance);
client_characteristics->Version = 0;
client_characteristics->Length = sizeof(*client_characteristics);
client_characteristics->ClientAttachProvider = _ebpf_extension_client_attach_provider;
client_characteristics->ClientDetachProvider = _ebpf_extension_client_detach_provider;
client_characteristics->ClientCleanupBindingContext = _ebpf_extension_client_cleanup_binding_context;
client_registration_instance->Version = 0;
client_registration_instance->Size = sizeof(*client_registration_instance);
client_registration_instance->NpiId = &local_client_context->npi_id;
client_registration_instance->ModuleId = &local_client_context->client_id;
client_registration_instance->NpiSpecificCharacteristics = &local_client_context->client_data;
status = NmrRegisterClient(client_characteristics, local_client_context, &local_client_context->nmr_client_handle);
if (!NT_SUCCESS(status)) {
return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD;
goto Done;
}
if (!local_client_context->provider_is_attached) {
return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD;
goto Done;
}
*provider_binding_context = local_client_context->provider_binding_context;
*provider_data = local_client_context->provider_data;
*provider_dispatch_table = local_client_context->provider_dispatch_table;
*client_context = local_client_context;
local_client_context = NULL;
return_value = EBPF_ERROR_SUCCESS;
Done:
ebpf_free(local_client_context);
return return_value;
}
void
ebpf_extension_unload(ebpf_extension_client_t* client_context)
{
NTSTATUS status;
if (client_context) {
status = NmrDeregisterClient(client_context->nmr_client_handle);
if (status == STATUS_PENDING)
NmrWaitForClientDeregisterComplete(client_context->nmr_client_handle);
}
ebpf_free(client_context);
}
NTSTATUS
_ebpf_extension_provider_attach_client(
HANDLE nmr_binding_handle,
PVOID provider_context,
const NPI_REGISTRATION_INSTANCE* client_registration_instance,
void* client_binding_context,
const void* client_dispatch,
void** provider_binding_context,
const void** provider_dispatch)
{
NTSTATUS status;
ebpf_error_code_t return_value;
ebpf_extension_provider_t* local_provider_context = (ebpf_extension_provider_t*)provider_context;
ebpf_extension_provider_binding_context* local_provider_binding_context = NULL;
UNREFERENCED_PARAMETER(nmr_binding_handle);
UNREFERENCED_PARAMETER(client_binding_context);
// Check that the interface matches.
if (memcmp(
client_registration_instance->NpiId,
&local_provider_context->npi_id,
sizeof(local_provider_context->npi_id)) != 0) {
status = STATUS_NOINTERFACE;
goto Done;
}
local_provider_binding_context = (ebpf_extension_provider_binding_context*)ebpf_allocate(
sizeof(ebpf_extension_provider_binding_context), EBPF_MEMORY_NO_EXECUTE);
if (!local_provider_binding_context) {
status = STATUS_NOINTERFACE;
goto Done;
}
local_provider_binding_context->client_id = client_registration_instance->ModuleId->Guid;
local_provider_binding_context->client_detach_callback = local_provider_context->client_detach_callback;
if (local_provider_context->client_attach_callback) {
return_value = local_provider_context->client_attach_callback(
client_binding_context,
&local_provider_binding_context->client_id,
(const ebpf_extension_data_t*)client_registration_instance->NpiSpecificCharacteristics,
(const ebpf_extension_dispatch_table_t*)client_dispatch);
if (return_value != EBPF_ERROR_SUCCESS) {
status = STATUS_NOINTERFACE;
goto Done;
}
}
*provider_binding_context = local_provider_binding_context;
local_provider_binding_context = NULL;
*provider_dispatch = local_provider_context->provider_dispatch_table;
status = STATUS_SUCCESS;
Done:
ebpf_free(local_provider_binding_context);
return status;
}
NTSTATUS
_ebpf_extension_provider_detach_client(void* provider_binding_context)
{
ebpf_extension_provider_binding_context* local_provider_binding_context =
(ebpf_extension_provider_binding_context*)provider_binding_context;
if (local_provider_binding_context->client_detach_callback)
local_provider_binding_context->client_detach_callback(&local_provider_binding_context->client_id);
return STATUS_SUCCESS;
}
void
_ebpf_extension_provider_cleanup_binding_context(void* provider_binding_context)
{
ebpf_free(provider_binding_context);
}
ebpf_error_code_t
ebpf_provider_load(
ebpf_extension_provider_t** provider_context,
const GUID* interface_id,
void* provider_binding_context,
const ebpf_extension_data_t* provider_data,
const ebpf_extension_dispatch_table_t* provider_dispatch_table,
ebpf_provider_client_attach_callback_t client_attach_callback,
ebpf_provider_client_detach_callback_t client_detach_callback)
{
ebpf_error_code_t return_value;
ebpf_extension_provider_t* local_provider_context;
NPI_PROVIDER_CHARACTERISTICS* provider_characteristics;
NPI_REGISTRATION_INSTANCE* provider_registration_instance;
NTSTATUS status;
local_provider_context = ebpf_allocate(sizeof(ebpf_extension_provider_t), EBPF_MEMORY_NO_EXECUTE);
if (!local_provider_context) {
return_value = EBPF_ERROR_OUT_OF_RESOURCES;
goto Done;
}
memset(local_provider_context, 0, sizeof(ebpf_extension_client_t));
local_provider_context->provider_binding_context = provider_binding_context;
local_provider_context->provider_data = provider_data;
local_provider_context->npi_id = *interface_id;
local_provider_context->provider_id.Length = sizeof(local_provider_context->provider_id);
local_provider_context->provider_id.Type = MIT_GUID;
local_provider_context->provider_dispatch_table = provider_dispatch_table;
local_provider_context->client_attach_callback = client_attach_callback;
local_provider_context->client_detach_callback = client_detach_callback;
status = ExUuidCreate(&local_provider_context->provider_id.Guid);
if (!NT_SUCCESS(status)) {
return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD;
goto Done;
}
provider_characteristics = &(local_provider_context->provider_characteristics);
provider_registration_instance = &(provider_characteristics->ProviderRegistrationInstance);
provider_characteristics->Version = 0;
provider_characteristics->Length = sizeof(*provider_characteristics);
provider_characteristics->ProviderAttachClient = _ebpf_extension_provider_attach_client;
provider_characteristics->ProviderDetachClient = _ebpf_extension_provider_detach_client;
provider_characteristics->ProviderCleanupBindingContext = _ebpf_extension_provider_cleanup_binding_context;
provider_registration_instance->Version = 0;
provider_registration_instance->Size = sizeof(*provider_registration_instance);
provider_registration_instance->NpiId = &local_provider_context->npi_id;
provider_registration_instance->ModuleId = &local_provider_context->provider_id;
provider_registration_instance->NpiSpecificCharacteristics = &local_provider_context->provider_data;
status = NmrRegisterProvider(
provider_characteristics, local_provider_context, &local_provider_context->nmr_provider_handle);
if (!NT_SUCCESS(status)) {
return_value = EBPF_ERROR_EXTENSION_FAILED_TO_LOAD;
goto Done;
}
*provider_context = local_provider_context;
local_provider_context = NULL;
return_value = EBPF_ERROR_SUCCESS;
Done:
ebpf_free(local_provider_context);
return return_value;
}
void
epbf_provider_unload(ebpf_extension_provider_t* provider_context)
{
NTSTATUS status;
if (provider_context) {
status = NmrDeregisterProvider(provider_context->nmr_provider_handle);
if (status == STATUS_PENDING)
NmrWaitForProviderDeregisterComplete(provider_context->nmr_provider_handle);
}
ebpf_free(provider_context);
}

Просмотреть файл

@ -43,7 +43,8 @@
<ClCompile Include="..\ebpf_hash_table.c" />
<ClCompile Include="..\ebpf_object.c" />
<ClCompile Include="..\ebpf_pinning_table.c" />
<ClCompile Include="platform_kernel.c" />
<ClCompile Include="ebpf_extension_kernel.c" />
<ClCompile Include="ebpf_platform_kernel.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\include\ebpf_platform.h" />

Просмотреть файл

@ -19,7 +19,7 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="platform_kernel.c">
<ClCompile Include="ebpf_platform_kernel.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ebpf_hash_table.c">
@ -34,6 +34,9 @@
<ClCompile Include="..\ebpf_handle.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ebpf_extension_kernel.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\include\ebpf_platform.h">

Просмотреть файл

@ -0,0 +1,237 @@
/*
* Copyright (c) Microsoft Corporation
* SPDX-License-Identifier: MIT
*/
#include "ebpf_platform.h"
typedef struct _ebpf_extension_client
{
GUID client_id;
GUID interface_id;
void* client_binding_context;
const ebpf_extension_data_t* client_data;
const ebpf_extension_dispatch_table_t* client_dispatch_table;
} ebpf_extension_client_t;
typedef struct _ebpf_extension_provider
{
GUID interface_id;
void* provider_binding_context;
const ebpf_extension_data_t* provider_data;
const ebpf_extension_dispatch_table_t* provider_dispatch_table;
ebpf_provider_client_attach_callback_t client_attach_callback;
ebpf_provider_client_detach_callback_t client_detach_callback;
ebpf_hash_table_t* client_table;
} ebpf_extension_provider_t;
ebpf_lock_t _ebpf_provider_table_lock = {0};
ebpf_hash_table_t* _ebpf_provider_table = NULL;
ebpf_error_code_t
ebpf_extension_load(
ebpf_extension_client_t** client_context,
const GUID* interface_id,
void* client_binding_context,
const ebpf_extension_data_t* client_data,
const ebpf_extension_dispatch_table_t* client_dispatch_table,
void** provider_binding_context,
ebpf_extension_data_t** provider_data,
ebpf_extension_dispatch_table_t** provider_dispatch_table)
{
ebpf_error_code_t return_value;
ebpf_lock_state_t state;
ebpf_extension_provider_t* local_extension_provider = NULL;
ebpf_extension_provider_t** hash_table_find_result = NULL;
ebpf_extension_client_t* local_extension_client = NULL;
ebpf_lock_lock(&_ebpf_provider_table_lock, &state);
if (!_ebpf_provider_table) {
return_value = EBPF_ERROR_NOT_FOUND;
goto Done;
}
local_extension_client = ebpf_allocate(sizeof(ebpf_extension_client_t), EBPF_MEMORY_NO_EXECUTE);
if (!local_extension_client) {
return_value = EBPF_ERROR_OUT_OF_RESOURCES;
goto Done;
}
memset(local_extension_client, 0, sizeof(ebpf_extension_client_t));
local_extension_client->client_binding_context = client_binding_context;
local_extension_client->client_data = client_data;
local_extension_client->client_dispatch_table = client_dispatch_table;
local_extension_client->interface_id = *interface_id;
ebpf_guid_create(&local_extension_client->client_id);
return_value =
ebpf_hash_table_find(_ebpf_provider_table, (const uint8_t*)interface_id, (uint8_t**)&hash_table_find_result);
if (return_value != EBPF_ERROR_SUCCESS) {
return_value = EBPF_ERROR_NOT_FOUND;
goto Done;
}
local_extension_provider = *hash_table_find_result;
return_value = ebpf_hash_table_update(
local_extension_provider->client_table,
(const uint8_t*)&local_extension_client->client_id,
(const uint8_t*)&local_extension_client);
if (return_value != EBPF_ERROR_SUCCESS) {
goto Done;
}
if (local_extension_provider->client_attach_callback) {
return_value = local_extension_provider->client_attach_callback(
local_extension_client->client_binding_context,
&local_extension_client->client_id,
client_data,
client_dispatch_table);
if (return_value != EBPF_ERROR_SUCCESS) {
return_value = EBPF_ERROR_NOT_FOUND;
goto Done;
}
}
*client_context = local_extension_client;
local_extension_client = NULL;
*provider_binding_context = local_extension_provider->provider_binding_context;
*provider_data = local_extension_provider->provider_data;
*provider_dispatch_table = local_extension_provider->provider_dispatch_table;
Done:
if (local_extension_provider && local_extension_client) {
ebpf_hash_table_delete(
local_extension_provider->client_table, (const uint8_t*)&local_extension_client->client_id);
}
ebpf_lock_unlock(&_ebpf_provider_table_lock, &state);
return return_value;
}
void
ebpf_extension_unload(ebpf_extension_client_t* client_context)
{
ebpf_error_code_t return_value;
ebpf_lock_state_t state;
ebpf_extension_provider_t** hash_table_find_result = NULL;
ebpf_extension_provider_t* local_extension_provider = NULL;
if (client_context)
return;
ebpf_lock_lock(&_ebpf_provider_table_lock, &state);
if (!_ebpf_provider_table) {
goto Done;
}
return_value = ebpf_hash_table_find(
_ebpf_provider_table, (const uint8_t*)&client_context->interface_id, (uint8_t**)&hash_table_find_result);
if (return_value != EBPF_ERROR_SUCCESS) {
goto Done;
}
local_extension_provider = *hash_table_find_result;
if (local_extension_provider->client_detach_callback) {
local_extension_provider->client_detach_callback(&client_context->client_id);
}
ebpf_hash_table_delete(local_extension_provider->client_table, (const uint8_t*)&client_context->client_id);
Done:
ebpf_free(client_context);
ebpf_lock_unlock(&_ebpf_provider_table_lock, &state);
}
ebpf_error_code_t
ebpf_provider_load(
ebpf_extension_provider_t** provider_context,
const GUID* interface_id,
void* provider_binding_context,
const ebpf_extension_data_t* provider_data,
const ebpf_extension_dispatch_table_t* provider_dispatch_table,
ebpf_provider_client_attach_callback_t client_attach_callback,
ebpf_provider_client_detach_callback_t client_detach_callback)
{
ebpf_error_code_t return_value;
ebpf_lock_state_t state;
ebpf_extension_provider_t* local_extension_provider = NULL;
ebpf_lock_lock(&_ebpf_provider_table_lock, &state);
if (!_ebpf_provider_table) {
return_value =
ebpf_hash_table_create(&_ebpf_provider_table, ebpf_allocate, ebpf_free, sizeof(GUID), sizeof(void*), NULL);
if (return_value != EBPF_ERROR_SUCCESS)
goto Done;
}
return_value =
ebpf_hash_table_find(_ebpf_provider_table, (const uint8_t*)interface_id, (uint8_t**)&local_extension_provider);
if (return_value == EBPF_ERROR_SUCCESS) {
return_value = EBPF_ERROR_DUPLICATE_NAME;
local_extension_provider = NULL;
goto Done;
}
local_extension_provider = ebpf_allocate(sizeof(ebpf_extension_provider_t), EBPF_MEMORY_NO_EXECUTE);
if (!local_extension_provider) {
return_value = EBPF_ERROR_OUT_OF_RESOURCES;
goto Done;
}
memset(local_extension_provider, 0, sizeof(ebpf_extension_provider_t));
local_extension_provider->interface_id = *interface_id;
local_extension_provider->provider_binding_context = provider_binding_context;
local_extension_provider->provider_data = provider_data;
local_extension_provider->provider_dispatch_table = provider_dispatch_table;
local_extension_provider->client_attach_callback = client_attach_callback;
local_extension_provider->client_detach_callback = client_detach_callback;
return_value = ebpf_hash_table_create(
&local_extension_provider->client_table, ebpf_allocate, ebpf_free, sizeof(GUID), sizeof(void*), NULL);
if (return_value != EBPF_ERROR_SUCCESS) {
goto Done;
}
return_value = ebpf_hash_table_update(
_ebpf_provider_table, (const uint8_t*)interface_id, (const uint8_t*)&local_extension_provider);
if (return_value != EBPF_ERROR_SUCCESS)
goto Done;
*provider_context = local_extension_provider;
local_extension_provider = NULL;
Done:
ebpf_lock_unlock(&_ebpf_provider_table_lock, &state);
ebpf_free(local_extension_provider);
return return_value;
}
void
epbf_provider_unload(ebpf_extension_provider_t* provider_context)
{
ebpf_error_code_t return_value;
ebpf_lock_state_t state;
ebpf_extension_provider_t* local_extension_provider = NULL;
GUID next_key;
if (!provider_context)
return;
ebpf_lock_lock(&_ebpf_provider_table_lock, &state);
if (!_ebpf_provider_table) {
goto Done;
}
ebpf_hash_table_delete(_ebpf_provider_table, (const uint8_t*)&provider_context->interface_id);
return_value = ebpf_hash_table_next_key(_ebpf_provider_table, NULL, (uint8_t*)&next_key);
if (return_value == EBPF_ERROR_NO_MORE_KEYS) {
ebpf_hash_table_destroy(_ebpf_provider_table);
_ebpf_provider_table = NULL;
}
Done:
ebpf_lock_unlock(&_ebpf_provider_table_lock, &state);
ebpf_free(local_extension_provider);
}

Просмотреть файл

@ -7,6 +7,7 @@
#include <intsafe.h>
#include <map>
#include <mutex>
#include <random>
#include <set>
#include <stdbool.h>
#include <stdint.h>
@ -327,3 +328,10 @@ ebpf_free_timer_work_item(ebpf_timer_work_item_t* work_item)
CloseThreadpoolTimer(work_item->threadpool_timer);
ebpf_free(work_item);
}
ebpf_error_code_t
ebpf_guid_create(GUID* new_guid)
{
UuidCreate(new_guid);
return EBPF_ERROR_SUCCESS;
}

Просмотреть файл

@ -31,7 +31,8 @@
<ClCompile Include="..\ebpf_hash_table.c" />
<ClCompile Include="..\ebpf_object.c" />
<ClCompile Include="..\ebpf_pinning_table.c" />
<ClCompile Include="platform_user.cpp" />
<ClCompile Include="ebpf_extension_user.c" />
<ClCompile Include="ebpf_platform_user.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>

Просмотреть файл

@ -27,7 +27,7 @@
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="platform_user.cpp">
<ClCompile Include="ebpf_platform_user.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ebpf_pinning_table.c">
@ -45,5 +45,8 @@
<ClCompile Include="..\ebpf_hash_table.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ebpf_extension_user.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

Просмотреть файл

@ -97,7 +97,7 @@
<PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
</Midl>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib;$(DDK_LIB_PATH)\netio.lib;</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -117,7 +117,7 @@
<PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
</Midl>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib;execution_context_kernel.lib;ubpf_kernel.lib</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib;$(DDK_LIB_PATH)\netio.lib;execution_context_kernel.lib;ubpf_kernel.lib</AdditionalDependencies>
<AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>

Просмотреть файл

@ -741,3 +741,66 @@ TEST_CASE("epoch_test_two_threads", "[epoch_test_two_threads]")
thread_1.join();
thread_2.join();
}
TEST_CASE("extension_test", "[extension_test]")
{
auto client_function = []() { return EBPF_ERROR_SUCCESS; };
auto provider_function = []() { return EBPF_ERROR_SUCCESS; };
auto provider_attach = [](const GUID* client_id,
void* client_binding_context,
const ebpf_extension_data_t* client_data,
const ebpf_extension_dispatch_table_t* client_dispatch_table) {
UNREFERENCED_PARAMETER(client_id);
UNREFERENCED_PARAMETER(client_data);
UNREFERENCED_PARAMETER(client_dispatch_table);
UNREFERENCED_PARAMETER(client_binding_context);
return EBPF_ERROR_SUCCESS;
};
auto provider_detach = [](const GUID* client_id) {
UNREFERENCED_PARAMETER(client_id);
return EBPF_ERROR_SUCCESS;
};
ebpf_extension_dispatch_table_t client_dispatch_table = {
0, sizeof(ebpf_extension_dispatch_table_t), client_function};
ebpf_extension_dispatch_table_t provider_dispatch_table = {
0, sizeof(ebpf_extension_dispatch_table_t), provider_function};
ebpf_extension_data_t client_data;
ebpf_extension_data_t provider_data;
GUID interface_id;
ebpf_extension_dispatch_table_t* returned_provider_dispatch_table;
ebpf_extension_data_t* returned_provider_data;
ebpf_extension_provider_t* provider_context;
ebpf_extension_client_t* client_context;
void* provider_binding_context;
ebpf_guid_create(&interface_id);
REQUIRE(
ebpf_provider_load(
&provider_context,
&interface_id,
nullptr,
&provider_data,
&provider_dispatch_table,
provider_attach,
provider_detach) == EBPF_ERROR_SUCCESS);
REQUIRE(
ebpf_extension_load(
&client_context,
&interface_id,
nullptr,
&client_data,
&client_dispatch_table,
&provider_binding_context,
&returned_provider_data,
&returned_provider_dispatch_table) == EBPF_ERROR_SUCCESS);
REQUIRE(returned_provider_data == &provider_data);
REQUIRE(returned_provider_dispatch_table == &provider_dispatch_table);
ebpf_extension_unload(client_context);
epbf_provider_unload(provider_context);
}