Monitors access to a specific file path in the guest. Tracks NtOpenFile/NtCreateFile opens matching the path, handles created via NtDuplicateObject, and reports all operations on those handles (read, write, close, etc.). Supports normal and JSON output.
#include <log4cxx/logger.h>
#include <boost/algorithm/string.hpp>
#include <boost/functional/hash.hpp>
#include <boost/program_options.hpp>
#include <algorithm>
#include <csignal>
#include <iostream>
#include <mutex>
#include <string>
#include <unordered_set>
namespace po = boost::program_options;
po::variables_map& vm);
std::unique_ptr<Domain>
domain;
static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("introvirt.tools.ivcallmon"));
static std::string normalize_path(const std::string& path) {
std::string result = path;
std::replace(result.begin(), result.end(), '/', '\\');
boost::algorithm::to_lower(result);
if (result.size() >= 4 && result.substr(0, 4) == "\\??\\") {
result = result.substr(4);
}
return result;
}
static bool path_matches(const std::string& user_path_normalized,
if (obj_attr == nullptr)
return false;
std::string guest_path;
try {
if (guest_path.empty())
} catch (...) {
return false;
}
std::string guest_normalized = normalize_path(guest_path);
LOG4CXX_DEBUG(logger, "checking: " << guest_normalized << " == " << user_path_normalized);
return guest_normalized == user_path_normalized;
}
switch (index) {
case SystemCallIndex::NtCreateFile:
return static_cast<const NtCreateFile*
>(handler)->ObjectAttributes();
case SystemCallIndex::NtOpenFile:
return static_cast<const NtOpenFile*
>(handler)->ObjectAttributes();
case SystemCallIndex::NtQueryAttributesFile:
case SystemCallIndex::NtQueryFullAttributesFile:
case SystemCallIndex::NtDeleteFile:
return static_cast<const NtDeleteFile*
>(handler)->ObjectAttributes();
default:
return nullptr;
}
}
public:
FileMonitor(
const std::string& target_path,
bool flush,
bool json)
: target_path_normalized_(normalize_path(target_path)), flush_(flush), json_(json) {}
if (event.
os_type() != OS::Windows)
return;
case EventType::EVENT_FAST_SYSCALL:
handle_syscall(wevent);
break;
case EventType::EVENT_FAST_SYSCALL_RET:
handle_sysret(wevent);
break;
default:
break;
}
}
private:
using HandleKey = std::pair<uint64_t, uint64_t>;
if (handler ==
nullptr || !handler->
supported())
return;
uint64_t pid = wevent.
task().
pid();
switch (index) {
case SystemCallIndex::NtCreateFile:
case SystemCallIndex::NtOpenFile: {
if (path_matches(target_path_normalized_, obj_attr, wevent.
task().
pcr())) {
emit_event(wevent);
}
break;
}
case SystemCallIndex::NtQueryAttributesFile:
case SystemCallIndex::NtQueryFullAttributesFile:
case SystemCallIndex::NtDeleteFile: {
if (path_matches(target_path_normalized_, obj_attr, wevent.
task().
pcr())) {
emit_event(wevent);
}
break;
}
case SystemCallIndex::NtClose: {
uint64_t handle =
static_cast<const NtClose*
>(handler)->Handle();
std::lock_guard lock(handles_mtx_);
if (handles_.count(HandleKey(pid, handle))) {
handles_.erase(HandleKey(pid, handle));
emit_event(wevent);
}
break;
}
case SystemCallIndex::NtDuplicateObject: {
break;
}
case SystemCallIndex::NtReadFile:
case SystemCallIndex::NtWriteFile:
case SystemCallIndex::NtQueryInformationFile:
case SystemCallIndex::NtSetInformationFile:
case SystemCallIndex::NtDeviceIoControlFile: {
uint64_t file_handle = get_file_handle(handler, index);
std::lock_guard lock(handles_mtx_);
if (handles_.count(HandleKey(pid, file_handle))) {
emit_event(wevent);
}
break;
}
default:
break;
}
}
if (handler ==
nullptr || !handler->
supported())
return;
if (!nt_handler->result().NT_SUCCESS())
return;
uint64_t pid = wevent.
task().
pid();
switch (index) {
case SystemCallIndex::NtCreateFile: {
static_cast<const NtCreateFile*
>(handler)->ObjectAttributes();
if (path_matches(target_path_normalized_, obj_attr, wevent.
task().
pcr())) {
uint64_t handle =
static_cast<const NtCreateFile*
>(handler)->FileHandle();
std::lock_guard lock(handles_mtx_);
handles_.insert(HandleKey(pid, handle));
emit_event(wevent);
}
break;
}
case SystemCallIndex::NtOpenFile: {
static_cast<const NtOpenFile*
>(handler)->ObjectAttributes();
if (path_matches(target_path_normalized_, obj_attr, wevent.
task().
pcr())) {
uint64_t handle =
static_cast<const NtOpenFile*
>(handler)->FileHandle();
std::lock_guard lock(handles_mtx_);
handles_.insert(HandleKey(pid, handle));
emit_event(wevent);
}
break;
}
case SystemCallIndex::NtDuplicateObject: {
uint64_t src_handle =
static_cast<const NtDuplicateObject*
>(handler)->SourceHandle();
uint64_t tgt_handle =
static_cast<const NtDuplicateObject*
>(handler)->TargetHandle();
std::lock_guard lock(handles_mtx_);
if (handles_.count(HandleKey(pid, src_handle))) {
handles_.insert(HandleKey(pid, tgt_handle));
emit_event(wevent);
}
break;
}
case SystemCallIndex::NtQueryAttributesFile:
case SystemCallIndex::NtQueryFullAttributesFile:
case SystemCallIndex::NtDeleteFile:
emit_event(wevent);
break;
default:
break;
}
}
switch (index) {
case SystemCallIndex::NtReadFile:
case SystemCallIndex::NtWriteFile:
case SystemCallIndex::NtQueryInformationFile:
case SystemCallIndex::NtSetInformationFile:
case SystemCallIndex::NtDeviceIoControlFile:
default:
return 0;
}
}
std::lock_guard lock(output_mtx_);
if (json_) {
std::cout << wevent.
json() <<
'\n';
} else {
std::cout <<
"Vcpu " << vcpu.
id() <<
": [" << wevent.
task().
pid() <<
":"
}
if (flush_)
std::cout.flush();
}
const std::string target_path_normalized_;
const bool flush_;
const bool json_;
std::unordered_set<HandleKey, boost::hash<HandleKey>> handles_;
std::mutex handles_mtx_;
std::mutex output_mtx_;
};
int main(
int argc,
char** argv) {
po::options_description desc("Options");
std::string domain_name;
std::string target_path;
desc.add_options()
("domain,D", po::value<std::string>(&domain_name)->required(), "The domain name or ID to attach to")
("path,P", po::value<std::string>(&target_path)->required(), "The guest file path to monitor (e.g. C:\\path\\to\\file)")
("no-flush", "Don't flush the output buffer after each event")
("json", "Output JSON format")
("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 << "ivfilemon only supports Windows guests\n";
return 1;
}
domain->system_call_filter().enabled(
true);
true);
guest->set_system_call_filter(
domain->system_call_filter(), SystemCallIndex::NtOpenFile,
true);
guest->set_system_call_filter(
domain->system_call_filter(), SystemCallIndex::NtDeleteFile,
true);
guest->set_system_call_filter(
domain->system_call_filter(),
SystemCallIndex::NtQueryAttributesFile, true);
guest->set_system_call_filter(
domain->system_call_filter(),
SystemCallIndex::NtQueryFullAttributesFile, true);
guest->set_system_call_filter(
domain->system_call_filter(), SystemCallIndex::NtClose,
true);
guest->set_system_call_filter(
domain->system_call_filter(), SystemCallIndex::NtDuplicateObject,
true);
guest->set_system_call_filter(
domain->system_call_filter(), SystemCallIndex::NtReadFile,
true);
guest->set_system_call_filter(
domain->system_call_filter(), SystemCallIndex::NtWriteFile,
true);
guest->set_system_call_filter(
domain->system_call_filter(),
SystemCallIndex::NtQueryInformationFile, true);
guest->set_system_call_filter(
domain->system_call_filter(),
SystemCallIndex::NtSetInformationFile, true);
guest->set_system_call_filter(
domain->system_call_filter(),
SystemCallIndex::NtDeviceIoControlFile, true);
domain->intercept_system_calls(
true);
FileMonitor monitor(target_path, !vm.count(
"no-flush"), vm.count(
"json"));
return 0;
}
po::variables_map& vm) {
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
if (vm.count("help")) {
std::cout << "ivfilemon - Monitor guest file access by path\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 ivfilemon.cc:102
~FileMonitor()
Definition ivfilemon.cc:125
void process_event(Event &event) override
Process an incoming event.
Definition ivfilemon.cc:107
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 Json::Value json() const =0
Serialize the event into JSON.
virtual EventType type() const =0
Get the type of event.
virtual Vcpu & vcpu()=0
Get the Vcpu that triggered the event.
virtual std::string name() const =0
Get a string represenatation of the system call name.
virtual void hook_return(bool enabled)=0
Instruct that the system call's return should be hooked.
virtual bool supported() const =0
Check if this system call is supported by a more specific handler.
virtual void write(std::ostream &os=std::cout) const =0
Write a human-readable description of this system call.
A class representing a single virtual processor.
Definition Vcpu.hh:33
virtual uint32_t id() const =0
Get the number of this Vcpu.
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.
Base class for Windows system calls.
Definition WindowsSystemCall.hh:30
The KPCR (Kernel Processor Control Region) is used by Windows to hold information about the current t...
Definition KPCR.hh:32
Handler class for the NtClose system call.
Definition NtClose.hh:31
Handler class for the NtCreateFile system call.
Definition NtCreateFile.hh:40
Handler class for the NtDeleteFile system call.
Definition NtDeleteFile.hh:32
Handler class for the NtDeviceIoControlFile system call.
Definition NtDeviceIoControlFile.hh:32
Handler class for the NtDuplicateObject system call.
Definition NtDuplicateObject.hh:33
Handler class for the NtOpenFile system call.
Definition NtOpenFile.hh:38
Handler class for the NtQueryAttributesFile system call.
Definition NtQueryAttributesFile.hh:33
Handler class for the NtQueryFullAttributesFile system call.
Definition NtQueryFullAttributesFile.hh:33
Handler class for the NtReadWriteFile system call.
Definition NtReadWriteFile.hh:32
Base type for NT system calls.
Definition NtSystemCall.hh:29
Definition OBJECT_ATTRIBUTES.hh:38
virtual std::string ObjectName() const =0
virtual const std::string & FullPath(const KPCR &kpcr) const =0
int main(int argc, char **argv)
Definition main.c:35
Classes related to the Windows NT kernel.
Definition APPHELPCACHESERVICECLASS.hh:23
Classes related to Microsoft Windows guests.
Definition LanguageId.hh:21
SystemCallIndex
This is our "normalized" list of Windows system calls.
Definition SystemCallIndex.hh:30
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