Files
rbmm b7c7d7dc00 s
2019-08-12 15:06:08 +03:00

722 lines
18 KiB
C++

#include "StdAfx.h"
_NT_BEGIN
//#define _PRINT_CPP_NAMES_
#include "../inc/asmfunc.h"
LONG gFlags;
extern "C" {
PDRIVER_OBJECT g_DriverObject;
}
enum{
flImageNotifySet,
};
#pragma intrinsic(_lrotr, _lrotl)
#define RUN_ONCE_STATUS_TO_CONTEXT(status) ((PVOID)(ULONG_PTR)(_lrotl(status, 4) & ~((1 << RTL_RUN_ONCE_CTX_RESERVED_BITS) - 1)))
#define RUN_ONCE_CONTEXT_TO_STATUS(Context) ((NTSTATUS)_lrotr((ULONG)(ULONG_PTR)Context, 4))
/*++
NTSTATUS DLL_INFORMATION::MapSection(PVOID& hmod);
if DLL section mapped not at preferred base (STATUS_IMAGE_NOT_AT_BASE, STATUS_IMAGE_AT_DIFFERENT_BASE):
1.) system need relocate image to new base, as result modify it pages
(allocate new private physical pages for modification), after this pages already not shared
2.) win10 1703+: system by unknown reason unload it, insert task to LdrpRetryQueue and return STATUS_RETRY
and then tryed load it again (by using full path (!!) instead known section handle).
so mapping DLL not at preferred base have serious penalty
for avoid this - map our shellcode at different base - for not occupying preferred base
note that because our shellcode is based-independent - any base (STATUS_IMAGE_NOT_AT_BASE) is ok for us.
we not need relocated this mapping
for force shellcode not occupy preferred base:
1.) before map section - reserve preffered memory range with ZwAllocateVirtualMemory (MEM_RESERVE, PAGE_NOACCESS)
2.) map section (waited return status - STATUS_IMAGE_NOT_AT_BASE)
3.) free reserved range
however if load native 64-bit dll in wow64 process - preferred DLL base almost certainly will be in wow reserved memory range
-------------------------------------------------------------------------------
difference between STATUS_IMAGE_NOT_AT_BASE and STATUS_IMAGE_AT_DIFFERENT_BASE:
if section can not be mapped at base:
when in ZwMapViewOfSection:
AllocationType containing MEM_DIFFERENT_IMAGE_BASE_OK flag(used inside LdrLoadDll) -
section RELOCATED and returned STATUS_IMAGE_AT_DIFFERENT_BASE
otherwise - section NOT relocated and STATUS_IMAGE_NOT_AT_BASE returned.
when STATUS_IMAGE_NOT_AT_BASE returned need process relocs
when STATUS_IMAGE_AT_DIFFERENT_BASE relocs already applied (in kernel) - not need relocate
we use base-independed code in section, we not need relocation
so better not use MEM_DIFFERENT_IMAGE_BASE_OK and got unrelocated view
--*/
struct DLL_INFORMATION
{
RTL_RUN_ONCE SectionStatus;
PVOID Section, PreferredAddress;
ULONG rva_1, SizeOfImage;
NTSTATUS CreateDllSection();
NTSTATUS GetSection(PVOID* pSection)
{
union {
PVOID Context;
NTSTATUS status;
};
if (RtlRunOnceBeginInitialize(&SectionStatus, 0, &Context) == STATUS_PENDING)
{
KAPC_STATE as;
KeStackAttachProcess(PsInitialSystemProcess, &as);
Context = RUN_ONCE_STATUS_TO_CONTEXT(CreateDllSection());
KeUnstackDetachProcess(&as);
RtlRunOnceComplete(&SectionStatus, 0, Context);
}
status = RUN_ONCE_CONTEXT_TO_STATUS(Context);
*pSection = Section;
return status;
}
NTSTATUS MapSection(PVOID& hmod)
{
PVOID ReservedAddress = PreferredAddress, BaseAddress = 0;
SIZE_T RegionSize = SizeOfImage, ViewSize = 0;
NTSTATUS status = ZwAllocateVirtualMemory(NtCurrentProcess(), &ReservedAddress, 0, &RegionSize, MEM_RESERVE, PAGE_NOACCESS);
bool bFreeMemory = false;
switch (status)
{
case STATUS_SUCCESS:
bFreeMemory = true;
case STATUS_CONFLICTING_ADDRESSES:
static LARGE_INTEGER ZeroOffset;
if (0 <= (status = MmMapViewOfSection(Section, IoGetCurrentProcess(),
&BaseAddress, 0, 0, &ZeroOffset, &ViewSize, ViewUnmap, 0, PAGE_EXECUTE)))
{
hmod = BaseAddress;
}
break;
}
DbgPrint("MapSection[%x]: %x %p\n", status, bFreeMemory, BaseAddress);
if (bFreeMemory)
{
ZwFreeVirtualMemory(NtCurrentProcess(), &ReservedAddress, &RegionSize, MEM_RELEASE);
}
return status;
}
};
namespace DLL_32
{
#include "../dlldemo/md5_32.h" // autogenerated file, on post-build step for DllDemo
DLL_INFORMATION di {};
}
#ifdef _WIN64
namespace DLL_64
{
#include "../dlldemo/md5_64.h" // autogenerated file, on post-build step for DllDemo
DLL_INFORMATION di {};
}
#endif
#ifdef _WIN64
#define NATIVE_DLL DLL_64
#define WOW_DLL DLL_32
#else
#define NATIVE_DLL DLL_32
#endif
// RtlAddressInSectionTable not exported in kernel
PVOID AddressInSectionTable
(
PIMAGE_NT_HEADERS NtHeaders,
PVOID Base,
ULONG Rva
)
{
if (ULONG NumberOfSections = NtHeaders->FileHeader.NumberOfSections)
{
PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(NtHeaders);
do
{
ULONG o = Rva - pish->VirtualAddress;
if (o < pish->Misc.VirtualSize)
{
return (PBYTE)Base + pish->PointerToRawData + o;
}
} while (pish++, --NumberOfSections);
}
return 0;
}
NTSTATUS MapPeAsData(HANDLE hFile, PVOID* BaseAddress, PSIZE_T ViewSize)
{
HANDLE hSection;
NTSTATUS status = ZwCreateSection(&hSection, SECTION_MAP_READ, 0, 0, PAGE_READONLY, SEC_COMMIT, hFile);
if (0 <= status)
{
status = ZwMapViewOfSection(hSection, NtCurrentProcess(), BaseAddress, 0, 0, 0, ViewSize, ViewUnmap, 0, PAGE_READONLY);
}
ZwClose(hSection);
return status;
}
NTSTATUS IsFileOk(HANDLE hFile, PVOID _md5, ULONG FileSize, PULONG rva, PULONG AddressOfEntryPoint, PULONG SizeOfImage)
{
NTSTATUS status;
BCRYPT_ALG_HANDLE hAlgorithm;
if (0 <= (status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_MD5_ALGORITHM, 0, 0)))
{
union {
ULONG64 align;
UCHAR md5[16];
};
BCRYPT_HASH_HANDLE hHash;
if (0 <= (status = BCryptCreateHash(hAlgorithm, &hHash, 0, 0, 0, 0, 0)))
{
PVOID BaseAddress = 0;
SIZE_T ViewSize = 0;
if (0 <= (status = MapPeAsData(hFile, &BaseAddress, &ViewSize)))
{
status = FileSize > ViewSize ? STATUS_INVALID_IMAGE_HASH : BCryptHashData(hHash, (PUCHAR)BaseAddress, FileSize, 0);
if (0 <= status && 0 <= (status = BCryptFinishHash(hHash, md5, sizeof(md5), 0)))
{
DbgPrint("md5_0: %016I64x%016I64x\nmd5_1: %016I64x%016I64x\n",
*(1 + (PULONG64)_md5), *(PULONG64)_md5,
*(1 + (PULONG64)md5), *(PULONG64)md5);
if (memcmp(md5, _md5, sizeof(md5)))
{
status = STATUS_INVALID_IMAGE_HASH;
}
else
{
status = STATUS_INVALID_IMAGE_FORMAT;
if (PIMAGE_NT_HEADERS pinth = RtlImageNtHeader(BaseAddress))
{
status = STATUS_PROCEDURE_NOT_FOUND;
ULONG size, Ordinal = 1;
PIMAGE_EXPORT_DIRECTORY pied = (PIMAGE_EXPORT_DIRECTORY)
RtlImageDirectoryEntryToData(BaseAddress, FALSE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size);
if (pied && size >= sizeof(IMAGE_EXPORT_DIRECTORY) && (Ordinal -= pied->Base) < pied->NumberOfFunctions)
{
if (PULONG AddressOfFunctions = (PULONG)AddressInSectionTable(pinth, BaseAddress, pied->AddressOfFunctions))
{
*rva = AddressOfFunctions[Ordinal];
*AddressOfEntryPoint = pinth->OptionalHeader.AddressOfEntryPoint;
*SizeOfImage = pinth->OptionalHeader.SizeOfImage;
status = STATUS_SUCCESS;
}
}
}
}
}
ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
}
BCryptDestroyHash(hHash);
}
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
}
DbgPrint("IsFileOk=%x\n", status);
return status;
}
NTSTATUS CreateKnownSection(HANDLE hFile, PCOBJECT_ATTRIBUTES poaKernel32, PCUNICODE_STRING My, PVOID* pTransferAddress, PVOID *pSection)
{
ULONG cb = 0, rcb = 256;
static volatile UCHAR guz;
PVOID stack = alloca(guz);
HANDLE hSection;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, const_cast<PUNICODE_STRING>(My), OBJ_CASE_INSENSITIVE|OBJ_PERMANENT };
// look for system (smss.exe) assigned SD for known dlls
NTSTATUS status = ZwOpenSection(&hSection, READ_CONTROL, const_cast<POBJECT_ATTRIBUTES>(poaKernel32));
if (0 <= status)
{
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(oa.SecurityDescriptor = alloca(rcb - cb), stack);
}
status = ZwQuerySecurityObject(hSection,
PROCESS_TRUST_LABEL_SECURITY_INFORMATION|
DACL_SECURITY_INFORMATION|LABEL_SECURITY_INFORMATION|OWNER_SECURITY_INFORMATION,
oa.SecurityDescriptor, cb, &rcb);
} while (status == STATUS_BUFFER_TOO_SMALL);
ZwClose(hSection);
if (0 <= status)
{
status = ZwCreateSection(&hSection, SECTION_MAP_EXECUTE|SECTION_QUERY, &oa, 0, PAGE_EXECUTE, SEC_IMAGE, hFile);
if (0 <= status)
{
SECTION_IMAGE_INFORMATION sii;
status = ZwQuerySection(hSection, SectionImageInformation, &sii, sizeof(sii), 0);
DbgPrint("[%08x]:%wZ<%p> at %p\n", status, My, hSection, sii.TransferAddress);
PVOID Section = 0;
if (0 <= status)
{
status = ObReferenceObjectByHandle(hSection, 0, 0, KernelMode, &Section, 0);
}
ZwClose(hSection);
if (0 <= status)
{
*pTransferAddress = sii.TransferAddress;
*pSection = Section;
}
}
}
}
return status;
}
NTSTATUS DLL_INFORMATION::CreateDllSection()
{
ULONG FileSize;
PVOID md5;
PCOBJECT_ATTRIBUTES poaKernel32, poaDLL;
PCUNICODE_STRING pMy;
#ifdef _WIN64
if (this == &WOW_DLL::di)
{
STATIC_UNICODE_STRING(My, "\\KnownDlls32\\{EBB50DDB-F6AA-492d-94E3-1D51B299F627}.DLL");
STATIC_OBJECT_ATTRIBUTES(oaKernel32, "\\KnownDlls32\\kernel32.dll");
STATIC_OBJECT_ATTRIBUTES(oaDLL, "\\systemroot\\syswow64\\{EBB50DDB-F6AA-492d-94E3-1D51B299F627}.DLL");
poaKernel32 = &oaKernel32, pMy = &My, poaDLL = &oaDLL;
FileSize = WOW_DLL::FileSize;
md5 = &WOW_DLL::md5;
}
else
#endif
{
STATIC_UNICODE_STRING(My, "\\KnownDlls\\{EBB50DDB-F6AA-492d-94E3-1D51B299F627}.DLL");
STATIC_OBJECT_ATTRIBUTES(oaKernel32, "\\KnownDlls\\kernel32.dll");
STATIC_OBJECT_ATTRIBUTES(oaDLL, "\\systemroot\\system32\\{EBB50DDB-F6AA-492d-94E3-1D51B299F627}.DLL");
poaKernel32 = &oaKernel32, pMy = &My, poaDLL = &oaDLL;
FileSize = NATIVE_DLL::FileSize;
md5 = &NATIVE_DLL::md5;
}
HANDLE hFile;
IO_STATUS_BLOCK iosb;
NTSTATUS status = ZwOpenFile(&hFile, FILE_GENERIC_READ|FILE_EXECUTE,
const_cast<POBJECT_ATTRIBUTES>(poaDLL), &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
DbgPrint("OpenFile(%wZ)=%x %p\n", poaDLL->ObjectName, status, hFile);
if (0 <= status)
{
FILE_STANDARD_INFORMATION fsi;
if (0 <= (status = ZwQueryInformationFile(hFile, &iosb, &fsi, sizeof(fsi), FileStandardInformation)))
{
DbgPrint("EndOfFile=%I64x\n_FileSize=%x\n", fsi.EndOfFile.QuadPart, FileSize);
if (fsi.EndOfFile.QuadPart == FileSize)
{
ULONG AddressOfEntryPoint;
if (0 <= (status = IsFileOk(hFile, md5, FileSize, &rva_1, &AddressOfEntryPoint, &SizeOfImage)))
{
PVOID TransferAddress;
status = CreateKnownSection(hFile, poaKernel32, pMy, &TransferAddress, &Section);
PreferredAddress = (PBYTE)TransferAddress - AddressOfEntryPoint;
}
}
else
{
status = STATUS_INVALID_IMAGE_HASH;
}
}
ZwClose(hFile);
}
return status;
}
VOID CALLBACK RundownRoutine(PKAPC )ASM_FUNCTION;
VOID CALLBACK KernelRoutine(PKAPC , PKNORMAL_ROUTINE *, PVOID * , PVOID * ,PVOID * )ASM_FUNCTION;
VOID CALLBACK NormalRoutine(PVOID , PVOID ,PVOID )ASM_FUNCTION;
VOID CALLBACK _RundownRoutine(PKAPC Apc)
{
CPP_FUNCTION;
DbgPrint("--Apc<%p>\n", Apc);
delete Apc;
}
extern "C"
NTSYSAPI
BOOLEAN NTAPI KeTestAlertThread ( IN KPROCESSOR_MODE AlertMode );
VOID CALLBACK _NormalRoutine (
PKAPC Apc,
PVOID Section,
DLL_INFORMATION* pdi
)
{
CPP_FUNCTION;
DbgPrint("NormalRoutine(%p, %p, %p)\n", Apc, Section, pdi);
PVOID BaseAddress;
NTSTATUS status = pdi->MapSection(BaseAddress);
ObfDereferenceObject(Section);
if (status != STATUS_IMAGE_NOT_AT_BASE)
{
DbgPrint("!!!!! MapViewOfSection = %x, %p\n", status, BaseAddress);
}
if (0 <= status)
{
union {
PVOID pvNormalRoutine;
PKNORMAL_ROUTINE NormalRoutine;
};
PVOID NormalContext = BaseAddress;
pvNormalRoutine = (PBYTE)BaseAddress + pdi->rva_1;
// pvNormalRoutine is valid for CFG because this is exported address from image,
// which have IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT and
// pvNormalRoutine was in GuardCFFunctionTable (__guard_fids_table)
#ifdef _WIN64
if (pdi == &WOW_DLL::di) PsWrapApcWow64Thread(&NormalContext, &pvNormalRoutine);
#endif
KeInitializeApc(Apc, KeGetCurrentThread(), OriginalApcEnvironment,
KernelRoutine, RundownRoutine, NormalRoutine, UserMode, NormalContext);
ObfReferenceObject(g_DriverObject);
if (KeInsertQueueApc(Apc, NtCurrentProcess(), BaseAddress, IO_NO_INCREMENT))
{
DbgPrint("InsertQueueApc(%p, %p, %p)\n", BaseAddress, NormalContext, NormalRoutine);
// force call user mode apc
KeTestAlertThread(UserMode);
return ;
}
ObfDereferenceObject(g_DriverObject);
MmUnmapViewOfSection(IoGetCurrentProcess(), BaseAddress);
}
DbgPrint("!!!!!!!!!!!!!!!!!!! _NormalRoutine\n");
// delete Apc;
_RundownRoutine(Apc);
}
VOID CALLBACK _KernelRoutine(
PKAPC Apc,
PKNORMAL_ROUTINE * /*NormalRoutine*/,
PVOID * /*NormalContext*/,
PVOID * /*SystemArgument1*/,
PVOID * /*SystemArgument2*/
)
{
CPP_FUNCTION;
DbgPrint("_KernelRoutine(%p, %x)\n", Apc, Apc->ApcMode);
if (Apc->ApcMode == KernelMode)
{
// stage #1 - kernel mode apc
ObfReferenceObject(g_DriverObject);//NormalRoutine will be called
return ;
}
// stage #2 - user mode apc, free Apc object
_RundownRoutine(Apc);
}
BOOLEAN SuffixUnicodeString(PCUNICODE_STRING FullName, PCUNICODE_STRING ShortName)
{
if (ShortName->Length < FullName->Length)
{
UNICODE_STRING us = {
ShortName->Length, us.Length,
(PWSTR)RtlOffsetToPointer(FullName->Buffer, FullName->Length - us.Length)
};
return RtlEqualUnicodeString(&us, ShortName, TRUE);
}
return FALSE;
}
/*++
we called because somebody call ZwMapViewOfSection with SEC_IMAGE
but this can be not true load library: (ArbitraryUserPointer -> L"*\\kernel32.dll" in true load lib)
smss.exe map kernel32.dll during create \\KnownDlls (ArbitraryUserPointer == 0 in this case)
wow64 process several time map kernel32.dll (32 and 64 bit) with WOW64_IMAGE_SECTION or NOT_AN_IMAGE
--*/
BOOLEAN IsByLdrLoadDll(PCUNICODE_STRING ShortName)
{
UNICODE_STRING Name;
__try
{
PNT_TIB Teb = (PNT_TIB)PsGetCurrentThreadTeb();
if (!Teb || !(Name.Buffer = (PWSTR)Teb->ArbitraryUserPointer))
{
return FALSE;
}
ProbeForRead(Name.Buffer, sizeof(WCHAR), __alignof(WCHAR));
Name.Length = (USHORT)wcsnlen(Name.Buffer, MAXSHORT);
if (Name.Length == MAXSHORT)
{
return FALSE;
}
Name.MaximumLength = Name.Length <<= 1;
DbgPrint("ArbitraryUserPointer=%wZ\n", &Name);
return SuffixUnicodeString(&Name, ShortName);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgPrint("!!! IsByLdrLoadDll\n");
}
return FALSE;
}
BOOLEAN IsTargetProcess()
{
PEPROCESS Process = IoGetCurrentProcess();
PCSTR name = PsGetProcessImageFileName(Process);
return PsIsProcessBeingDebugged(Process) || !_stricmp("notepad.exe", name);
}
void BeginInject(DLL_INFORMATION* pdi)
{
PVOID Section;
if (0 <= pdi->GetSection(&Section))
{
// for do main job out of critical region
if (PKAPC Apc = new(NonPagedPool) KAPC)
{
KeInitializeApc(Apc, KeGetCurrentThread(), OriginalApcEnvironment,
KernelRoutine, RundownRoutine, NormalRoutine, KernelMode, Apc);
DbgPrint("++Apc<%p> \n", Apc);
ObfReferenceObject(g_DriverObject);
ObfReferenceObject(Section);
if (!KeInsertQueueApc(Apc, Section, pdi, IO_NO_INCREMENT))
{
ObfDereferenceObject(Section);
RundownRoutine(Apc);
}
}
}
}
VOID CALLBACK OnLoadImage(
IN PUNICODE_STRING FullImageName,
IN HANDLE ProcessId, // where image is mapped
IN PIMAGE_INFO ImageInfo
)
{
#if 0
DbgPrint("%x %p(%p) %p %x %wZ\n",
#ifdef _WIN64
IoIs32bitProcess(0),
#else
0,
#endif
ProcessId, PsGetCurrentProcessId(), ImageInfo->ImageBase, ImageInfo->ImageSize, FullImageName);
#endif
STATIC_UNICODE_STRING(kernel32, "\\kernel32.dll");
if (
!ImageInfo->SystemModeImage &&
ProcessId == PsGetCurrentProcessId() && // section can be "remote" mapped from another process
SuffixUnicodeString(FullImageName, &kernel32) &&
IsByLdrLoadDll(&kernel32) //&& IsTargetProcess()
)
{
#if 0 // insert native or wow dll in process
DLL_INFORMATION* pdi;
#ifdef _WIN64
if (IoIs32bitProcess(0))
{
pdi = &WOW_DLL::di;
}
else
#endif
{
pdi = &NATIVE_DLL::di;
}
BeginInject(pdi);
#else // insert both native and wow dll in wow64 process (just for demo)
BeginInject(&NATIVE_DLL::di);
#ifdef _WIN64
if (IoIs32bitProcess(0)) BeginInject(&WOW_DLL::di);
#endif
#endif
}
}
void FreeLoadImageData()
{
if (_bittestandreset(&gFlags, flImageNotifySet)) PsRemoveLoadImageNotifyRoutine(OnLoadImage);
PVOID Context;
if (STATUS_SUCCESS == RtlRunOnceBeginInitialize(&NATIVE_DLL::di.SectionStatus, RTL_RUN_ONCE_CHECK_ONLY, &Context))
{
if (0 <= RUN_ONCE_CONTEXT_TO_STATUS(Context))
{
// we access di.Section only inside OnLoadImage
// but it already completed and no more will be called (PsRemoveLoadImageNotifyRoutine)
// NormalRoutine yet can be executing, but it have own referenced Section pointer
DbgPrint("delete section %p\n", NATIVE_DLL::di.Section);
ObMakeTemporaryObject(NATIVE_DLL::di.Section);
ObfDereferenceObject(NATIVE_DLL::di.Section);
}
}
#ifdef _WIN64
if (STATUS_SUCCESS == RtlRunOnceBeginInitialize(&WOW_DLL::di.SectionStatus, RTL_RUN_ONCE_CHECK_ONLY, &Context))
{
if (0 <= RUN_ONCE_CONTEXT_TO_STATUS(Context))
{
// we access di.Section only inside OnLoadImage
// but it already completed and no more will be called (PsRemoveLoadImageNotifyRoutine)
// NormalRoutine yet can be executing, but it have own referenced Section pointer
DbgPrint("delete section %p\n", WOW_DLL::di.Section);
ObMakeTemporaryObject(WOW_DLL::di.Section);
ObfDereferenceObject(WOW_DLL::di.Section);
}
}
#endif
}
void NTAPI DriverUnload(PDRIVER_OBJECT DriverObject)
{
FreeLoadImageData();
DbgPrint("DriverUnload(%p)\n", DriverObject);
}
extern "C" NTSTATUS NTAPI DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DbgPrint("DriverLoad(%p, %wZ)\n", DriverObject, RegistryPath);
g_DriverObject = DriverObject;
DriverObject->DriverUnload = DriverUnload;
NTSTATUS status = PsSetLoadImageNotifyRoutine(OnLoadImage);
if (0 <= status)
{
_bittestandset(&gFlags, flImageNotifySet);
}
return status;
}
_NT_END