From d5a7011ad20ba5ba91f1371f9d40154035d5d768 Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Tue, 11 Jan 2022 14:35:24 +0100 Subject: [PATCH 1/8] checkout specific libxdc commit --- compile_qemu_nyx.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/compile_qemu_nyx.sh b/compile_qemu_nyx.sh index 7225af05ca..cae4137b9c 100755 --- a/compile_qemu_nyx.sh +++ b/compile_qemu_nyx.sh @@ -37,6 +37,7 @@ if [ ! -f "/usr/lib/libxdc.so" ] || [ ! -f "/usr/include/libxdc.h" ]; then sudo make install cd .. cd libxdc + git checkout 641de7539e99f7faf5c8e8f1c8a4b37a9df52a5f sudo make install cd .. fi From bc1219efebeba3080b990615fef991a789bc7aa2 Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Fri, 21 Jan 2022 04:25:28 +0100 Subject: [PATCH 2/8] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 81dc8c56f8..d551e3ae8c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # QEMU-NYX -This repository contains Nyx's fork of Qemu. To enable Hypervisor based snapshots, Intel-PT based tracing, and Redqueen style magic byte resolution, we made various extensions to QEMU. This includes the ability to quickly reset memory and devices, ontain precise disassembly of the code running (even when code is partially swapped out / unavailable) & intel-PT decoding, instrument code running in the VM with breakpoint based hooks as well as communicating with a fuzzing frontend (e.g. based on libnyx). +This repository contains Nyx's fork of QEMU. To enable Hypervisor based snapshots, Intel-PT based tracing, and REDQUEEN style magic byte resolution, we made various extensions to QEMU. This includes the ability to quickly reset memory and devices, obtain precise disassembly of the code running (even when code is partially swapped out / unavailable) & Intel-PT decoding, instrument code running in the VM with breakpoint-based hooks as well as communicating with a fuzzing frontend (e.g. based on [libnyx](https://github.com/nyx-fuzz/libnyx)). You can find more detailed information in our main repository. @@ -20,7 +20,7 @@ If you found and fixed a bug on your own: We are very open to patches, please cr ### License -This tool is provided under **AGPL license**. +This tool is provided under **GPLv2 license**. **Free Software Hell Yeah!** From 389cf8fbab96b2c5ab2b3600ac04bff583cd8021 Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Fri, 21 Jan 2022 06:57:10 +0100 Subject: [PATCH 3/8] fix compile script --- compile_qemu_nyx.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/compile_qemu_nyx.sh b/compile_qemu_nyx.sh index f09ad70682..c5859606c7 100755 --- a/compile_qemu_nyx.sh +++ b/compile_qemu_nyx.sh @@ -81,15 +81,8 @@ compile () { echo "[!] QEMU-Nyx is ready!" } -cd libxdc git submodule init git submodule update -cd .. - -cd capstone_v4 -git submodule init -git submodule update -cd .. if [ "$#" == 0 ] ; then error From 902306beb01d858dcbcbaf0e1be26ce9dd0f293f Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Fri, 21 Jan 2022 07:03:40 +0100 Subject: [PATCH 4/8] fix compile script (update only specific submodules) --- compile_qemu_nyx.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compile_qemu_nyx.sh b/compile_qemu_nyx.sh index c5859606c7..e36fe285f8 100755 --- a/compile_qemu_nyx.sh +++ b/compile_qemu_nyx.sh @@ -82,7 +82,8 @@ compile () { } git submodule init -git submodule update +git submodule update libxdc +git submodule update capstone_v4 if [ "$#" == 0 ] ; then error From 586d46c86f8fbc2563683c119b5a07ca85b55f0f Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Fri, 21 Jan 2022 18:11:32 +0100 Subject: [PATCH 5/8] bug fix: don't remap more guest page frames from the input buffer than required in case the input buffer size is smaller than the initial value --- nyx/helpers.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/nyx/helpers.c b/nyx/helpers.c index f1507bb18e..c4b332a3c2 100644 --- a/nyx/helpers.c +++ b/nyx/helpers.c @@ -104,6 +104,10 @@ static void resize_coverage_bitmap(uint32_t new_bitmap_size){ /* pass the actual bitmap buffer size to the front-end */ GET_GLOBAL_STATE()->auxilary_buffer->capabilites.agent_coverage_bitmap_size = new_bitmap_size; + + if(new_bitmap_size & (PAGE_SIZE-1)){ + GET_GLOBAL_STATE()->shared_bitmap_size = (new_bitmap_size & ~(PAGE_SIZE-1)) + PAGE_SIZE; + } } bool apply_capabilities(CPUState *cpu){ @@ -127,6 +131,11 @@ bool apply_capabilities(CPUState *cpu){ debug_printf("GET_GLOBAL_STATE()->cap_cr3: %lx\n", GET_GLOBAL_STATE()->cap_cr3); debug_printf("--------------------------\n"); + if (GET_GLOBAL_STATE()->input_buffer_size != GET_GLOBAL_STATE()->shared_payload_buffer_size){ + resize_shared_memory(GET_GLOBAL_STATE()->input_buffer_size, &GET_GLOBAL_STATE()->shared_payload_buffer_size, NULL, GET_GLOBAL_STATE()->shared_payload_buffer_fd); + GET_GLOBAL_STATE()->shared_payload_buffer_size = GET_GLOBAL_STATE()->input_buffer_size; + } + if(GET_GLOBAL_STATE()->cap_compile_time_tracing_buffer_vaddr&0xfff){ fprintf(stderr, "[QEMU-Nyx] Error: guest's trace bitmap v_addr (0x%lx) is not page aligned!\n", GET_GLOBAL_STATE()->cap_compile_time_tracing_buffer_vaddr); return false; @@ -157,10 +166,6 @@ bool apply_capabilities(CPUState *cpu){ set_cap_agent_ijon_trace_bitmap(GET_GLOBAL_STATE()->auxilary_buffer, true); } - if (GET_GLOBAL_STATE()->input_buffer_size != GET_GLOBAL_STATE()->shared_payload_buffer_size){ - resize_shared_memory(GET_GLOBAL_STATE()->input_buffer_size, &GET_GLOBAL_STATE()->shared_payload_buffer_size, NULL, GET_GLOBAL_STATE()->shared_payload_buffer_fd); - GET_GLOBAL_STATE()->shared_payload_buffer_size = GET_GLOBAL_STATE()->input_buffer_size; - } /* pass the actual input buffer size to the front-end */ GET_GLOBAL_STATE()->auxilary_buffer->capabilites.agent_input_buffer_size = GET_GLOBAL_STATE()->shared_payload_buffer_size; From 6ca723cb8463889ab41d8b7b8b3613ad43ced80e Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Fri, 21 Jan 2022 20:20:13 +0100 Subject: [PATCH 6/8] exit after nyx_abort() has been called if the frontend continues to send data --- nyx/helpers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/nyx/helpers.c b/nyx/helpers.c index c4b332a3c2..9f850395ca 100644 --- a/nyx/helpers.c +++ b/nyx/helpers.c @@ -18,6 +18,7 @@ void nyx_abort(char* msg){ set_abort_reason_auxiliary_buffer(GET_GLOBAL_STATE()->auxilary_buffer, msg, strlen(msg)); synchronization_lock(); + exit(1); } bool is_called_in_fuzzing_mode(const char* hypercall){ From c2c69cfc528398d9db9363b92f8c50db4008c98f Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Fri, 21 Jan 2022 20:23:52 +0100 Subject: [PATCH 7/8] abort if a configuration was not set or received (via GET_HOST / SET_AGENT) or if either was executed twice --- nyx/hypercall/configuration.c | 12 ++++++++++++ nyx/hypercall/hypercall.c | 11 +++++++++++ nyx/state/snapshot_state.c | 4 ++++ nyx/state/state.c | 3 +++ nyx/state/state.h | 3 +++ 5 files changed, 33 insertions(+) diff --git a/nyx/hypercall/configuration.c b/nyx/hypercall/configuration.c index b31baf0f98..3b54415653 100644 --- a/nyx/hypercall/configuration.c +++ b/nyx/hypercall/configuration.c @@ -12,6 +12,11 @@ void handle_hypercall_kafl_get_host_config(struct kvm_run *run, CPUState *cpu, u return; } + if (GET_GLOBAL_STATE()->get_host_config_done){ + nyx_abort((char*)"KVM_EXIT_KAFL_GET_HOST_CONFIG called twice..."); + return; + } + memset((void*)&config, 0, sizeof(host_config_t)); config.host_magic = NYX_HOST_MAGIC; @@ -21,6 +26,7 @@ void handle_hypercall_kafl_get_host_config(struct kvm_run *run, CPUState *cpu, u config.payload_buffer_size = GET_GLOBAL_STATE()->shared_payload_buffer_size; write_virtual_memory(vaddr, (uint8_t*)&config, sizeof(host_config_t), cpu); + GET_GLOBAL_STATE()->get_host_config_done = true; } void handle_hypercall_kafl_set_agent_config(struct kvm_run *run, CPUState *cpu, uint64_t hypercall_arg){ @@ -31,6 +37,11 @@ void handle_hypercall_kafl_set_agent_config(struct kvm_run *run, CPUState *cpu, return; } + if (GET_GLOBAL_STATE()->set_agent_config_done){ + nyx_abort((char*)"KVM_EXIT_KAFL_SET_AGENT_CONFIG called twice..."); + return; + } + X86CPU *cpux86 = X86_CPU(cpu); CPUX86State *env = &cpux86->env; @@ -89,4 +100,5 @@ void handle_hypercall_kafl_set_agent_config(struct kvm_run *run, CPUState *cpu, fprintf(stderr, "[QEMU-Nyx] Error: %s - failed (vaddr: 0x%lx)!\n", __func__, vaddr); exit(1); } + GET_GLOBAL_STATE()->set_agent_config_done = true; } \ No newline at end of file diff --git a/nyx/hypercall/hypercall.c b/nyx/hypercall/hypercall.c index 3fae6e54c1..64703657e2 100644 --- a/nyx/hypercall/hypercall.c +++ b/nyx/hypercall/hypercall.c @@ -108,6 +108,12 @@ bool handle_hypercall_kafl_next_payload(struct kvm_run *run, CPUState *cpu, uint synchronization_lock(); } else { + + if (GET_GLOBAL_STATE()->set_agent_config_done == false){ + nyx_abort((char*)"KVM_EXIT_KAFL_SET_AGENT_CONFIG was not called..."); + return false; + } + if(!setup_snapshot_once){ //pt_reset_bitmap(); @@ -193,6 +199,11 @@ static void handle_hypercall_get_payload(struct kvm_run *run, CPUState *cpu, uin return; } + if (GET_GLOBAL_STATE()->get_host_config_done == false){ + nyx_abort((char*)"KVM_EXIT_KAFL_GET_HOST_CONFIG was not called..."); + return; + } + if(hypercall_enabled && !setup_snapshot_once){ QEMU_PT_PRINTF(CORE_PREFIX, "Payload Address:\t%lx", hypercall_arg); kvm_arch_get_registers(cpu); diff --git a/nyx/state/snapshot_state.c b/nyx/state/snapshot_state.c index bf45351171..ea0ffe3560 100644 --- a/nyx/state/snapshot_state.c +++ b/nyx/state/snapshot_state.c @@ -128,6 +128,10 @@ void deserialize_state(const char* filename_prefix){ assert(apply_capabilities(qemu_get_cpu(0))); remap_payload_buffer(nyx_global_state->payload_buffer, ((CPUState *)qemu_get_cpu(0)) ); + + /* makes sure that we are allowed to enter the fuzzing loop */ + nyx_global_state->get_host_config_done = true; + nyx_global_state->set_agent_config_done = true; } else{ fprintf(stderr, "[QEMU-Nyx]: this feature is currently missing\n"); diff --git a/nyx/state/state.c b/nyx/state/state.c index 9a2c827f01..9dde7c97f1 100644 --- a/nyx/state/state.c +++ b/nyx/state/state.c @@ -107,6 +107,9 @@ void state_init_global(void){ global_state.pt_trace_mode_force = false; global_state.num_dirty_pages = 0; + + global_state.get_host_config_done = false; + global_state.set_agent_config_done = false; global_state.sharedir = sharedir_new(); diff --git a/nyx/state/state.h b/nyx/state/state.h index 3ed5645a9f..7a9367f481 100644 --- a/nyx/state/state.h +++ b/nyx/state/state.h @@ -139,6 +139,9 @@ typedef struct qemu_nyx_state_s{ uint32_t num_dirty_pages; + bool get_host_config_done; + bool set_agent_config_done; + /* capabilites */ uint8_t cap_timeout_detection; uint8_t cap_only_reload_mode; From c023bfb750d74052663dde3e341318b7ca1e1a16 Mon Sep 17 00:00:00 2001 From: Sergej Schumilo Date: Tue, 22 Feb 2022 19:35:16 +0100 Subject: [PATCH 8/8] bug fix: don't reuse ram_offset as physical address to register PF in snapshot blocklist (breaks memory access and shared memory if address is above 0x0C0000000) --- nyx/memory_access.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/nyx/memory_access.c b/nyx/memory_access.c index e5e4bcdf30..ad6dfec8d5 100644 --- a/nyx/memory_access.c +++ b/nyx/memory_access.c @@ -167,13 +167,13 @@ bool remap_payload_slot(uint64_t phys_addr, uint32_t slot, CPUState *cpu){ uint32_t i = slot; - phys_addr = address_to_ram_offset(phys_addr); + uint64_t phys_addr_ram_offset = address_to_ram_offset(phys_addr); QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if(!memcmp(block->idstr, "pc.ram", 6)){ /* TODO: put assert calls here */ - munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE); - mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)); + munmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), x86_64_PAGE_SIZE); + mmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)); //printf("MMUNMAP: %d\n", munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE)); //printf("MMAP: %p\n", mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE))); @@ -205,9 +205,8 @@ bool remap_slot(uint64_t addr, uint32_t slot, CPUState *cpu, int fd, uint64_t sh fprintf(stderr, "[QEMU-Nyx] Check if the buffer is present in the guest's memory...\n"); exit(1); } - - phys_addr = address_to_ram_offset(phys_addr); } + uint64_t phys_addr_ram_offset = address_to_ram_offset(phys_addr); //printf("phys_addr -> %lx\n", phys_addr); @@ -216,8 +215,8 @@ bool remap_slot(uint64_t addr, uint32_t slot, CPUState *cpu, int fd, uint64_t sh QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if(!memcmp(block->idstr, "pc.ram", 6)){ /* TODO: put assert calls here */ - munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE); - mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, (i*x86_64_PAGE_SIZE)); + munmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), x86_64_PAGE_SIZE); + mmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, (i*x86_64_PAGE_SIZE)); //printf("MMUNMAP: %d\n", munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE)); //printf("MMAP: %p\n", mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, (i*x86_64_PAGE_SIZE))); @@ -239,14 +238,14 @@ bool remap_payload_slot_protected(uint64_t phys_addr, uint32_t slot, CPUState *c uint32_t i = slot; - phys_addr = address_to_ram_offset(phys_addr); + uint64_t phys_addr_ram_offset = address_to_ram_offset(phys_addr); QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if(!memcmp(block->idstr, "pc.ram", 6)){ /* TODO: put assert calls here */ - munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE); - mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ , MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)); + munmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), x86_64_PAGE_SIZE); + mmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), 0x1000, PROT_READ , MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)); //printf("MMUNMAP: %d\n", munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE)); //printf("MMAP: %p\n", mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ , MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE))); @@ -296,28 +295,28 @@ bool remap_payload_buffer(uint64_t virt_guest_addr, CPUState *cpu){ assert(phys_addr != INVALID_ADDRESS); - phys_addr = address_to_ram_offset(phys_addr); + uint64_t phys_addr_ram_offset = address_to_ram_offset(phys_addr); QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { if(!memcmp(block->idstr, "pc.ram", 6)){ //printf("MMUNMAP: %d\n", munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE)); - if(munmap((void*)(((uint64_t)block->host) + phys_addr), x86_64_PAGE_SIZE) == -1){ + if(munmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), x86_64_PAGE_SIZE) == -1){ fprintf(stderr, "munmap failed!\n"); //exit(1); assert(false); } //printf("MMAP: %lx\n", mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE))); - if(mmap((void*)(((uint64_t)block->host) + phys_addr), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)) == MAP_FAILED){ + if(mmap((void*)(((uint64_t)block->host) + phys_addr_ram_offset), 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, GET_GLOBAL_STATE()->shared_payload_buffer_fd, (i*x86_64_PAGE_SIZE)) == MAP_FAILED){ fprintf(stderr, "mmap failed!\n"); //exit(1); assert(false); } - memset((block->host) + phys_addr, 0xab, 0x1000); + memset((block->host) + phys_addr_ram_offset, 0xab, 0x1000); if(GET_GLOBAL_STATE()->protect_payload_buffer){ - mprotect((block->host) + phys_addr, 0x1000, PROT_READ); + mprotect((block->host) + phys_addr_ram_offset, 0x1000, PROT_READ); } fast_reload_blacklist_page(get_fast_reload_snapshot(), phys_addr);