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, ¤t_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(¤t_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, ¤t_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, ¤t_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;
}