def ql_create_assembler(archtype: QL_ARCH, archendian: QL_ENDIAN, reg_cpsr=None) -> Ks: if archtype == QL_ARCH.X86: ks = Ks(KS_ARCH_X86, KS_MODE_32) elif archtype == QL_ARCH.X8664: ks = Ks(KS_ARCH_X86, KS_MODE_64) elif archtype == QL_ARCH.ARM: mode = KS_MODE_THUMB if reg_cpsr & __reg_cpsr_v[ archendian] else KS_MODE_ARM ks = Ks(KS_ARCH_ARM, mode) # FIXME: should be: mode + __ks_endian[archendian] elif archtype == QL_ARCH.ARM_THUMB: ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB) elif archtype == QL_ARCH.ARM64: ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN) elif archtype == QL_ARCH.MIPS: ks = Ks(KS_ARCH_MIPS, KS_MODE_MIPS32 + __ks_endian[archendian]) elif archtype == QL_ARCH.A8086: ks = Ks(KS_ARCH_X86, KS_MODE_16) elif archtype == QL_ARCH.EVM: raise NotImplementedError('evm') else: raise QlErrorArch(f'{archtype:d}') return ks
def ql_create_disassembler(archtype: QL_ARCH, archendian: QL_ENDIAN, reg_cpsr=None) -> Cs: if archtype == QL_ARCH.X86: md = Cs(CS_ARCH_X86, CS_MODE_32) elif archtype == QL_ARCH.X8664: md = Cs(CS_ARCH_X86, CS_MODE_64) elif archtype == QL_ARCH.ARM: mode = CS_MODE_THUMB if reg_cpsr & __reg_cpsr_v[ archendian] else CS_MODE_ARM md = Cs(CS_ARCH_ARM, mode) # FIXME: should be: mode + __cs_endian[archendian] elif archtype == QL_ARCH.ARM_THUMB: md = Cs(CS_ARCH_ARM, CS_MODE_THUMB) elif archtype == QL_ARCH.ARM64: md = Cs(CS_ARCH_ARM64, CS_MODE_ARM) elif archtype == QL_ARCH.MIPS: md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + __cs_endian[archendian]) elif archtype == QL_ARCH.A8086: md = Cs(CS_ARCH_X86, CS_MODE_16) elif archtype == QL_ARCH.EVM: raise NotImplementedError('evm') else: raise QlErrorArch(f'{archtype:d}') return md
def run(self): ql = self.ql # intel architecture uefi implementation only if ql.arch.type not in (QL_ARCH.X86, QL_ARCH.X8664): raise QlErrorArch("Unsupported architecture") # x86-64 arch only if ql.arch.type != QL_ARCH.X8664: raise QlErrorArch( "Only 64-bit modules are supported at the moment") self.loaded_image_protocol_guid = ql.os.profile[ "LOADED_IMAGE_PROTOCOL"]["Guid"] self.tpl = 4 # TPL_APPLICATION # TODO: assign context to os rather than loader self.dxe_context = self.__init_dxe_environment(ql) self.smm_context = self.__init_smm_environment(ql) self.entry_point = 0 self.load_address = 0 try: for dependency in ql.argv: # TODO: determine whether this is an smm or dxe module is_smm_module = 'Smm' in dependency if is_smm_module: self.context = self.smm_context else: self.context = self.dxe_context self.map_and_load(dependency, self.context) ql.log.info(f"Done loading modules") except QlMemoryMappedError: ql.log.critical("Could not map dependency") self.set_exit_hook(self.dxe_context.end_of_execution_ptr) self.set_exit_hook(self.smm_context.end_of_execution_ptr) self.execute_next_module()
def get_init_uc(self) -> Uc: if self.ql.archendian == QL_ENDIAN.EB: mode = UC_MODE_ARM + UC_MODE_BIG_ENDIAN elif self.ql.archtype == QL_ARCH.ARM_THUMB: mode = UC_MODE_THUMB elif self.ql.archtype == QL_ARCH.ARM: mode = UC_MODE_ARM else: raise QlErrorArch(f'unsupported arch type {self.ql.archtype}') return Uc(UC_ARCH_ARM, mode)
def create_assembler(self) -> Ks: # note: we do not cache the assembler instance; rather we refresh it # each time to make sure thumb mode is taken into account if self.ql.archtype == QL_ARCH.ARM: # FIXME: mode should take endianess into account mode = KS_MODE_THUMB if self.__is_thumb() else KS_MODE_ARM elif self.ql.archtype == QL_ARCH.ARM_THUMB: mode = KS_MODE_THUMB else: raise QlErrorArch(f'unexpected arch type {self.ql.archtype}') return Ks(KS_ARCH_ARM, mode)
def create_disassembler(ql: Qiling): if ql.archtype == QL_ARCH.ARM: # QL_ARM reg_cpsr = ql.reg.cpsr mode = CS_MODE_ARM if ql.archendian == QL_ENDIAN.EB: reg_cpsr_v = 0b100000 # reg_cpsr_v = 0b000000 else: reg_cpsr_v = 0b100000 if reg_cpsr & reg_cpsr_v != 0: mode = CS_MODE_THUMB if ql.archendian == QL_ENDIAN.EB: md = Cs(CS_ARCH_ARM, mode) # md = Cs(CS_ARCH_ARM, mode + CS_MODE_BIG_ENDIAN) else: md = Cs(CS_ARCH_ARM, mode) elif ql.archtype == QL_ARCH.ARM_THUMB: md = Cs(CS_ARCH_ARM, CS_MODE_THUMB) elif ql.archtype == QL_ARCH.X86: # QL_X86 md = Cs(CS_ARCH_X86, CS_MODE_32) elif ql.archtype == QL_ARCH.X8664: # QL_X86_64 md = Cs(CS_ARCH_X86, CS_MODE_64) elif ql.archtype == QL_ARCH.ARM64: # QL_ARM64 md = Cs(CS_ARCH_ARM64, CS_MODE_ARM) elif ql.archtype == QL_ARCH.A8086: # QL_A8086 md = Cs(CS_ARCH_X86, CS_MODE_16) elif ql.archtype == QL_ARCH.MIPS: # QL_MIPS32 if ql.archendian == QL_ENDIAN.EB: md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_BIG_ENDIAN) else: md = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_LITTLE_ENDIAN) else: raise QlErrorArch( "[!] Unknown arch defined in utils.py (debug output mode)") return md
def run(self): # intel architecture uefi implementation only if self.ql.archtype not in (QL_ARCH.X86, QL_ARCH.X8664): raise QlErrorArch("Unsupported architecture") # x86-64 arch only if self.ql.archtype != QL_ARCH.X8664: raise QlErrorArch("Only 64 bit arch is supported at the moment") 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 arch_key = { QL_ARCH.X86: "OS32", QL_ARCH.X8664: "OS64" }[self.ql.archtype] # -------- init BS / RT / DXE data structures and protocols -------- os_profile = self.ql.os.profile[arch_key] self.dxe_context = context.DxeContext(self.ql) # initialize and locate heap heap_base = int(os_profile["heap_address"], 0) heap_size = int(os_profile["heap_size"], 0) self.dxe_context.init_heap(heap_base, heap_size) self.heap_base_address = heap_base self.ql.log.info(f"Located heap at {heap_base:#010x}") # initialize and locate stack stack_base = int(os_profile["stack_address"], 0) stack_size = int(os_profile["stack_size"], 0) self.dxe_context.init_stack(stack_base, stack_size) sp = stack_base + stack_size - CPU_STACK_ALIGNMENT self.ql.log.info(f"Located stack at {sp:#010x}") # TODO: statically allocating 256 KiB for ST, RT, BS, DS and Configuration Tables. # however, this amount of memory is rather arbitrary gST = self.dxe_context.heap.alloc(256 * 1024) st.initialize(self.ql, gST) protocols = ( EfiSmmAccess2Protocol, EfiSmmBase2Protocol, ) for proto in protocols: self.dxe_context.install_protocol(proto.descriptor, 1) # workaround self.ql.os.heap = self.dxe_context.heap # -------- init SMM data structures and protocols -------- smm_profile = self.ql.os.profile['SMRAM'] self.smm_context = context.SmmContext(self.ql) # initialize and locate SMM heap heap_base = int(smm_profile["heap_address"], 0) heap_size = int(smm_profile["heap_size"], 0) self.smm_context.init_heap(heap_base, heap_size) self.ql.log.info(f"Located SMM heap at {heap_base:#010x}") # TODO: statically allocating 256 KiB for SMM ST. # however, this amount of memory is rather arbitrary gSmst = self.smm_context.heap.alloc(256 * 1024) smst.initialize(self.ql, gSmst) self.in_smm = False protocols = (EfiSmmCpuProtocol, EfiSmmSwDispatch2Protocol) for proto in protocols: self.smm_context.install_protocol(proto.descriptor, 1) # map mmio ranges # TODO: move to somehwere more appropriate (+ hook accesses?) mmio_map = self.ql.os.profile["MMIO"] self.ql.mem.map(int(mmio_map['sbreg_base'], 0), int(mmio_map['sbreg_size'], 0)) # set stack and frame pointers self.ql.reg.rsp = sp self.ql.reg.rbp = sp self.entry_point = 0 self.load_address = 0 self.next_image_base = int(os_profile["image_address"], 0) try: for dependency in self.ql.argv: self.map_and_load(dependency) except QlMemoryMappedError: self.ql.log.critical("Couldn't map dependency") self.ql.log.info(f"Done with loading {self.ql.path}") # set up an end-of-execution hook to regain control when module is done # executing (i.e. when the entry point function returns). that should be # set on a non-executable address, so SystemTable's address was picked self.end_of_execution_ptr = gST self.ql.hook_address(hook_EndOfExecution, self.end_of_execution_ptr) self.execute_next_module()
def load(self, pe: Optional[pefile.PE]): # set stack pointer self.ql.log.info("Initiate stack address at 0x%x " % self.stack_address) self.ql.mem.map(self.stack_address, self.stack_size, info="[stack]") if pe is not None: image_name = os.path.basename(self.path) image_base = pe.OPTIONAL_HEADER.ImageBase image_size = self.ql.mem.align_up(pe.OPTIONAL_HEADER.SizeOfImage) # if default base address is taken, use the one specified in profile if not self.ql.mem.is_available(image_base, image_size): image_base = self.image_address pe.relocate_image(image_base) self.entry_point = image_base + pe.OPTIONAL_HEADER.AddressOfEntryPoint self.pe_image_address = image_base self.pe_image_size = image_size self.ql.log.info(f'Loading {self.path} to {image_base:#x}') self.ql.log.info(f'PE entry point at {self.entry_point:#x}') self.ql.mem.map(image_base, image_size, info=f'[{image_name}]') self.images.append( Image(image_base, image_base + pe.NT_HEADERS.OPTIONAL_HEADER.SizeOfImage, os.path.abspath(self.path))) if self.is_driver: self.init_driver_object() self.init_registry_path() self.init_eprocess() self.init_ki_user_shared_data() # set IRQ Level in CR8 to PASSIVE_LEVEL self.ql.arch.regs.write(UC_X86_REG_CR8, 0) # setup CR4, enabling: DE, PSE, PAE, MCE, PGE, OSFXSR and OSXMMEXCPT. # some drivers may check this at initialized self.ql.arch.regs.write(UC_X86_REG_CR4, 0b0000011011111000) else: # initialize thread information block self.init_teb() self.init_peb() self.init_ldr_data() self.init_exports(pe) # add image to ldr table self.add_ldr_data_table_entry(image_name) pe.parse_data_directories() # done manipulating pe file; write its contents into memory self.ql.mem.write(image_base, bytes(pe.get_memory_mapped_image())) if self.is_driver: # security cookie can be written only after image has been loaded to memory self.init_security_cookie(pe, image_base) # Stack should not init at the very bottom. Will cause errors with Dlls top_of_stack = self.stack_address + self.stack_size - 0x1000 if self.ql.arch.type == QL_ARCH.X86: bp_reg = 'ebp' sp_reg = 'esp' elif self.ql.arch.type == QL_ARCH.X8664: bp_reg = 'rbp' sp_reg = 'rsp' else: raise QlErrorArch(f'unexpected arch type: {self.ql.arch.type}') # we are about to load some dlls and call their DllMain functions. # the stack should be set first self.ql.arch.regs.write(bp_reg, top_of_stack) self.ql.arch.regs.write(sp_reg, top_of_stack) # load system dlls for each in self.sys_dlls: super().load_dll(each, self.is_driver) # parse directory entry import self.ql.log.debug(f'Init imports for {self.path}') super().init_imports(pe, self.is_driver) self.ql.log.debug(f'Done loading {self.path}') if pe.is_driver(): args = ((POINTER, self.driver_object_address), (POINTER, self.regitry_path_address)) self.ql.log.debug('Setting up call frame for DriverEntry:') self.ql.log.debug( f' PDRIVER_OBJECT DriverObject : {args[0][1]:#010x}') self.ql.log.debug( f' PUNICODE_STRING RegistryPath : {args[1][1]:#010x}') # We know that a driver will return, so if the user did not configure stop # options, write a sentinel return value ret = None if self.ql.stop_options else self.ql.stack_write( 0, 0xdeadc0de) # set up call frame for DriverEntry self.ql.os.fcall.call_native(self.entry_point, args, ret) elif pe.is_dll(): args = ( (POINTER, image_base), (DWORD, 1), # DLL_PROCESS_ATTACH (POINTER, 0)) self.ql.log.debug('Setting up call frame for DllMain:') self.ql.log.debug( f' HINSTANCE hinstDLL : {args[0][1]:#010x}') self.ql.log.debug( f' DWORD fdwReason : {args[1][1]:#010x}') self.ql.log.debug( f' LPVOID lpReserved : {args[2][1]:#010x}') # set up call frame for DllMain self.ql.os.fcall.call_native(self.entry_point, args, None) elif pe is None: self.filepath = b"" self.ql.mem.map(self.entry_point, self.ql.os.code_ram_size, info="[shellcode]") self.init_teb() self.init_peb() self.init_ldr_data() # write shellcode to memory self.ql.mem.write(self.entry_point, self.ql.code) top_of_stack = self.stack_address + self.stack_size if self.ql.arch.type == QL_ARCH.X86: bp_reg = 'ebp' sp_reg = 'esp' elif self.ql.arch.type == QL_ARCH.X8664: bp_reg = 'rbp' sp_reg = 'rsp' else: raise QlErrorArch(f'unexpected arch type: {self.ql.arch.type}') self.ql.arch.regs.write(bp_reg, top_of_stack) self.ql.arch.regs.write(sp_reg, top_of_stack) # load dlls for each in self.init_dlls: super().load_dll(each, self.is_driver) # move entry_point to ql.os self.ql.os.entry_point = self.entry_point self.init_sp = self.ql.arch.regs.arch_sp