Merge pull request #7 from c01db33f/qemu-nyx-4.2.0

Reimplemented x86_64 page table walking code.
This commit is contained in:
Sergej Schumilo 2022-04-07 07:09:14 +02:00 committed by GitHub
commit ea4bdcd6d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 221 additions and 202 deletions

View File

@ -210,7 +210,7 @@ static void handle_hypercall_get_payload(struct kvm_run *run, CPUState *cpu, uin
CPUX86State *env = &(X86_CPU(cpu))->env; CPUX86State *env = &(X86_CPU(cpu))->env;
GET_GLOBAL_STATE()->parent_cr3 = env->cr[3] & 0xFFFFFFFFFFFFF000ULL; GET_GLOBAL_STATE()->parent_cr3 = env->cr[3] & 0xFFFFFFFFFFFFF000ULL;
QEMU_PT_PRINTF(CORE_PREFIX, "Payload CR3:\t%lx", (uint64_t)GET_GLOBAL_STATE()->parent_cr3 ); QEMU_PT_PRINTF(CORE_PREFIX, "Payload CR3:\t%lx", (uint64_t)GET_GLOBAL_STATE()->parent_cr3 );
//print_48_paging2(GET_GLOBAL_STATE()->parent_cr3); //print_48_pagetables(GET_GLOBAL_STATE()->parent_cr3);
if(hypercall_arg&0xFFF){ if(hypercall_arg&0xFFF){
fprintf(stderr, "[QEMU-Nyx] Error: Payload buffer is not page-aligned! (0x%lx)\n", hypercall_arg); fprintf(stderr, "[QEMU-Nyx] Error: Payload buffer is not page-aligned! (0x%lx)\n", hypercall_arg);

View File

@ -569,223 +569,242 @@ void remove_all_breakpoints(CPUState *cpu){
} }
#define PPAGE_SIZE 0x1000
#define PENTRIES 0x200 #define PENTRIES 0x200
#define PLEVEL_4_SHIFT 12 #define PPAGE_SIZE 0x1000
#define PLEVEL_3_SHIFT 21
#define PLEVEL_2_SHIFT 30
#define PLEVEL_1_SHIFT 39
#define SIGN_EXTEND_TRESHOLD 0x100
#define SIGN_EXTEND 0xFFFF000000000000ULL
#define PAGETABLE_MASK 0x1FFFFFFFFF000ULL
#define PML4_ENTRY_MASK 0x1FFFFFFFFF000ULL
#define PML3_ENTRY_MASK 0x1FFFFC0000000ULL
#define PML2_ENTRY_MASK 0x1FFFFFFE00000ULL
#define CHECK_BIT(var,pos) !!(((var) & (1ULL<<(pos))))
static void write_address(uint64_t address, uint64_t size, uint64_t prot){
//fprintf(stderr, "%s %lx %lx %lx\n", __func__, address, size, prot);
static uint64_t next_address = PAGETABLE_MASK;
static uint64_t last_address = 0x0;
static uint64_t last_prot = 0;
if((address != next_address || prot != last_prot)){
/* do not print guard pages or empty pages without any permissions */
if(last_address && prot && (last_address+size != next_address || prot != last_prot)){
fprintf(stderr, "%016lx - %016lx %c%c%c\n",
last_address, next_address,
CHECK_BIT(last_prot, 1) ? 'W' : '-',
CHECK_BIT(last_prot, 2) ? 'U' : 'K',
!CHECK_BIT(last_prot, 63)? 'X' : '-');
}
last_address = address;
}
next_address = address+size;
last_prot = prot;
static bool read_memory(uint64_t address, uint64_t* buffer, size_t size, bool read_from_snapshot) {
if (unlikely(address == INVALID_ADDRESS)) {
return false;
} }
void print_48_paging2(uint64_t cr3){ if (unlikely(read_from_snapshot)) {
uint64_t paging_entries_level_1[PENTRIES]; return read_snapshot_memory(
uint64_t paging_entries_level_2[PENTRIES]; get_fast_reload_snapshot(),
uint64_t paging_entries_level_3[PENTRIES]; address, (uint8_t *)buffer, size);
uint64_t paging_entries_level_4[PENTRIES];
uint64_t address_identifier_1, address_identifier_2, address_identifier_3, address_identifier_4;
uint32_t i1, i2, i3,i4;
cpu_physical_memory_rw((cr3&PAGETABLE_MASK), (uint8_t *) paging_entries_level_1, PPAGE_SIZE, false);
for(i1 = 0; i1 < 512; i1++){
if(paging_entries_level_1[i1]){
address_identifier_1 = ((uint64_t)i1) << PLEVEL_1_SHIFT;
if (i1 & SIGN_EXTEND_TRESHOLD){
address_identifier_1 |= SIGN_EXTEND;
}
if(CHECK_BIT(paging_entries_level_1[i1], 0)){ /* otherwise swapped out */
cpu_physical_memory_rw((paging_entries_level_1[i1]&PAGETABLE_MASK), (uint8_t *) paging_entries_level_2, PPAGE_SIZE, false);
for(i2 = 0; i2 < PENTRIES; i2++){
if(paging_entries_level_2[i2]){
address_identifier_2 = (((uint64_t)i2) << PLEVEL_2_SHIFT) + address_identifier_1;
if (CHECK_BIT(paging_entries_level_2[i2], 0)){ /* otherwise swapped out */
if((paging_entries_level_2[i2]&PAGETABLE_MASK) == (paging_entries_level_1[i1]&PAGETABLE_MASK)){
/* loop */
continue;
} }
if (CHECK_BIT(paging_entries_level_2[i2], 7)){ // NB: This API exposed by exec.h doesn't signal failure, although it can
write_address(address_identifier_2, 0x40000000, (uint64_t)paging_entries_level_2[i2] & ((1ULL<<63) | (1ULL<<2) | (1ULL<<1))); // fail. Figure out how to expose the address space object instead and then
// we can actually check the return value here. Until then, will clear the
// buffer contents first.
memset(buffer, 0, size);
cpu_physical_memory_rw(address, (uint8_t*)buffer, size, false);
return true;
} }
else{
/* otherwise this PDPE references a 1GB page */ __attribute__((always_inline))
cpu_physical_memory_rw((paging_entries_level_2[i2]&PAGETABLE_MASK), (uint8_t *) paging_entries_level_3, PPAGE_SIZE, false); static bool bit(uint64_t value, uint8_t lsb) {
for(i3 = 0; i3 < PENTRIES; i3++){ return (value >> lsb) & 1;
if(paging_entries_level_3[i3]){
address_identifier_3 = (((uint64_t)i3) << PLEVEL_3_SHIFT) + address_identifier_2;
if (CHECK_BIT(paging_entries_level_3[i3], 0)){ /* otherwise swapped out */
if (CHECK_BIT(paging_entries_level_3[i3], 7)){
write_address(address_identifier_3, 0x200000, (uint64_t)paging_entries_level_3[i3] & ((1ULL<<63) | (1ULL<<2) | (1ULL<<1)));
} }
else{
cpu_physical_memory_rw((paging_entries_level_3[i3]&PAGETABLE_MASK), (uint8_t *) paging_entries_level_4, PPAGE_SIZE, false); __attribute__((always_inline))
for(i4 = 0; i4 < PENTRIES; i4++){ static uint64_t bits(uint64_t value, uint8_t lsb, uint8_t msb) {
if(paging_entries_level_4[i4]){ return (value & ((0xffffffffffffffffull >> (64 - (msb - lsb + 1))) << lsb)) >> lsb;
address_identifier_4 = (((uint64_t)i4) << PLEVEL_4_SHIFT) + address_identifier_3;
if (CHECK_BIT(paging_entries_level_4[i4], 0)){
write_address(address_identifier_4, 0x1000, (uint64_t)paging_entries_level_4[i4] & ((1ULL<<63) | (1ULL<<2) | (1ULL<<1)));
} }
// Helper function to load an entire pagetable table. These are PENTRIES
// 64-bit entries, so entries must point to a sufficiently large buffer.
static bool load_table(uint64_t address, uint64_t* entries, bool read_from_snapshot) {
if (unlikely(!read_memory(address, entries, 512 * sizeof(*entries), read_from_snapshot))) {
return false;
} }
return true;
} }
// Helper function to load a single pagetable entry. We simplify things by
// returning the same invalid value (0) for both non-present entries and
// any other error conditions, since we don't need to handle these cases
// differently.
static uint64_t load_entry(uint64_t address, uint64_t index,
bool read_from_snapshot) {
uint64_t entry = 0;
if (unlikely(!read_memory(address + (index * sizeof(entry)), &entry, sizeof(entry),
read_from_snapshot))) {
return 0;
} }
// Check that the entry is present.
if (unlikely(!bit(entry, 0))) {
return 0;
}
return entry;
}
static void print_page(uint64_t address, uint64_t entry, size_t size, bool s, bool w, bool x) {
fprintf(stderr, " %c%c%c %016llx %zx",
s ? 's' : 'u', w ? 'w' : 'r', x ? 'x' : '-',
(bits(entry, 12, 51) << 12) & ~(size - 1), size);
}
static void print_48_pte(uint64_t address, uint64_t pde_entry, bool read_from_snapshot,
bool s, bool w, bool x) {
uint64_t pte_address = bits(pde_entry, 12, 51) << 12;
uint64_t pte_table[PENTRIES];
if (!load_table(pte_address, pte_table, read_from_snapshot)) {
return;
}
for (size_t i = 0; i < PENTRIES; ++i) {
uint64_t entry = pte_table[i];
if (entry) {
fprintf(stderr, "\n 1 %016llx", address | i << 12, entry);
}
if (!bit(entry, 0)) {
// Not present.
} else {
print_page(address | i << 12, entry, 0x1000,
s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63));
} }
} }
} }
} static void print_48_pde(uint64_t address, uint64_t pdpte_entry, bool read_from_snapshot,
} bool s, bool w, bool x) {
} uint64_t pde_address = bits(pdpte_entry, 12, 51) << 12;
} uint64_t pde_table[PENTRIES];
}
} if (!load_table(pde_address, pde_table, read_from_snapshot)) {
} return;
write_address(0, 0x1000, 0);
} }
for (size_t i = 0; i < PENTRIES; ++i) {
uint64_t entry = pde_table[i];
static uint64_t* load_page_table(uint64_t page_table_address, uint64_t* paging_entries_buffer, uint8_t level, bool read_from_snapshot, bool *success){ if (entry) {
if(page_table_address == INVALID_ADDRESS){ fprintf(stderr, "\n 2 %016llx", address | i << 21, entry);
*success = false;
} }
if (read_from_snapshot){ if (!bit(entry, 0)) {
*success = read_snapshot_memory(get_fast_reload_snapshot(), page_table_address, (uint8_t *) paging_entries_buffer, PPAGE_SIZE); // Not present.
} else if (bit(entry, 7)) {
print_page(address | i << 21, entry, 0x200000,
s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63));
} else {
print_48_pte(address | i << 21, entry, read_from_snapshot,
s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63));
} }
else{
cpu_physical_memory_rw(page_table_address, (uint8_t *) paging_entries_buffer, PPAGE_SIZE, false);
*success = true; /* fix this */
} }
return paging_entries_buffer; }
static void print_48_pdpte(uint64_t address, uint64_t pml4_entry, bool read_from_snapshot,
bool s, bool w, bool x) {
uint64_t pdpte_address = bits(pml4_entry, 12, 51) << 12;
uint64_t pdpte_table[PENTRIES];
if (!load_table(pdpte_address, pdpte_table, read_from_snapshot)) {
return;
}
for (size_t i = 0; i < PENTRIES; ++i) {
uint64_t entry = pdpte_table[i];
if (entry) {
fprintf(stderr, "\n 3 %016llx", address | i << 30, entry);
}
if (!bit(entry, 0)) {
// Not present.
} else if (bit(entry, 7)) {
print_page(address | i << 30, entry, 0x40000000,
s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63));
} else {
print_48_pde(address | i << 30, entry, read_from_snapshot,
s & !bit(entry, 2), w & bit(entry, 1), x & !bit(entry, 63));
}
}
}
static void print_48_pagetables_(uint64_t cr3, bool read_from_snapshot) {
uint64_t pml4_address = bits(cr3, 12, 51) << 12;
uint64_t pml4_table[PENTRIES];
if (!load_table(pml4_address, pml4_table, read_from_snapshot)) {
return;
}
for (size_t i = 0; i < PENTRIES; ++i) {
uint64_t entry = pml4_table[i];
uint64_t address = i << 39;
// Ensure canonical virtual address
if (bit(address, 47)) {
address |= 0xffff000000000000ul;
}
if (entry) {
fprintf(stderr, "\n4 %016llx", address, entry);
}
if (bit(entry, 0)) {
print_48_pdpte(address, entry, read_from_snapshot,
!bit(entry, 2), bit(entry, 1), !bit(entry, 63));
}
}
}
void print_48_pagetables(uint64_t cr3) {
static bool printed = false;
if (!printed) {
fprintf(stderr, "pagetables for cr3 %lx", cr3);
print_48_pagetables_(cr3, false);
printed = true;
fprintf(stderr, "\n");
}
} }
static uint64_t get_48_paging_phys_addr(uint64_t cr3, uint64_t addr, bool read_from_snapshot) { static uint64_t get_48_paging_phys_addr(uint64_t cr3, uint64_t addr, bool read_from_snapshot) {
/* signedness broken af -> fix me! */ uint64_t pml4_address = bits(cr3, 12, 51) << 12;
uint16_t pml_4_index = (addr & 0xFF8000000000ULL) >> 39; uint64_t pml4_offset = bits(addr, 39, 47);
uint16_t pml_3_index = (addr & 0x0007FC0000000UL) >> 30; uint64_t pml4_entry = load_entry(pml4_address, pml4_offset, read_from_snapshot);
uint16_t pml_2_index = (addr & 0x000003FE00000UL) >> 21; if (unlikely(!pml4_entry)) {
uint16_t pml_1_index = (addr & 0x00000001FF000UL) >> 12;
uint64_t address_identifier_4;
uint64_t paging_entries_buffer[PENTRIES];
uint64_t* paging_entries_buffer_ptr = NULL;
uint64_t page_table_address = 0;
bool success = false;
page_table_address = (cr3&PAGETABLE_MASK);
paging_entries_buffer_ptr = load_page_table(page_table_address, paging_entries_buffer, 0, read_from_snapshot, &success);
if (unlikely(success == false)){
goto fail;
}
if(paging_entries_buffer_ptr[pml_4_index]){
address_identifier_4 = ((uint64_t)pml_4_index) << PLEVEL_1_SHIFT;
if (pml_4_index & SIGN_EXTEND_TRESHOLD){
address_identifier_4 |= SIGN_EXTEND;
}
if(CHECK_BIT(paging_entries_buffer_ptr[pml_4_index], 0)){ /* otherwise swapped out */
page_table_address = (paging_entries_buffer_ptr[pml_4_index]&PAGETABLE_MASK);
paging_entries_buffer_ptr = load_page_table(page_table_address, paging_entries_buffer, 1, read_from_snapshot, &success);
if (unlikely(success == false)){
goto fail;
}
if(paging_entries_buffer_ptr[pml_3_index]){
if (CHECK_BIT(paging_entries_buffer_ptr[pml_3_index], 0)){ /* otherwise swapped out */
if (CHECK_BIT(paging_entries_buffer_ptr[pml_3_index], 7)){
/* 1GB PAGE */
return (paging_entries_buffer_ptr[pml_3_index] & PML3_ENTRY_MASK) | (0x7FFFFFFF & addr);
}
else{
page_table_address = (paging_entries_buffer_ptr[pml_3_index]&PAGETABLE_MASK);
paging_entries_buffer_ptr = load_page_table(page_table_address, paging_entries_buffer, 2, read_from_snapshot, &success);
if (unlikely(success == false)){
goto fail;
}
if(paging_entries_buffer_ptr[pml_2_index]){
if (CHECK_BIT(paging_entries_buffer_ptr[pml_2_index], 0)){ /* otherwise swapped out */
if (CHECK_BIT(paging_entries_buffer_ptr[pml_2_index], 7)){
/* 2MB PAGE */
return (paging_entries_buffer_ptr[pml_2_index] & PML2_ENTRY_MASK) | (0x3FFFFF & addr);
}
else{
page_table_address = (paging_entries_buffer_ptr[pml_2_index]&PAGETABLE_MASK);
paging_entries_buffer_ptr = load_page_table(page_table_address, paging_entries_buffer, 3, read_from_snapshot, &success);
if (unlikely(success == false)){
goto fail;
}
if(paging_entries_buffer_ptr[pml_1_index]){
if (CHECK_BIT(paging_entries_buffer_ptr[pml_1_index], 0)){
/* 4 KB PAGE */
return (paging_entries_buffer_ptr[pml_1_index] & PML4_ENTRY_MASK) | (0xFFF & addr);
}
}
}
}
}
}
}
}
}
}
fail:
return INVALID_ADDRESS; return INVALID_ADDRESS;
} }
uint64_t pdpte_address = bits(pml4_entry, 12, 51) << 12;
uint64_t pdpte_offset = bits(addr, 30, 38);
uint64_t pdpte_entry = load_entry(pdpte_address, pdpte_offset, read_from_snapshot);
if (unlikely(!pdpte_entry)) {
return INVALID_ADDRESS;
}
if (unlikely(bit(pdpte_entry, 7))) {
// 1GByte page translation.
uint64_t page_address = bits(pdpte_entry, 12, 51) << 12;
uint64_t page_offset = bits(addr, 0, 29);
return page_address + page_offset;
}
uint64_t pde_address = bits(pdpte_entry, 12, 51) << 12;
uint64_t pde_offset = bits(addr, 21, 29);
uint64_t pde_entry = load_entry(pde_address, pde_offset, read_from_snapshot);
if (unlikely(!pde_entry)) {
return INVALID_ADDRESS;
}
if (unlikely(bit(pde_entry, 7))) {
// 2MByte page translation.
uint64_t page_address = bits(pde_entry, 12, 51) << 12;
uint64_t page_offset = bits(addr, 0, 20);
return page_address + page_offset;
}
uint64_t pte_address = bits(pde_entry, 12, 51) << 12;
uint64_t pte_offset = bits(addr, 12, 20);
uint64_t pte_entry = load_entry(pte_address, pte_offset, read_from_snapshot);
if (unlikely(!pte_entry)) {
return INVALID_ADDRESS;
}
// 4Kbyte page translation.
uint64_t page_address = bits(pte_entry, 12, 51) << 12;
uint64_t page_offset = bits(addr, 0, 11);
return page_address + page_offset;
}
//#define DEBUG_48BIT_WALK //#define DEBUG_48BIT_WALK
bool read_virtual_memory(uint64_t address, uint8_t* data, uint32_t size, CPUState *cpu){ bool read_virtual_memory(uint64_t address, uint8_t* data, uint32_t size, CPUState *cpu){

View File

@ -66,7 +66,7 @@ bool dump_page_cr3_snapshot(uint64_t address, uint8_t* data, CPUState *cpu, uint
bool dump_page_cr3_ht(uint64_t address, uint8_t* data, CPUState *cpu, uint64_t cr3); bool dump_page_cr3_ht(uint64_t address, uint8_t* data, CPUState *cpu, uint64_t cr3);
bool is_addr_mapped_cr3_snapshot(uint64_t address, CPUState *cpu, uint64_t cr3); bool is_addr_mapped_cr3_snapshot(uint64_t address, CPUState *cpu, uint64_t cr3);
void print_48_paging2(uint64_t cr3); void print_48_pagetables(uint64_t cr3);
bool dump_page_ht(uint64_t address, uint8_t* data, CPUState *cpu); bool dump_page_ht(uint64_t address, uint8_t* data, CPUState *cpu);