def map_and_load(self, path, execute_now=False): ql = self.ql pe = PE(path, fast_load=True) # Make sure no module will occupy the NULL page if self.next_image_base > pe.OPTIONAL_HEADER.ImageBase: IMAGE_BASE = self.next_image_base pe.relocate_image(IMAGE_BASE) else: IMAGE_BASE = pe.OPTIONAL_HEADER.ImageBase IMAGE_SIZE = ql.mem.align(pe.OPTIONAL_HEADER.SizeOfImage, 0x1000) while IMAGE_BASE + IMAGE_SIZE < self.heap_base_address: if not ql.mem.is_mapped(IMAGE_BASE, 1): self.next_image_base = IMAGE_BASE + 0x10000 ql.mem.map(IMAGE_BASE, IMAGE_SIZE) pe.parse_data_directories() data = bytearray(pe.get_memory_mapped_image()) ql.mem.write(IMAGE_BASE, bytes(data)) logging.info("[+] Loading %s to 0x%x" % (path, IMAGE_BASE)) entry_point = IMAGE_BASE + pe.OPTIONAL_HEADER.AddressOfEntryPoint if self.entry_point == 0: # Setting entry point to the first loaded module entry point, so the debugger can break. self.entry_point = entry_point logging.info("[+] PE entry point at 0x%x" % entry_point) self.install_loaded_image_protocol(IMAGE_BASE, IMAGE_SIZE) self.images.append( self.coverage_image( IMAGE_BASE, IMAGE_BASE + pe.NT_HEADERS.OPTIONAL_HEADER.SizeOfImage, path)) if execute_now: logging.info( f'[+] Running from 0x{entry_point:x} of {path}') assembler = self.ql.create_assembler() code = f""" mov rcx, {IMAGE_BASE} mov rdx, {self.gST} mov rax, {entry_point} call rax """ runcode, _ = assembler.asm(code) ptr = ql.os.heap.alloc(len(runcode)) ql.mem.write(ptr, bytes(runcode)) ql.os.exec_arbitrary(ptr, ptr + len(runcode)) else: self.modules.append((path, IMAGE_BASE, entry_point, pe)) return True else: IMAGE_BASE += 0x10000 pe.relocate_image(IMAGE_BASE) return False
def map_and_load(self, path: str, exec_now: bool = False): """Map and load a module into memory. The specified module would be mapped and loaded into the address set in the `next_image_base` member. It is the caller's responsibility to make sure that the memory is available. On success, `next_image_base` will be updated accordingly. Args: path : path of the module binary to load exec_now : execute module right away; will be enququed if not Raises: QlMemoryMappedError : when `next_image_base` is not available """ ql = self.ql pe = PE(path, fast_load=True) # use image base only if it does not point to NULL image_base = pe.OPTIONAL_HEADER.ImageBase or self.next_image_base image_size = ql.mem.align(pe.OPTIONAL_HEADER.SizeOfImage, 0x1000) assert (image_base % 0x1000) == 0, 'image base is expected to be page-aligned' if image_base != pe.OPTIONAL_HEADER.ImageBase: pe.relocate_image(image_base) pe.parse_data_directories() data = bytes(pe.get_memory_mapped_image()) ql.mem.map(image_base, image_size, info="[module]") ql.mem.write(image_base, data) ql.log.info(f'Module {path} loaded to {image_base:#x}') entry_point = image_base + pe.OPTIONAL_HEADER.AddressOfEntryPoint ql.log.info(f'Module entry point at {entry_point:#x}') # the 'entry_point' member is used by the debugger. if not set, set it # to the first loaded module entry point so the debugger can break if self.entry_point == 0: self.entry_point = entry_point self.install_loaded_image_protocol(image_base, image_size) # this would be used later be os.find_containing_image self.images.append( self.coverage_image(image_base, image_base + image_size, path)) # update next memory slot to allow sequencial loading. its availability # is unknown though self.next_image_base = image_base + image_size module_info = (path, image_base, entry_point) # execute the module right away or enqueue it if exec_now: # call entry point while retaining the current return address self.execute_module(*module_info, eoe_trap=None) else: self.modules.append(module_info)