finished demo, added screenshots and binaries

merge-requests/1/head
_xeroxz 3 years ago
parent df74ce7951
commit 42c5af8f2b

Binary file not shown.

Binary file not shown.

@ -10,69 +10,122 @@ namespace bluepill
command.present = true;
command.option = vmcall_option::dirbase;
hypercall(VMCALL_KEY, &command);
// can throw invalid opcode if hypervisor is not loaded...
__try
{
hypercall(VMCALL_KEY, &command);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return {};
}
return command.dirbase;
}
auto translate(void* dirbase, void* virt_addr) -> u64
auto translate(u64 dirbase, void* virt_addr) -> u64
{
if (!dirbase || !virt_addr)
return false;
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.dirbase = dirbase;
command.translate.virt_addr = reinterpret_cast<u64>(virt_addr);
hypercall(VMCALL_KEY, &command);
// can throw invalid opcode if hypervisor is not loaded...
__try
{
hypercall(VMCALL_KEY, &command);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return {};
}
return command.translate.phys_addr;
}
auto read_phys(void* dest, void* phys_src, u64 size) -> bool
auto read_phys(u64 phys_src, void* virt_dest, u64 size) -> bool
{
if (!phys_src || !virt_dest || !size)
return false;
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.virt_dest = reinterpret_cast<u64>(virt_dest);
command.read_phys.phys_src = phys_src;
command.read_phys.dirbase_dest = get_dirbase();
command.read_phys.size = size;
hypercall(VMCALL_KEY, &command);
// can throw invalid opcode if hypervisor is not loaded...
__try
{
hypercall(VMCALL_KEY, &command);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
return command.result;
}
auto write_phys(void* phys_dest, void* src, u64 size) -> bool
auto write_phys(u64 phys_dest, void* src, u64 size) -> bool
{
if (!phys_dest || !src || !size)
return false;
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.phys_dest = phys_dest;
command.write_phys.dirbase_src = get_dirbase();
command.write_phys.size = size;
hypercall(VMCALL_KEY, &command);
// can throw invalid opcode if hypervisor is not loaded...
__try
{
hypercall(VMCALL_KEY, &command);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
return command.result;
}
auto copy_virt(void* dirbase_src, void* virt_src, void* dirbase_dest, void* virt_dest, u64 size) -> bool
auto copy_virt(u64 dirbase_src, void* virt_src, u64 dirbase_dest, void* virt_dest, u64 size) -> bool
{
if (!dirbase_src || !virt_src || !dirbase_dest || !virt_dest)
return false;
vmcall_command_t command{};
memset(&command, NULL, sizeof command);
command.present = true;
command.copy_virt.dirbase_dest = reinterpret_cast<u64>(dirbase_dest);
command.option = vmcall_option::copy_virt;
command.copy_virt.dirbase_dest = 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.dirbase_src = dirbase_src;
command.copy_virt.virt_src = reinterpret_cast<u64>(virt_src);
command.copy_virt.size = size;
hypercall(VMCALL_KEY, &command);
// can throw invalid opcode if hypervisor is not loaded...
__try
{
hypercall(VMCALL_KEY, &command);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
return command.result;
}
}

@ -1,6 +1,7 @@
#pragma once
#include <Windows.h>
#include <intrin.h>
#include "vdm_ctx/vdm_ctx.hpp"
#define VMCALL_KEY 0xC0FFEE
using u8 = unsigned char;
@ -73,16 +74,38 @@ namespace bluepill
// vmcall into the hypervisor...
extern "C" u64 hypercall(u64 key, pvmcall_command_t command);
// get vmexiting logical processors pml4...
// get vmexiting logical processors pml4 physical address...
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;
auto read_phys(u64 phys_src, void* virt_dest, u64 size) -> bool;
auto write_phys(u64 phys_dest, void* src, u64 size) -> bool;
// translate virtual to physical...
auto translate(void* dirbase, void* virt_addr)->u64;
auto translate(u64 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;
// copy virtual memory between two address spaces... page protections are ignored...
//
// WARNING:
// COW (copy on write) will not be triggered, if you write to ntdll.dll,
// kernel32.dll, kernelbase.dll, or any other globally mapped DLL's all processes
// will see the patch unless that process has already triggered COW on the page you are changing...
auto copy_virt(u64 dirbase_src, void* virt_src, u64 dirbase_dest, void* virt_dest, u64 size) -> bool;
template <class T>
inline auto rpm(u64 dirbase, u64 addr) -> T
{
T result{};
copy_virt(dirbase, (void*)addr,
get_dirbase(), (void*)&result, sizeof T);
return result;
}
template <class T>
inline auto wpm(u64 dirbase, u64 addr, const T& data) -> bool
{
return copy_virt(get_dirbase(), (void*)&data,
dirbase, (void*)addr, sizeof T);
}
}

@ -6,29 +6,16 @@ auto __cdecl main(int argc, char** argv) -> void
vdm::read_phys_t _read_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
return bluepill::read_phys(buffer, addr, size);
return bluepill::read_phys(
reinterpret_cast<u64>(addr), buffer, size);
};
vdm::write_phys_t _write_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
return bluepill::write_phys(addr, buffer, size);
return bluepill::write_phys(
reinterpret_cast<u64>(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 =
@ -40,7 +27,6 @@ auto __cdecl main(int argc, char** argv) -> void
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);
@ -54,4 +40,15 @@ auto __cdecl main(int argc, char** argv) -> void
std::printf("[+] kernel MZ -> 0x%x\n", mz_bytes);
std::getchar();
const auto explorer_pid = util::get_pid("explorer.exe");
const auto explorer_dirbase = vdm.get_dirbase(explorer_pid);
const auto explorer_base = vdm.get_base_address(explorer_pid);
std::printf("explorer.exe pid -> %d\n", explorer_pid);
std::printf("explorer.exe dirbase -> 0x%p\n", explorer_dirbase);
std::printf("explorer.exe base address -> 0x%p\n", explorer_base);
std::printf("explorer.exe MZ -> 0x%x\n", bluepill::rpm<short>(explorer_dirbase, explorer_base));
bluepill::wpm<short>(explorer_dirbase, explorer_base, 0xDE);
std::getchar();
}

@ -32,4 +32,18 @@ using PEPROCESS = PVOID;
using PsLookupProcessByProcessId = NTSTATUS(__fastcall*)(
HANDLE ProcessId,
PEPROCESS* Process
);
);
typedef union
{
uint64_t flags;
struct
{
uint64_t reserved1 : 3;
uint64_t page_level_write_through : 1;
uint64_t page_level_cache_disable : 1;
uint64_t reserved2 : 7;
uint64_t pml4_pfn : 36;
uint64_t reserved3 : 16;
};
} cr3;

