status_t
vmi_register_event(
    vmi_instance_t vmi,
    vmi_event_t* event)
{
    status_t rc = VMI_FAILURE;
 
#ifdef ENABLE_SAFETY_CHECKS
    if (!vmi) {
        dbprint(VMI_DEBUG_EVENTS, "LibVMI wasn't initialized!\n");
        return VMI_FAILURE;
    }
    if (!(vmi->init_flags & (VMI_INIT_EVENTS | VMI_INIT_DOMAINWATCH))) {
        dbprint(VMI_DEBUG_EVENTS, "LibVMI wasn't initialized with events!\n");
        return VMI_FAILURE;
    }
    if (!event) {
        dbprint(VMI_DEBUG_EVENTS, "No event given!\n");
        return VMI_FAILURE;
    }
    if (event->version > VMI_EVENTS_VERSION) {
        dbprint(VMI_DEBUG_EVENTS, "The caller requires a newer version of LibVMI!\n");
        return VMI_FAILURE;
    }
    if (event->version < VMI_EVENTS_VERSION) {
        /*
         * Note: backwards-compatibility can be implemented by defining an internal
         *  header for the older ABI and handling the calls according to the version
         *  that was requested.
         *  This is left as a TODO for when it becomes necessary.
         */
        dbprint(VMI_DEBUG_EVENTS, "The caller requires an older version of LibVMI!\n");
        return VMI_FAILURE;
    }
    if (!event->callback) {
        dbprint(VMI_DEBUG_EVENTS, "No event callback function specified!\n");
        return VMI_FAILURE;
    }
#endif
 
    switch (event->type) {
 
        case VMI_EVENT_REGISTER:
            rc = register_reg_event(vmi, event);
            break;
        case VMI_EVENT_MEMORY:
            rc = register_mem_event(vmi, event);
            break;
        case VMI_EVENT_SINGLESTEP:
            rc = register_singlestep_event(vmi, event);
            break;
        case VMI_EVENT_INTERRUPT:
            rc = register_interrupt_event(vmi, event);
            break;
        case VMI_EVENT_GUEST_REQUEST:
            rc = register_guest_requested_event(vmi, event);
            break;
        case VMI_EVENT_CPUID:
            rc = register_cpuid_event(vmi, event);
            break;
        case VMI_EVENT_DEBUG_EXCEPTION:
            rc = register_debug_event(vmi, event);
            break;
        case VMI_EVENT_PRIVILEGED_CALL:
            rc = register_privcall_event(vmi, event);
            break;
        case VMI_EVENT_DESCRIPTOR_ACCESS:
            rc = register_desc_access_event(vmi, event);
            break;
        case VMI_EVENT_FAILED_EMULATION:
            rc = register_failed_emulation_event(vmi, event);
            break;
        case VMI_EVENT_DOMAIN_WATCH:
            rc = register_watch_domain_event(vmi, event);
            break;
        default:
            dbprint(VMI_DEBUG_EVENTS, "Unknown event type: %d\n", event->type);
            break;
    }
 
    return rc;
}
status_t register_mem_event(vmi_instance_t vmi, vmi_event_t *event)
{
    if ( event->mem_event.generic )
        return register_mem_event_generic(vmi, event);
    else
        return register_mem_event_on_gfn(vmi, event);
}
static status_t register_mem_event_generic(vmi_instance_t vmi, vmi_event_t *event)
{
    if ( event->mem_event.gfn != ~0ULL ) {
        dbprint(VMI_DEBUG_EVENTS, "GFN must be ~0 for generic mem event types.\n");
        return VMI_FAILURE;
    }
 
    if ( g_hash_table_size(vmi->mem_events_on_gfn) ) {
        dbprint(VMI_DEBUG_EVENTS, "You already have page specific mem event handlers registered.\n");
        return VMI_FAILURE;
    }
 
    if ( g_hash_table_lookup(vmi->mem_events_generic, &event->mem_event.in_access) ) {
        dbprint(VMI_DEBUG_EVENTS, "An event is already registered for this tpye of access violation\n");
        return VMI_FAILURE;
    }
 
    gint *access = g_slice_new(gint);
    *access = event->mem_event.in_access;
 
    g_hash_table_insert_compat(vmi->mem_events_generic, access, event);
    return VMI_SUCCESS;
}
static status_t register_mem_event_on_gfn(vmi_instance_t vmi, vmi_event_t *event)
{
    if ( VMI_MEMACCESS_INVALID == event->mem_event.in_access ) {
        dbprint(VMI_DEBUG_EVENTS, "Invalid VMI_MEMACCESS requested: %d\n",
                event->mem_event.in_access);
        return VMI_FAILURE;
    }
 
    if ( g_hash_table_size(vmi->mem_events_generic) ) {
        dbprint(VMI_DEBUG_EVENTS, "You already have generic mem event handlers registered.\n");
        return VMI_FAILURE;
    }
 
    // Page already has an event registered
    if ( g_hash_table_lookup(vmi->mem_events_on_gfn, &event->mem_event.gfn) ) {
        dbprint(VMI_DEBUG_EVENTS,
                "An event is already registered on this page: %"PRIu64"\n",
                event->mem_event.gfn);
        return VMI_FAILURE;
    }
 
    if (VMI_SUCCESS == driver_set_mem_access(vmi, event->mem_event.gfn,
            event->mem_event.in_access,
            event->slat_id)) {
        g_hash_table_insert_compat(vmi->mem_events_on_gfn, g_slice_dup(addr_t, &event->mem_event.gfn), event);
 
        if ( event->mem_event.gfn > (vmi->max_physical_address >> vmi->page_shift) )
            vmi->max_physical_address = event->mem_event.gfn << vmi->page_shift;
 
        return VMI_SUCCESS;
    }
 
    return VMI_FAILURE;
}

🌱 Back to Garden