#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <stdio.h>
#include <inttypes.h>
#include <signal.h>
 
#include <libvmi/libvmi.h>
#include <libvmi/events.h>
 
#define PAGE_SIZE 1 << 12
 
reg_t cr3;
reg_t lstar;
reg_t sysenter_ip;
vmi_event_t cr3_event;
vmi_event_t msr_syscall_lm_event;
vmi_event_t msr_syscall_sysenter_event;
vmi_event_t kernel_vsyscall_event;
bool mem_events_registered;
 
/*
 * Per Linux ABI, this VA represents the start of the vsyscall page
 *  If vsyscall support is enabled (deprecated or disabled on many newer
 *  3.0+ kernels), it is accessible at this address in every process.
 */
addr_t vsyscall = 0xffffffffff600000;
 
void print_event(vmi_event_t event)
{
    printf("PAGE ACCESS: %c%c%c for GFN %"PRIx64" (offset %06"PRIx64") gla %016"PRIx64" (vcpu %"PRIu32")\n",
           (event.mem_event.out_access & VMI_MEMACCESS_R) ? 'r' : '-',
           (event.mem_event.out_access & VMI_MEMACCESS_W) ? 'w' : '-',
           (event.mem_event.out_access & VMI_MEMACCESS_X) ? 'x' : '-',
           event.mem_event.gfn,
           event.mem_event.offset,
           event.mem_event.gla,
           event.vcpu_id
          );
}
 
 
/* MSR registers used to hold system calls in x86_64. Note that compat mode is
 *  used in concert with long mode for certain system calls.
 *  e.g. in 3.2.0 ioctl, getrlimit, etc. (see /usr/include/asm-generic/unistd.h)
 * MSR_STAR     -    legacy mode SYSCALL target (not addressed here)
 * MSR_CSTAR    -    compat mode SYSCALL target (not addressed here)
 * MSR_LSTAR    -    long mode SYSCALL target
 *
 * Note that modern code tends to employ the sysenter and/or vDSO mechanisms for
 *    performance reasons.
 */
 
event_response_t msr_syscall_sysenter_cb(vmi_instance_t vmi, vmi_event_t *event)
{
    print_event(*event);
 
    if ( event->x86_regs->rip != sysenter_ip ) {
        vmi_clear_event(vmi, event, NULL);
        vmi_step_event(vmi, event, event->vcpu_id, 1, NULL);
        return 0;
    }
 
    reg_t rdi, rax;
 
    vmi_get_vcpureg(vmi, &rax, RAX, event->vcpu_id);
    vmi_get_vcpureg(vmi, &rdi, RDI, event->vcpu_id);
 
    printf("SYSENTER_IP Syscall happened: RAX(syscall#)=%u RDI(1st argument)=%u\n",
           (unsigned int)rax, (unsigned int)rdi);
 
    vmi_clear_event(vmi, event, NULL);
    return 0;
}
 
event_response_t vsyscall_cb(vmi_instance_t vmi, vmi_event_t *event)
{
    print_event(*event);
 
    if ( event->x86_regs->rip != vsyscall ) {
        vmi_clear_event(vmi, event, NULL);
        vmi_step_event(vmi, event, event->vcpu_id, 1, NULL);
        return 0;
    }
 
    reg_t rdi, rax;
    vmi_get_vcpureg(vmi, &rax, RAX, event->vcpu_id);
    vmi_get_vcpureg(vmi, &rdi, RDI, event->vcpu_id);
 
    printf("VSYSCALL Syscall happened: RAX(syscall#)=%u RDI(1st argument)=%u\n", (unsigned int)rax, (unsigned int)rdi);
 
    vmi_clear_event(vmi, event, NULL);
    return 0;
}
 
event_response_t syscall_lm_cb(vmi_instance_t vmi, vmi_event_t *event)
{
    print_event(*event);
 
    if ( event->mem_event.offset != (VMI_BIT_MASK(0,11) & lstar) ) {
        vmi_clear_event(vmi, event, NULL);
        vmi_step_event(vmi, event, event->vcpu_id, 1, NULL);
        return 0;
    }
 
    reg_t rdi, rax;
    vmi_get_vcpureg(vmi, &rax, RAX, event->vcpu_id);
    vmi_get_vcpureg(vmi, &rdi, RDI, event->vcpu_id);
 
    printf("LSTAR Syscall happened: RAX(syscall#)=%u RDI(1st argument)=%u\n", (unsigned int)rax, (unsigned int)rdi);
 
    vmi_clear_event(vmi, event, NULL);
    return 0;
}
 