@ -1,6 +1,7 @@
#pragma once
#include <Windows.h>
#include <ntstatus.h>
#include <tlhelp32.h>
#include <cstdint>
#include <string_view>
@ -88,6 +89,35 @@ namespace util
return &nt_headers->FileHeader;
}
__forceinline auto get_pid(const char* proc_name) -> std::uint32_t
{
PROCESSENTRY32 proc_info;
proc_info.dwSize = sizeof(proc_info);
HANDLE proc_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (proc_snapshot == INVALID_HANDLE_VALUE)
return NULL;
Process32First(proc_snapshot, &proc_info);
if (!strcmp(proc_info.szExeFile, proc_name))
{
CloseHandle(proc_snapshot);
return proc_info.th32ProcessID;
}
while (Process32Next(proc_snapshot, &proc_info))
{
if (!strcmp(proc_info.szExeFile, proc_name))
{
CloseHandle(proc_snapshot);
return proc_info.th32ProcessID;
}
}
CloseHandle(proc_snapshot);
return NULL;
}
__forceinline auto get_kmodule_base(const char* module_name) -> std::uintptr_t
{
void* buffer = nullptr;

@ -37,14 +37,42 @@ namespace vdm
search_thread.join();
}
void vdm_ctx::set_read(read_phys_t& read_func)
auto vdm_ctx::get_peprocess(std::uint32_t pid) -> PEPROCESS
{
this->read_phys = read_func;
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;
}
void vdm_ctx::set_write(write_phys_t& write_func)
auto vdm_ctx::get_dirbase(std::uint32_t pid) -> std::uintptr_t
{
this->write_phys = write_func;
const auto peproc =
reinterpret_cast<std::uintptr_t>(
get_peprocess(pid));
if (!peproc)
return {};
return rkm<cr3>(peproc + 0x28).pml4_pfn << 12;
}
auto vdm_ctx::get_base_address(std::uint32_t pid) -> std::uintptr_t
{
static const auto ps_get_base_addr =
util::get_kmodule_export(
"ntoskrnl.exe", "PsGetProcessSectionBaseAddress");
return syscall<std::uintptr_t(*)(PEPROCESS)>(
ps_get_base_addr, get_peprocess(pid));
}
void vdm_ctx::rkm(void* dst, void* src, std::size_t size)
@ -52,7 +80,7 @@ namespace vdm
static const auto ntoskrnl_memcpy =
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
this->syscall<decltype(&memcpy)>(
syscall<decltype(&memcpy)>(
ntoskrnl_memcpy, dst, src, size);
}
@ -61,7 +89,7 @@ namespace vdm
static const auto ntoskrnl_memcpy =
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
this->syscall<decltype(&memcpy)>(
syscall<decltype(&memcpy)>(
ntoskrnl_memcpy, dst, src, size);
}
@ -75,6 +103,10 @@ namespace vdm
PAGE_READWRITE
));
// you must write to the VirtualAlloc
// page in order for the PTE to be created...
memset(page_data, NULL, PAGE_4KB);
for (auto page = 0u; page < length; page += PAGE_4KB)
{
if (vdm::syscall_address.load())
@ -91,6 +123,7 @@ namespace vdm
reinterpret_cast<void*>(
address + page + nt_page_offset));
}
VirtualFree(page_data, PAGE_4KB, MEM_DECOMMIT);
}

