ipi interrupt, readme, some bug fix

This commit is contained in:
lhodges1 2023-10-30 22:57:24 +11:00
parent 61e0527f66
commit a482faceca
13 changed files with 285 additions and 48 deletions

View file

@ -4,18 +4,37 @@
- Attached thread detection
- Process module .text section integrity checks
- NMI and APC stackwalks (to allow excellent system coverage)
- NMI and APC stackwalking
- IPI stackwalking which is a relatively unknown method compared to NMIs and APCs
- Handle stripping via obj callbacks
- Process handle table enumeration
- System module verification
- Unlinked process detection via PTE walking and checking against a robust process structure signature
- System module .text integrity checks (see known issues)
- Unlinked process detection
- Hidden thread detection via KPRCB
- Hidden thread detection via PspCid table
- Dispatch routine validation
- Extraction of hardware identifiers via SMBIOS parsing and PhysicalDriveN querying
- Extraction of hardware identifiers
- EPT hook detection (currently detects hyperdbg and DdiMon)
- Driver integrity checks both locally and over server
- Test signing detection
- Hypervisor detection via instruction emulation testing and timing checks
- Hypervisor detection
# planned features
- Heartbeat between components
- ntoskrnl integrity checks (currently in progress)
- some way of identifying spoofed stacks
- some way of dynamically resolving offsets. Will probably use a pdb parser but i am working on a debuglib atm using the windows debug api. We will see.
- some form of cr3 protection
- some more detection methods other then stackwalking xD
- various forms of encryption and other things
# known issues
- the system module validation works on my vm but not on my main pc, not sure if others will experience the same issues. am however working on a fix
feel free to open any issues if you find more.
# some things to note:
@ -28,6 +47,7 @@
# how 2 use
1. use the osr loader to load the driver at "system" load.
- NOTE: its important that you only click "Register" in the OSR loader, dont actually load the driver only register it. Then restart. This is very important as the driver needs an accurate representation of system threads and processes in order for many of the detection methods to work.
2. inject dll into program you want to protect, i used notepad for testing
3. logs will be printed to dbgview and the usermode dll via stdout

View file

@ -1311,6 +1311,19 @@ KeInitializeApc(
_In_opt_ PVOID NormalContext
);
NTSTATUS
NTAPI
MmCopyVirtualMemory
(
PEPROCESS SourceProcess,
PVOID SourceAddress,
PEPROCESS TargetProcess,
PVOID TargetAddress,
SIZE_T BufferSize,
KPROCESSOR_MODE PreviousMode,
PSIZE_T ReturnSize
);
NTKERNELAPI
BOOLEAN
NTAPI

View file

@ -1228,8 +1228,9 @@ DriverEntry(
return STATUS_FAILED_DRIVER_ENTRY;
}
ValidateSystemModules();
//ValidateSystemModules();
//ValidateNtoskrnl();
LaunchInterProcessInterrupt(NULL);
DEBUG_LOG("DonnaAC Driver Entry Complete");
return STATUS_SUCCESS;

View file

@ -601,7 +601,7 @@ ComputeHashOfBuffer(
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("BCryptFinishHash failed with status %x", status);
return status;
goto end;
}
*HashResult = resulting_hash;
@ -1902,10 +1902,66 @@ NTSTATUS
ValidateNtoskrnl()
{
NTSTATUS status = STATUS_SUCCESS;
SIZE_T bytes_written = 0;
KAPC_STATE apc_state = { 0 };
PVOID memory_buffer = NULL;
ULONG memory_buffer_size = 0;
PRTL_MODULE_EXTENDED_INFO module_info = NULL;
SYSTEM_MODULES modules = { 0 };
status = GetSystemModuleInformation(&modules);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
return status;
}
module_info = (PRTL_MODULE_EXTENDED_INFO)modules.address;
DEBUG_LOG("Module base: %llx", module_info->ImageBase);
PVOID buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, module_info->ImageSize, POOL_TAG_INTEGRITY);
if (!buffer)
goto end;
status = MmCopyVirtualMemory(
PsInitialSystemProcess,
module_info->ImageBase,
PsGetCurrentProcess(),
buffer,
module_info->ImageSize,
KernelMode,
&bytes_written
);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("MmCopyVirtualMemory failed with status %x", status);
goto end;
}
status = StoreModuleExecutableRegionsInBuffer(
&memory_buffer,
buffer,
module_info->ImageSize,
&memory_buffer_size
);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("StoreModuleExecutableRegionsInBuffer failed with status %x", status);
goto end;
}
DEBUG_LOG("buf size: %lx", memory_buffer_size);
end:
if (buffer)
ExFreePoolWithTag(buffer, POOL_TAG_INTEGRITY);
if (modules.address)
ExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);