bool register_mem_events(vmi_instance_t vmi, vmi_event_t *event)
{
    // use cr3 value that's being loaded
    addr_t cr3 = event->reg_event.value;
    addr_t phys_lstar = 0;
    addr_t phys_sysenter_ip = 0;
    bool ret = false;
    addr_t phys_vsyscall = 0;
 
    // Get the value of lstar for the system.
    // NOTE: all vCPUs have the same value for this register
    lstar = event->x86_regs->msr_lstar;
    vmi_get_vcpureg(vmi, &sysenter_ip, SYSENTER_EIP, 0);
    printf("vcpu %u MSR_LSTAR == %llx\n", event->vcpu_id, (unsigned long long)lstar);
    printf("vcpu %u MSR_SYSENTER_IP == %llx\n", event->vcpu_id, (unsigned long long)sysenter_ip);
 
    // Translate to a physical address.
    vmi_pagetable_lookup(vmi, event->x86_regs->cr3, lstar, &phys_lstar);
    printf("Physical LSTAR == %llx\n", (unsigned long long)phys_lstar);
 
    vmi_pagetable_lookup(vmi, event->x86_regs->cr3, sysenter_ip, &phys_sysenter_ip);
    printf("Physical SYSENTER_IP == %llx\n", (unsigned long long)phys_sysenter_ip);
 
    // Get only the page that the handler starts.
    printf("LSTAR Physical PFN == %llx\n", (unsigned long long)(phys_lstar >> 12));
    printf("SYSENTER_IP Physical PFN == %llx\n", (unsigned long long)(phys_sysenter_ip >> 12));
    printf("phys_vsyscall Physical PFN == %llx\n", (unsigned long long)(phys_vsyscall >> 12));
 
    // Setup a default event for tracking memory at the syscall handler.
    msr_syscall_sysenter_event.version = VMI_EVENTS_VERSION;
    msr_syscall_sysenter_event.type = VMI_EVENT_MEMORY;
    msr_syscall_sysenter_event.mem_event.gfn = phys_sysenter_ip >> 12;
    msr_syscall_sysenter_event.mem_event.in_access = VMI_MEMACCESS_X;
    msr_syscall_sysenter_event.callback=msr_syscall_sysenter_cb;
 
    msr_syscall_lm_event.version = VMI_EVENTS_VERSION;
    msr_syscall_lm_event.type = VMI_EVENT_MEMORY;
    msr_syscall_lm_event.mem_event.gfn = phys_lstar >> 12;
    msr_syscall_lm_event.mem_event.in_access = VMI_MEMACCESS_X;
    msr_syscall_lm_event.callback=syscall_lm_cb;
 
    kernel_vsyscall_event.version = VMI_EVENTS_VERSION;
    kernel_vsyscall_event.type = VMI_EVENT_MEMORY;
    kernel_vsyscall_event.mem_event.gfn = phys_vsyscall >> 12;
    kernel_vsyscall_event.mem_event.in_access = VMI_MEMACCESS_X;
    kernel_vsyscall_event.callback=vsyscall_cb;
 
    if ( phys_sysenter_ip && VMI_SUCCESS == vmi_register_event(vmi, &msr_syscall_sysenter_event) )
        ret = true;
    else
        printf("Failed to register memory event on MSR_SYSENTER_IP page\n");
 
    if ( phys_lstar && VMI_SUCCESS == vmi_register_event(vmi, &msr_syscall_lm_event) )
        ret = true;
    else
        printf("Failed to register memory event on MSR_LSTAR page\n");
 
    if ( phys_vsyscall && VMI_SUCCESS == vmi_register_event(vmi, &kernel_vsyscall_event) )
        ret = true;
    else
        printf("Failed to register memory event on vsyscall page\n");
 
    return ret;
}
 
