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.present = true;
command.option = vmcall_option::dirbase; 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; 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{}; vmcall_command_t command{};
memset(&command, NULL, sizeof command); memset(&command, NULL, sizeof command);
command.present = true; command.present = true;
command.option = vmcall_option::translate; 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); 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; 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{}; vmcall_command_t command{};
memset(&command, NULL, sizeof command); memset(&command, NULL, sizeof command);
command.present = true; command.present = true;
command.option = vmcall_option::read_phys; command.option = vmcall_option::read_phys;
command.read_phys.virt_dest = reinterpret_cast<u64>(dest); command.read_phys.virt_dest = reinterpret_cast<u64>(virt_dest);
command.read_phys.phys_src = reinterpret_cast<u64>(phys_src); command.read_phys.phys_src = phys_src;
command.read_phys.dirbase_dest = get_dirbase(); command.read_phys.dirbase_dest = get_dirbase();
command.read_phys.size = size; 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; 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{}; vmcall_command_t command{};
memset(&command, NULL, sizeof command); memset(&command, NULL, sizeof command);
command.present = true; command.present = true;
command.option = vmcall_option::write_phys; command.option = vmcall_option::write_phys;
command.write_phys.virt_src = reinterpret_cast<u64>(src); 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.dirbase_src = get_dirbase();
command.write_phys.size = size; 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; 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{}; vmcall_command_t command{};
memset(&command, NULL, sizeof command); memset(&command, NULL, sizeof command);
command.present = true; 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.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.virt_src = reinterpret_cast<u64>(virt_src);
command.copy_virt.size = size; 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; return command.result;
} }
} }

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <Windows.h> #include <Windows.h>
#include <intrin.h> #include <intrin.h>
#include "vdm_ctx/vdm_ctx.hpp"
#define VMCALL_KEY 0xC0FFEE #define VMCALL_KEY 0xC0FFEE
using u8 = unsigned char; using u8 = unsigned char;
@ -73,16 +74,38 @@ namespace bluepill
// vmcall into the hypervisor... // vmcall into the hypervisor...
extern "C" u64 hypercall(u64 key, pvmcall_command_t command); 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; auto get_dirbase() -> u64;
// read/write physical memory... auto read_phys(u64 phys_src, void* virt_dest, u64 size) -> bool;
auto read_phys(void* dest, void* phys_src, u64 size) -> bool; auto write_phys(u64 phys_dest, void* src, u64 size) -> bool;
auto write_phys(void* phys_dest, void* src, u64 size) -> bool;
// translate virtual to physical... // 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... // 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; //
// 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 = vdm::read_phys_t _read_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool [&](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 = vdm::write_phys_t _write_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool [&](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); vdm::vdm_ctx vdm(_read_phys, _write_phys);
const auto ntoskrnl_base = 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 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("[+] %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 base address -> 0x%p\n", ntoskrnl_base);
std::printf("[+] ntoskrnl memcpy address -> 0x%p\n", ntoskrnl_memcpy); 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::printf("[+] kernel MZ -> 0x%x\n", mz_bytes);
std::getchar(); 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*)( using PsLookupProcessByProcessId = NTSTATUS(__fastcall*)(
HANDLE ProcessId, HANDLE ProcessId,
PEPROCESS* Process 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 #pragma once
#include <Windows.h> #include <Windows.h>
#include <ntstatus.h> #include <ntstatus.h>
#include <tlhelp32.h>
#include <cstdint> #include <cstdint>
#include <string_view> #include <string_view>
@ -88,6 +89,35 @@ namespace util
return &nt_headers->FileHeader; 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 __forceinline auto get_kmodule_base(const char* module_name) -> std::uintptr_t
{ {
void* buffer = nullptr; void* buffer = nullptr;

@ -37,14 +37,42 @@ namespace vdm
search_thread.join(); 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) void vdm_ctx::rkm(void* dst, void* src, std::size_t size)
@ -52,7 +80,7 @@ namespace vdm
static const auto ntoskrnl_memcpy = static const auto ntoskrnl_memcpy =
util::get_kmodule_export("ntoskrnl.exe", "memcpy"); util::get_kmodule_export("ntoskrnl.exe", "memcpy");
this->syscall<decltype(&memcpy)>( syscall<decltype(&memcpy)>(
ntoskrnl_memcpy, dst, src, size); ntoskrnl_memcpy, dst, src, size);
} }
@ -61,7 +89,7 @@ namespace vdm
static const auto ntoskrnl_memcpy = static const auto ntoskrnl_memcpy =
util::get_kmodule_export("ntoskrnl.exe", "memcpy"); util::get_kmodule_export("ntoskrnl.exe", "memcpy");
this->syscall<decltype(&memcpy)>( syscall<decltype(&memcpy)>(
ntoskrnl_memcpy, dst, src, size); ntoskrnl_memcpy, dst, src, size);
} }
@ -75,6 +103,10 @@ namespace vdm
PAGE_READWRITE 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) for (auto page = 0u; page < length; page += PAGE_4KB)
{ {
if (vdm::syscall_address.load()) if (vdm::syscall_address.load())
@ -91,6 +123,7 @@ namespace vdm
reinterpret_cast<void*>( reinterpret_cast<void*>(
address + page + nt_page_offset)); address + page + nt_page_offset));
} }
VirtualFree(page_data, PAGE_4KB, MEM_DECOMMIT); VirtualFree(page_data, PAGE_4KB, MEM_DECOMMIT);
} }

