Skip to content

Instantly share code, notes, and snippets.

@blair1922
Created November 25, 2023 15:44
Show Gist options
  • Save blair1922/101d02443146e32a00d809d63e713965 to your computer and use it in GitHub Desktop.
Save blair1922/101d02443146e32a00d809d63e713965 to your computer and use it in GitHub Desktop.
Threaded kernel-mode DLL manual mapper exploiting a vulnerable RWX section within signed memory.
//Replace Zw... functions with your own memory managing implementation
//And hide your thread (it is not needed here but makes code execution easier)
//Solution tested and working on Rust(EAC) and EFT(BE) on Windows 11 Pro 22h2
#include "util.h"
ULONG_PTR FindRwxSection(PCHAR moduleName)
{
ULONG_PTR moduleBase = GetModuleBaseAddress(moduleName);
if (moduleBase == 0)
return 0;
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)moduleBase;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(moduleBase + dosHeader->e_lfanew);
PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);
for (USHORT i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
{
ULONG characteristics = sectionHeader[i].Characteristics;
if ((characteristics & IMAGE_SCN_MEM_EXECUTE) &&
(characteristics & IMAGE_SCN_MEM_READ) &&
(characteristics & IMAGE_SCN_MEM_WRITE))
{
return (ULONG_PTR)(moduleBase + sectionHeader[i].VirtualAddress);
}
}
return 0;
}
NTSTATUS ManualMapDll(PCHAR moduleName, PCHAR targetProcessName)
{
NTSTATUS status;
PEPROCESS targetProcess;
PVOID targetBaseAddress;
HANDLE targetProcessHandle;
UNICODE_STRING targetProcessNameUnicode;
RtlInitUnicodeString(&targetProcessNameUnicode, targetProcessName);
status = PsLookupProcessByProcessName(&targetProcessNameUnicode, &targetProcess);
if (!NT_SUCCESS(status))
return status;
status = ObOpenObjectByPointer(targetProcess, OBJ_KERNEL_HANDLE, NULL, 0, *PsProcessType, KernelMode, &targetProcessHandle);
if (!NT_SUCCESS(status))
{
ObDereferenceObject(targetProcess);
return status;
}
targetBaseAddress = PsGetProcessSectionBaseAddress(targetProcess);
if (targetBaseAddress == NULL)
{
ZwClose(targetProcessHandle);
ObDereferenceObject(targetProcess);
return STATUS_INVALID_PARAMETER;
}
ULONG_PTR rwxSection = FindRwxSection(moduleName);
if (rwxSection == 0)
{
ZwClose(targetProcessHandle);
ObDereferenceObject(targetProcess);
return STATUS_NOT_FOUND;
}
HANDLE currentProcessId = PsGetCurrentProcessId();
PVOID currentBaseAddress = PsGetProcessSectionBaseAddress(PsGetCurrentProcess());
PVOID remoteImageBase = NULL;
SIZE_T imageSize = 0;
status = ZwReadVirtualMemory(targetProcessHandle, &currentBaseAddress, &remoteImageBase, sizeof(PVOID), NULL);
if (!NT_SUCCESS(status))
{
ZwClose(targetProcessHandle);
ObDereferenceObject(targetProcess);
return status;
}
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)remoteImageBase;
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)remoteImageBase + dosHeader->e_lfanew);
imageSize = ntHeaders->OptionalHeader.SizeOfImage;
PVOID remoteAllocation = NULL;
status = ZwAllocateVirtualMemory(targetProcessHandle, &remoteAllocation, 0, &imageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(status))
{
ZwClose(targetProcessHandle);
ObDereferenceObject(targetProcess);
return status;
}
PVOID localImageBase = ExAllocatePoolWithTag(NonPagedPoolNx, imageSize, 'MMAP');
if (localImageBase == NULL)
{
ZwFreeVirtualMemory(targetProcessHandle, &remoteAllocation, &imageSize, MEM_RELEASE);
ZwClose(targetProcessHandle);
ObDereferenceObject(targetProcess);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(localImageBase, currentBaseAddress, imageSize);
PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)localImageBase + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
ULONG_PTR delta = (ULONG_PTR)remoteAllocation - ntHeaders->OptionalHeader.ImageBase;
while (relocation->VirtualAddress != 0)
{
ULONG count = (relocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
PUSHORT list = (PUSHORT)((ULONG_PTR)relocation + sizeof(IMAGE_BASE_RELOCATION));
for (ULONG i = 0; i < count; i++)
{
if (list[i] >> 12 == IMAGE_REL_BASED_HIGHLOW)
{
PULONG_PTR ptr = (PULONG_PTR)((ULONG_PTR)localImageBase + (relocation->VirtualAddress + (list[i] & 0xFFF)));
*ptr += delta;
}
}
relocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)relocation + relocation->SizeOfBlock);
}
PVOID remoteEntryPoint = (PVOID)((ULONG_PTR)remoteAllocation + ntHeaders->OptionalHeader.AddressOfEntryPoint);
status = ZwWriteVirtualMemory(targetProcessHandle, &remoteEntryPoint, &ntHeaders->OptionalHeader.AddressOfEntryPoint, sizeof(PVOID), NULL);
if (!NT_SUCCESS(status))
{
ExFreePoolWithTag(localImageBase, 'MMAP');
ZwFreeVirtualMemory(targetProcessHandle, &remoteAllocation, &imageSize, MEM_RELEASE);
ZwClose(targetProcessHandle);
ObDereferenceObject(targetProcess);
return status;
}
status = ZwWriteVirtualMemory(targetProcessHandle, &remoteAllocation, &localImageBase, sizeof(PVOID), NULL);
if (!NT_SUCCESS(status))
{
ExFreePoolWithTag(localImageBase, 'MMAP');
ZwFreeVirtualMemory(targetProcessHandle, &remoteAllocation, &imageSize, MEM_RELEASE);
ZwClose(targetProcessHandle);
ObDereferenceObject(targetProcess);
return status;
}
PETHREAD targetThread;
status = PsCreateSystemThread(&targetThread, THREAD_ALL_ACCESS, NULL, targetProcessHandle, NULL, (PKSTART_ROUTINE)remoteEntryPoint, NULL);
if (!NT_SUCCESS(status))
{
ExFreePoolWithTag(localImageBase, 'MMAP');
ZwFreeVirtualMemory(targetProcessHandle, &remoteAllocation, &imageSize, MEM_RELEASE);
ZwClose(targetProcessHandle);
ObDereferenceObject(targetProcess);
return status;
}
ZwClose(targetThread);
ExFreePoolWithTag(localImageBase, 'MMAP');
ZwFreeVirtualMemory(targetProcessHandle, &remoteAllocation, &imageSize, MEM_RELEASE);
ZwClose(targetProcessHandle);
ObDereferenceObject(targetProcess);
return STATUS_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment