Executes a command in the guest by injecting a process creation (e.g. NtCreateUserProcess/CreateProcess). Demonstrates system-call injection and waiting for completion via event handling.
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <csignal>
#include <cstring>
#include <iostream>
#include <memory>
#include <thread>
namespace po = boost::program_options;
po::variables_map& vm);
std::unique_ptr<Domain>
domain;
while (timeout) {
timeout = sleep(timeout);
}
std::cerr << "Time expired, exiting...\n";
}
}
std::cerr << "Interrupted by signal, exiting...\n";
}
}
public:
const std::string& args, const std::string& directory, bool no_window,
bool show_exit_code, bool show_console_out, bool admin, uint64_t session_id,
launcher_(launcher), target_(target), args_(args), directory_(directory),
show_exit_code_(show_exit_code), show_console_out_(show_console_out), admin_(admin),
no_window_(no_window), session_id_(session_id), system_call_monitor_(system_call_monitor),
unsupported_(all) {}
auto startupinfo = inject::allocate<STARTUPINFOW>();
if (no_window_) {
}
auto procinfo = inject::allocate<kernel32::PROCESS_INFORMATION>();
std::string cmdline = target_;
if (!args_.empty())
cmdline += ' ' + args_;
auto guest_cmdline = inject::allocate(Utf16String::convert(cmdline));
std::optional<inject::GuestAllocation<char16_t[]>> guest_directory;
if (!directory_.empty()) {
guest_directory.emplace(inject::allocate(Utf16String::convert(directory_)));
pguest_directory = guest_directory->ptr();
}
result = inject::function_call<CreateProcessW>(
nullptr, guest_cmdline,
nullptr,
nullptr,
false, dwCreationFlags, nullptr,
pguest_directory, startupinfo, procinfo);
std::cerr << "Created process [" << procinfo->dwProcessId() << ':'
<< procinfo->dwThreadId() << "]\n";
new_pid_ = procinfo->dwProcessId();
if (admin_) {
auto new_process = handle_table->ProcessObject(procinfo->hProcess());
auto& token = new_process->Token();
token.PrivilegesPresent(0xFFFFFFFFFFFFFFFF);
token.PrivilegesEnabled(0xFFFFFFFFFFFFFFFF);
for (auto& group : token.Groups()) {
if (group->Attributes().SE_GROUP_USE_FOR_DENY_ONLY()) {
group->Attributes(new_flags);
}
}
}
inject::system_call<nt::NtClose>(procinfo->hProcess());
inject::system_call<nt::NtResumeThread>(procinfo->hThread(), nullptr);
inject::system_call<nt::NtClose>(procinfo->hThread());
} else {
std::cerr <<
"Failed to launch process: " <<
GetLastError() << std::endl;
}
}
auto& process = event.task().pcr().CurrentThread().Process();
return (process.Session() && process.Session()->SessionID() == session_id_);
}
auto& process = event.task().pcr().CurrentThread().Process();
auto vad = process.VadRoot();
if (!vad)
return false;
for (auto& entry : vad->VadTreeInOrder()) {
if (entry->FileObject()) {
try {
std::string file_name(boost::to_lower_copy(entry->FileObject()->FileName()));
if (boost::ends_with(file_name, "user32.dll")) {
return true;
}
}
}
}
return false;
}
return (launcher_.empty() || boost::starts_with(event.
task().
process_name(), launcher_));
}
event.
type() == EventType::EVENT_REBOOT)) {
exit(64);
}
if (event.
task().
pid() != new_pid_) {
if (posted_message_.test_and_set() == 0) {
#if 0
if (!inject::system_call<win32k::NtUserPostMessage>(
0xffff, win32k::WM_PAINT, 0, 0)) {
posted_message_.clear();
}
} else {
posted_message_.clear();
}
#endif
return;
}
}
return;
if (started_.test_and_set() == 0) {
posted_message_.test_and_set();
return;
}
{
std::lock_guard lifelock(lifecycle_mtx_);
launched_ = true;
if (terminated_) {
}
}
if (!system_call_monitor_ && !show_exit_code_ && !show_console_out_) {
return;
}
if (!unsupported_)
}
return;
}
if (event.
type() == EventType::EVENT_FAST_SYSCALL) {
case SystemCallIndex::NtTerminateProcess: {
auto* terminate_process =
if (!terminate_process->will_return()) {
if (show_exit_code_) {
std::cout <<
"Process Exited: " << terminate_process->
ExitStatus() <<
" ("
<< terminate_process->ExitStatus().
value() <<
")\n";
}
std::lock_guard lifelock(lifecycle_mtx_);
terminated_ = true;
if (launched_)
}
break;
}
case SystemCallIndex::NtDeviceIoControlFile: {
if (!show_console_out_)
break;
auto* device_ioctl =
if (device_ioctl->IoControlCode() !=
static_cast<uint32_t>(condrv::ConsoleRequestIoctl::ConsoleCallServerGeneric))
break;
ConsoleRequestIoctl::ConsoleCallServerGeneric) {
auto& requestData = console_ioctl.GenericRequest();
if (requestData.RequestCode() !=
ConsoleCallServerGenericRequestCode::WriteConsole)
break;
std::cout << writeRequest.Data();
}
break;
}
default:
break;
}
}
if (system_call_monitor_) {
return;
}
}
int result() {
return result_; }
private:
std::string launcher_;
std::string target_;
std::string args_;
std::string directory_;
const bool show_exit_code_;
const bool show_console_out_;
const bool admin_;
const bool no_window_;
int result_ = 0;
uint64_t new_pid_ = 0;
uint64_t session_id_;
std::atomic_flag started_ = false;
std::atomic_flag posted_message_ = false;
std::mutex lifecycle_mtx_;
bool launched_ = false;
bool terminated_ = false;
const bool unsupported_;
};
int main(
int argc,
char** argv) {
po::options_description desc("Options");
std::string domain_name;
std::string process_name;
std::string target_file;
std::string arguments;
std::string working_directory;
unsigned int timeout;
desc.add_options()
("domain,D", po::value<std::string>(&domain_name)->required(), "The domain name or ID attach to")
("target,t", po::value<std::string>(&target_file)->required(), "The target file to execute in the guest")
("args,a", po::value<std::string>(&arguments), "Arguments to pass to the executable")
("console,c", "Display console output from the launched process")
("directory,d", po::value<std::string>(&working_directory), "Set the working directory of the launched process")
("exitcode,e", "Wait for the program to exit and display the exit code")
("nowindow,n", "Do not create a window for the new process")
("admin", "Run as a privileged process. Removes default value for --procname.")
("timeout,T", po::value<unsigned int>(&timeout)->default_value(0), "A timeout after which we exit. 0 for infinite.")
("procname,P", po::value<std::string>(&process_name)->default_value("explorer"), "The name of a process to hijack")
("syscall,S", "Monitor system calls executed by the new process")
("no-flush", "Don't flush the output buffer after each event")
("json", "Output JSON format")
("help", "Display program help")
("unsupported", "Display system calls that we don't have handlers for (for syscalls)");
desc.add_options()(category.c_str(),
std::string("Enable " + category + " related system calls").c_str());
}
std::cout.sync_with_stdio(false);
po::variables_map vm;
auto hypervisor = Hypervisor::instance();
try {
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 <<
"Unsupported OS: " <<
domain->guest()->os() <<
'\n';
return 1;
}
std::unique_ptr<SystemCallMonitor> syscall_monitor;
if (vm.count("syscall")) {
if (vm.count("exitcode") || vm.count("console")) {
std::cerr << "Cannot use --syscall mode with --exitcode or --console\n";
return 10;
}
syscall_monitor = std::make_unique<SystemCallMonitor>(
!vm.count("no-flush"), vm.count("json"), vm.count("unsupported"));
if (vm.count("unsupported") == 0) {
bool category_used = false;
if (
domain->guest()->os() == OS::Windows) {
if (vm.count(category)) {
category_used = true;
}
}
}
if (!category_used) {
if (
domain->guest()->os() == OS::Windows) {
}
}
}
}
SystemCallIndex::NtTerminateProcess, true);
uint64_t session_id = 0xFFFFFFFFFFFFFFFF;
auto& kernel = guest->kernel();
auto CidTable = kernel.CidTable();
auto handles = CidTable->open_handles();
for (auto& entry : handles) {
try {
if (entry->ObjectHeader()->type() == ObjectType::Process) {
auto process = kernel.process(entry->ObjectHeader()->Body());
if (boost::istarts_with(process->ImageFileName(), process_name)) {
if (process->Session()) {
session_id = process->Session()->SessionID();
break;
}
}
}
}
}
if (session_id == 0xFFFFFFFFFFFFFFFF) {
std::cerr << "Failed to find the session ID of the target process" << std::endl;
return 20;
}
if (vm.count("console")) {
guest->set_system_call_filter(
domain->system_call_filter(),
SystemCallIndex::NtDeviceIoControlFile, true);
}
domain->intercept_system_calls(
true);
if (timeout != 0) {
timeout_thread.detach();
}
vm.count("nowindow"), vm.count("exitcode"), vm.count("console"),
vm.count("admin"), session_id, syscall_monitor.get(),
vm.count("unsupported"));
return tool.result();
std::cerr << ex;
return 99;
}
}
po::variables_map& vm) {
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
if (vm.count("help")) {
std::cout << "ivexec - Execute a file in the guest" << '\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 SystemCallMonitor.hh:21
void process_event(Event &event) override
Process an incoming event.
Definition SystemCallMonitor.hh:23
A class representing a single Domain.
Definition Domain.hh:44
virtual SystemCallFilter & system_call_filter()=0
Get the system call filter for this Domain.
virtual void interrupt()=0
Interrupt a poll() call.
virtual TaskFilter & task_filter()=0
Get the task filter for this domain.
Interface for an event poller callback.
Definition EventCallback.hh:29
Interface class for hypervisor events.
Definition Event.hh:43
virtual EventTaskInformation & task()=0
Get the task information.
virtual EventType type() const =0
Get the type of event.
void enabled(bool enabled)
Set if the filter is enabled.
void add_pid(uint64_t pid)
Add a process ID.
void clear()
Clear all filters.
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
Definition WindowsEvent.hh:26
virtual WindowsSystemCallEvent & syscall()=0
Get system call event information.
virtual WindowsGuest & guest()=0
virtual WindowsEventTaskInformation & task()=0
Get the task information.
A representation of a Windows Guest OS.
Definition WindowsGuest.hh:33
virtual void enable_category(const std::string &category, SystemCallFilter &filter) const =0
Enable a specific category for a filter.
virtual void default_syscall_filter(SystemCallFilter &filter) const =0
Configure a system call filter for all supported calls.
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.
A wrapper class for an ioctl to \Device\ConDrv\CurrentOut.
Definition ConDrvIoctl.hh:31
Definition ConsoleCallServerGenericWriteRequest.hh:28
virtual THREAD & CurrentThread()=0
Get the currently active thread.
Handler class for the NtDeviceIoControlFile system call.
Definition NtDeviceIoControlFile.hh:32
Handler class for the NtTerminateProcess system call.
Definition NtTerminateProcess.hh:32
virtual NTSTATUS ExitStatus() const =0
Getter for ExitStatus.
virtual std::unique_ptr< HANDLE_TABLE > ObjectTable()=0
Get the handle table for this process, used for looking up objects by handle number.
Definition SID_AND_ATTRIBUTES.hh:54
virtual const PROCESS & Process() const =0
#define unlikely(x)
Definition compiler.hh:27
bool interrupted
Definition ivcallmon.cc:43
std::unique_ptr< Domain > domain
Definition ivexec.cc:50
void wait_for_timeout(unsigned int timeout)
Definition ivexec.cc:52
int main(int argc, char **argv)
Definition main.c:35
Code for handling ioctls to condrv.sys.
Definition ConsoleCallServerGenericRequestCode.hh:23
ConsoleRequestIoctl
Definition ConsoleRequestIoctl.hh:25
Definition CreationFlags.hh:20
constexpr unsigned int SW_HIDE
Definition STARTUPINFOA.hh:32
@ CREATE_NEW_CONSOLE
Definition CreationFlags.hh:27
@ CREATE_UNICODE_ENVIRONMENT
Definition CreationFlags.hh:28
constexpr unsigned int STARTF_USESHOWWINDOW
Definition STARTUPINFOA.hh:29
Classes related to the Windows NT kernel.
Definition APPHELPCACHESERVICECLASS.hh:23
@ SE_GROUP_ENABLED
Definition SID_AND_ATTRIBUTES.hh:40
@ SE_GROUP_ENABLED_BY_DEFAULT
Definition SID_AND_ATTRIBUTES.hh:39
@ SE_GROUP_MANDATORY
Definition SID_AND_ATTRIBUTES.hh:38
@ NORMAL_PRIORITY_CLASS
Definition PRIORITY_CLASS.hh:27
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