def init_uc(self): # Calculate required memory pe = pefile.PE(self.sample.path) self.sample.BASE_ADDR = pe.OPTIONAL_HEADER.ImageBase # 0x400000 self.sample.unpacker.BASE_ADDR = self.sample.BASE_ADDR self.sample.virtualmemorysize = self.getVirtualMemorySize() self.STACK_ADDR = 0x0 self.STACK_SIZE = 1024 * 1024 STACK_START = self.STACK_ADDR + self.STACK_SIZE # self.sample.unpacker.secs += [{"name": "stack", "vaddr": self.STACK_ADDR, "vsize": self.STACK_SIZE}] stack_sec_header = IMAGE_SECTION_HEADER( "stack".encode('ascii'), self.STACK_SIZE, self.STACK_ADDR, self.STACK_SIZE, 0, 0, 0, 0, 0, 0, ) self.sample.unpacker.secs.append(SectionHeader(stack_sec_header)) self.HOOK_ADDR = STACK_START + 0x3000 + 0x1000 # Start unicorn emulator with x86-32bit architecture self.uc = Uc(UC_ARCH_X86, UC_MODE_32) if self.sample.unpacker.startaddr is None: self.sample.unpacker.startaddr = self.entrypoint(pe) self.sample.loaded_image = pe.get_memory_mapped_image(ImageBase=self.sample.BASE_ADDR) self.sample.virtualmemorysize = align(self.sample.virtualmemorysize + 0x10000, page_size=4096) # Space possible IAT rebuilding self.sample.unpacker.virtualmemorysize = self.sample.virtualmemorysize self.uc.mem_map(self.sample.BASE_ADDR, self.sample.virtualmemorysize) self.uc.mem_write(self.sample.BASE_ADDR, self.sample.loaded_image) self.setup_processinfo() # Load DLLs self.load_dll(f"{os.path.dirname(unipacker.__file__)}/DLLs/KernelBase.dll", 0x73D00000) self.load_dll(f"{os.path.dirname(unipacker.__file__)}/DLLs/kernel32.dll", 0x755D0000) self.load_dll(f"{os.path.dirname(unipacker.__file__)}/DLLs/ntdll.dll", 0x77400000) # initialize machine registers self.uc.mem_map(self.STACK_ADDR, self.STACK_SIZE) self.uc.reg_write(UC_X86_REG_ESP, self.STACK_ADDR + int(self.STACK_SIZE / 2)) self.uc.reg_write(UC_X86_REG_EBP, self.STACK_ADDR + int(self.STACK_SIZE / 2)) self.uc.mem_write(self.uc.reg_read(UC_X86_REG_ESP) + 0x8, bytes([1])) # -> PEtite Stack Operations? self.uc.reg_write(UC_X86_REG_EAX, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_EBX, self.PEB_BASE) self.uc.reg_write(UC_X86_REG_ECX, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_EDX, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_ESI, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_EDI, self.sample.unpacker.startaddr) self.uc.reg_write(UC_X86_REG_EFLAGS, 0x244) new_pe = PE(self.uc, self.sample.BASE_ADDR) prot_val = lambda x, y: True if x & y != 0 else False for s in new_pe.section_list: self.sample.atn[( s.VirtualAddress + self.sample.BASE_ADDR, s.VirtualAddress + self.sample.BASE_ADDR + s.VirtualSize)] = convert_to_string( s.Name) self.sample.ntp[convert_to_string(s.Name)] = ( prot_val(s.Characteristics, 0x20000000), prot_val(s.Characteristics, 0x40000000), prot_val(s.Characteristics, 0x80000000)) # for s in pe.sections: # atn[(s.VirtualAddress + self.sample.BASE_ADDR, s.VirtualAddress + self.sample.BASE_ADDR + s.Misc_VirtualSize)] = s.Name # ntp[s.Name] = (s.IMAGE_SCN_MEM_EXECUTE, s.IMAGE_SCN_MEM_READ, s.IMAGE_SCN_MEM_WRITE) # init syscall handling and prepare hook memory for return values self.apicall_handler = WinApiCalls(self) self.uc.mem_map(self.HOOK_ADDR, 0x1000) # self.sample.unpacker.secs += [{"name": "hooks", "vaddr": self.HOOK_ADDR, "vsize": 0x1000}] hook_sec_header = IMAGE_SECTION_HEADER( "hooks".encode('ascii'), 0x1000, self.HOOK_ADDR, 0x1000, 0, 0, 0, 0, 0, 0, ) self.sample.unpacker.secs.append(SectionHeader(stack_sec_header)) hexstr = bytes.fromhex('000000008b0425') + struct.pack('<I', self.HOOK_ADDR) + bytes.fromhex( 'c3') # mov eax, [HOOK]; ret -> values of syscall are stored in eax self.uc.mem_write(self.HOOK_ADDR, hexstr) # handle imports # TODO Update when custom loader available for lib in pe.DIRECTORY_ENTRY_IMPORT: descriptor = ImportDescriptor(None, lib.struct.Characteristics, lib.struct.TimeDateStamp, lib.struct.ForwarderChain, lib.struct.Name, lib.struct.FirstThunk) fct_list = [] for i in lib.imports: fct_list.append(i.name) imp = Import(descriptor, lib.dll.decode('ascii'), fct_list) self.sample.original_imports.append(imp) for func in lib.imports: func_name = func.name.decode() if func.name is not None else f"no name: 0x{func.address:02x}" dll_name = lib.dll.decode() if lib.dll is not None else "-- unknown --" self.sample.imports.add(func_name) curr_hook_addr = self.apicall_handler.add_hook(self.uc, func_name, dll_name) self.uc.mem_write(func.address, struct.pack('<I', curr_hook_addr)) hdr = PE(self.uc, self.sample.BASE_ADDR) # Patch DLLs with hook # Hardcoded values used for speed improvement -> Offsets can be calculated with utils.calc_export_offset_of_dll self.apicall_handler.add_hook(self.uc, "VirtualProtect", "KernelBase.dll", 0x73D00000 + 0x1089f0) self.apicall_handler.add_hook(self.uc, "VirtualAlloc", "KernelBase.dll", 0x73D00000 + 0xd4600) self.apicall_handler.add_hook(self.uc, "VirtualFree", "KernelBase.dll", 0x73D00000 + 0xd4ae0) self.apicall_handler.add_hook(self.uc, "LoadLibraryA", "KernelBase.dll", 0x73D00000 + 0xf20d0) self.apicall_handler.add_hook(self.uc, "GetProcAddress", "KernelBase.dll", 0x73D00000 + 0x102870) self.apicall_handler.add_hook(self.uc, "VirtualProtect", "kernel32.dll", 0x755D0000 + 0x16760) self.apicall_handler.add_hook(self.uc, "VirtualAlloc", "kernel32.dll", 0x755D0000 + 0x166a0) self.apicall_handler.add_hook(self.uc, "VirtualFree", "kernel32.dll", 0x755D0000 + 0x16700) self.apicall_handler.add_hook(self.uc, "LoadLibraryA", "kernel32.dll", 0x755D0000 + 0x157b0) self.apicall_handler.add_hook(self.uc, "GetProcAddress", "kernel32.dll", 0x755D0000 + 0x14ee0) # Add hooks self.uc.hook_add(UC_HOOK_CODE, self.hook_code) self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE | UC_HOOK_MEM_FETCH, self.hook_mem_access) self.uc.hook_add(UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_mem_invalid)
def dump_image(self, uc, base_addr, virtualmemorysize, apicall_handler, sample, path="unpacked.exe"): ntp = apicall_handler.ntp dllname_to_functionlist = sample.dllname_to_functionlist if len(sample.allocated_chunks) == 0: total_size = virtualmemorysize else: total_size = sorted(sample.allocated_chunks)[-1][1] - base_addr virtualmemorysize = total_size print(f"Totalsize:{hex(total_size)}, " f"VirtualMemorySize:{hex(virtualmemorysize)}") print_chunks(sample.allocated_chunks) try: hdr = PE(uc, base_addr) except InvalidPEFile as i: print("Invalid PE File... Cannot dump") return old_number_of_sections = hdr.pe_header.NumberOfSections print("Setting unpacked Entry Point") print(f"OEP:{hex(uc.reg_read(UC_X86_REG_EIP) - base_addr)}") hdr.opt_header.AddressOfEntryPoint = uc.reg_read( UC_X86_REG_EIP) - base_addr print("Fixing Imports...") hdr = self.fix_imports(uc, hdr, virtualmemorysize, total_size, dllname_to_functionlist, sample.original_imports) print("Fixing sections") self.fix_sections(hdr, old_number_of_sections, virtualmemorysize) print("Set IAT-Directory to 0 (VA and Size)") hdr.data_directories[12].VirtualAddress = 0 hdr.data_directories[12].Size = 0 print( f"RVA to import table: {hex(hdr.data_directories[1].VirtualAddress)}" ) if (virtualmemorysize - 0xE000) <= hdr.data_directories[ 1].VirtualAddress <= virtualmemorysize or len( sample.allocated_chunks) != 0 or True: print(f"Totalsize:{hex(total_size)}, " f"VirtualMemorySize:{hex(virtualmemorysize)}, " f"Allocated chunks: {sample.allocated_chunks}") # print("Relocating Headers to End of Image") # hdr.dos_header.e_lfanew = virtualmemorysize - 0x10000 # hdr = self.add_section(hdr, '.newhdr', 0x10000, virtualmemorysize-0x10000) # print("Adding new import section") # hdr = self.add_section(hdr, '.nimdata', 0xe000, (virtualmemorysize - 0x10000) + 0x2000) # print("Appending allocated chunks at the end of the image") # hdr = self.chunk_to_image_section_hdr(hdr, base_addr, sample.allocated_chunks) # TODO Fix chunk unmapped space with 0 else: virtualmemorysize -= 0x10000 total_size = virtualmemorysize hdr.sync(uc) print("Fixing SizeOfImage...") hdr.opt_header.SizeOfImage = alignments( total_size, hdr.opt_header.SectionAlignment) print("Fixing Memory Protection of Sections") hdr = self.fix_section_mem_protections(hdr, ntp) hdr.sync(uc) print("Fixing Checksum") hdr = self.fix_checksum(uc, hdr, base_addr, total_size) hdr.sync(uc) dllcharacteristics = hdr.opt_header.DllCharacteristics & 0xFFBF hdr.opt_header.DllCharacteristics = dllcharacteristics # Remove Dynamic Base hdr.sync(uc) print(f"Dumping state to {path}") pe_write(uc, base_addr, total_size, path)