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
|
||||
hypercall proc
|
||||
cpuid
|
||||
vmcall
|
||||
ret
|
||||
hypercall endp
|
||||
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 <intrin.h>
|
||||
#include "hypercall.h"
|
||||
#include "bluepill.h"
|
||||
#include "vdm_ctx/vdm_ctx.hpp"
|
||||
|
||||
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();
|
||||
}
|
@ -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