@ -25,13 +25,15 @@ namespace vdm
{
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);
auto get_peprocess(std::uint32_t pid) -> PEPROCESS;
auto get_dirbase(std::uint32_t pid) -> std::uintptr_t;
auto get_base_address(std::uint32_t pid) -> std::uintptr_t;
template <class T, class ... Ts>
__forceinline std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args) const
std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args) const
{
static const auto proc =
GetProcAddress(
@ -65,7 +67,7 @@ namespace vdm
}
template <class T>
__forceinline auto rkm(std::uintptr_t addr) -> T
auto rkm(std::uintptr_t addr) -> T
{
T buffer;
rkm((void*)&buffer, (void*)addr, sizeof T);
@ -73,31 +75,15 @@ namespace vdm
}
template <class T>
__forceinline void wkm(std::uintptr_t addr, const T& value)
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;
}
read_phys_t read_phys;
write_phys_t write_phys;
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;
};
}

@ -137,16 +137,6 @@ auto exit_handler(hv::pguest_registers regs) -> void
{
if (regs->rcx == VMCALL_KEY)
{
// test SEH... IST is currently boonk...
__try
{
*reinterpret_cast<int*>(0x0) = 0xDE;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
__debugbreak();
}
cr3 dirbase;
__vmx_vmread(VMCS_GUEST_CR3, &dirbase.flags);

@ -52,7 +52,6 @@ namespace idt
result.segment_selector = readcs();
result.gate_type = SEGMENT_DESCRIPTOR_TYPE_INTERRUPT_GATE;
result.present = true;
result.ist_index = ist_index;
result.offset_high = idt_handler.offset_high;
result.offset_middle = idt_handler.offset_middle;

@ -145,7 +145,15 @@ namespace mm
if (!mapped_src)
return false;
memcpy(mapped_dest, mapped_src, current_size);
__try
{
memcpy(mapped_dest, mapped_src, current_size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
guest_phys += current_size;
guest_virt += current_size;
size -= current_size;
@ -188,7 +196,15 @@ namespace mm
if (!mapped_src)
return false;
memcpy(mapped_dest, mapped_src, current_size);
__try
{
memcpy(mapped_dest, mapped_src, current_size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
guest_phys += current_size;
guest_virt += current_size;
size -= current_size;
@ -225,12 +241,21 @@ namespace mm
// copy directly between the two pages...
auto current_size = min(dest_size, src_size);
memcpy(mapped_dest, mapped_src, current_size);
__try
{
memcpy(mapped_dest, mapped_src, current_size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
virt_src += current_size;
virt_dest += current_size;
size -= current_size;
}
return true;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Loading…
Cancel
Save