event_response_t cr3_all_tasks_callback(vmi_instance_t vmi, vmi_event_t *event)
{
    printf("CR3=%"PRIx64" executing on vcpu %"PRIu32". Previous CR3=%"PRIx64"\n",
           event->reg_event.value, event->vcpu_id, event->reg_event.previous);
 
    if ( !mem_events_registered )
        mem_events_registered = register_mem_events(vmi, event);
 
    return 0;
}
 
static int interrupted = 0;
static void close_handler(int sig)
{
    interrupted = sig;
}
 
int main (int argc, char **argv)
{
    vmi_instance_t vmi = NULL;
    status_t status = VMI_SUCCESS;
    struct sigaction act;
    char *name = NULL;
    vmi_init_data_t *init_data = NULL;
 
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <name of VM> [<socket>]\n", argv[0]);
        exit(1);
    }
 
    // Arg 1 is the VM name.
    name = argv[1];
 
    if (argc == 3) {
        char *path = argv[2];
 
        init_data = malloc(sizeof(vmi_init_data_t) + sizeof(vmi_init_data_entry_t));
        init_data->count = 1;
        init_data->entry[0].type = VMI_INIT_DATA_KVMI_SOCKET;
        init_data->entry[0].data = strdup(path);
    }
 
    /* for a clean exit */
    act.sa_handler = close_handler;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGHUP,  &act, NULL);
    sigaction(SIGTERM, &act, NULL);
    sigaction(SIGINT,  &act, NULL);
    sigaction(SIGALRM, &act, NULL);
 
    vmi_mode_t mode;
    if (VMI_FAILURE == vmi_get_access_mode(NULL, name, VMI_INIT_DOMAINNAME, init_data, &mode)) {
        printf("Failed to find a supported hypervisor with LibVMI\n");
        return 1;
    }
 
    /* 
		initialize the libvmi library 
		
		vmi_init -> xen_init_vmi -> xen_init_events
 
		*/
    if (VMI_FAILURE == vmi_init(&vmi, mode, name, VMI_INIT_DOMAINNAME | VMI_INIT_EVENTS, init_data, NULL)) {
        printf("Failed to init LibVMI library.\n");
        return 1;
    }
 
    if ( VMI_PM_UNKNOWN == vmi_init_paging(vmi, 0) ) {
        printf("Failed to init determine paging.\n");
        vmi_destroy(vmi);
        return 1;
    }
 
    printf("LibVMI init succeeded!\n");
 
    /* Configure an event to track when the process is running.
     * (The CR3 register is updated on task context switch, allowing
     *  us to follow as various tasks are scheduled and run upon the CPU)
     */
    memset(&cr3_event, 0, sizeof(vmi_event_t));
    cr3_event.version = VMI_EVENTS_VERSION;
    cr3_event.type = VMI_EVENT_REGISTER;
    cr3_event.callback = cr3_all_tasks_callback;
 
    /* Observe only write events to the given register.
     *   NOTE: read events are unsupported at this time.
     */
    cr3_event.reg_event.reg = CR3;
    cr3_event.reg_event.in_access = VMI_REGACCESS_W;
 
    if ( VMI_SUCCESS == vmi_register_event(vmi, &cr3_event) ) {
        while (!interrupted) {
            printf("Waiting for events...\n");
            status = vmi_events_listen(vmi,500);
            if (status != VMI_SUCCESS) {
                printf("Error waiting for events, quitting...\n");
                interrupted = -1;
            }
        }
    }
 
    vmi_pause_vm(vmi);
 
    // Process any events that may have been left
    if ( vmi_are_events_pending(vmi) > 0 )
        vmi_events_listen(vmi, 0);
 
    vmi_clear_event(vmi, &cr3_event, NULL);
    vmi_clear_event(vmi, &msr_syscall_lm_event, NULL);
    vmi_clear_event(vmi, &msr_syscall_sysenter_event, NULL);
    vmi_clear_event(vmi, &kernel_vsyscall_event, NULL);
 
    vmi_resume_vm(vmi);
 
    printf("Finished with test.\n");
 
    // cleanup any memory associated with the libvmi instance
    vmi_destroy(vmi);
 
    if (init_data) {
        free(init_data->entry[0].data);
        free(init_data);
    }
 
    return 0;
}

🌱 Back to Garden