pfns = Frames of machine real-physical memory are identified with Page Frame Numbers

gfns = Frames of guest pseudo-physical memory are identified by Guest Frame Numbers

bool inject_trap_pa(drakvuf_t drakvuf,
    drakvuf_trap_t* trap,
    addr_t pa)
{
    trap->last_ttl_rst = time(NULL);
 
    // check if already marked
    vmi_instance_t vmi = drakvuf->vmi;
    xen_pfn_t current_gfn = pa >> 12;
 
 

to determine the page number, shift the address right by 12 bits

http://www2.cs.uregina.ca/~hamilton/courses/330/notes/memory/paging.html

Here it checks if the breakpoint was already set on this address:

struct wrapper* container = (struct wrapper*)g_hash_table_lookup(drakvuf->breakpoint_lookup_pa, &pa);
 
   
if (container)
{
    g_hash_table_insert(drakvuf->breakpoint_lookup_trap,
        trap,
        container);
    container->traps = g_slist_prepend(container->traps, trap);
 
    GSList* traps = (GSList*)g_hash_table_lookup(drakvuf->breakpoint_lookup_gfn, &current_gfn);
    traps = g_slist_append(traps, &container->breakpoint.pa);
 
    /* this should never happen but at least it makes some static analyzers happy */
    if ( 1 == g_slist_length(traps) )
        g_hash_table_insert(drakvuf->breakpoint_lookup_gfn,
            g_memdup_compat(&current_gfn, sizeof(xen_pfn_t)),
            traps);
 
    return 1;
}
 

DRAKVUF works with shadow copies of pages (also called as views):

“When the breakpoint is hit, or if something is trying to scan the code, we simply switch the view to the unaltered view for the duration of a single-step, then switch back to the trapped view.”

container = (struct wrapper*)g_slice_alloc0(sizeof(struct wrapper));
if ( !container )
    return 0;
 
container->drakvuf = drakvuf;
container->traps = g_slist_prepend(container->traps, trap);
container->breakpoint.pa = pa;
 
/* Let's see if we have already created the shadow copy of this page */
struct remapped_gfn* remapped_gfn = (struct remapped_gfn*)g_hash_table_lookup(drakvuf->remapped_gfns, &current_gfn);
 
// If not, create it!
 
if ( !remapped_gfn )
{
    remapped_gfn = (struct remapped_gfn*)g_slice_alloc0(sizeof(struct remapped_gfn));
    if ( !remapped_gfn )
        goto err_exit;
 
    remapped_gfn->o = current_gfn;
    remapped_gfn->r = ++(drakvuf->max_gpfn);
 
    int rc = xc_domain_populate_physmap_exact(drakvuf->xen->xc, drakvuf->domID, 1, 0, 0, &remapped_gfn->r);
    PRINT_DEBUG("Physmap populated? %i\n", rc);
    if (rc < 0)
    {
        g_slice_free(struct remapped_gfn, remapped_gfn);
        remapped_gfn = NULL;
        goto err_exit;
    }
 
    g_hash_table_insert(drakvuf->remapped_gfns,
        &remapped_gfn->o,
        remapped_gfn);
}
/*
* The page may have been remapped previously but if it has no active traps
* then the contents may be stale, so we copy it in that case just to make sure
*/
if (!g_hash_table_lookup(drakvuf->breakpoint_lookup_gfn, &remapped_gfn->o) )
{
  uint8_t backup[VMI_PS_4KB];
  if ( VMI_FAILURE == vmi_read_pa(drakvuf->vmi, current_gfn<<12, VMI_PS_4KB, &backup, NULL) )
  {
      fprintf(stderr, "Reading original page contents before remapping failed\n");
      goto err_exit;
  }
 
  if ( VMI_SUCCESS == vmi_write_pa(drakvuf->vmi, remapped_gfn->r << 12, VMI_PS_4KB, &backup, NULL) )
      PRINT_DEBUG("Copied trapped page to new location\n");
  else
  {
      // TODO cleanup
      fprintf(stderr, "Copying trapped page to new location FAILED\n");
      goto err_exit;
  }
}
if ( !remapped_gfn->active )
    {
        PRINT_DEBUG("Activating remapped gfns in the altp2m views!\n");
        remapped_gfn->active = 1;
 
        if (VMI_FAILURE == vmi_slat_change_gfn(
                drakvuf->vmi, drakvuf->altp2m_idx, current_gfn, remapped_gfn->r))
        {
            PRINT_DEBUG("%s: Failed to change gfn on view %u\n", __FUNCTION__, drakvuf->altp2m_idx);
            goto err_exit;
        }
        if (VMI_FAILURE == vmi_slat_change_gfn(
                drakvuf->vmi, drakvuf->altp2m_idr, remapped_gfn->r, drakvuf->sink_page_gfn))
        {
            PRINT_DEBUG("%s: Failed to change gfn on view %u\n", __FUNCTION__, drakvuf->altp2m_idr);
            goto err_exit;
        }
 
        if (VMI_FAILURE == vmi_slat_change_gfn(
                drakvuf->vmi, drakvuf->altp2m_idrx, remapped_gfn->r, drakvuf->sink_page_gfn))
        {
            PRINT_DEBUG("%s: Failed to change gfn on view %u\n", __FUNCTION__, drakvuf->altp2m_idrx);
            goto err_exit;
        }
 
    }
/*
* We MUST set guard and guard2 memaccess _after_ remapping as otherwise remapping
* overwrites the memaccess settings.
*/
container->breakpoint.guard.type = MEMACCESS;
/* We need to merge rights of the previous traps on this page (if any) */
container->breakpoint.guard.memaccess.access = VMI_MEMACCESS_RW;
container->breakpoint.guard.memaccess.type = PRE;
container->breakpoint.guard.memaccess.gfn = current_gfn;
 
container->breakpoint.guard2.type = MEMACCESS;
container->breakpoint.guard2.memaccess.access = VMI_MEMACCESS_RWX;
container->breakpoint.guard2.memaccess.type = PRE;
container->breakpoint.guard2.memaccess.gfn = remapped_gfn->r;
 
container->breakpoint.guard3.type = MEMACCESS;
container->breakpoint.guard3.memaccess.access = VMI_MEMACCESS_W;
container->breakpoint.guard3.memaccess.type = PRE;
container->breakpoint.guard3.memaccess.gfn = current_gfn;
 
container->breakpoint.guard4.type = MEMACCESS;
container->breakpoint.guard4.memaccess.access = VMI_MEMACCESS_RWX;
container->breakpoint.guard4.memaccess.type = PRE;
container->breakpoint.guard4.memaccess.gfn = remapped_gfn->r;
addr_t rpa = (remapped_gfn->r<<12) + (container->breakpoint.pa & VMI_BIT_MASK(0, 11));
uint8_t test;
 
if (VMI_FAILURE == vmi_read_8_pa(vmi, pa, &test))
{
    PRINT_DEBUG("FAILED TO READ @ 0x%lx !\n", container->breakpoint.pa);
    goto err_exit;
}
 
if (test == bp)
{
    PRINT_DEBUG("Double-trap location @ 0x%lx !\n", container->breakpoint.pa);
    container->breakpoint.doubletrap = 1;
}
else
{
    container->breakpoint.doubletrap = 0;
 
    if ( VMI_FAILURE == vmi_write_8_pa(vmi, rpa, &bp) )
    {
        PRINT_DEBUG("Using breakpoint instruction @ 0x%lx is not possible. Using fallback.\n", container->breakpoint.pa);
        container->breakpoint.guard.memaccess.access |= VMI_MEMACCESS_X;
    }
}
 
#define TRAP 0xCC
 
(...)
 
static uint8_t bp = TRAP;
if ( !inject_trap_mem(drakvuf, &container->breakpoint.guard, 0) )
{
    PRINT_DEBUG("[IDX] Failed to create guard trap for the breakpoint!\n");
    goto err_exit;
}
 
if ( !inject_trap_mem(drakvuf, &container->breakpoint.guard2, 1) )
{
    PRINT_DEBUG("[IDX] Failed to create guard2 trap for the breakpoint!\n");
    goto err_exit;
}
 
 
/*
 * We don't use inject_trap_mem for guard3 and guard4 because the settings on them are fixed and no external
 * trap can change the memaccess settings for them.
 */
 
if ( VMI_FAILURE == vmi_set_mem_event(drakvuf->vmi, container->breakpoint.guard3.memaccess.gfn, container->breakpoint.guard3.memaccess.access, drakvuf->altp2m_idrx) )
{
    PRINT_DEBUG("[IDRX] Failed to create guard3 trap for the breakpoint!\n");
    goto err_exit;
}
 
if ( VMI_FAILURE == vmi_set_mem_event(drakvuf->vmi, container->breakpoint.guard4.memaccess.gfn, container->breakpoint.guard4.memaccess.access, drakvuf->altp2m_idrx) )
{
    PRINT_DEBUG("[IDRX] Failed to create guard4 trap for the breakpoint!\n");
    goto err_exit;
}
 
// list of traps on this page
GSList* traps = (GSList*)g_hash_table_lookup(drakvuf->breakpoint_lookup_gfn, &current_gfn);
traps = g_slist_append(traps, &container->breakpoint.pa);
 
// save trap location into lookup tree
uint64_t _current_gfn = current_gfn;
g_hash_table_insert(drakvuf->breakpoint_lookup_gfn, g_memdup_compat(&_current_gfn, sizeof(uint64_t)), traps);
g_hash_table_insert(drakvuf->breakpoint_lookup_pa, g_memdup_compat(&container->breakpoint.pa, sizeof(addr_t)), container);
g_hash_table_insert(drakvuf->breakpoint_lookup_trap, trap, container);
 
PRINT_DEBUG("\t\tTrap added @ PA 0x%" PRIx64 " RPA 0x%" PRIx64 " Page %" PRIu64 " for %s.\n",
    container->breakpoint.pa, rpa, pa >> 12, trap->name);
return 1;
 
err_exit:
	if ( container->traps )
	    g_slist_free(container->traps);
	if ( remapped_gfn )
	    g_hash_table_remove(drakvuf->remapped_gfns, &remapped_gfn->o);
	g_slice_free(struct wrapper, container);
	return 0;
}

🌱 Back to Garden