Monitors Windows API function calls in a guest by setting breakpoints on specified library/function names. Demonstrates breakpoint creation and event handling for a target process.
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <log4cxx/logger.h>
#include <atomic>
#include <csignal>
#include <iostream>
#include <mutex>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
namespace po = boost::program_options;
static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("introvirt.tools.ivcallmon"));
po::variables_map& vm);
static bool wildcard_match(const char* pp, const char* ss) {
if (*pp == '\0')
return *ss == '\0';
if (*pp == '*')
return wildcard_match(pp + 1, ss) || (*ss != '\0' && wildcard_match(pp, ss + 1));
if (*pp == '?' && *ss != '\0')
return wildcard_match(pp + 1, ss + 1);
return (*pp == *ss && *ss != '\0' && wildcard_match(pp + 1, ss + 1));
}
static bool symbol_matches_pattern(const std::string& pattern, const std::string& symbol) {
std::string p = pattern;
std::string s = symbol;
boost::algorithm::to_lower(p);
boost::algorithm::to_lower(s);
return wildcard_match(p.c_str(), s.c_str());
}
std::unique_ptr<Domain>
domain;
public:
return;
std::cout << "[" << event.task().pid() << ':' << event.task().tid() << "] "
<< event.task().process_name() << '\n';
std::cout <<
" Hit breakpoint " <<
name_ <<
'\n';
const auto& vcpu = event.vcpu();
const auto& regs = vcpu.registers();
auto test = ppreturn_address.get();
this, std::placeholders::_1));
std::cout.flush();
}
return;
LOG4CXX_ERROR(logger,
"BAD return RSP 0x" << std::hex << event.
vcpu().
registers().
rsp()
<< std::dec <<
" for " <<
name_);
return;
}
std::cout << "[" << event.task().pid() << ':' << event.task().tid() << "] "
<< event.task().process_name() << '\n';
std::cout <<
" Return hit for " <<
name_ << std::endl;
}
:
domain_(src.domain_),
bp_(std::move(src.bp_)),
name_(std::move(src.name_)),
}
bp_ = std::move(src.bp_);
name_ = std::move(src.name_);
return *this;
}
uint64_t pid)
}
public:
std::shared_ptr<Breakpoint>
bp_;
};
public:
for (const auto& symbol : symbols) {
std::string lower_sym = symbol;
boost::algorithm::to_lower(lower_sym);
std::vector<std::string> parts;
boost::algorithm::split(parts, lower_sym, boost::algorithm::is_any_of("!"),
boost::algorithm::token_compress_off);
if (parts.size() != 2) {
throw std::invalid_argument("Invalid symbol format: " + symbol);
}
std::string module_name = parts[0];
std::string symbol_name = parts[1];
requested_symbols_[module_name].insert(symbol_name);
LOG4CXX_DEBUG(logger, "Requested symbol: " << module_name << "!" << symbol_name);
}
for (const auto& kv : requested_symbols_) {
std::string dll = kv.first;
if (!boost::algorithm::iends_with(dll, ".dll"))
dll += ".dll";
requested_dlls_.insert(dll);
LOG4CXX_DEBUG(logger, "Requested DLL: " << dll);
}
LOG4CXX_DEBUG(logger, "Requested symbols: " << requested_symbols_.size());
LOG4CXX_DEBUG(logger, "Requested DLLs: " << requested_dlls_.size());
}
return;
}
try {
case EventType::EVENT_FAST_SYSCALL:
handle_syscall(wevent);
break;
case EventType::EVENT_FAST_SYSCALL_RET:
handle_sysret(wevent);
break;
case EventType::EVENT_CR_WRITE:
return;
}
if (!initial_check_.test_and_set()) {
LOG4CXX_DEBUG(logger, "First CR3 write event, turning off CR3 monitoring");
domain->intercept_cr_writes(3,
false);
set_breakpoints(wevent);
}
break;
default:
LOG4CXX_ERROR(logger,
"Unhandled event: " << event.
type());
break;
}
LOG4CXX_ERROR(logger, "Unhandled Address not present error during event processing for "
<< ex.what());
}
}
private:
if (!initial_check_.test_and_set()) {
LOG4CXX_DEBUG(logger, "First syscall event, setting breakpoints");
set_breakpoints(wevent);
}
case SystemCallIndex::NtMapViewOfSection: {
}
default:
break;
}
}
case SystemCallIndex::NtMapViewOfSection: {
if (
likely(handler->result().NT_SUCCESS())) {
LOG4CXX_DEBUG(logger, "NtMapViewOfSection succeeded, setting breakpoints");
set_breakpoints(wevent);
}
}
default:
break;
}
}
std::lock_guard<std::mutex> lock(mtx_);
if (all_symbols_resolved_) {
return;
}
LOG4CXX_INFO(logger,
"Setting breakpoints for " << wevent.
task().
process_name());
if (!vadroot) {
LOG4CXX_DEBUG(logger,
"No VAD root found for " << wevent.
task().
process_name());
return;
}
for (auto entry : vadroot->VadTreeInOrder()) {
if (!entry->Protection().isExecutable()) {
continue;
}
auto file_object = entry->FileObject();
if (!file_object) {
continue;
}
const std::string filename = file_object->FileName();
std::string matched_dll;
for (const auto& dll : requested_dlls_) {
if (boost::algorithm::iends_with(filename, dll)) {
matched_dll = dll;
break;
}
}
if (matched_dll.empty() || found_dlls_.count(matched_dll)) {
continue;
}
std::string module_name = matched_dll;
if (boost::algorithm::iends_with(module_name, ".dll")) {
module_name.resize(module_name.size() - 4);
}
auto it = requested_symbols_.find(module_name);
if (it == requested_symbols_.end()) {
continue;
}
const std::set<std::string>& patterns = it->second;
LOG4CXX_DEBUG(logger, "Found symbols for " << module_name << ": " << patterns.size());
try {
auto lib =
auto& pdb = lib->pdb();
LOG4CXX_INFO(logger, "Loaded PE for " << matched_dll);
for (const auto& symbol : pdb.global_symbols()) {
if (!symbol->function() && !symbol->code()) {
continue;
}
bool matched = false;
for (const auto& pattern : patterns) {
if (symbol_matches_pattern(pattern, symbol->name())) {
matched = true;
break;
}
}
if (!matched) {
continue;
}
try {
entry->StartingAddress() + symbol->image_offset());
breakpoints_.emplace_back(wevent.
domain(), ptr, symbol->
name(),
process.UniqueProcessId());
LOG4CXX_INFO(logger, "Added breakpoint for " << matched_dll << "!"
<< symbol->name());
LOG4CXX_DEBUG(logger, "Address not present for " << matched_dll << "!"
<< symbol->name());
}
}
LOG4CXX_DEBUG(logger, "PE not present for " << matched_dll);
continue;
}
found_dlls_.insert(matched_dll);
if (found_dlls_.size() == requested_dlls_.size()) {
domain->intercept_system_calls(
false);
all_symbols_resolved_ = true;
return;
}
}
}
private:
std::mutex mtx_;
bool all_symbols_resolved_ = false;
std::atomic_flag initial_check_ = false;
std::unordered_map<std::string, std::set<std::string>> requested_symbols_;
std::set<std::string> requested_dlls_;
std::set<std::string> found_dlls_;
std::set<std::string> found_symbols_;
std::vector<BreakpointHandler> breakpoints_;
};
int main(
int argc,
char** argv) {
po::options_description desc("Options");
std::string domain_name;
std::string process_name;
std::vector<std::string> symbols;
desc.add_options()
("domain,D", po::value<std::string>(&domain_name)->required(), "The domain name or ID attach to")
("procname,P", po::value<std::string>(&process_name)->required(), "A process name to filter for")
("symbol,s", po::value<std::vector<std::string>>(&symbols), "Symbols in the form Module!Symbol to breakpoint. Default: ntdll!Nt*")
("help", "Display program help");
std::cout.sync_with_stdio(false);
po::variables_map vm;
auto hypervisor = Hypervisor::instance();
domain = hypervisor->attach_domain(domain_name);
if (!
domain->detect_guest()) {
std::cerr << "Failed to detect guest OS\n";
return 1;
}
if (
domain->guest()->os() != OS::Windows) {
std::cerr << "ivcallmon only supports Windows guests\n";
return 1;
}
if (!process_name.empty()) {
domain->task_filter().add_name(process_name);
}
if (symbols.empty()) {
symbols.push_back("ntdll!Nt*");
}
domain->system_call_filter().enabled(
true);
true);
domain->intercept_system_calls(
true);
domain->intercept_cr_writes(3,
true);
return 0;
}
po::variables_map& vm) {
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
if (vm.count("help")) {
std::cout << "ivcallmon - Watch guest library calls" << '\n';
std::cout << desc << '\n';
exit(0);
}
po::notify(vm);
} catch (po::error& e) {
std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
std::cerr << desc << std::endl;
exit(1);
}
}
Definition ivcallmon.cc:72
void return_hit(Event &event)
Definition ivcallmon.cc:102
Domain * domain_
Definition ivcallmon.cc:145
std::shared_ptr< Breakpoint > return_bp_
Definition ivcallmon.cc:147
~BreakpointHandler()=default
std::shared_ptr< Breakpoint > bp_
Definition ivcallmon.cc:146
std::string name_
Definition ivcallmon.cc:148
uint64_t return_rsp_
Definition ivcallmon.cc:150
uint64_t return_tid_
Definition ivcallmon.cc:151
BreakpointHandler & operator=(BreakpointHandler &&src) noexcept
Definition ivcallmon.cc:126
uint64_t pid_
Definition ivcallmon.cc:149
void breakpoint_hit(Event &event)
Definition ivcallmon.cc:74
Definition ivcallmon.cc:154
~CallMonitor()
Definition ivcallmon.cc:216
void process_event(Event &event) override
Process an incoming event.
Definition ivcallmon.cc:182
virtual int index() const =0
A class representing a single Domain.
Definition Domain.hh:44
virtual std::string name() const =0
Get the name of the Domain, if it exists.
virtual std::shared_ptr< Breakpoint > create_breakpoint(const guest_ptr< void > &address, std::function< void(Event &)> callback)=0
Create an execution breakpoint.
Interface for an event poller callback.
Definition EventCallback.hh:29
Interface class for hypervisor events.
Definition Event.hh:43
virtual OS os_type() const =0
virtual Domain & domain()=0
Get the Domain that the event is for.
virtual EventTaskInformation & task()=0
Get the task information.
virtual EventType type() const =0
Get the type of event.
virtual ControlRegisterEvent & cr()=0
Get control register access event information.
virtual Vcpu & vcpu()=0
Get the Vcpu that triggered the event.
virtual void hook_return(bool enabled)=0
Instruct that the system call's return should be hooked.
virtual Registers & registers()=0
Get the processor's registers.
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
Definition WindowsEvent.hh:26
virtual WindowsSystemCallEvent & syscall()=0
Get system call event information.
virtual WindowsEventTaskInformation & task()=0
Get the task information.
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.
virtual SystemCallIndex index() const =0
virtual WindowsSystemCall * handler()=0
Gets the associated system call handler with this event.
virtual THREAD & CurrentThread()=0
Get the currently active thread.
Handler class for the NtMapViewOfSection system call.
Definition NtMapViewOfSection.hh:34
virtual std::shared_ptr< const MMVAD > VadRoot() const =0
virtual const PROCESS & Process() const =0
virtual uint64_t rsp() const =0
Get the rsp register value.
#define likely(x)
Definition compiler.hh:26
#define unlikely(x)
Definition compiler.hh:27
std::unique_ptr< Domain > domain
Definition ivcallmon.cc:68
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
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
unique_ptr< Domain > domain
Definition vmcall_interface.cc:48