This example demonstrates how to implement a simple VMCALL interface between a guest and the hypervisor using IntroVirt. The guest can make VMCALLs with specific service codes to request services from the hypervisor.
This example shows how IntroVirt can be used to add powerful protections to in-guest applications with minimal code changes.
#include <boost/program_options.hpp>
#include <algorithm>
#include <csignal>
#include <functional>
#include <iostream>
using namespace std;
namespace po = boost::program_options;
0xF002
};
po::variables_map& vm);
public:
case EventType::EVENT_HYPERCALL:
cout << "Hypercall handled.\n";
break;
case EventType::EVENT_FAST_SYSCALL:
break;
case EventType::EVENT_FAST_SYSCALL_RET:
break;
default:
cout << "Unhandled event type: " << event.type() << "\n";
break;
}
}
switch (wevent.syscall().index()) {
case SystemCallIndex::NtTerminateProcess: {
if (!handler->will_return() || handler->target_pid() == wevent.task().pid()) {
auto& task = event.task();
cout << task.process_name() << " [" << task.pid() << ":" << task.tid() << "]\n";
cout << '\t' << "Self terminated. Read-only memory protections removed.\n";
}
auto& task = event.task();
cout << task.process_name() << " [" << task.pid() << ":" << task.tid() << "]\n";
cout << '\t' << "Self terminated. Process protections removed.\n";
}
break;
} else {
handler->ProcessHandle(0xFFFFFFFFFFFFFFFF);
cout << "Blocked termination of protected PID " << handler->target_pid()
<< " by " << wevent.task().process_name() << "[" << wevent.task().pid()
<< ":" << wevent.task().tid() << "]\n";
break;
}
}
wevent.syscall().hook_return(true);
handler->data("target_pid", make_shared<uint64_t>(handler->target_pid()));
break;
}
case SystemCallIndex::NtOpenProcess: {
auto* client_id = handler->ClientId();
const uint64_t target_pid = client_id->UniqueProcess();
if (desired_access.has(nt::PROCESS_TERMINATE) ||
desired_access.has(nt::PROCESS_VM_WRITE) ||
desired_access.has(nt::PROCESS_VM_OPERATION) ||
desired_access.has(nt::PROCESS_CREATE_THREAD) ||
desired_access.has(nt::PROCESS_CREATE_PROCESS) ||
desired_access.has(nt::PROCESS_SET_INFORMATION)) {
cout << "Blocked NtOpenProcess attempt for protected PID " << target_pid
<< " by " << wevent.task().process_name() << "[" << wevent.task().pid()
<< ":" << wevent.task().tid() << "]\n";
}
}
break;
}
default:
break;
}
}
if (
unlikely(wevent.syscall().index() != SystemCallIndex::NtTerminateProcess)) {
return;
}
if (!handler->result().NT_SUCCESS()) {
return;
}
const uint64_t target_pid = *(static_pointer_cast<uint64_t>(handler->data("target_pid")));
auto& task = event.task();
cout << task.process_name() << " [" << task.pid() << ":" << task.tid() << "]\n";
cout << '\t' << "Terminated PID " << target_pid << '\n';
}
}
const auto& task = event.task();
auto& vcpu = event.vcpu();
auto& regs = vcpu.registers();
cout << task.process_name() << " [" << task.pid() << ":" << task.tid() << "]\n";
cout << hex;
cout << '\t' << "RIP: 0x" << regs.rip() << '\n';
cout << '\t' << "RAX: 0x" << regs.rax() << '\n';
cout << '\t' << "RCX: 0x" << regs.rcx() << '\n';
cout << '\t' << "RDX: 0x" << regs.rdx() << '\n';
cout << '\t' << "R8: 0x" << regs.r8() << '\n';
cout << '\t' << "R9: 0x" << regs.r9() << '\n';
cout << dec;
int return_code = 1;
switch (regs.rcx()) {
cout << '\t' << "CSTRING_REVERSE requested\n";
break;
cout << '\t' << "WRITE_PROTECT requested\n";
break;
cout << '\t' << "PROTECT_PROCESS requested\n";
break;
default:
cout << '\t' << "Unknown service code: 0x" << hex << regs.rcx() << dec << '\n';
break;
}
cout << '\t' << "Returning status code: " << return_code << '\n';
regs.rax(return_code);
}
auto& vcpu = event.vcpu();
auto& regs = vcpu.registers();
try {
cout <<
'\t' <<
"Reversing input string [" << str.
get() <<
"]\n";
cout <<
'\t' <<
"Reversed string is now [" << str.
get() <<
"]\n";
cout << ex;
return -1;
}
cout << '\t' << "String reversed successfully\n";
return 0;
}
auto& vcpu = event.vcpu();
auto& regs = vcpu.registers();
try {
const uint64_t length = regs.r8();
cout << '\t' << "Write protecting buffer [" << pBuffer << " Len: " << length << "]\n";
auto wp =
domain->create_watchpoint(
pBuffer, length, false, true, false,
cout << '\t' << "Watchpoint created successfully\n";
cerr << "Failed to create watchpoint: " << ex;
return -1;
}
return 0;
}
const auto& task = event.task();
auto& vcpu = event.vcpu();
auto& regs = vcpu.registers();
cout << "Memory access violation in " << task.process_name() << " [" << task.pid() << ":"
<< task.tid() << "]\n";
cout << task.process_name() << " [" << task.pid() << ":" << task.tid() << "]\n";
cout << '\t' << "Process wrote to read-only memory!\n";
cout << '\t' << "Physical Address: " << event.mem_access().physical_address() << '\n';
cout << '\t' << "RIP: 0x" << hex << regs.rip() << dec << '\n';
vcpu.inject_exception(x86::Exception::GP_FAULT, 0);
}
}
auto& task = event.task();
cout << '\t' << "Protected PID " << task.pid()
<< " from termination, injection, and debugging\n";
return 0;
}
}
};
int main(
int argc,
char** argv) {
string domain_name;
po::options_description desc("Options");
desc.add_options()("domain,D", po::value<string>(&domain_name)->required(),
"The domain name or ID attach to")("help", "Display program help");
po::variables_map vm;
auto hypervisor = Hypervisor::instance();
domain = hypervisor->attach_domain(domain_name);
if (!
domain->detect_guest()) {
cerr << "Failed to detect guest OS\n";
return 1;
}
if (
domain->guest()->os() != OS::Windows) {
cerr << "This example only supports Windows guests\n";
return 1;
}
true);
guest->set_system_call_filter(
domain->system_call_filter(), SystemCallIndex::NtOpenProcess,
true);
domain->system_call_filter().enabled(
true);
domain->intercept_system_calls(
true);
}
}
po::variables_map& vm) {
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
if (vm.count("help")) {
cout << "vmcall_interface - Example VMCALL communication\n";
cout << desc << '\n';
exit(0);
}
po::notify(vm);
} catch (po::error& e) {
cerr << "ERROR: " << e.what() << endl << endl;
cerr << desc << endl;
exit(1);
}
}
Definition vmcall_interface.cc:71
void memory_access_violation(Event &event)
Definition vmcall_interface.cc:393
void handle_sysret(Event &event)
Definition vmcall_interface.cc:217
void handle_hypercall(Event &event)
Definition vmcall_interface.cc:264
void handle_syscall(Event &event)
Definition vmcall_interface.cc:102
int service_protect_process(Event &event)
Definition vmcall_interface.cc:422
map< uint64_t, list< unique_ptr< Watchpoint > > > read_only_protections_
Definition vmcall_interface.cc:445
set< uint64_t > protected_pids_
Definition vmcall_interface.cc:448
int service_string_reverse(Event &event)
Definition vmcall_interface.cc:326
void cleanup()
Definition vmcall_interface.cc:437
void process_event(Event &event) override
Definition vmcall_interface.cc:80
int service_write_protect(Event &event)
Definition vmcall_interface.cc:358
mutex mtx_
Definition vmcall_interface.cc:453
Interface for an event poller callback.
Definition EventCallback.hh:29
Interface class for hypervisor events.
Definition Event.hh:43
virtual MemAccessEvent & mem_access()=0
Get memory access event information.
virtual EventType type() const =0
Get the type of event.
virtual Vcpu & vcpu()=0
Get the Vcpu that triggered the event.
virtual bool write_violation() const =0
Returns true if the event was caused by a write attempt.
Base class for exceptions with stack unwinding.
Definition TraceableException.hh:31
Thrown when translating a guest virtual address is marked as not present.
Definition VirtualAddressNotPresentException.hh:31
Definition guest_ptr.hh:88
auto get() const
Definition guest_ptr.hh:246
pointer_type begin() const
Array operations for non-pointer types.
Definition guest_ptr.hh:332
pointer_type end() const
Definition guest_ptr.hh:337
Definition WindowsEvent.hh:26
A representation of a Windows Guest OS.
Definition WindowsGuest.hh:33
virtual bool set_system_call_filter(SystemCallFilter &filter, SystemCallIndex index, bool value) const =0
Configure a system call filter intercept.
Handler class for the NtOpenProcess system call.
Definition NtOpenProcess.hh:34
virtual PROCESS_ACCESS_MASK DesiredAccess() const =0
Getter for DesiredAccess.
Handler class for the NtTerminateProcess system call.
Definition NtTerminateProcess.hh:32
#define unlikely(x)
Definition compiler.hh:27
std::unique_ptr< Domain > domain
Definition ivmemwatch.cc:47
int main(int argc, char **argv)
Definition main.c:35
Classes related to Microsoft Windows guests.
Definition LanguageId.hh:21
Core IntroVirt classes.
Definition Cr0.hh:20
basic_guest_ptr< char[], void, _Physical > map_guest_cstring(const basic_guest_ptr< _Tp, _PtrType, _Physical > &ptr, size_t max_length=0xFFFF)
Helper function for map_guest_str<char>
Definition guest_ptr.hh:1404
void sig_handler(int signum)
Definition vmcall_interface.cc:571
void parse_program_options(int argc, char **argv, po::options_description &desc, po::variables_map &vm)
Definition vmcall_interface.cc:581
IVServiceCode
Definition vmcall_interface.cc:52
@ PROTECT_PROCESS
Definition vmcall_interface.cc:55
@ WRITE_PROTECT
Definition vmcall_interface.cc:54
@ CSTRING_REVERSE
Definition vmcall_interface.cc:53
EventHandler event_handler
Definition vmcall_interface.cc:457
unique_ptr< Domain > domain
Definition vmcall_interface.cc:48