def __init__(self, ql): self.swsmi_handlers = {} self.smbase = int(ql.os.profile.get("SMM", "smbase"), 0) self.smram_size = int(ql.os.profile.get("SMM", "smram_size"), 0) self.heap_size = int(ql.os.profile.get("SMM", "heap_size"), 0) self.swsmi_args = {} if self.smram_size - self.heap_size < 0x10000: raise RuntimeError(f"SMRAM must be at least 64kb in size") if ql.mem.is_available(self.smbase, self.smram_size): # Reserve SMRAM. ql.mem.map(self.smbase, self.smram_size - self.heap_size) # Create the SMM heap, which will occupy the upper portion of SMRAM. self.heap = QlMemoryHeap(ql, self.smbase + self.smram_size - self.heap_size, self.smbase + self.smram_size) else: raise RuntimeError(f"Can't allocate SMRAM at 0x{self.smbase:x}-0x{self.smbase+self.smram_size:x}, \ region is already occupied") # Points to an optional handler context which was specified when the # handler was registered. self.context_buffer = self.heap.alloc(self.PAGE_SIZE) # A pointer to a collection of data in memory that will # be conveyed from a non-MM environment into an MM environment. self.comm_buffer = self.heap.alloc(self.PAGE_SIZE)
class UefiContext: def __init__(self, ql): self.ql = ql self.heap = None self.protocols = {} def init_heap(self, base, size): self.heap = QlMemoryHeap(self.ql, base, base + size) def init_stack(self, base, size): self.ql.mem.map(base, size) def install_protocol(self, proto_desc, handle, address=None): guid = proto_desc['guid'] if handle not in self.protocols: self.protocols[handle] = {} if guid in self.protocols[handle]: logging.warning( f'a protocol with guid {guid} is already installed') if address is None: struct_class = proto_desc['struct'] address = self.heap.alloc(struct_class.sizeof()) instance = init_struct(self.ql, address, proto_desc) instance.saveTo(self.ql, address) self.protocols[handle][guid] = address
def __init__(self, ql): self.swsmi_handlers = [] self.smbase = int(ql.os.profile.get("SMM", "smbase"), 0) self.smram_size = int(ql.os.profile.get("SMM", "smram_size"), 0) self.heap_size = int(ql.os.profile.get("SMM", "heap_size"), 0) self.swsmi_args = {} # Communication buffer self.comm_buffer = ql.os.heap.alloc(ctypes.sizeof(EFI_SMM_SW_CONTEXT)) self.comm_buffer_size = ql.os.heap.alloc(ctypes.sizeof( ctypes.c_void_p)) ql.mem.write( self.comm_buffer_size, ctypes.sizeof(EFI_SMM_SW_CONTEXT).to_bytes( ctypes.sizeof(ctypes.c_void_p), 'little')) if self.smram_size - self.heap_size < 0x10000: raise RuntimeError(f"SMRAM must be at least 64kb in size") if ql.mem.is_available(self.smbase, self.smram_size): # Reserve SMRAM and create the SMM heap. The SMM heap will occupy the upper portion of SMRAM. ql.mem.map(self.smbase, self.smram_size - self.heap_size) self.heap = QlMemoryHeap( ql, self.smbase + self.smram_size - self.heap_size, self.smbase + self.smram_size) else: raise RuntimeError( f"Can't allocate SMRAM at 0x{self.smbase:x}-0x{self.smbase+self.smram_size:x}, \ region is already occupied")
def run(self): self.path = self.ql.path self.init_dlls = [b"ntdll.dll", b"kernel32.dll", b"user32.dll"] self.pe_entry_point = 0 self.sizeOfStackReserve = 0 if self.ql.archtype == QL_ARCH.X86: self.stack_address = int( self.ql.os.profile.get("OS32", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS32", "stack_size"), 16) self.image_address = int( self.ql.os.profile.get("OS32", "image_address"), 16) self.dll_address = int( self.ql.os.profile.get("OS32", "dll_address"), 16) self.entry_point = int( self.ql.os.profile.get("OS32", "entry_point"), 16) self.ql.os.heap_base_address = int( self.ql.os.profile.get("OS32", "heap_address"), 16) self.ql.os.heap_base_size = int( self.ql.os.profile.get("OS32", "heap_size"), 16) self.structure_last_addr = FS_SEGMENT_ADDR elif self.ql.archtype == QL_ARCH.X8664: self.stack_address = int( self.ql.os.profile.get("OS64", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS64", "stack_size"), 16) self.image_address = int( self.ql.os.profile.get("OS64", "image_address"), 16) self.dll_address = int( self.ql.os.profile.get("OS64", "dll_address"), 16) self.entry_point = int( self.ql.os.profile.get("OS64", "entry_point"), 16) self.ql.os.heap_base_address = int( self.ql.os.profile.get("OS64", "heap_address"), 16) self.ql.os.heap_base_size = int( self.ql.os.profile.get("OS64", "heap_size"), 16) self.structure_last_addr = GS_SEGMENT_ADDR self.dlls = {} self.import_symbols = {} self.export_symbols = {} self.import_address_table = {} self.ldr_list = [] self.pe_image_address = 0 self.pe_image_address_size = 0 self.dll_size = 0 self.dll_last_address = self.dll_address # compatible with ql.__enable_bin_patch() self.load_address = 0 self.ql.os.heap = QlMemoryHeap( self.ql, self.ql.os.heap_base_address, self.ql.os.heap_base_address + self.ql.os.heap_base_size) self.ql.os.setupComponents() self.ql.os.entry_point = self.entry_point self.cmdline = bytes(((str(self.ql.os.userprofile)) + "Desktop\\" + (self.ql.targetname) + "\x00"), "utf-8") self.load()
class UefiContext(ABC): def __init__(self, ql): self.ql = ql self.heap = None self.protocols = {} # These members must be initialized before attempting to install a configuration table. self.conf_table_array = [] self.conf_table_array_ptr = 0 self.conf_table_data_ptr = 0 self.conf_table_data_next_ptr = 0 def init_heap(self, base, size): self.heap = QlMemoryHeap(self.ql, base, base + size) def init_stack(self, base, size): self.ql.mem.map(base, size) def install_protocol(self, proto_desc, handle, address=None): guid = proto_desc['guid'] if handle not in self.protocols: self.protocols[handle] = {} if guid in self.protocols[handle]: self.ql.log.warning( f'a protocol with guid {guid} is already installed') if address is None: struct_class = proto_desc['struct'] address = self.heap.alloc(struct_class.sizeof()) instance = init_struct(self.ql, address, proto_desc) instance.saveTo(self.ql, address) self.protocols[handle][guid] = address def install_configuration_table(self, guid, table): guid = guid.lower() confs = self.conf_table_array # find configuration table entry by guid. if found, idx would be set to the entry index # in the array. if not, idx would be set to one past end of array if guid not in confs: confs.append(guid) idx = confs.index(guid) ptr = self.conf_table_array_ptr + (idx * EFI_CONFIGURATION_TABLE.sizeof()) instance = EFI_CONFIGURATION_TABLE() instance.VendorGuid = str_to_guid(guid) instance.VendorTable = table instance.saveTo(self.ql, ptr)
def run(self): self.load_address = self.ql.os.entry_point # for consistency self.ql.mem.map(self.ql.os.entry_point, self.ql.os.code_ram_size, info="[code]") self.ql.mem.write(self.ql.os.entry_point, self.ql.code) heap_address = self.ql.os.entry_point + self.ql.os.code_ram_size heap_size = int(self.ql.os.profile.get("CODE", "heap_size"), 16) self.ql.os.heap = QlMemoryHeap(self.ql, heap_address, heap_address + heap_size) self.ql.arch.regs.arch_sp = heap_address - 0x1000 return
def __init__(self, ql): QlOs.__init__(self, ql) self.ql = ql self.PE_RUN = True self.last_error = 0 # variables used inside hooks self.user_defined_api = {} self.hooks_variables = {} self.syscall_count = {} self.argv = self.ql.argv self.env = self.ql.env self.ql.uc = self.ql.arch.init_uc self.ql.hook_mem_unmapped(ql_x86_windows_hook_mem_error) if self.ql.archtype == QL_ARCH.X8664: self.stack_address = 0x7ffffffde000 self.stack_size = 0x40000 self.HEAP_BASE_ADDR = 0x500000000 self.HEAP_SIZE = 0x5000000 elif self.ql.archtype == QL_ARCH.X86: self.stack_address = 0xfffdd000 self.stack_size = 0x21000 self.HEAP_BASE_ADDR = 0x5000000 self.HEAP_SIZE = 0x5000000 if self.ql.stack_address == 0: self.ql.stack_address = self.stack_address if self.ql.stack_size == 0: self.ql.stack_size = self.stack_size """ Load Heap module FIXME: We need to refactor this """ self.heap = QlMemoryHeap(self.ql, self.HEAP_BASE_ADDR, self.HEAP_BASE_ADDR + self.HEAP_SIZE) self.setupGDT() # hook win api self.ql.hook_code(self.hook_winapi)
def load(self): """ initiate UC needs to be in loader, or else it will kill execve Note: This is Windows, but for the sake of same with others OS """ self.ql.uc = self.ql.arch.init_uc self.ql.hook_mem_unmapped(ql_x86_windows_hook_mem_error) if self.ql.archtype == QL_X8664: self.stack_address = 0x7ffffffde000 self.stack_size = 0x40000 self.ql.code_address = 0x140000000 self.ql.code_size = 10 * 1024 * 1024 self.HEAP_BASE_ADDR = 0x500000000 self.HEAP_SIZE = 0x5000000 elif self.ql.archtype == QL_X86: self.stack_address = 0xfffdd000 self.stack_size = 0x21000 self.ql.code_address = 0x40000 self.ql.code_size = 10 * 1024 * 1024 self.HEAP_BASE_ADDR = 0x5000000 self.HEAP_SIZE = 0x5000000 if self.ql.stack_address == 0: self.ql.stack_address = self.stack_address if self.ql.stack_size == 0: self.ql.stack_size = self.stack_size """ Load Heap module FIXME: We need to refactor this """ self.heap = QlMemoryHeap(self.ql, self.HEAP_BASE_ADDR, self.HEAP_BASE_ADDR + self.HEAP_SIZE) self.setupGDT() # hook win api self.ql.hook_code(self.hook_winapi)
def run(self): self.init_dlls = [b"ntdll.dll", b"kernel32.dll", b"user32.dll"] self.sys_dlls = [b"ntdll.dll", b"kernel32.dll"] self.pe_entry_point = 0 self.sizeOfStackReserve = 0 if not self.ql.code: self.pe = pefile.PE(self.path, fast_load=True) self.is_driver = self.pe.is_driver() if self.is_driver == True: self.init_dlls.append(b"ntoskrnl.exe") self.sys_dlls.append(b"ntoskrnl.exe") if self.ql.archtype == QL_ARCH.X86: self.stack_address = int( self.ql.os.profile.get("OS32", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS32", "stack_size"), 16) self.image_address = int( self.ql.os.profile.get("OS32", "image_address"), 16) self.dll_address = int( self.ql.os.profile.get("OS32", "dll_address"), 16) self.entry_point = int( self.ql.os.profile.get("OS32", "entry_point"), 16) self.ql.os.heap_base_address = int( self.ql.os.profile.get("OS32", "heap_address"), 16) self.ql.os.heap_base_size = int( self.ql.os.profile.get("OS32", "heap_size"), 16) self.structure_last_addr = FS_SEGMENT_ADDR elif self.ql.archtype == QL_ARCH.X8664: self.stack_address = int( self.ql.os.profile.get("OS64", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS64", "stack_size"), 16) self.image_address = int( self.ql.os.profile.get("OS64", "image_address"), 16) self.dll_address = int( self.ql.os.profile.get("OS64", "dll_address"), 16) self.entry_point = int( self.ql.os.profile.get("OS64", "entry_point"), 16) self.ql.os.heap_base_address = int( self.ql.os.profile.get("OS64", "heap_address"), 16) self.ql.os.heap_base_size = int( self.ql.os.profile.get("OS64", "heap_size"), 16) self.structure_last_addr = GS_SEGMENT_ADDR self.dlls = {} self.import_symbols = {} self.export_symbols = {} self.import_address_table = {} self.ldr_list = [] self.pe_image_address = 0 self.pe_image_address_size = 0 self.dll_size = 0 self.dll_last_address = self.dll_address # compatible with ql.__enable_bin_patch() self.load_address = 0 self.ql.os.heap = QlMemoryHeap( self.ql, self.ql.os.heap_base_address, self.ql.os.heap_base_address + self.ql.os.heap_base_size) self.ql.os.setupComponents() self.ql.os.entry_point = self.entry_point cmdline = (str( self.ql.os.userprofile)) + "Desktop\\" + self.ql.targetname self.filepath = bytes(cmdline + "\x00", "utf-8") for arg in self.argv[1:]: if ' ' in arg: cmdline += f' "{arg}"' else: cmdline += f' {arg}' cmdline += "\x00" self.cmdline = bytes(cmdline, "utf-8") self.load()
class QlLoaderPE_UEFI(QlLoader): def __init__(self, ql): super(QlLoaderPE_UEFI, self).__init__(ql) self.ql = ql self.modules = [] self.events = {} self.handle_dict = {} self.notify_list = [] self.next_image_base = 0x10000 @contextmanager def map_memory(self, addr, size): self.ql.mem.map(addr, size) try: yield finally: self.ql.mem.unmap(addr, size) def install_loaded_image_protocol(self, image_base, image_size, entry_point): loaded_image_protocol = EFI_LOADED_IMAGE_PROTOCOL() loaded_image_protocol.Revision = int( self.ql.os.profile["LOADED_IMAGE_PROTOCOL"]["revision"], 16) loaded_image_protocol.ParentHandle = 0 loaded_image_protocol.SystemTable = self.system_table_ptr loaded_image_protocol.DeviceHandle = image_base loaded_image_protocol.FilePath = 0 # This is a handle to a complex path object, skip it for now. loaded_image_protocol.LoadOptionsSize = 0 loaded_image_protocol.LoadOptions = 0 loaded_image_protocol.ImageBase = image_base loaded_image_protocol.ImageSize = image_size loaded_image_protocol.ImageCodeType = EfiLoaderCode loaded_image_protocol.ImageDataType = EfiLoaderData loaded_image_protocol.Unload = 0 loaded_image_protocol_ptr = self.heap.alloc( ctypes.sizeof(EFI_LOADED_IMAGE_PROTOCOL)) self.ql.mem.write(loaded_image_protocol_ptr, convert_struct_to_bytes(loaded_image_protocol)) self.handle_dict[image_base] = { self.loaded_image_protocol_guid: loaded_image_protocol_ptr } self.loaded_image_protocol_modules.append(image_base) def map_and_load(self, path, execute_now=False, callback_ctx=None): ql = self.ql pe = pefile.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)) ql.nprint("[+] 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 ql.nprint("[+] PE entry point at 0x%x" % entry_point) self.install_loaded_image_protocol(IMAGE_BASE, IMAGE_SIZE, entry_point) self.images.append( self.coverage_image( IMAGE_BASE, IMAGE_BASE + pe.NT_HEADERS.OPTIONAL_HEADER.SizeOfImage, path)) if execute_now: self.OOO_EOE_callbacks.append(callback_ctx) # X64 shadow store - The caller is responsible for allocating space for parameters to the callee, and must always allocate sufficient space to store four register parameters ql.reg.rsp -= pointer_size * 4 self.execute_module(path, IMAGE_BASE, entry_point, self.OOO_EOE_ptr) ql.stack_push( entry_point ) # Return from here to the entry point of the loaded module. else: self.modules.append((path, IMAGE_BASE, entry_point, pe)) return True else: IMAGE_BASE += 0x10000 pe.relocate_image(IMAGE_BASE) return False def unload_modules(self): for handle in self.loaded_image_protocol_modules: dic = self.handle_dict[handle] buf = bytes( self.ql.mem.read(dic[self.loaded_image_protocol_guid], ctypes.sizeof(EFI_LOADED_IMAGE_PROTOCOL))) buffer = ctypes.create_string_buffer(buf) loaded_image_protocol = EFI_LOADED_IMAGE_PROTOCOL() ctypes.memmove(ctypes.addressof(loaded_image_protocol), buffer, ctypes.sizeof(loaded_image_protocol)) unload_ptr = struct.unpack("Q", loaded_image_protocol.Unload)[0] if unload_ptr != 0: self.ql.stack_push(self.end_of_execution_ptr) self.ql.reg.rcx = handle self.ql.reg.rip = unload_ptr self.ql.nprint( f'[+] Unloading module 0x{handle:x}, calling 0x{unload_ptr:x}' ) self.loaded_image_protocol_modules.remove(handle) return True return False def execute_module(self, path, image_base, entry_point, EOE_ptr): self.ql.stack_push(EOE_ptr) self.ql.reg.rcx = image_base self.ql.reg.rdx = self.system_table_ptr self.ql.reg.rip = entry_point self.ql.os.entry_point = entry_point self.ql.nprint(f'[+] Running from 0x{entry_point:x} of {path}') def execute_next_module(self): path, image_base, entry_point, pe = self.modules.pop(0) self.execute_module(path, image_base, entry_point, self.end_of_execution_ptr) def run(self): self.loaded_image_protocol_guid = self.ql.os.profile[ "LOADED_IMAGE_PROTOCOL"]["guid"] self.loaded_image_protocol_modules = [] self.tpl = 4 # TPL_APPLICATION self.user_defined_api = self.ql.os.user_defined_api self.user_defined_api_onenter = self.ql.os.user_defined_api_onenter self.user_defined_api_onexit = self.ql.os.user_defined_api_onexit if self.ql.archtype == QL_ARCH.X8664: self.heap_base_address = int( self.ql.os.profile.get("OS64", "heap_address"), 16) self.heap_base_size = int( self.ql.os.profile.get("OS64", "heap_size"), 16) elif self.ql.archtype == QL_ARCH.X86: self.heap_base_address = int( self.ql.os.profile.get("OS32", "heap_address"), 16) self.heap_base_size = int( self.ql.os.profile.get("OS32", "heap_size"), 16) self.heap = QlMemoryHeap(self.ql, self.heap_base_address, self.heap_base_address + self.heap_base_size) self.entry_point = 0 self.load_address = 0 if self.ql.archtype == QL_ARCH.X8664: self.stack_address = int( self.ql.os.profile.get("OS64", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS64", "stack_size"), 16) elif self.ql.archtype == QL_ARCH.X86: self.stack_address = int( self.ql.os.profile.get("OS32", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS32", "stack_size"), 16) # set stack pointer self.ql.nprint("[+] Initiate stack address at 0x%x" % self.stack_address) self.ql.mem.map(self.stack_address, self.stack_size) # Stack should not init at the very bottom. Will cause errors with Dlls sp = self.stack_address + self.stack_size - 0x1000 if self.ql.archtype == QL_ARCH.X86: raise QlErrorArch("[!] Only 64 bit arch supported for now.") elif self.ql.archtype == QL_ARCH.X8664: self.ql.reg.rsp = sp self.ql.reg.rbp = sp else: raise QlErrorArch("[!] Unknown ql.arch") # set SystemTable to image base for now pointer_size = ctypes.sizeof(ctypes.c_void_p) system_table_heap_size = 1024 * 1024 system_table_heap = self.heap.alloc(system_table_heap_size) self.ql.mem.write(system_table_heap, b'\x90' * system_table_heap_size) self.system_table_ptr = system_table_heap system_table = EFI_SYSTEM_TABLE() system_table_heap_ptr = system_table_heap + ctypes.sizeof( EFI_SYSTEM_TABLE) self.runtime_services_ptr = system_table_heap_ptr system_table.RuntimeServices = self.runtime_services_ptr system_table_heap_ptr += ctypes.sizeof(EFI_RUNTIME_SERVICES) system_table_heap_ptr, self.runtime_services = hook_EFI_RUNTIME_SERVICES( self.ql, system_table_heap_ptr) boot_services_ptr = system_table_heap_ptr system_table.BootServices = boot_services_ptr system_table_heap_ptr += ctypes.sizeof(EFI_BOOT_SERVICES) system_table_heap_ptr, boot_services, efi_mm_system_table = hook_EFI_BOOT_SERVICES( self.ql, system_table_heap_ptr) self.efi_configuration_table_ptr = system_table_heap_ptr system_table.ConfigurationTable = self.efi_configuration_table_ptr efi_mm_system_table.MmConfigurationTable = self.efi_configuration_table_ptr system_table.NumberOfTableEntries = 2 system_table_heap_ptr += ctypes.sizeof( EFI_CONFIGURATION_TABLE ) * 100 # We don't expect more then a few entries. efi_configuration_table = EFI_CONFIGURATION_TABLE() # 0x7739f24c, 0x93d7, 0x11d4, {0x9a, 0x3a, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ efi_configuration_table.VendorGuid.Data1 = int( self.ql.os.profile.get("HOB_LIST", "data1"), 16) efi_configuration_table.VendorGuid.Data2 = int( self.ql.os.profile.get("HOB_LIST", "data2"), 16) efi_configuration_table.VendorGuid.Data3 = int( self.ql.os.profile.get("HOB_LIST", "data3"), 16) data4 = ast.literal_eval(self.ql.os.profile.get("HOB_LIST", "data4")) datalist = 0 for data4_list in data4: efi_configuration_table.VendorGuid.Data4[datalist] = data4_list datalist += 1 VendorTable_ptr = system_table_heap_ptr write_int64(self.ql, VendorTable_ptr, int(self.ql.os.profile.get("HOB_LIST", "vendortable"), 16)) system_table_heap_ptr += pointer_size efi_configuration_table.VendorTable = VendorTable_ptr self.efi_configuration_table = [self.ql.os.profile["HOB_LIST"]["guid"]] self.ql.mem.write(self.efi_configuration_table_ptr, convert_struct_to_bytes(efi_configuration_table)) self.mm_system_table_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_MM_SYSTEM_TABLE) self.smm_base2_protocol_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_SMM_BASE2_PROTOCOL) system_table_heap_ptr, smm_base2_protocol, efi_mm_system_table = install_EFI_SMM_BASE2_PROTOCOL( self.ql, system_table_heap_ptr, efi_mm_system_table) self.handle_dict[1] = { self.ql.os.profile.get("EFI_SMM_BASE2_PROTOCOL", "guid"): self.smm_base2_protocol_ptr } self.mm_access_protocol_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_MM_ACCESS_PROTOCOL) system_table_heap_ptr, mm_access_protocol = install_EFI_MM_ACCESS_PROTOCOL( self.ql, system_table_heap_ptr) self.handle_dict[1][self.ql.os.profile.get( "EFI_MM_ACCESS_PROTOCOL", "guid")] = self.mm_access_protocol_ptr self.smm_sw_dispatch2_protocol_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_SMM_SW_DISPATCH2_PROTOCOL) system_table_heap_ptr, smm_sw_dispatch2_protocol = install_EFI_SMM_SW_DISPATCH2_PROTOCOL( self.ql, system_table_heap_ptr) self.handle_dict[1][self.ql.os.profile.get( "EFI_SMM_SW_DISPATCH2_PROTOCOL", "guid")] = self.smm_sw_dispatch2_protocol_ptr self.dxe_services_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_DXE_SERVICES) system_table_heap_ptr, dxe_services = install_EFI_DXE_SERVICES( self.ql, system_table_heap_ptr) efi_configuration_table = EFI_CONFIGURATION_TABLE() efi_configuration_table.VendorGuid.Data1 = int( self.ql.os.profile.get("DXE_SERVICE_TABLE", "data1"), 16) efi_configuration_table.VendorGuid.Data2 = int( self.ql.os.profile.get("DXE_SERVICE_TABLE", "data2"), 16) efi_configuration_table.VendorGuid.Data3 = int( self.ql.os.profile.get("DXE_SERVICE_TABLE", "data3"), 16) data4 = ast.literal_eval( self.ql.os.profile.get("DXE_SERVICE_TABLE", "data4")) datalist = 0 for data4_list in data4: efi_configuration_table.VendorGuid.Data4[datalist] = data4_list datalist += 1 efi_configuration_table.VendorTable = self.dxe_services_ptr self.ql.mem.write( self.efi_configuration_table_ptr + ctypes.sizeof(EFI_CONFIGURATION_TABLE), convert_struct_to_bytes(efi_configuration_table)) self.efi_configuration_table.append( self.ql.os.profile.get("DXE_SERVICE_TABLE", "guid")) self.ql.mem.write(self.runtime_services_ptr, convert_struct_to_bytes(self.runtime_services)) self.ql.mem.write(boot_services_ptr, convert_struct_to_bytes(boot_services)) self.ql.mem.write(self.system_table_ptr, convert_struct_to_bytes(system_table)) self.ql.mem.write(self.mm_system_table_ptr, convert_struct_to_bytes(efi_mm_system_table)) self.ql.mem.write(self.smm_base2_protocol_ptr, convert_struct_to_bytes(smm_base2_protocol)) self.ql.mem.write(self.mm_access_protocol_ptr, convert_struct_to_bytes(mm_access_protocol)) self.ql.mem.write(self.smm_sw_dispatch2_protocol_ptr, convert_struct_to_bytes(smm_sw_dispatch2_protocol)) self.ql.mem.write(self.dxe_services_ptr, convert_struct_to_bytes(dxe_services)) for dependency in self.ql.argv: if not self.map_and_load(dependency): raise QlErrorFileType("Can't map dependency") self.ql.nprint("[+] Done with loading %s" % self.ql.path) #return address self.end_of_execution_ptr = system_table_heap_ptr self.ql.mem.write(self.end_of_execution_ptr, b'\xcc') system_table_heap_ptr += pointer_size self.ql.hook_address(hook_EndOfExecution, self.end_of_execution_ptr) self.OOO_EOE_ptr = system_table_heap_ptr self.ql.hook_address(hook_OutOfOrder_EndOfExecution, self.OOO_EOE_ptr) system_table_heap_ptr += pointer_size self.OOO_EOE_callbacks = [] self.execute_next_module() def restore_runtime_services(self): self.ql.mem.write(self.runtime_services_ptr, convert_struct_to_bytes(self.runtime_services))
def run(self): self.loaded_image_protocol_guid = self.ql.os.profile[ "LOADED_IMAGE_PROTOCOL"]["guid"] self.loaded_image_protocol_modules = [] self.tpl = 4 # TPL_APPLICATION self.user_defined_api = self.ql.os.user_defined_api self.user_defined_api_onenter = self.ql.os.user_defined_api_onenter self.user_defined_api_onexit = self.ql.os.user_defined_api_onexit if self.ql.archtype == QL_ARCH.X8664: self.heap_base_address = int( self.ql.os.profile.get("OS64", "heap_address"), 16) self.heap_base_size = int( self.ql.os.profile.get("OS64", "heap_size"), 16) elif self.ql.archtype == QL_ARCH.X86: self.heap_base_address = int( self.ql.os.profile.get("OS32", "heap_address"), 16) self.heap_base_size = int( self.ql.os.profile.get("OS32", "heap_size"), 16) self.heap = QlMemoryHeap(self.ql, self.heap_base_address, self.heap_base_address + self.heap_base_size) self.entry_point = 0 self.load_address = 0 if self.ql.archtype == QL_ARCH.X8664: self.stack_address = int( self.ql.os.profile.get("OS64", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS64", "stack_size"), 16) elif self.ql.archtype == QL_ARCH.X86: self.stack_address = int( self.ql.os.profile.get("OS32", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS32", "stack_size"), 16) # set stack pointer self.ql.nprint("[+] Initiate stack address at 0x%x" % self.stack_address) self.ql.mem.map(self.stack_address, self.stack_size) # Stack should not init at the very bottom. Will cause errors with Dlls sp = self.stack_address + self.stack_size - 0x1000 if self.ql.archtype == QL_ARCH.X86: raise QlErrorArch("[!] Only 64 bit arch supported for now.") elif self.ql.archtype == QL_ARCH.X8664: self.ql.reg.rsp = sp self.ql.reg.rbp = sp else: raise QlErrorArch("[!] Unknown ql.arch") # set SystemTable to image base for now pointer_size = ctypes.sizeof(ctypes.c_void_p) system_table_heap_size = 1024 * 1024 system_table_heap = self.heap.alloc(system_table_heap_size) self.ql.mem.write(system_table_heap, b'\x90' * system_table_heap_size) self.system_table_ptr = system_table_heap system_table = EFI_SYSTEM_TABLE() system_table_heap_ptr = system_table_heap + ctypes.sizeof( EFI_SYSTEM_TABLE) self.runtime_services_ptr = system_table_heap_ptr system_table.RuntimeServices = self.runtime_services_ptr system_table_heap_ptr += ctypes.sizeof(EFI_RUNTIME_SERVICES) system_table_heap_ptr, self.runtime_services = hook_EFI_RUNTIME_SERVICES( self.ql, system_table_heap_ptr) boot_services_ptr = system_table_heap_ptr system_table.BootServices = boot_services_ptr system_table_heap_ptr += ctypes.sizeof(EFI_BOOT_SERVICES) system_table_heap_ptr, boot_services, efi_mm_system_table = hook_EFI_BOOT_SERVICES( self.ql, system_table_heap_ptr) self.efi_configuration_table_ptr = system_table_heap_ptr system_table.ConfigurationTable = self.efi_configuration_table_ptr efi_mm_system_table.MmConfigurationTable = self.efi_configuration_table_ptr system_table.NumberOfTableEntries = 2 system_table_heap_ptr += ctypes.sizeof( EFI_CONFIGURATION_TABLE ) * 100 # We don't expect more then a few entries. efi_configuration_table = EFI_CONFIGURATION_TABLE() # 0x7739f24c, 0x93d7, 0x11d4, {0x9a, 0x3a, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ efi_configuration_table.VendorGuid.Data1 = int( self.ql.os.profile.get("HOB_LIST", "data1"), 16) efi_configuration_table.VendorGuid.Data2 = int( self.ql.os.profile.get("HOB_LIST", "data2"), 16) efi_configuration_table.VendorGuid.Data3 = int( self.ql.os.profile.get("HOB_LIST", "data3"), 16) data4 = ast.literal_eval(self.ql.os.profile.get("HOB_LIST", "data4")) datalist = 0 for data4_list in data4: efi_configuration_table.VendorGuid.Data4[datalist] = data4_list datalist += 1 VendorTable_ptr = system_table_heap_ptr write_int64(self.ql, VendorTable_ptr, int(self.ql.os.profile.get("HOB_LIST", "vendortable"), 16)) system_table_heap_ptr += pointer_size efi_configuration_table.VendorTable = VendorTable_ptr self.efi_configuration_table = [self.ql.os.profile["HOB_LIST"]["guid"]] self.ql.mem.write(self.efi_configuration_table_ptr, convert_struct_to_bytes(efi_configuration_table)) self.mm_system_table_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_MM_SYSTEM_TABLE) self.smm_base2_protocol_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_SMM_BASE2_PROTOCOL) system_table_heap_ptr, smm_base2_protocol, efi_mm_system_table = install_EFI_SMM_BASE2_PROTOCOL( self.ql, system_table_heap_ptr, efi_mm_system_table) self.handle_dict[1] = { self.ql.os.profile.get("EFI_SMM_BASE2_PROTOCOL", "guid"): self.smm_base2_protocol_ptr } self.mm_access_protocol_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_MM_ACCESS_PROTOCOL) system_table_heap_ptr, mm_access_protocol = install_EFI_MM_ACCESS_PROTOCOL( self.ql, system_table_heap_ptr) self.handle_dict[1][self.ql.os.profile.get( "EFI_MM_ACCESS_PROTOCOL", "guid")] = self.mm_access_protocol_ptr self.smm_sw_dispatch2_protocol_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_SMM_SW_DISPATCH2_PROTOCOL) system_table_heap_ptr, smm_sw_dispatch2_protocol = install_EFI_SMM_SW_DISPATCH2_PROTOCOL( self.ql, system_table_heap_ptr) self.handle_dict[1][self.ql.os.profile.get( "EFI_SMM_SW_DISPATCH2_PROTOCOL", "guid")] = self.smm_sw_dispatch2_protocol_ptr self.dxe_services_ptr = system_table_heap_ptr system_table_heap_ptr += ctypes.sizeof(EFI_DXE_SERVICES) system_table_heap_ptr, dxe_services = install_EFI_DXE_SERVICES( self.ql, system_table_heap_ptr) efi_configuration_table = EFI_CONFIGURATION_TABLE() efi_configuration_table.VendorGuid.Data1 = int( self.ql.os.profile.get("DXE_SERVICE_TABLE", "data1"), 16) efi_configuration_table.VendorGuid.Data2 = int( self.ql.os.profile.get("DXE_SERVICE_TABLE", "data2"), 16) efi_configuration_table.VendorGuid.Data3 = int( self.ql.os.profile.get("DXE_SERVICE_TABLE", "data3"), 16) data4 = ast.literal_eval( self.ql.os.profile.get("DXE_SERVICE_TABLE", "data4")) datalist = 0 for data4_list in data4: efi_configuration_table.VendorGuid.Data4[datalist] = data4_list datalist += 1 efi_configuration_table.VendorTable = self.dxe_services_ptr self.ql.mem.write( self.efi_configuration_table_ptr + ctypes.sizeof(EFI_CONFIGURATION_TABLE), convert_struct_to_bytes(efi_configuration_table)) self.efi_configuration_table.append( self.ql.os.profile.get("DXE_SERVICE_TABLE", "guid")) self.ql.mem.write(self.runtime_services_ptr, convert_struct_to_bytes(self.runtime_services)) self.ql.mem.write(boot_services_ptr, convert_struct_to_bytes(boot_services)) self.ql.mem.write(self.system_table_ptr, convert_struct_to_bytes(system_table)) self.ql.mem.write(self.mm_system_table_ptr, convert_struct_to_bytes(efi_mm_system_table)) self.ql.mem.write(self.smm_base2_protocol_ptr, convert_struct_to_bytes(smm_base2_protocol)) self.ql.mem.write(self.mm_access_protocol_ptr, convert_struct_to_bytes(mm_access_protocol)) self.ql.mem.write(self.smm_sw_dispatch2_protocol_ptr, convert_struct_to_bytes(smm_sw_dispatch2_protocol)) self.ql.mem.write(self.dxe_services_ptr, convert_struct_to_bytes(dxe_services)) for dependency in self.ql.argv: if not self.map_and_load(dependency): raise QlErrorFileType("Can't map dependency") self.ql.nprint("[+] Done with loading %s" % self.ql.path) #return address self.end_of_execution_ptr = system_table_heap_ptr self.ql.mem.write(self.end_of_execution_ptr, b'\xcc') system_table_heap_ptr += pointer_size self.ql.hook_address(hook_EndOfExecution, self.end_of_execution_ptr) self.OOO_EOE_ptr = system_table_heap_ptr self.ql.hook_address(hook_OutOfOrder_EndOfExecution, self.OOO_EOE_ptr) system_table_heap_ptr += pointer_size self.OOO_EOE_callbacks = [] self.execute_next_module()
def run(self): self.profile = self.ql.profile stack_address = int(self.profile.get("OS64", "stack_address"), 16) stack_size = int(self.profile.get("OS64", "stack_size"), 16) vmmap_trap_address = int(self.profile.get("OS64", "vmmap_trap_address"), 16) self.heap_address = int(self.profile.get("OS64", "heap_address"), 16) self.heap_size = int(self.profile.get("OS64", "heap_size"), 16) self.stack_address = stack_address self.stack_size = stack_size if self.ql.code: self.ql.mem.map(self.ql.os.entry_point, self.ql.os.code_ram_size, info="[shellcode_stack]") self.ql.os.entry_point = (self.ql.os.entry_point + 0x200000 - 0x1000) self.ql.mem.write(self.entry_point, self.ql.code) self.ql.reg.arch_sp = self.ql.os.entry_point return self.ql.os.macho_task = MachoTask() self.ql.os.macho_fs = FileSystem(self.ql) self.ql.os.macho_mach_port = MachPort(2187) self.ql.os.macho_port_manager = MachPortManager(self.ql, self.ql.os.macho_mach_port) self.ql.os.macho_host_server = MachHostServer(self.ql) self.ql.os.macho_task_server = MachTaskServer(self.ql) self.envs = env_dict_to_array(self.env) self.apples = self.ql.os.transform_to_relative_path(self.ql.path) self.ql.os.heap = QlMemoryHeap(self.ql, self.heap_address, self.heap_address + self.heap_size) # FIXME: Not working due to overlarge mapping, need to fix it # vm_shared_region_enter(self.ql) map_commpage(self.ql) self.ql.os.thread_management = QlMachoThreadManagement(self.ql) self.ql.os.macho_thread = QlMachoThread(self.ql) self.ql.os.thread_management.cur_thread = self.ql.os.macho_thread self.ql.os.macho_vmmap_end = vmmap_trap_address self.stack_sp = stack_address + stack_size self.macho_file = MachoParser(self.ql, self.ql.path) self.is_driver = (self.macho_file.header.file_type == 0xb) self.loading_file = self.macho_file self.slide = int(self.profile.get("LOADER", "slide"), 16) self.dyld_slide = int(self.profile.get("LOADER", "dyld_slide"), 16) self.string_align = 8 self.ptr_align = 8 self.binary_entry = 0x0 self.proc_entry = 0x0 self.argvs = [self.ql.path] self.argc = 1 self.using_dyld = False self.vm_end_addr = 0x0 self.ql.mem.map(self.stack_address, self.stack_size, info="[stack]") if self.is_driver: self.loadDriver(self.stack_address) self.ql.hook_code(hook_kernel_api) else: self.loadMacho() self.stack_address = (int(self.stack_sp)) self.ql.reg.arch_sp = self.stack_address # self.stack_sp self.init_sp = self.ql.reg.arch_sp self.ql.os.macho_task.min_offset = page_align_end(self.vm_end_addr, PAGE_SIZE)
def enable_low_heap(ql): """ The low heap is guaranteed to allocate memory below the 4GB boundary. """ heap_base = ql.mem.find_free_space(size=0x1024**2, max_addr=0xffffffff) ql.os.low_heap = QlMemoryHeap(ql, heap_base, heap_base + 0x1024**2)
def run(self): self.loaded_image_protocol_guid = self.ql.os.profile[ "LOADED_IMAGE_PROTOCOL"]["guid"] self.loaded_image_protocol_modules = [] self.tpl = 4 # TPL_APPLICATION self.user_defined_api = self.ql.os.user_defined_api if self.ql.archtype == QL_ARCH.X8664: self.heap_base_address = int( self.ql.os.profile.get("OS64", "heap_address"), 16) self.heap_base_size = int( self.ql.os.profile.get("OS64", "heap_size"), 16) elif self.ql.archtype == QL_ARCH.X86: self.heap_base_address = int( self.ql.os.profile.get("OS32", "heap_address"), 16) self.heap_base_size = int( self.ql.os.profile.get("OS32", "heap_size"), 16) self.heap = QlMemoryHeap(self.ql, self.heap_base_address, self.heap_base_address + self.heap_base_size) self.entry_point = 0 self.load_address = 0 if self.ql.archtype == QL_ARCH.X8664: self.stack_address = int( self.ql.os.profile.get("OS64", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS64", "stack_size"), 16) elif self.ql.archtype == QL_ARCH.X86: self.stack_address = int( self.ql.os.profile.get("OS32", "stack_address"), 16) self.stack_size = int(self.ql.os.profile.get("OS32", "stack_size"), 16) # set stack pointer self.ql.nprint("[+] Initiate stack address at 0x%x" % self.stack_address) self.ql.mem.map(self.stack_address, self.stack_size) # Stack should not init at the very bottom. Will cause errors with Dlls sp = self.stack_address + self.stack_size - 0x1000 if self.ql.archtype == QL_ARCH.X86: raise QlErrorArch("[!] Only 64 bit arch supported for now.") elif self.ql.archtype == QL_ARCH.X8664: self.ql.reg.rsp = sp self.ql.reg.rbp = sp else: raise QlErrorArch("[!] Unknown ql.arch") # set SystemTable to image base for now pointer_size = ctypes.sizeof(ctypes.c_void_p) system_table_heap_size = 1024 * 1024 system_table_heap = self.heap.alloc(system_table_heap_size) self.ql.mem.write(system_table_heap, b'\x90' * system_table_heap_size) self.system_table_ptr = system_table_heap system_table = EFI_SYSTEM_TABLE() system_table_heap_ptr = system_table_heap + ctypes.sizeof( EFI_SYSTEM_TABLE) runtime_services_ptr = system_table_heap_ptr system_table.RuntimeServices = runtime_services_ptr system_table_heap_ptr += ctypes.sizeof(EFI_RUNTIME_SERVICES) system_table_heap_ptr, runtime_services = hook_EFI_RUNTIME_SERVICES( self.ql, system_table_heap_ptr) boot_services_ptr = system_table_heap_ptr system_table.BootServices = boot_services_ptr system_table_heap_ptr += ctypes.sizeof(EFI_BOOT_SERVICES) system_table_heap_ptr, boot_services = hook_EFI_BOOT_SERVICES( self.ql, system_table_heap_ptr) self.efi_configuration_table_ptr = system_table_heap_ptr system_table.ConfigurationTable = self.efi_configuration_table_ptr system_table.NumberOfTableEntries = 1 system_table_heap_ptr += ctypes.sizeof( EFI_CONFIGURATION_TABLE ) * 100 # We don't expect more then a few entries. efi_configuration_table = EFI_CONFIGURATION_TABLE() # 0x7739f24c, 0x93d7, 0x11d4, {0x9a, 0x3a, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ efi_configuration_table.VendorGuid.Data1 = int( self.ql.os.profile.get("GUID", "data1"), 16) efi_configuration_table.VendorGuid.Data2 = int( self.ql.os.profile.get("GUID", "data2"), 16) efi_configuration_table.VendorGuid.Data3 = int( self.ql.os.profile.get("GUID", "data3"), 16) data4 = ast.literal_eval(self.ql.os.profile.get("GUID", "data4")) datalist = 0 for data4_list in data4: efi_configuration_table.VendorGuid.Data4[datalist] = data4_list datalist += 1 efi_configuration_table.VendorTable = self.ql.os.profile.getint( "GUID", "vendortable") self.efi_configuration_table = [ self.ql.os.profile["GUID"]["configuration_table"] ] self.ql.mem.write(runtime_services_ptr, convert_struct_to_bytes(runtime_services)) self.ql.mem.write(boot_services_ptr, convert_struct_to_bytes(boot_services)) self.ql.mem.write(self.efi_configuration_table_ptr, convert_struct_to_bytes(efi_configuration_table)) self.ql.mem.write(self.system_table_ptr, convert_struct_to_bytes(system_table)) # Make sure no module will occupy the NULL page with self.map_memory(0, 0x1000): if len(self.ql.argv) > 1: for dependency in self.ql.argv[1:]: if not self.map_and_load(dependency): raise QlErrorFileType("Can't map dependency") # Load main module self.map_and_load(self.ql.path) self.ql.nprint("[+] Done with loading %s" % self.ql.path) #return address self.end_of_execution_ptr = system_table_heap_ptr self.ql.mem.write(self.end_of_execution_ptr, b'\xcc') system_table_heap_ptr += pointer_size self.ql.hook_address(hook_EndOfExecution, self.end_of_execution_ptr) self.notify_ptr = system_table_heap_ptr system_table_heap_ptr += pointer_size self.execute_next_module()
def init_heap(self, base, size): self.heap = QlMemoryHeap(self.ql, base, base + size)
def __init__(self, ql: Qiling): super().__init__(ql) self.ql = ql def __make_fcall_selector( atype: QL_ARCH) -> Callable[[int], QlFunctionCall]: """ [internal] Generate a fcall selection function based on the required calling convention. This is unique to 32-bits Windows, which may need to call both CDECL and STDCALL functions. The 64-bits version, on the other hand, always use MS64. To maintain the same behavior across Windows versions, the fcall selection function for 64-bit is designed to ignore the calling convention identifier and always return a MS64 fcall instance. """ __fcall_objs = { fncc.STDCALL: QlFunctionCall(ql, intel.stdcall(ql.arch)), fncc.CDECL: QlFunctionCall(ql, intel.cdecl(ql.arch)), fncc.MS64: QlFunctionCall(ql, intel.ms64(ql.arch)) } __selector = { QL_ARCH.X86: lambda cc: __fcall_objs[cc], QL_ARCH.X8664: lambda cc: __fcall_objs[fncc.MS64] } return __selector[atype] self.fcall_select = __make_fcall_selector(ql.arch.type) self.fcall = self.fcall_select(fncc.CDECL) self.stats = QlWinStats() ossection = f'OS{self.ql.arch.bits}' heap_base = self.profile.getint(ossection, 'heap_address') heap_size = self.profile.getint(ossection, 'heap_size') self.heap = QlMemoryHeap(self.ql, heap_base, heap_base + heap_size) sysdrv = self.profile.get('PATH', 'systemdrive') windir = self.profile.get('PATH', 'windir') username = self.profile.get('USER', 'username') self.windir = ntpath.join(sysdrv, windir) self.winsys = ntpath.join(sysdrv, windir, 'System32') self.userprofile = ntpath.join(sysdrv, 'Users', username) self.username = username self.PE_RUN = False self.last_error = 0 # variables used inside hooks self.hooks_variables = {} self.syscall_count = {} self.argv = self.ql.argv self.env = self.ql.env self.pid = self.profile.getint('KERNEL', 'pid') self.services = {} self.load() # only after handle manager has been set up we can assign the standard streams self.stdin = self._stdin self.stdout = self._stdout self.stderr = self._stderr
class UefiContext(ABC): def __init__(self, ql: Qiling): self.ql = ql self.heap: QlMemoryHeap self.top_of_stack: int self.protocols = {} self.loaded_image_protocol_modules: MutableSequence[int] = [] self.next_image_base: int # These members must be initialized before attempting to install a configuration table. self.conf_table_data_ptr = 0 self.conf_table_data_next_ptr = 0 self.conftable: UefiConfTable self.end_of_execution_ptr: int # TODO: implement save state def save(self) -> Mapping[str, Any]: return {} # TODO: implement restore state def restore(self, saved_state: Mapping[str, Any]): pass def init_heap(self, base: int, size: int): self.heap = QlMemoryHeap(self.ql, base, base + size) def init_stack(self, base: int, size: int): self.ql.mem.map(base, size, info='[stack]') self.top_of_stack = (base + size - 1) & ~(CPU_STACK_ALIGNMENT - 1) def install_protocol(self, proto_desc: Mapping, handle: int, address: int = None, from_hook: bool = False): guid = proto_desc['guid'] if handle not in self.protocols: self.protocols[handle] = {} if guid in self.protocols[handle]: self.ql.log.warning( f'a protocol with guid {guid} is already installed') if address is None: struct_class = proto_desc['struct'] address = self.heap.alloc(struct_class.sizeof()) instance = utils.init_struct(self.ql, address, proto_desc) instance.saveTo(self.ql, address) self.protocols[handle][guid] = address return self.notify_protocol(handle, guid, address, from_hook) def notify_protocol(self, handle: int, protocol: str, interface: int, from_hook: bool): for (event_id, event_dic) in self.ql.loader.events.items(): if event_dic['Guid'] == protocol: if event_dic['CallbackArgs'] == None: # To support smm notification, we use None for CallbackArgs on SmmRegisterProtocolNotify # and updare it here. guid = utils.str_to_guid(protocol) guid_ptr = self.heap.alloc(guid.sizeof()) guid.saveTo(self.ql, guid_ptr) event_dic['CallbackArgs'] = [guid_ptr, interface, handle] # The event was previously registered by 'RegisterProtocolNotify'. utils.signal_event(self.ql, event_id) return utils.execute_protocol_notifications(self.ql, from_hook)
class UefiContext(ABC): def __init__(self, ql: Qiling): self.ql = ql self.heap = None self.protocols = {} # These members must be initialized before attempting to install a configuration table. self.conf_table_array = [] self.conf_table_array_ptr = 0 self.conf_table_data_ptr = 0 self.conf_table_data_next_ptr = 0 def init_heap(self, base: int, size: int): self.heap = QlMemoryHeap(self.ql, base, base + size) def init_stack(self, base: int, size: int): self.ql.mem.map(base, size) def install_protocol(self, proto_desc: Mapping, handle, address: int = None, from_hook: bool = False): guid = proto_desc['guid'] if handle not in self.protocols: self.protocols[handle] = {} if guid in self.protocols[handle]: self.ql.log.warning(f'a protocol with guid {guid} is already installed') if address is None: struct_class = proto_desc['struct'] address = self.heap.alloc(struct_class.sizeof()) instance = init_struct(self.ql, address, proto_desc) instance.saveTo(self.ql, address) self.protocols[handle][guid] = address return self.notify_protocol(handle, guid, address, from_hook) def notify_protocol(self, handle, protocol, interface, from_hook): for (event_id, event_dic) in self.ql.loader.events.items(): if event_dic['Guid'] == protocol: if event_dic['CallbackArgs'] == None: # To support smm notification, we use None for CallbackArgs on SmmRegisterProtocolNotify # and updare it here. guid = str_to_guid(protocol) guid_ptr = self.heap.alloc(guid.sizeof()) guid.saveTo(self.ql, guid_ptr) event_dic['CallbackArgs'] = [guid_ptr, interface, handle] # The event was previously registered by 'RegisterProtocolNotify'. signal_event(self.ql, event_id) return execute_protocol_notifications(self.ql, from_hook) def install_configuration_table(self, guid: str, table: int): guid = guid.lower() confs = self.conf_table_array # find configuration table entry by guid. if found, idx would be set to the entry index # in the array. if not, idx would be set to one past end of array if guid not in confs: confs.append(guid) idx = confs.index(guid) ptr = self.conf_table_array_ptr + (idx * EFI_CONFIGURATION_TABLE.sizeof()) instance = EFI_CONFIGURATION_TABLE() instance.VendorGuid = str_to_guid(guid) instance.VendorTable = table instance.saveTo(self.ql, ptr)