@ -25,13 +25,15 @@ namespace vdm
{ {
public: public:
explicit vdm_ctx(read_phys_t& read_func, write_phys_t& write_func); 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 rkm(void* dst, void* src, std::size_t size);
void wkm(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> 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 = static const auto proc =
GetProcAddress( GetProcAddress(
@ -65,7 +67,7 @@ namespace vdm
} }
template <class T> template <class T>
__forceinline auto rkm(std::uintptr_t addr) -> T auto rkm(std::uintptr_t addr) -> T
{ {
T buffer; T buffer;
rkm((void*)&buffer, (void*)addr, sizeof T); rkm((void*)&buffer, (void*)addr, sizeof T);
@ -73,31 +75,15 @@ namespace vdm
} }
template <class T> 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); wkm((void*)addr, (void*)&value, sizeof T);
} }
__forceinline auto get_peprocess(std::uint32_t pid) -> PEPROCESS read_phys_t read_phys;
{ write_phys_t write_phys;
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: private:
void locate_syscall(std::uintptr_t begin, std::uintptr_t end) const; void locate_syscall(std::uintptr_t begin, std::uintptr_t end) const;
bool valid_syscall(void* syscall_addr) 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) if (regs->rcx == VMCALL_KEY)
{ {
// test SEH... IST is currently boonk...
__try
{
*reinterpret_cast<int*>(0x0) = 0xDE;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
__debugbreak();
}
cr3 dirbase; cr3 dirbase;
__vmx_vmread(VMCS_GUEST_CR3, &dirbase.flags); __vmx_vmread(VMCS_GUEST_CR3, &dirbase.flags);

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

@ -145,7 +145,15 @@ namespace mm
if (!mapped_src) if (!mapped_src)
return false; 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_phys += current_size;
guest_virt += current_size; guest_virt += current_size;
size -= current_size; size -= current_size;
@ -188,7 +196,15 @@ namespace mm
if (!mapped_src) if (!mapped_src)
return false; 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_phys += current_size;
guest_virt += current_size; guest_virt += current_size;
size -= current_size; size -= current_size;
@ -225,12 +241,21 @@ namespace mm
// copy directly between the two pages... // copy directly between the two pages...
auto current_size = min(dest_size, src_size); 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_src += current_size;
virt_dest += current_size; virt_dest += current_size;
size -= current_size; size -= current_size;
} }
return true; 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