View file

@ -55,4 +55,7 @@ ScanForSignature(
NTSTATUS
ValidateSystemModules();
NTSTATUS
ValidateNtoskrnl();
#endif

View file

@ -38,6 +38,8 @@ DispatchApcOperation(
#define IOCTL_REQUEST_HARDWARE_INFORMATION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20016, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_INITIATE_APC_OPERATION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20017, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CHECK_FOR_EPT_HOOK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20018, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_LAUNCH_IPI_INTERRUPT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20019, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_VALIDATE_SYSTEM_MODULES CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20020, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define APC_OPERATION_STACKWALK 0x1
@ -358,6 +360,28 @@ DeviceControl(
break;
case IOCTL_LAUNCH_IPI_INTERRUPT:
status = LaunchInterProcessInterrupt(Irp);
if (!NT_SUCCESS(status))
DEBUG_ERROR("LaunchInterProcessInterrupt failed with status %x", status);
break;
case IOCTL_VALIDATE_SYSTEM_MODULES:
/*
* Currently the validation is buggy, once the validation is better will
* probably bugcheck the system.
*/
status = ValidateSystemModules();
if (!NT_SUCCESS(status))
DEBUG_ERROR("ValidateSystemModules failed with status %x", status);
break;
default:
DEBUG_ERROR("Invalid IOCTL passed to driver: %lx", stack_location->Parameters.DeviceIoControl.IoControlCode);
status = STATUS_INVALID_PARAMETER;

View file

@ -951,7 +951,7 @@ NmiCallback(
)
{
UNREFERENCED_PARAMETER(Handled);
__debugbreak();
PVOID current_thread = KeGetCurrentThread();
NMI_CALLBACK_DATA thread_data = { 0 };
PNMI_CONTEXT nmi_context = (PNMI_CONTEXT)Context;
@ -1488,3 +1488,79 @@ FreeApcStackwalkApcContextInformation(
ExFreePoolWithTag(Context->modules, POOL_TAG_APC);
}
/*
* Since NMI evasion methods are becoming commonplace, we can use interprocess
* interrupts. For now i am just using the same nmi methods. To accomplish this
* we can use KeIpiGenericCall, which runs a specified routine on all processors
* simultaneously. The callback routine runs at IRQL IPI_LEVEL which is > DIRQL.
*/
NTSTATUS
LaunchInterProcessInterrupt(
_In_ PIRP Irp
)
{
PAGED_CODE();
NTSTATUS status;
SYSTEM_MODULES system_modules = { 0 };
NMI_CONTEXT ipi_context = { 0 };
PVOID callback_handle;
ipi_context.core_count = KeQueryActiveProcessorCountEx(0);
ipi_context.nmi_core_context =
ExAllocatePool2(POOL_FLAG_NON_PAGED, ipi_context.core_count * sizeof(NMI_CORE_CONTEXT), NMI_CONTEXT_POOL);
if (!ipi_context.nmi_core_context)
return STATUS_MEMORY_NOT_ALLOCATED;
ipi_context.stack_frames =
ExAllocatePool2(POOL_FLAG_NON_PAGED, ipi_context.core_count * STACK_FRAME_POOL_SIZE, STACK_FRAMES_POOL);
if (!ipi_context.stack_frames)
goto end;
ipi_context.thread_data_pool =
ExAllocatePool2(POOL_FLAG_NON_PAGED, ipi_context.core_count * sizeof(NMI_CALLBACK_DATA), THREAD_DATA_POOL);
if (!ipi_context.thread_data_pool)
goto end;
/*
* We query the system modules each time since they can potentially
* change at any time
*/
status = GetSystemModuleInformation(&system_modules);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("Error retriving system module information");
return status;
}
KeIpiGenericCall(NmiCallback, &ipi_context);
/*
* since the routines are run simultaneously, once we've reached here we can be sure
* all routines have run.
*/
status = AnalyseNmiData(&ipi_context, &system_modules, Irp);
end:
if (!NT_SUCCESS(status))
DEBUG_ERROR("Error analysing ipi interrupt data");
if (system_modules.address)
ExFreePoolWithTag(system_modules.address, SYSTEM_MODULES_POOL);
if (ipi_context.nmi_core_context)
ExFreePoolWithTag(ipi_context.nmi_core_context, NMI_CONTEXT_POOL);
if (ipi_context.stack_frames)
ExFreePoolWithTag(ipi_context.stack_frames, STACK_FRAMES_POOL);
if (ipi_context.thread_data_pool)
ExFreePoolWithTag(ipi_context.thread_data_pool, THREAD_DATA_POOL);
return status;
}

View file

@ -117,4 +117,9 @@ FlipKThreadMiscFlagsFlag(
_In_ BOOLEAN NewValue
);
NTSTATUS
LaunchInterProcessInterrupt(
_In_ PIRP Irp
);
#endif

View file

@ -74,7 +74,7 @@ VOID kernelmode::Driver::RunNmiCallbacks()
* 2. Checks the IOCTL dispatch routines to ensure they lie within the module
*/
VOID kernelmode::Driver::VerifySystemModules()
VOID kernelmode::Driver::VerifySystemModuleDriverObjects()
{
BOOLEAN status;
DWORD bytes_returned;
@ -546,6 +546,44 @@ VOID kernelmode::Driver::CheckForEptHooks()
LOG_ERROR("failed to check for ept hooks %x", GetLastError());
}
VOID kernelmode::Driver::LaunchIpiInterrupt()
{
BOOLEAN status;
status = DeviceIoControl(
this->driver_handle,
IOCTL_LAUNCH_IPI_INTERRUPT,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
);
if (status == NULL)
LOG_ERROR("failed to launch ipi interrupt %x", GetLastError());
}
VOID kernelmode::Driver::ValidateSystemModules()
{
BOOLEAN status;
status = DeviceIoControl(
this->driver_handle,
IOCTL_VALIDATE_SYSTEM_MODULES,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
);
if (status == NULL)
LOG_ERROR("failed to validate system modules %x", GetLastError());
}
VOID kernelmode::Driver::CheckDriverHeartbeat()
{

View file

@ -23,6 +23,8 @@
#define IOCTL_REQUEST_HARDWARE_INFORMATION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20016, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_INITIATE_APC_OPERATION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20017, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CHECK_FOR_EPT_HOOK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20018, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_LAUNCH_IPI_INTERRUPT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20019, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_VALIDATE_SYSTEM_MODULES CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20020, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define MAX_REPORTS_PER_IRP 20
@ -64,7 +66,7 @@ namespace kernelmode
~Driver();
VOID RunNmiCallbacks();
VOID VerifySystemModules();
VOID VerifySystemModuleDriverObjects();
VOID RunCallbackReportQueue();
VOID DetectSystemVirtualization();
VOID QueryReportQueue();
@ -78,6 +80,8 @@ namespace kernelmode
VOID SendClientHardwareInformation();
VOID CheckForHiddenThreads();
VOID CheckForEptHooks();
VOID LaunchIpiInterrupt();
VOID ValidateSystemModules();
BOOLEAN InitiateApcOperation( INT OperationId );
};

View file

@ -11,9 +11,9 @@ void kernelmode::KManager::RunNmiCallbacks()
this->thread_pool->QueueJob( [ this ]() { this->driver_interface->RunNmiCallbacks(); } );
}
void kernelmode::KManager::VerifySystemModules()
void kernelmode::KManager::VerifySystemModuleDriverObjects()
{
this->thread_pool->QueueJob( [ this ]() { this->driver_interface->VerifySystemModules(); } );
this->thread_pool->QueueJob( [ this ]() { this->driver_interface->VerifySystemModuleDriverObjects(); } );
}
void kernelmode::KManager::MonitorCallbackReports()
@ -74,4 +74,14 @@ VOID kernelmode::KManager::CheckForHiddenThreads()
VOID kernelmode::KManager::CheckForEptHooks()
{
this->thread_pool->QueueJob([this]() { this->driver_interface->CheckForEptHooks(); });
}
VOID kernelmode::KManager::LaunchIpiInterrupt()
{
this->thread_pool->QueueJob([this]() { this->driver_interface->LaunchIpiInterrupt(); });
}
VOID kernelmode::KManager::ValidateSystemModules()
{
this->thread_pool->QueueJob([this]() { this->driver_interface->ValidateSystemModules(); });
}

View file

@ -18,7 +18,7 @@ namespace kernelmode
KManager( LPCWSTR DriverName, std::shared_ptr<global::ThreadPool> ThreadPool, std::shared_ptr<global::Client> ReportInterface);
VOID RunNmiCallbacks();
VOID VerifySystemModules();
VOID VerifySystemModuleDriverObjects();
VOID MonitorCallbackReports();
VOID DetectSystemVirtualization();
VOID EnumerateHandleTables();
@ -31,6 +31,8 @@ namespace kernelmode
VOID InitiateApcStackwalkOperation();
VOID CheckForHiddenThreads();
VOID CheckForEptHooks();
VOID LaunchIpiInterrupt();
VOID ValidateSystemModules();
};
}

