parent
51f47aa52e
commit
71005f896f
@ -0,0 +1,78 @@
|
|||||||
|
#include "bluepill.h"
|
||||||
|
|
||||||
|
namespace bluepill
|
||||||
|
{
|
||||||
|
auto get_dirbase() -> u64
|
||||||
|
{
|
||||||
|
vmcall_command_t command{};
|
||||||
|
memset(&command, NULL, sizeof command);
|
||||||
|
|
||||||
|
command.present = true;
|
||||||
|
command.option = vmcall_option::dirbase;
|
||||||
|
|
||||||
|
hypercall(VMCALL_KEY, &command);
|
||||||
|
return command.dirbase;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto translate(void* dirbase, void* virt_addr) -> u64
|
||||||
|
{
|
||||||
|
vmcall_command_t command{};
|
||||||
|
memset(&command, NULL, sizeof command);
|
||||||
|
|
||||||
|
command.present = true;
|
||||||
|
command.option = vmcall_option::translate;
|
||||||
|
command.translate.dirbase = reinterpret_cast<u64>(dirbase);
|
||||||
|
command.translate.virt_addr = reinterpret_cast<u64>(virt_addr);
|
||||||
|
|
||||||
|
hypercall(VMCALL_KEY, &command);
|
||||||
|
return command.translate.phys_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto read_phys(void* dest, void* phys_src, u64 size) -> bool
|
||||||
|
{
|
||||||
|
vmcall_command_t command{};
|
||||||
|
memset(&command, NULL, sizeof command);
|
||||||
|
|
||||||
|
command.present = true;
|
||||||
|
command.option = vmcall_option::read_phys;
|
||||||
|
command.read_phys.virt_dest = reinterpret_cast<u64>(dest);
|
||||||
|
command.read_phys.phys_src = reinterpret_cast<u64>(phys_src);
|
||||||
|
command.read_phys.dirbase_dest = get_dirbase();
|
||||||
|
command.read_phys.size = size;
|
||||||
|
|
||||||
|
hypercall(VMCALL_KEY, &command);
|
||||||
|
return command.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto write_phys(void* phys_dest, void* src, u64 size) -> bool
|
||||||
|
{
|
||||||
|
vmcall_command_t command{};
|
||||||
|
memset(&command, NULL, sizeof command);
|
||||||
|
|
||||||
|
command.present = true;
|
||||||
|
command.option = vmcall_option::write_phys;
|
||||||
|
command.write_phys.virt_src = reinterpret_cast<u64>(src);
|
||||||
|
command.write_phys.phys_dest = reinterpret_cast<u64>(phys_dest);
|
||||||
|
command.write_phys.dirbase_src = get_dirbase();
|
||||||
|
command.write_phys.size = size;
|
||||||
|
|
||||||
|
hypercall(VMCALL_KEY, &command);
|
||||||
|
return command.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto copy_virt(void* dirbase_src, void* virt_src, void* dirbase_dest, void* virt_dest, u64 size) -> bool
|
||||||
|
{
|
||||||
|
vmcall_command_t command{};
|
||||||
|
memset(&command, NULL, sizeof command);
|
||||||
|
|
||||||
|
command.present = true;
|
||||||
|
command.copy_virt.dirbase_dest = reinterpret_cast<u64>(dirbase_dest);
|
||||||
|
command.copy_virt.virt_dest = reinterpret_cast<u64>(virt_dest);
|
||||||
|
command.copy_virt.dirbase_src = reinterpret_cast<u64>(dirbase_src);
|
||||||
|
command.copy_virt.virt_src = reinterpret_cast<u64>(virt_src);
|
||||||
|
command.copy_virt.size = size;
|
||||||
|
|
||||||
|
hypercall(VMCALL_KEY, &command);
|
||||||
|
return command.result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
#define VMCALL_KEY 0xC0FFEE
|
||||||
|
|
||||||
|
using u8 = unsigned char;
|
||||||
|
using u16 = unsigned short;
|
||||||
|
using u32 = unsigned int;
|
||||||
|
using u64 = unsigned long long;
|
||||||
|
using u128 = __m128;
|
||||||
|
|
||||||
|
using s8 = char;
|
||||||
|
using s16 = short;
|
||||||
|
using s32 = int;
|
||||||
|
using s64 = long long;
|
||||||
|
|
||||||
|
namespace bluepill
|
||||||
|
{
|
||||||
|
enum class vmcall_option
|
||||||
|
{
|
||||||
|
translate,
|
||||||
|
copy_virt,
|
||||||
|
write_phys,
|
||||||
|
read_phys,
|
||||||
|
dirbase
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _vmcall_command_t
|
||||||
|
{
|
||||||
|
bool present;
|
||||||
|
bool result;
|
||||||
|
vmcall_option option;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 dirbase;
|
||||||
|
u64 virt_addr;
|
||||||
|
u64 phys_addr;
|
||||||
|
} translate;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 virt_src;
|
||||||
|
u64 dirbase_src;
|
||||||
|
u64 virt_dest;
|
||||||
|
u64 dirbase_dest;
|
||||||
|
u64 size;
|
||||||
|
} copy_virt;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 virt_src;
|
||||||
|
u64 dirbase_src;
|
||||||
|
u64 phys_dest;
|
||||||
|
u64 size;
|
||||||
|
} write_phys;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 phys_src;
|
||||||
|
u64 dirbase_dest;
|
||||||
|
u64 virt_dest;
|
||||||
|
u64 size;
|
||||||
|
} read_phys;
|
||||||
|
|
||||||
|
u64 dirbase;
|
||||||
|
};
|
||||||
|
|
||||||
|
} vmcall_command_t, * pvmcall_command_t;
|
||||||
|
|
||||||
|
// vmcall into the hypervisor...
|
||||||
|
extern "C" u64 hypercall(u64 key, pvmcall_command_t command);
|
||||||
|
|
||||||
|
// get vmexiting logical processors pml4...
|
||||||
|
auto get_dirbase() -> u64;
|
||||||
|
|
||||||
|
// read/write physical memory...
|
||||||
|
auto read_phys(void* dest, void* phys_src, u64 size) -> bool;
|
||||||
|
auto write_phys(void* phys_dest, void* src, u64 size) -> bool;
|
||||||
|
|
||||||
|
// translate virtual to physical...
|
||||||
|
auto translate(void* dirbase, void* virt_addr)->u64;
|
||||||
|
|
||||||
|
// copy virtual memory between two address spaces... page protections are ignored...
|
||||||
|
auto copy_virt(void* dirbase_src, void* virt_src, void* dirbase_dest, void* virt_dest, u64 size) -> bool;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
.code
|
.code
|
||||||
hypercall proc
|
hypercall proc
|
||||||
cpuid
|
vmcall
|
||||||
ret
|
ret
|
||||||
hypercall endp
|
hypercall endp
|
||||||
end
|
end
|
@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <intrin.h>
|
|
||||||
|
|
||||||
using u8 = unsigned char;
|
|
||||||
using u16 = unsigned short;
|
|
||||||
using u32 = unsigned int;
|
|
||||||
using u64 = unsigned long long;
|
|
||||||
using u128 = __m128;
|
|
||||||
|
|
||||||
using s8 = char;
|
|
||||||
using s16 = short;
|
|
||||||
using s32 = int;
|
|
||||||
using s64 = long long;
|
|
||||||
|
|
||||||
namespace bluepill
|
|
||||||
{
|
|
||||||
constexpr auto key = 0xC0FFEE;
|
|
||||||
extern "C" u64 hypercall(u64 key);
|
|
||||||
}
|
|
@ -1,9 +1,57 @@
|
|||||||
#include <iostream>
|
#include "bluepill.h"
|
||||||
#include <intrin.h>
|
#include "vdm_ctx/vdm_ctx.hpp"
|
||||||
#include "hypercall.h"
|
|
||||||
|
|
||||||
int main()
|
auto __cdecl main(int argc, char** argv) -> void
|
||||||
{
|
{
|
||||||
std::printf("hypercall result: 0x%x\n", bluepill::hypercall(bluepill::key));
|
vdm::read_phys_t _read_phys =
|
||||||
|
[&](void* addr, void* buffer, std::size_t size) -> bool
|
||||||
|
{
|
||||||
|
return bluepill::read_phys(buffer, addr, size);
|
||||||
|
};
|
||||||
|
|
||||||
|
vdm::write_phys_t _write_phys =
|
||||||
|
[&](void* addr, void* buffer, std::size_t size) -> bool
|
||||||
|
{
|
||||||
|
return bluepill::write_phys(addr, buffer, size);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto dirbase =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
bluepill::get_dirbase());
|
||||||
|
|
||||||
|
std::printf("current dirbase -> 0x%p\n", dirbase);
|
||||||
|
std::getchar();
|
||||||
|
|
||||||
|
const auto nt_shutdown_phys =
|
||||||
|
bluepill::translate(dirbase,
|
||||||
|
util::get_kmodule_export("ntoskrnl.exe",
|
||||||
|
vdm::syscall_hook.second));
|
||||||
|
|
||||||
|
std::printf("NtShutdownSystem translated (phys) -> 0x%p\n", nt_shutdown_phys);
|
||||||
|
std::getchar();
|
||||||
|
|
||||||
|
vdm::vdm_ctx vdm(_read_phys, _write_phys);
|
||||||
|
const auto ntoskrnl_base =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
util::get_kmodule_base("ntoskrnl.exe"));
|
||||||
|
|
||||||
|
const auto ntoskrnl_memcpy =
|
||||||
|
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
|
||||||
|
|
||||||
|
std::printf("[+] %s physical address -> 0x%p\n", vdm::syscall_hook.first, vdm::syscall_address.load());
|
||||||
|
std::printf("[+] %s page offset -> 0x%x\n", vdm::syscall_hook.first, vdm::nt_page_offset);
|
||||||
|
|
||||||
|
std::printf("[+] ntoskrnl base address -> 0x%p\n", ntoskrnl_base);
|
||||||
|
std::printf("[+] ntoskrnl memcpy address -> 0x%p\n", ntoskrnl_memcpy);
|
||||||
|
|
||||||
|
short mz_bytes = 0;
|
||||||
|
vdm.syscall<decltype(&memcpy)>(
|
||||||
|
ntoskrnl_memcpy,
|
||||||
|
&mz_bytes,
|
||||||
|
ntoskrnl_base,
|
||||||
|
sizeof mz_bytes
|
||||||
|
);
|
||||||
|
|
||||||
|
std::printf("[+] kernel MZ -> 0x%x\n", mz_bytes);
|
||||||
std::getchar();
|
std::getchar();
|
||||||
}
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "ntdll.lib")
|
||||||
|
#define PAGE_4KB 0x1000
|
||||||
|
|
||||||
|
constexpr auto SystemModuleInformation = 11;
|
||||||
|
typedef struct _RTL_PROCESS_MODULE_INFORMATION
|
||||||
|
{
|
||||||
|
HANDLE Section;
|
||||||
|
PVOID MappedBase;
|
||||||
|
PVOID ImageBase;
|
||||||
|
ULONG ImageSize;
|
||||||
|
ULONG Flags;
|
||||||
|
USHORT LoadOrderIndex;
|
||||||
|
USHORT InitOrderIndex;
|
||||||
|
USHORT LoadCount;
|
||||||
|
USHORT OffsetToFileName;
|
||||||
|
UCHAR FullPathName[256];
|
||||||
|
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;
|
||||||
|
|
||||||
|
typedef struct _RTL_PROCESS_MODULES
|
||||||
|
{
|
||||||
|
ULONG NumberOfModules;
|
||||||
|
RTL_PROCESS_MODULE_INFORMATION Modules[1];
|
||||||
|
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;
|
||||||
|
|
||||||
|
typedef LARGE_INTEGER PHYSICAL_ADDRESS, * PPHYSICAL_ADDRESS;
|
||||||
|
|
||||||
|
using PEPROCESS = PVOID;
|
||||||
|
using PsLookupProcessByProcessId = NTSTATUS(__fastcall*)(
|
||||||
|
HANDLE ProcessId,
|
||||||
|
PEPROCESS* Process
|
||||||
|
);
|
@ -0,0 +1,234 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <ntstatus.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string_view>
|
||||||
|
#include <map>
|
||||||
|
#include "nt.hpp"
|
||||||
|
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
inline std::map<std::uintptr_t, std::size_t> pmem_ranges{};
|
||||||
|
__forceinline auto is_valid(std::uintptr_t addr) -> bool
|
||||||
|
{
|
||||||
|
for (auto range : pmem_ranges)
|
||||||
|
if (addr >= range.first && addr <= range.first + range.second)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma pack (push, 1)
|
||||||
|
struct PhysicalMemoryPage//CM_PARTIAL_RESOURCE_DESCRIPTOR
|
||||||
|
{
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t shareDisposition;
|
||||||
|
uint16_t flags;
|
||||||
|
uint64_t pBegin;
|
||||||
|
uint32_t sizeButNotExactly;
|
||||||
|
uint32_t pad;
|
||||||
|
|
||||||
|
static constexpr uint16_t cm_resource_memory_large_40{ 0x200 };
|
||||||
|
static constexpr uint16_t cm_resource_memory_large_48{ 0x400 };
|
||||||
|
static constexpr uint16_t cm_resource_memory_large_64{ 0x800 };
|
||||||
|
|
||||||
|
uint64_t size()const noexcept
|
||||||
|
{
|
||||||
|
if (flags & cm_resource_memory_large_40)
|
||||||
|
return uint64_t{ sizeButNotExactly } << 8;
|
||||||
|
else if (flags & cm_resource_memory_large_48)
|
||||||
|
return uint64_t{ sizeButNotExactly } << 16;
|
||||||
|
else if (flags & cm_resource_memory_large_64)
|
||||||
|
return uint64_t{ sizeButNotExactly } << 32;
|
||||||
|
else
|
||||||
|
return uint64_t{ sizeButNotExactly };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PhysicalMemoryPage) == 20);
|
||||||
|
#pragma pack (pop)
|
||||||
|
|
||||||
|
inline const auto init_ranges = ([&]() -> bool
|
||||||
|
{
|
||||||
|
HKEY h_key;
|
||||||
|
DWORD type, size;
|
||||||
|
LPBYTE data;
|
||||||
|
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory", 0, KEY_READ, &h_key);
|
||||||
|
RegQueryValueEx(h_key, ".Translated", NULL, &type, NULL, &size); //get size
|
||||||
|
data = new BYTE[size];
|
||||||
|
RegQueryValueEx(h_key, ".Translated", NULL, &type, data, &size);
|
||||||
|
DWORD count = *(DWORD*)(data + 16);
|
||||||
|
auto pmi = data + 24;
|
||||||
|
for (int dwIndex = 0; dwIndex < count; dwIndex++)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
pmem_ranges.emplace(*(uint64_t*)(pmi + 0), *(uint64_t*)(pmi + 8));
|
||||||
|
#else
|
||||||
|
const PhysicalMemoryPage& page{ *(PhysicalMemoryPage*)(pmi - 4) };
|
||||||
|
pmem_ranges.emplace(page.pBegin, page.size());
|
||||||
|
#endif
|
||||||
|
pmi += 20;
|
||||||
|
}
|
||||||
|
delete[] data;
|
||||||
|
RegCloseKey(h_key);
|
||||||
|
return true;
|
||||||
|
})();
|
||||||
|
|
||||||
|
__forceinline auto get_file_header(void* base_addr) -> PIMAGE_FILE_HEADER
|
||||||
|
{
|
||||||
|
PIMAGE_DOS_HEADER dos_headers =
|
||||||
|
reinterpret_cast<PIMAGE_DOS_HEADER>(base_addr);
|
||||||
|
|
||||||
|
PIMAGE_NT_HEADERS nt_headers =
|
||||||
|
reinterpret_cast<PIMAGE_NT_HEADERS>(
|
||||||
|
reinterpret_cast<DWORD_PTR>(base_addr) + dos_headers->e_lfanew);
|
||||||
|
|
||||||
|
return &nt_headers->FileHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline auto get_kmodule_base(const char* module_name) -> std::uintptr_t
|
||||||
|
{
|
||||||
|
void* buffer = nullptr;
|
||||||
|
DWORD buffer_size = NULL;
|
||||||
|
|
||||||
|
auto status = NtQuerySystemInformation(
|
||||||
|
static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation),
|
||||||
|
buffer, buffer_size, &buffer_size);
|
||||||
|
|
||||||
|
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||||
|
{
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
status = NtQuerySystemInformation(
|
||||||
|
static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation),
|
||||||
|
buffer, buffer_size, &buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NT_SUCCESS(status))
|
||||||
|
{
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||||
|
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||||
|
{
|
||||||
|
const std::string current_module_name = std::string(reinterpret_cast<char*>(modules->Modules[idx].FullPathName) + modules->Modules[idx].OffsetToFileName);
|
||||||
|
if (!_stricmp(current_module_name.c_str(), module_name))
|
||||||
|
{
|
||||||
|
const uint64_t result = reinterpret_cast<uint64_t>(modules->Modules[idx].ImageBase);
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline auto get_kmodule_export(const char* module_name, const char* export_name, bool rva = false) -> void*
|
||||||
|
{
|
||||||
|
void* buffer = nullptr;
|
||||||
|
DWORD buffer_size = NULL;
|
||||||
|
|
||||||
|
NTSTATUS status = NtQuerySystemInformation(
|
||||||
|
static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation),
|
||||||
|
buffer,
|
||||||
|
buffer_size,
|
||||||
|
&buffer_size
|
||||||
|
);
|
||||||
|
|
||||||
|
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
||||||
|
{
|
||||||
|
VirtualFree(buffer, 0, MEM_RELEASE);
|
||||||
|
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
status = NtQuerySystemInformation(
|
||||||
|
static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation),
|
||||||
|
buffer,
|
||||||
|
buffer_size,
|
||||||
|
&buffer_size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NT_SUCCESS(status))
|
||||||
|
{
|
||||||
|
VirtualFree(buffer, 0, MEM_RELEASE);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
||||||
|
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
||||||
|
{
|
||||||
|
// find module and then load library it
|
||||||
|
const std::string current_module_name =
|
||||||
|
std::string(reinterpret_cast<char*>(
|
||||||
|
modules->Modules[idx].FullPathName) +
|
||||||
|
modules->Modules[idx].OffsetToFileName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!_stricmp(current_module_name.c_str(), module_name))
|
||||||
|
{
|
||||||
|
std::string full_path = reinterpret_cast<char*>(modules->Modules[idx].FullPathName);
|
||||||
|
full_path.replace(full_path.find("\\SystemRoot\\"),
|
||||||
|
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
||||||
|
|
||||||
|
const auto module_base =
|
||||||
|
LoadLibraryEx(
|
||||||
|
full_path.c_str(),
|
||||||
|
NULL,
|
||||||
|
DONT_RESOLVE_DLL_REFERENCES
|
||||||
|
);
|
||||||
|
|
||||||
|
PIMAGE_DOS_HEADER p_idh;
|
||||||
|
PIMAGE_NT_HEADERS p_inh;
|
||||||
|
PIMAGE_EXPORT_DIRECTORY p_ied;
|
||||||
|
|
||||||
|
PDWORD addr, name;
|
||||||
|
PWORD ordinal;
|
||||||
|
|
||||||
|
p_idh = (PIMAGE_DOS_HEADER)module_base;
|
||||||
|
if (p_idh->e_magic != IMAGE_DOS_SIGNATURE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
p_inh = (PIMAGE_NT_HEADERS)((LPBYTE)module_base + p_idh->e_lfanew);
|
||||||
|
if (p_inh->Signature != IMAGE_NT_SIGNATURE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
p_ied = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)module_base +
|
||||||
|
p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
|
||||||
|
|
||||||
|
addr = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfFunctions);
|
||||||
|
name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames);
|
||||||
|
ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals);
|
||||||
|
|
||||||
|
// find exported function
|
||||||
|
for (auto i = 0; i < p_ied->AddressOfFunctions; i++)
|
||||||
|
{
|
||||||
|
if (!strcmp(export_name, (char*)module_base + name[i]))
|
||||||
|
{
|
||||||
|
if (!rva)
|
||||||
|
{
|
||||||
|
auto result = (void*)((std::uintptr_t)modules->Modules[idx].ImageBase + addr[ordinal[i]]);
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto result = (void*)addr[ordinal[i]];
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
#include "vdm_ctx.hpp"
|
||||||
|
|
||||||
|
namespace vdm
|
||||||
|
{
|
||||||
|
vdm_ctx::vdm_ctx(read_phys_t& read_func, write_phys_t& write_func)
|
||||||
|
:
|
||||||
|
read_phys(read_func),
|
||||||
|
write_phys(write_func)
|
||||||
|
{
|
||||||
|
// already found the syscall's physical page...
|
||||||
|
if (vdm::syscall_address.load())
|
||||||
|
return;
|
||||||
|
|
||||||
|
vdm::ntoskrnl = reinterpret_cast<std::uint8_t*>(
|
||||||
|
LoadLibraryExA("ntoskrnl.exe", NULL,
|
||||||
|
DONT_RESOLVE_DLL_REFERENCES));
|
||||||
|
|
||||||
|
nt_rva = reinterpret_cast<std::uint32_t>(
|
||||||
|
util::get_kmodule_export(
|
||||||
|
"ntoskrnl.exe",
|
||||||
|
syscall_hook.first,
|
||||||
|
true
|
||||||
|
));
|
||||||
|
|
||||||
|
vdm::nt_page_offset = nt_rva % PAGE_4KB;
|
||||||
|
// for each physical memory range, make a thread to search it
|
||||||
|
std::vector<std::thread> search_threads;
|
||||||
|
for (auto ranges : util::pmem_ranges)
|
||||||
|
search_threads.emplace_back(std::thread(
|
||||||
|
&vdm_ctx::locate_syscall,
|
||||||
|
this,
|
||||||
|
ranges.first,
|
||||||
|
ranges.second
|
||||||
|
));
|
||||||
|
|
||||||
|
for (std::thread& search_thread : search_threads)
|
||||||
|
search_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdm_ctx::set_read(read_phys_t& read_func)
|
||||||
|
{
|
||||||
|
this->read_phys = read_func;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdm_ctx::set_write(write_phys_t& write_func)
|
||||||
|
{
|
||||||
|
this->write_phys = write_func;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdm_ctx::rkm(void* dst, void* src, std::size_t size)
|
||||||
|
{
|
||||||
|
static const auto ntoskrnl_memcpy =
|
||||||
|
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
|
||||||
|
|
||||||
|
this->syscall<decltype(&memcpy)>(
|
||||||
|
ntoskrnl_memcpy, dst, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdm_ctx::wkm(void* dst, void* src, std::size_t size)
|
||||||
|
{
|
||||||
|
static const auto ntoskrnl_memcpy =
|
||||||
|
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
|
||||||
|
|
||||||
|
this->syscall<decltype(&memcpy)>(
|
||||||
|
ntoskrnl_memcpy, dst, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdm_ctx::locate_syscall(std::uintptr_t address, std::uintptr_t length) const
|
||||||
|
{
|
||||||
|
const auto page_data =
|
||||||
|
reinterpret_cast<std::uint8_t*>(
|
||||||
|
VirtualAlloc(
|
||||||
|
nullptr,
|
||||||
|
PAGE_4KB, MEM_COMMIT | MEM_RESERVE,
|
||||||
|
PAGE_READWRITE
|
||||||
|
));
|
||||||
|
|
||||||
|
for (auto page = 0u; page < length; page += PAGE_4KB)
|
||||||
|
{
|
||||||
|
if (vdm::syscall_address.load())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!read_phys(reinterpret_cast<void*>(address + page), page_data, PAGE_4KB))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// check the first 32 bytes of the syscall, if its the same, test that its the correct
|
||||||
|
// occurrence of these bytes (since dxgkrnl is loaded into physical memory at least 2 times now)...
|
||||||
|
if (!memcmp(page_data + nt_page_offset, ntoskrnl + nt_rva, 32))
|
||||||
|
if (valid_syscall(reinterpret_cast<void*>(address + page + nt_page_offset)))
|
||||||
|
syscall_address.store(
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
address + page + nt_page_offset));
|
||||||
|
}
|
||||||
|
VirtualFree(page_data, PAGE_4KB, MEM_DECOMMIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vdm_ctx::valid_syscall(void* syscall_addr) const
|
||||||
|
{
|
||||||
|
static std::mutex syscall_mutex;
|
||||||
|
syscall_mutex.lock();
|
||||||
|
|
||||||
|
static const auto proc =
|
||||||
|
GetProcAddress(
|
||||||
|
LoadLibraryA(syscall_hook.second),
|
||||||
|
syscall_hook.first
|
||||||
|
);
|
||||||
|
|
||||||
|
// 0: 48 31 c0 xor rax, rax
|
||||||
|
// 3 : c3 ret
|
||||||
|
std::uint8_t shellcode[] = { 0x48, 0x31, 0xC0, 0xC3 };
|
||||||
|
std::uint8_t orig_bytes[sizeof shellcode];
|
||||||
|
|
||||||
|
// save original bytes and install shellcode...
|
||||||
|
read_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
|
||||||
|
write_phys(syscall_addr, shellcode, sizeof shellcode);
|
||||||
|
|
||||||
|
auto result = reinterpret_cast<NTSTATUS(__fastcall*)(void)>(proc)();
|
||||||
|
write_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
|
||||||
|
syscall_mutex.unlock();
|
||||||
|
return result == STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
#include "../util/util.hpp"
|
||||||
|
|
||||||
|
namespace vdm
|
||||||
|
{
|
||||||
|
// change this to whatever you want :^)
|
||||||
|
constexpr std::pair<const char*, const char*> syscall_hook = { "NtShutdownSystem", "ntdll.dll" };
|
||||||
|
inline std::atomic<bool> is_page_found = false;
|
||||||
|
inline std::atomic<void*> syscall_address = nullptr;
|
||||||
|
inline std::uint16_t nt_page_offset;
|
||||||
|
inline std::uint32_t nt_rva;
|
||||||
|
inline std::uint8_t* ntoskrnl;
|
||||||
|
|
||||||
|
using read_phys_t = std::function<bool(void* addr, void* buffer, std::size_t size)>;
|
||||||
|
using write_phys_t = std::function<bool(void* addr, void* buffer, std::size_t size)>;
|
||||||
|
|
||||||
|
class vdm_ctx
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit vdm_ctx(read_phys_t& read_func, write_phys_t& write_func);
|
||||||
|
void set_read(read_phys_t& read_func);
|
||||||
|
void set_write(write_phys_t& write_func);
|
||||||
|
void rkm(void* dst, void* src, std::size_t size);
|
||||||
|
void wkm(void* dst, void* src, std::size_t size);
|
||||||
|
|
||||||
|
template <class T, class ... Ts>
|
||||||
|
__forceinline std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args) const
|
||||||
|
{
|
||||||
|
static const auto proc =
|
||||||
|
GetProcAddress(
|
||||||
|
LoadLibraryA(syscall_hook.second),
|
||||||
|
syscall_hook.first
|
||||||
|
);
|
||||||
|
|
||||||
|
static std::mutex syscall_mutex;
|
||||||
|
syscall_mutex.lock();
|
||||||
|
|
||||||
|
// jmp [rip+0x0]
|
||||||
|
std::uint8_t jmp_code[] =
|
||||||
|
{
|
||||||
|
0xff, 0x25, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
std::uint8_t orig_bytes[sizeof jmp_code];
|
||||||
|
*reinterpret_cast<void**>(jmp_code + 6) = addr;
|
||||||
|
read_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
|
||||||
|
|
||||||
|
// execute hook...
|
||||||
|
write_phys(vdm::syscall_address.load(), jmp_code, sizeof jmp_code);
|
||||||
|
auto result = reinterpret_cast<T>(proc)(args ...);
|
||||||
|
write_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
|
||||||
|
|
||||||
|
syscall_mutex.unlock();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
__forceinline auto rkm(std::uintptr_t addr) -> T
|
||||||
|
{
|
||||||
|
T buffer;
|
||||||
|
rkm((void*)&buffer, (void*)addr, sizeof T);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
__forceinline void wkm(std::uintptr_t addr, const T& value)
|
||||||
|
{
|
||||||
|
wkm((void*)addr, (void*)&value, sizeof T);
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline auto get_peprocess(std::uint32_t pid) -> PEPROCESS
|
||||||
|
{
|
||||||
|
static const auto ps_lookup_peproc =
|
||||||
|
util::get_kmodule_export(
|
||||||
|
"ntoskrnl.exe",
|
||||||
|
"PsLookupProcessByProcessId");
|
||||||
|
|
||||||
|
PEPROCESS peproc = nullptr;
|
||||||
|
this->syscall<PsLookupProcessByProcessId>(
|
||||||
|
ps_lookup_peproc,
|
||||||
|
(HANDLE)pid,
|
||||||
|
&peproc
|
||||||
|
);
|
||||||
|
return peproc;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void locate_syscall(std::uintptr_t begin, std::uintptr_t end) const;
|
||||||
|
bool valid_syscall(void* syscall_addr) const;
|
||||||
|
|
||||||
|
read_phys_t read_phys;
|
||||||
|
write_phys_t write_phys;
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in new issue