mirror of
https://github.com/BeneficialCode/WinArk
synced 2026-08-09 12:00:31 +00:00
464 lines
13 KiB
C++
464 lines
13 KiB
C++
#include "pch.h"
|
|
#include "Section.h"
|
|
#include "Logging.h"
|
|
#include "Memory.h"
|
|
#include "ObjectAttributes.h"
|
|
#include <ntimage.h>
|
|
#include "Helpers.h"
|
|
#include "NtMmApi.h"
|
|
|
|
|
|
extern PDRIVER_OBJECT g_DriverObject;
|
|
|
|
|
|
NTSTATUS Section::Initialize(SectionType type){
|
|
// Initialize this object
|
|
_type = type;
|
|
|
|
// Initialize our singleton
|
|
RtlRunOnceInitialize(&_sectionSingletonState);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Get DLL section object
|
|
NTSTATUS Section::GetSection(DllStats** ppSectionInfo) {
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
// make sure that Initialize was called!
|
|
ASSERT(_type == SectionType::Native || _type == SectionType::Wow);
|
|
// Use the single approach
|
|
PVOID Context = nullptr;
|
|
|
|
status = RtlRunOnceBeginInitialize(&_sectionSingletonState, 0, &Context);
|
|
if (!NT_SUCCESS(status)) {
|
|
LogError("RtlRunOnceBeginInitialize failed 0x%x", status);
|
|
return status;
|
|
}
|
|
if (status == STATUS_PENDING) {
|
|
// We get here only during the first initialization
|
|
Context = nullptr;
|
|
// Allocate memory
|
|
DllStats* pDllState = new (PagedPool, 'mSDk') DllStats;
|
|
do
|
|
{
|
|
if (!pDllState) {
|
|
break;
|
|
}
|
|
|
|
// Need to "trick" the system into creating a KnownDll section for us with the SD of
|
|
// the kernel32.dll section
|
|
|
|
// Temporarily attach the current thread to the address space of the system process
|
|
KAPC_STATE state;
|
|
KeStackAttachProcess(PsInitialSystemProcess, &state);
|
|
|
|
// Create our KnownDll section to bypass code integrity guard
|
|
status = CreateKnownDllSection(*pDllState);
|
|
|
|
// Revert back
|
|
KeUnstackDetachProcess(&state);
|
|
|
|
// Check the result
|
|
if (NT_SUCCESS(status)) {
|
|
Context = pDllState;
|
|
}
|
|
else {
|
|
LogError("ERROR: (0x%x), sectionType=%d", status, _type);
|
|
// Free memory
|
|
delete pDllState;
|
|
pDllState = nullptr;
|
|
}
|
|
} while (false);
|
|
|
|
// Finilize our singleton
|
|
status = RtlRunOnceComplete(&_sectionSingletonState, 0, Context);
|
|
if (!NT_SUCCESS(status)) {
|
|
// Error
|
|
LogError("ERROR: (0x%x) RtlRunOnceComplete, section type=%d", status, _type);
|
|
ASSERT(nullptr);
|
|
|
|
// Free memory
|
|
if (pDllState) {
|
|
delete pDllState;
|
|
pDllState = nullptr;
|
|
}
|
|
Context = nullptr;
|
|
}
|
|
}
|
|
|
|
if (ppSectionInfo)
|
|
*ppSectionInfo = (DllStats*)Context;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS Section::FreeSection() {
|
|
// Release resources held for the mapped section
|
|
NTSTATUS status;
|
|
|
|
PVOID Context = nullptr;
|
|
status = RtlRunOnceBeginInitialize(&_sectionSingletonState, 0, &Context);
|
|
if (NT_SUCCESS(status)) {
|
|
// We have initilized our singleton
|
|
|
|
// do we have the context - otherwise there's nothing to delete
|
|
if (Context) {
|
|
LogInfo("FreeSection ,sectionType=%d", _type);
|
|
DllStats* pDllStats = (DllStats*)Context;
|
|
|
|
ObMakeTemporaryObject(pDllStats->Section);
|
|
|
|
ObDereferenceObjectWithTag(pDllStats->Section, 'k23w');
|
|
pDllStats->Section = nullptr;
|
|
|
|
// Free memory
|
|
delete pDllStats;
|
|
Context = nullptr;
|
|
}
|
|
|
|
// Reset the singleton back
|
|
RtlRunOnceInitialize(&_sectionSingletonState);
|
|
}
|
|
else if (status == STATUS_UNSUCCESSFUL) {
|
|
// GetSection() wasn't called yet
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
// Error
|
|
LogError("Error: 0x%x RtlRunOnceBeginInitialize", status);
|
|
ASSERT(nullptr);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS Section::CreateKnownDllSection(DllStats& dllStats) {
|
|
// Create a known dll system section of our own
|
|
NTSTATUS status;
|
|
|
|
// Clear the return data (assuming only primitive data types)
|
|
memset(&dllStats, 0, sizeof(dllStats));
|
|
|
|
// need to steal a security descriptor from a existing KnownDll - we'll use kernel32
|
|
HANDLE hSectionK32;
|
|
|
|
POBJECT_ATTRIBUTES pKernel32Attr{};
|
|
PUNICODE_STRING pObjName{};
|
|
POBJECT_ATTRIBUTES pDllAttr{};
|
|
#ifdef _WIN64
|
|
if (_type == SectionType::Wow) {
|
|
UNICODE_STRING name = RTL_CONSTANT_STRING(L"\\KnownDlls32\\kernel32.dll");
|
|
static ObjectAttributes attr(&name, ObjectAttributesFlags::Caseinsensive);
|
|
static UNICODE_STRING objName = RTL_CONSTANT_STRING(L"\\KnownDlls32\\" INJECTED_DLL_FILE_NAME32);
|
|
UNICODE_STRING path = RTL_CONSTANT_STRING(INJECTED_DLL_NT_PATH_WOW);
|
|
static ObjectAttributes dllAttr(&path, ObjectAttributesFlags::Caseinsensive);
|
|
pKernel32Attr = &attr;
|
|
pObjName = &objName;
|
|
pDllAttr = &dllAttr;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
UNICODE_STRING name = RTL_CONSTANT_STRING(L"\\KnownDlls\\kernel32.dll");
|
|
static ObjectAttributes attr(&name, ObjectAttributesFlags::Caseinsensive);
|
|
static UNICODE_STRING objName = RTL_CONSTANT_STRING(L"\\KnownDlls\\" INJECTED_DLL_FILE_NAME32);
|
|
UNICODE_STRING path = RTL_CONSTANT_STRING(INJECTED_DLL_NT_PATH_WOW);
|
|
static ObjectAttributes dllAttr(&path, ObjectAttributesFlags::Caseinsensive);
|
|
pKernel32Attr = &attr;
|
|
pObjName = &objName;
|
|
pDllAttr = &dllAttr;
|
|
}
|
|
|
|
status = ZwOpenSection(&hSectionK32, READ_CONTROL, pKernel32Attr);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
LogError("ERROR: (0x%X) ZwOpenSection(hSectionK32), PID=%u, sectionType=%c\n",
|
|
status,
|
|
PsGetCurrentProcessId(),
|
|
_type);
|
|
return status;
|
|
}
|
|
|
|
status = STATUS_GENERIC_COMMAND_FAILED;
|
|
// INFO: Make our section "permanent", which means that it won't be deleted if all of its handle are closed
|
|
// and we will need to call ZwMakeTemporaryObject() on it first to allow it.
|
|
|
|
ObjectAttributes objAttr(pObjName, ObjectAttributesFlags::Caseinsensive | ObjectAttributesFlags::Permanent);
|
|
|
|
LogInfo("Got KnownDll Ok!\n");
|
|
ULONG size = 0;
|
|
for (;;) {
|
|
ULONG needSize = 0;
|
|
status = ZwQuerySecurityObject(hSectionK32, PROCESS_TRUST_LABEL_SECURITY_INFORMATION |
|
|
DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
|
|
objAttr.SecurityDescriptor, // SD
|
|
size, // mem size
|
|
&needSize);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
// Got it
|
|
break;
|
|
}
|
|
else if (STATUS_BUFFER_TOO_SMALL == status) {
|
|
// We need more memory
|
|
ASSERT(needSize > size);
|
|
if (objAttr.SecurityDescriptor) {
|
|
// Free previous memory
|
|
ExFreePool(objAttr.SecurityDescriptor);
|
|
objAttr.SecurityDescriptor = nullptr;
|
|
}
|
|
|
|
// Allocate mem
|
|
objAttr.SecurityDescriptor = ExAllocatePoolWithTag(PagedPool, needSize, 'm23k');
|
|
if (objAttr.SecurityDescriptor) {
|
|
// Need to retry
|
|
size = needSize;
|
|
}
|
|
else {
|
|
// Error
|
|
status = STATUS_MEMORY_NOT_ALLOCATED;
|
|
LogError("0x%x\n", status);
|
|
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
// Error
|
|
LogError("0x%x\n", status);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Close section
|
|
status = ZwClose(hSectionK32);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
// Now we can create our own seciton for our injected DLL in the KnownDlls kernel object directory
|
|
HANDLE hFile;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
do
|
|
{
|
|
status = ZwOpenFile(&hFile, FILE_GENERIC_READ | FILE_EXECUTE,
|
|
pDllAttr, &iosb, FILE_SHARE_READ,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (!NT_SUCCESS(status)) {
|
|
LogError("ERROR: (0x%X) ZwOpenFile, PID=%u, sectionType=%c, path=\"%wZ\"\n",
|
|
status, PsGetCurrentProcessId(),
|
|
_type, pDllAttr->ObjectName);
|
|
break;
|
|
}
|
|
|
|
// Open PE file as section
|
|
HANDLE hSection;
|
|
status = ZwCreateSection(&hSection, SECTION_MAP_EXECUTE | SECTION_QUERY,
|
|
&objAttr, 0, PAGE_EXECUTE, SEC_IMAGE, hFile);
|
|
if (!NT_SUCCESS(status)) {
|
|
LogError("ERROR: (0x%X) ZwCreateSection, PID=%u, sectionType=%c\n",
|
|
status, PsGetCurrentProcessId(), _type);
|
|
break;
|
|
}
|
|
|
|
PVOID base = nullptr;
|
|
SIZE_T viewSize = 0;
|
|
status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &base,
|
|
0, 0, 0, &viewSize, ViewUnmap, 0, PAGE_READONLY);
|
|
if (!NT_SUCCESS(status)) {
|
|
LogError("ERROR: (0x%X) ZwMapViewOfSection, PID=%u, sectionType=%c\n",
|
|
status, PsGetCurrentProcessId(),
|
|
_type);
|
|
break;
|
|
}
|
|
|
|
ULONG ordIndex = 1;
|
|
ULONG rva = 0;
|
|
__try {
|
|
status = STATUS_INVALID_IMAGE_FORMAT;
|
|
PIMAGE_NT_HEADERS pNtHdr = RtlImageNtHeader(base);
|
|
if (!pNtHdr) {
|
|
LogError("ERROR: (0x%X) RtlImageNtHeader, PID=%u, sectionType=%c\n",
|
|
status, PsGetCurrentProcessId(),
|
|
_type);
|
|
status = STATUS_INVALID_IMAGE_FORMAT;
|
|
break;
|
|
}
|
|
|
|
ULONG size;
|
|
PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)
|
|
RtlImageDirectoryEntryToData(base, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size);
|
|
if (pExportDir && size > sizeof(IMAGE_EXPORT_DIRECTORY)) {
|
|
ordIndex -= pExportDir->Base;
|
|
if (ordIndex < pExportDir->NumberOfFunctions) {
|
|
PULONG pFuncAddr = (PULONG)((BYTE*)base + pExportDir->AddressOfFunctions);
|
|
if (!pFuncAddr) {
|
|
LogError("ERROR: (0x%X) Bad AddressOfFunctions, PID=%u, sectionType=%c\n",
|
|
status,
|
|
(ULONG)(ULONG_PTR)PsGetCurrentProcessId(),
|
|
_type);
|
|
break;
|
|
}
|
|
rva = pFuncAddr[ordIndex];
|
|
if (rva > 0) {
|
|
UINT rvaGuid = Helpers::FindStringByGuid(base,
|
|
pNtHdr->OptionalHeader.SizeOfImage,
|
|
&dllGuid);
|
|
if (rvaGuid != -1) {
|
|
SECTION_IMAGE_INFORMATION info;
|
|
status = ZwQuerySection(hSection,
|
|
SectionImageInformation, &info, sizeof(info), nullptr);
|
|
if (!NT_SUCCESS(status)) {
|
|
LogError("ERROR: (0x%X) ZwQuerySection, PID=%u, sectionType=%c\n",
|
|
status, PsGetCurrentProcessId(), _type);
|
|
break;
|
|
}
|
|
|
|
status = ObReferenceObjectByHandleWithTag(hSection,
|
|
0, nullptr, KernelMode, 'SkFh',
|
|
&dllStats.Section, nullptr);
|
|
if (NT_SUCCESS(status)) {
|
|
dllStats.Type = _type;
|
|
dllStats.SizeOfImage = pNtHdr->OptionalHeader.SizeOfImage;
|
|
dllStats.ShellCodeRVA = rva;
|
|
dllStats.DllNameRVA = rvaGuid;
|
|
|
|
dllStats.PreferredAddress = (BYTE*)info.TransferAddress -
|
|
pNtHdr->OptionalHeader.AddressOfEntryPoint;
|
|
}
|
|
else {
|
|
LogError("ERROR: (0x%X) ObReferenceObjectByHandle, PID=%u, sectionType=%c\n",
|
|
status,
|
|
PsGetCurrentProcessId(),
|
|
_type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
LogError("ERROR: (0x%X) Bad RVA=%d, PID=%u, sectionType=%c\n",
|
|
status, rva, PsGetCurrentProcessId(),
|
|
_type);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
LogError("ERROR: (0x%X) Bad ordinal, PID=%u cnt=%u, sectionType=%c\n",
|
|
status,
|
|
PsGetCurrentProcessId(),
|
|
pExportDir->NumberOfFunctions,
|
|
_type);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
LogError("ERROR: (0x%X) Export directory, PID=%u size=%u, sectionType=%c\n",
|
|
status, PsGetCurrentProcessId(),
|
|
size, _type);
|
|
break;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER) {
|
|
LogError("#EXCEPTION: (0x%X) CreateKnownDllSection(PE-scan), PID=%u, sectionType=%c\n",
|
|
GetExceptionCode(),
|
|
PsGetCurrentProcessId(),
|
|
_type);
|
|
status = STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
|
|
status = ZwUnmapViewOfSection(NtCurrentProcess(), base);
|
|
} while (false);
|
|
|
|
|
|
}
|
|
|
|
// Free our memory
|
|
if (objAttr.SecurityDescriptor) {
|
|
ExFreePool(objAttr.SecurityDescriptor);
|
|
objAttr.SecurityDescriptor = nullptr;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS Section::InjectDLL(DllStats* pDllStats) {
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
if (!pDllStats->IsVaid()) {
|
|
// invalid data
|
|
ASSERT(NULL);
|
|
return STATUS_INVALID_PARAMETER_MIX;
|
|
}
|
|
// https://dennisbabkin.com/inside_nt_apc/
|
|
PKAPC pApc = (PKAPC)ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), 'cpak');
|
|
|
|
if (!pApc) {
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
}
|
|
|
|
/*KeInitializeApc(pApc, KeGetCurrentThread(),
|
|
OriginalApcEnvironment,
|
|
KernelRoutine, RundownRoutine, NormalRoutine,
|
|
KernelMode,
|
|
pApc, );*/
|
|
|
|
//Prevent our driver from unloading be incrementing its reference count
|
|
ObReferenceObject(g_DriverObject);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS Section::MapSectionForShellCode(DllStats* pDllStats,
|
|
PVOID* pBaseAddr) {
|
|
ASSERT(pDllStats);
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PVOID baseAddress = nullptr;
|
|
|
|
if (!pDllStats->IsVaid()) {
|
|
status = STATUS_INVALID_PARAMETER_MIX;
|
|
return status;
|
|
}
|
|
|
|
PVOID reservedAddr = pDllStats->PreferredAddress;
|
|
SIZE_T regionSize = pDllStats->SizeOfImage;
|
|
|
|
status = ZwAllocateVirtualMemory(NtCurrentProcess(),
|
|
&reservedAddr, 0, ®ionSize,
|
|
MEM_RESERVE, PAGE_NOACCESS);
|
|
|
|
if (status != STATUS_SUCCESS &&
|
|
status != STATUS_CONFLICTING_ADDRESSES) {
|
|
LogError("Error: (0x%X) MapSectionForShellCode > ZwAllocateVirtualMemory, PID=%u, sectionType=%c, "
|
|
"addr=0x%p, sz=0x%X\n",
|
|
status, PsGetCurrentProcessId(),
|
|
pDllStats->Type, pDllStats->PreferredAddress,
|
|
pDllStats->SizeOfImage);
|
|
return status;
|
|
}
|
|
|
|
SIZE_T viewSize = 0;
|
|
LARGE_INTEGER offset{};
|
|
|
|
status = MmMapViewOfSection(pDllStats->Section,
|
|
IoGetCurrentProcess(), &baseAddress, 0, 0,
|
|
&offset, &viewSize, ViewUnmap, 0, PAGE_EXECUTE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
LogError("ERROR: (0x%X) MapSectionForShellCode > MmMapViewOfSection, "
|
|
"PID = % u, sectionType = % c",
|
|
status,
|
|
PsGetCurrentProcessId(),
|
|
pDllStats->Type
|
|
);
|
|
return status;
|
|
}
|
|
|
|
regionSize = 0;
|
|
status = ZwFreeVirtualMemory(NtCurrentProcess(), &reservedAddr, ®ionSize, MEM_RELEASE);
|
|
|
|
if (pBaseAddr)
|
|
*pBaseAddr = baseAddress;
|
|
|
|
return status;
|
|
} |