View file

@ -39,50 +39,35 @@ DWORD WINAPI Init(HINSTANCE hinstDLL)
//std::cout << "RequestID: " << response.RequestId << " CanUserProceed: " <<
// response.CanUserProceed << " Reason: " << response.reason << std::endl;
/*
* Note that this is really just for testing the methods for extended periods of time.
* The "real business logic" would execute the methods with varying degrees of uncertaintity
* but still allow for bias, i.e we don't want NMI callbacks to be running every 10 seconds
* since they are "dangerous" for the CPU given the IRQL they run at.
*/
srand(time(NULL));
while (!GetAsyncKeyState(VK_DELETE))
{
int seed = (rand() % 10);
int seed = (rand() % 11);
std::cout << "Seed: " << seed << std::endl;
switch (seed)
{
case 0:
// safe
kmanager.EnumerateHandleTables();
break;
case 1:
kmanager.PerformIntegrityCheck();
break;
case 2:
kmanager.ScanPoolsForUnlinkedProcesses();
break;
case 3:
//safe
kmanager.VerifySystemModules();
break;
case 4:
kmanager.ValidateProcessModules();
break;
case 5:
kmanager.RunNmiCallbacks();
break;
case 6:
kmanager.CheckForAttachedThreads();
break;
case 7:
//safe
kmanager.InitiateApcStackwalkOperation();
break;
case 8:
//safe
kmanager.CheckForHiddenThreads();
break;
case 9:
kmanager.CheckForEptHooks();
break;
case 0: { kmanager.EnumerateHandleTables(); break; }
case 1: { kmanager.PerformIntegrityCheck(); break; }
case 2: { kmanager.ScanPoolsForUnlinkedProcesses(); break; }
case 3: { kmanager.VerifySystemModuleDriverObjects(); break; }
case 4: { kmanager.ValidateProcessModules(); break; }
case 5: { kmanager.RunNmiCallbacks(); break; }
case 6: { kmanager.CheckForAttachedThreads(); break; }
case 7: { kmanager.InitiateApcStackwalkOperation(); break; }
case 8: { kmanager.CheckForHiddenThreads(); break; }
case 9: { kmanager.CheckForEptHooks(); break; }
case 10: { kmanager.LaunchIpiInterrupt(); break; }
case 11: { kmanager.ValidateSystemModules(); break; }
}
kmanager.MonitorCallbackReports();