def load_test_case(self, test_case: TestCase) -> None: self.test_case = test_case # create and read a binary with open(test_case.bin_path, 'rb') as f: code = f.read() self.code_end = self.code_start + len(code) # initialize emulator in x86-64 mode emulator = Uc(uni.UC_ARCH_X86, uni.UC_MODE_64) try: # allocate memory emulator.mem_map(self.code_start, self.CODE_SIZE) emulator.mem_map(self.sandbox_base - self.WORKING_MEMORY_SIZE // 2, self.WORKING_MEMORY_SIZE) # write machine code to be emulated to memory emulator.mem_write(self.code_start, code) # set up callbacks emulator.hook_add(uni.UC_HOOK_MEM_READ | uni.UC_HOOK_MEM_WRITE, self.trace_mem_access, self) emulator.hook_add(uni.UC_HOOK_CODE, self.instruction_hook, self) self.emulator = emulator except UcError as e: LOGGER.error("[X86UnicornModel:load_test_case] %s" % e)
def runX86(code): output = [] def hook_interrupt(uc, intno, user_data): from unicorn.x86_const import UC_X86_REG_EAX, UC_X86_REG_EBX from unicorn.x86_const import UC_X86_REG_ECX, UC_X86_REG_EDX if intno != 0x80: uc.emu_stop() raise Error("Unknwon Interrupt") eax = uc.reg_read(UC_X86_REG_EAX) if eax == 4: # SYS_WRITE ebx = uc.reg_read(UC_X86_REG_EBX) ecx = uc.reg_read(UC_X86_REG_ECX) edx = uc.reg_read(UC_X86_REG_EDX) try: buf = uc.mem_read(ecx, edx) if ebx == 1: user_data.extend(map(chr, buf)) uc.reg_write(UC_X86_REG_EAX, edx) except UcError: uc.emu_stop() raise Error("Segmentation fault") elif eax == 1: uc.emu_stop() else: raise Error("Unknown system call") mu = Uc(UC_ARCH_X86, UC_MODE_32) mu.mem_map(ADDRESS, 0x1000) mu.mem_write(ADDRESS, code) mu.hook_add(UC_HOOK_INTR, hook_interrupt, output) mu.emu_start(ADDRESS, ADDRESS + len(code)) return ''.join(output)
def runmips(code): output = [] def hook_interrupt(uc, intno, user_data): from unicorn.mips_const import UC_MIPS_REG_2, UC_MIPS_REG_4 from unicorn.mips_const import UC_MIPS_REG_5, UC_MIPS_REG_6 if intno != 17: uc.emu_stop() raise Exception("Unknwon Interrupt") v0 = uc.reg_read(UC_MIPS_REG_2) if v0 == 4004: # SYS_WRITE a0 = uc.reg_read(UC_MIPS_REG_4) a1 = uc.reg_read(UC_MIPS_REG_5) a2 = uc.reg_read(UC_MIPS_REG_6) try: buf = uc.mem_read(a1, a2) if a0 == 1: user_data.extend(map(chr, buf)) uc.reg_write(UC_MIPS_REG_2, a2) except UcError: uc.emu_stop() raise Error("Segmentation fault") elif v0 == 4001: uc.emu_stop() else: uc.emu_stop() raise Error("Unknown system call") mu = Uc(UC_ARCH_MIPS, UC_MODE_MIPS32 + UC_MODE_LITTLE_ENDIAN) mu.mem_map(ADDRESS, 0x1000) mu.mem_write(ADDRESS, code) mu.hook_add(UC_HOOK_INTR, hook_interrupt, output) mu.emu_start(ADDRESS, ADDRESS + len(code)) return ''.join(output)
def run(): print("Start emulate ARM...") try: # 创建虚拟机 mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB) # 分配内存 ADDRESS = 0x10000 mu.mem_map(ADDRESS, 0x1000) mu.mem_write(ADDRESS, ARM_CODE) # 写寄存器 mu.reg_write(UC_ARM_REG_R0, 0x1234) mu.reg_write(UC_ARM_REG_R2, 0x6789) mu.reg_write(UC_ARM_REG_R3, 0x3333) # Hook 代码 mu.hook_add(UC_HOOK_CODE, hook_code, None, ADDRESS, ADDRESS + 0x1000) # 启动虚拟机 mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE)) # 获取结果 r0 = mu.reg_read(UC_ARM_REG_R0) r1 = mu.reg_read(UC_ARM_REG_R1) print(f">>> R0 = 0x{r0:x}") print(f">>> R1 = 0x{r1:x}") except UcError as e: print(f"Emulate error: {e}")
def test_multiprocess(self): record = [] def hook(uc, address, size, userdata): record.append(address) uc1 = Uc(UC_ARCH_X86, UC_MODE_32) uc1.hook_add(UC_HOOK_CODE, hook) uc1.mem_map(0, 0x2000) uc2 = Uc(UC_ARCH_X86, UC_MODE_32) uc2.hook_add(UC_HOOK_CODE, hook) uc2.mem_map(0, 0x2000) uc1.emu_start(0x1000, 0x1006) self.assertListEqual(record, [0x1000, 0x1002, 0x1004]) uc2.emu_start(0x1000, 0x1006) self.assertListEqual(record, [0x1000, 0x1002, 0x1004, 0x1000, 0x1002, 0x1004]) uc1.reg_write(UC_X86_REG_EAX, 5) context1 = uc1.context_save() uc2.reg_write(UC_X86_REG_EAX, 6) context2 = uc2.context_save() self.assertEqual(uc1.reg_read(UC_X86_REG_EAX), 5) self.assertEqual(uc2.reg_read(UC_X86_REG_EAX), 6) uc2.context_restore(context1) uc1.context_restore(context2) self.assertEqual(uc1.reg_read(UC_X86_REG_EAX), 6) self.assertEqual(uc2.reg_read(UC_X86_REG_EAX), 5)
def runX86_64(code): from unicorn.x86_const import UC_X86_INS_SYSCALL output = [] def hook_syscall(uc, user_data): from unicorn.x86_const import UC_X86_REG_RAX, UC_X86_REG_RDI from unicorn.x86_const import UC_X86_REG_RSI, UC_X86_REG_RDX rax = uc.reg_read(UC_X86_REG_RAX) if rax == 1: rdi = uc.reg_read(UC_X86_REG_RDI) rsi = uc.reg_read(UC_X86_REG_RSI) rdx = uc.reg_read(UC_X86_REG_RDX) try: buf = uc.mem_read(rsi, rdx) if rdi == 1: user_data.extend(map(chr, buf)) uc.reg_write(UC_X86_REG_RAX, rdx) except UcError: uc.emu_stop() raise Error("Segmentation fault") elif rax == 60: uc.emu_stop() else: raise Error("Unknown system call") mu = Uc(UC_ARCH_X86, UC_MODE_64) mu.mem_map(ADDRESS, 0x1000) mu.mem_write(ADDRESS, code) mu.hook_add(UC_HOOK_INSN, hook_syscall, output, 1, 0, UC_X86_INS_SYSCALL) mu.emu_start(ADDRESS, ADDRESS + len(code)) return ''.join(output)
def runarm64(code): output = [] def hook_interrupt(uc, intno, user_data): from unicorn.arm64_const import UC_ARM64_REG_X0, UC_ARM64_REG_X1 from unicorn.arm64_const import UC_ARM64_REG_X2, UC_ARM64_REG_X8 if intno != 2: uc.emu_stop() raise Exception("Unknwon Interrupt") x8 = uc.reg_read(UC_ARM64_REG_X8) if x8 == 64: # SYS_WRITE x0 = uc.reg_read(UC_ARM64_REG_X0) x1 = uc.reg_read(UC_ARM64_REG_X1) x2 = uc.reg_read(UC_ARM64_REG_X2) try: buf = uc.mem_read(x1, x2) if x0 == 1: user_data.extend(map(chr, buf)) uc.reg_write(UC_ARM64_REG_X0, x2) except UcError: uc.emu_stop() raise Error("Segmentation fault") elif x8 == 93: uc.emu_stop() else: uc.emu_stop() raise Error("Unknown system call") mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM) mu.mem_map(ADDRESS, 0x1000) mu.mem_write(ADDRESS, code) mu.hook_add(UC_HOOK_INTR, hook_interrupt, output) mu.emu_start(ADDRESS, ADDRESS + len(code)) return ''.join(output)
def test_hooks(self): record = [] def hook(uc, address, size, userdata): record.append(address) uc = Uc(UC_ARCH_X86, UC_MODE_32) uc.mem_map(0, 0x2000) uc.hook_add(UC_HOOK_CODE, hook) uc.emu_start(0x1000, 0x1006) self.assertListEqual(record, [0x1000, 0x1002, 0x1004])
def emulate(data, mode): try: stack = 0x90000 uc = Uc(UC_ARCH_X86, mode) uc.mem_map(stack, 0x1000 * 10) uc.mem_map(shellcode_code_base, 0x100000) uc.mem_write(shellcode_code_base, data) uc.reg_write(UC_X86_REG_ESP, stack + 0x1000) uc.hook_add(UC_HOOK_CODE, hook_instr, user_data=mode) uc.emu_start(shellcode_code_base, shellcode_code_base + len(data), 0, shellcode_limit) except unicorn.UcError: pass
def test_setip_after_emustop(self): record = [] uc = Uc(UC_ARCH_X86, UC_MODE_32) def hook(uc, address, size, userdata): record.append(address) def hook_stop(uc, address, size, userdata): if address == 0x1002: uc.emu_stop() uc.reg_write(UC_X86_REG_EIP, 0x1006) uc.mem_map(0, 0x2000) uc.hook_add(UC_HOOK_CODE, hook) uc.hook_add(UC_HOOK_CODE, hook_stop) uc.emu_start(0x1000, 0x1008) self.assertListEqual(record, [0x1000, 0x1002, 0x1006])
def test_exception_in_hook(self): uc = Uc(UC_ARCH_X86, UC_MODE_64) uc.mem_map(CODE_ADDR, 0x1000) uc.mem_write(CODE_ADDR, CODE) counter = HookCounter() uc.hook_add(UC_HOOK_CODE, counter.good_code_hook, begin=CODE_ADDR, end=CODE_ADDR + len(CODE)) uc.hook_add(UC_HOOK_CODE, counter.bad_code_hook, begin=CODE_ADDR, end=CODE_ADDR + len(CODE)) self.assertRaises(ValueError, uc.emu_start, CODE_ADDR, CODE_ADDR + len(CODE)) # Make sure hooks calls finish before raising (hook_calls == 2) self.assertEqual(counter.hook_calls, 2)
def test_x86_64_syscall(self): print("Emulate x86_64 code with 'syscall' instruction") ADDRESS = 0x1000000 X86_CODE64_SYSCALL = b"\x0f\x05" # SYSCALL # Initialize emulator in X86-64bit mode mu = Uc(UC_ARCH_X86, UC_MODE_64) # map 2MB memory for this emulation mu.mem_map(ADDRESS, 2 * 1024 * 1024) # write machine code to be emulated to memory mu.mem_write(ADDRESS, X86_CODE64_SYSCALL) def hook_syscall(mu, user_data): rax = mu.reg_read(UC_X86_REG_RAX) if rax == 0x100: mu.reg_write(UC_X86_REG_RAX, 0x200) else: print("ERROR: was not expecting rax=%d in syscall" % rax) # hook interrupts for syscall mu.hook_add(UC_HOOK_INSN, hook_syscall, None, 1, 0, UC_X86_INS_SYSCALL) # syscall handler is expecting rax=0x100 mu.reg_write(UC_X86_REG_RAX, 0x100) try: # emulate machine code in infinite time mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE64_SYSCALL)) except UcError as e: print("ERROR: %s" % e) # now print out some registers print(">>> Emulation done. Below is the CPU context") rax = mu.reg_read(UC_X86_REG_RAX) print(">>> RAX = 0x%x" % rax)
def setup_hooks(mu: Uc): # tracing all basic blocks # mu.hook_add(UC_HOOK_BLOCK, hook_block) # hook select instructions mu.hook_add(UC_HOOK_CODE, hook_instr) # trace all memory reads # mu.hook_add(UC_HOOK_MEM_READ, hook_mem_read) # mu.hook_add(UC_HOOK_MEM_READ_AFTER, hook_mem_read_after) # mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write) # hook unmapped memory operations mu.hook_add(UC_HOOK_MEM_READ_UNMAPPED, hook_mem_read_unmapped) mu.hook_add(UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_write_unmapped) mu.hook_add(UC_HOOK_MEM_FETCH_UNMAPPED, hook_mem_fetch_unmapped)
stack_size = 0x10000 * 3 stack_top = stack_base + stack_size - 0x4 mu.mem_map(stack_base, stack_size) mu.reg_write(UC_ARM_REG_SP, stack_top) # 分配数据内存 data_base = 0xF0000 data_size = 0x10000 * 3 mu.mem_map(data_base, data_size) mu.mem_write(data_base, a1) mu.reg_write(UC_ARM_REG_R0, data_base) # 修复 Got 表 mu.mem_write(image_base + 0x1EDB0, b"\xD9\x98\x00\x00") # 设置 Hook mu.hook_add(UC_HOOK_CODE, hook_code, None) mu.hook_add(UC_HOOK_MEM_UNMAPPED, hook_memory, None) # 设置需要 Run 的函数地址 func_start = image_base + 0x9B68 + 0x1 func_end = image_base + 0x9C2C try: mu.emu_start(func_start, func_end) r2 = mu.reg_read(UC_ARM_REG_R2) result = mu.mem_read(r2, 16) print(result.hex()) except UcError as e: print(f"UC run error {e}")
# originalBB if access == UC_MEM_WRITE and size == 4 and value == 1: print("WRITE: address:0x{0:016x} is decryptStatus".format( address)) if address in decryptStatus: decryptStatus.remove(address) uc.emu_stop() def hook_rwdata_backup(uc: Uc, access, address, size, value, data): if data_section_start <= address < data_section_end and access == UC_MEM_WRITE: data_backup.append((address, uc.mem_read(address, size))) # 添加内存读写hook emu.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_bss_access) # 添加内存恢复hook emu.hook_add(UC_HOOK_MEM_WRITE, hook_rwdata_backup) cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB) ks_thumb = Ks(KS_ARCH_ARM, KS_MODE_THUMB) ks_arm = Ks(KS_ARCH_ARM, KS_MODE_ARM) # 模拟执行各个函数 j_result = json.load(open('./result.json')) for entryBB, decryptBB, originalBB, in j_result: emu.reg_write(UC_ARM_REG_SP, STACK_ADDR + STACK_SIZE) print("Emulating entryBB") stage = 0 decryptStatus = set() data_backup = []
class emu: """ Loads ELF file to unicorn, sets watchpoints and stdin """ def __init__(self, fname, stdin, watchpoints=[], drcov=True, emulator_base=None, fw_entry_symbol="cont"): self.stdin = stdin self.exception = "" self.uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) self.fname = fname self.fd = open(fname, "rb") self.elf = elffile.ELFFile(self.fd) self.symbols = {} self.symbols_reverse = {} for i in range(self.elf.num_sections()): sec = self.elf.get_section(i) if sec.name == ".symtab": for sym in sec.iter_symbols(): self.symbols[sym.name] = sym.entry["st_value"] self.symbols_reverse[sym.entry["st_value"]] = sym.name self.results = [] self.result_id = 0 self.coverage_pc = set() self.coverage_bb = set() self.read = set() self.write = set() self.trace_initialized = False self.coverage_activity = {} self.read_activity = {} self.write_activity = {} self.stdout = "" self.stderr = "" self.emulator_base_start = None self.emulator_base_stop = None if fw_entry_symbol in self.symbols: self.fw_entry = self.symbols[fw_entry_symbol] # ignore everything until that symbol else: self.fw_entry = None #loading prog headrs self.state = [] self.segments = [] for i in range(self.elf.num_sections()): section = self.elf.get_section(i) if section.header["sh_flags"] & SH_FLAGS.SHF_ALLOC != 0: addr = section.header["sh_addr"] size = section.header["sh_size"] name = section.name #NOBITS sections contains no data in file #Will be initialized with zero if section.header["sh_type"] == "SHT_NOBITS": data = b"\x00" * size else: data = section.data() print("Found %s @ 0x%x - 0x%x (%d bytes)" % (name, addr, addr+len(data), len(data))) if emulator_base == addr: self.emulator_base_start = emulator_base self.emulator_base_stop = emulator_base + size self.segments += [(name, addr, size)] self.state += [(addr, size, data)] #compute memory map from sections self.maps = [] if self.emulator_base_start is not None: self.maps += [(self.emulator_base_start, self.emulator_base_stop)] self.segments = sorted(self.segments, key=lambda x:x[0]) for name, addr, size in self.segments: size += addr & 0x3ff addr = addr & (~0x3ff) altered = False for i in range(len(self.maps)): map_addr, map_size = self.maps[i] offset = addr - map_addr if addr >= map_addr and addr <= map_addr + map_size: self.maps[i] = (map_addr, self.pageresize(max(map_size, offset+size))) altered = True if not altered: self.maps += [(addr, self.pageresize(size))] for addr, size in self.maps: print("Mapping 0x%x - 0x%x (%d bytes)" % (addr, addr+size, size)) self.uc.mem_map(addr, size, UC_PROT_ALL) for addr,size,data in self.state: print("Loading 0x%x - 0x%x (%d bytes)" % (addr, addr+len(data), len(data))) self.uc.mem_write(addr, data) #stack stack = 0xdead0000 stack_size = 16384 print("Mapping Stack 0x%x - 0x%x (%d bytes)" % (stack, stack+stack_size, stack_size)) self.uc.mem_map(stack, stack_size, UC_PROT_ALL) self.uc.reg_write(arm_const.UC_ARM_REG_SP, stack + stack_size) #syscalls self.uc.hook_add(UC_HOOK_INTR, self.hook_intr, self) #tracing self.watchpoints = watchpoints self.uc.hook_add(UC_HOOK_CODE, self.hook_code, self) self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_mem_access, self) #prepare drcov file self.drcov = drcov if drcov: self.uc.hook_add(UC_HOOK_BLOCK, self.hook_bb, self) def pageresize(self, s, pagesize=1024): if s % pagesize == 0: return s return (int(s / pagesize) + 1) * pagesize """ We need to emulate read and write for emulation """ @staticmethod def hook_intr(uc, size, self): #print hex(uc.reg_read(arm_const.UC_ARM_REG_PC)) pc = uc.reg_read(arm_const.UC_ARM_REG_PC) for name in ["read","write"]: if self.symbols[name] <= pc and self.symbols[name] + 8 >= pc: #print name if name == "read": fd = uc.reg_read(arm_const.UC_ARM_REG_R0) target = uc.reg_read(arm_const.UC_ARM_REG_R1) size = uc.reg_read(arm_const.UC_ARM_REG_R2) data = self.stdin[:size] self.stdin = self.stdin[size:] uc.mem_write(target, data) self.uc.reg_write(arm_const.UC_ARM_REG_R0, len(data)) elif name == "write": fd = uc.reg_read(arm_const.UC_ARM_REG_R0) target = uc.reg_read(arm_const.UC_ARM_REG_R1) size = uc.reg_read(arm_const.UC_ARM_REG_R2) data = uc.mem_read(target, size) if fd == 1: self.stdout += data.decode("utf-8") sys.stdout.write(data.decode("utf-8")) else: self.stderr += data.decode("utf-8") sys.stderr.write(data.decode("utf-8")) else: print("unknown intr") """ Implement memory and code watchpoints """ @staticmethod def hook_bb(uc, address, size, self): if self.emulator_base_start is not None: if address >= self.emulator_base_start and address < self.emulator_base_stop: return #print(hex(address)) self.coverage_bb.add((address, size)) @staticmethod def hook_code(uc, address, size, self): # Unicorn will for some reason giv old register values after a crash # The last update seems to be on the entry of the bb self.regs = {} self.regs["r0"] = self.uc.reg_read(arm_const.UC_ARM_REG_R0) self.regs["r1"] = self.uc.reg_read(arm_const.UC_ARM_REG_R1) self.regs["r2"] = self.uc.reg_read(arm_const.UC_ARM_REG_R2) self.regs["r3"] = self.uc.reg_read(arm_const.UC_ARM_REG_R3) self.regs["r4"] = self.uc.reg_read(arm_const.UC_ARM_REG_R4) self.regs["r5"] = self.uc.reg_read(arm_const.UC_ARM_REG_R5) self.regs["r6"] = self.uc.reg_read(arm_const.UC_ARM_REG_R6) self.regs["r7"] = self.uc.reg_read(arm_const.UC_ARM_REG_R7) self.regs["r8"] = self.uc.reg_read(arm_const.UC_ARM_REG_R8) self.regs["r9"] = self.uc.reg_read(arm_const.UC_ARM_REG_R9) self.regs["r10"] = self.uc.reg_read(arm_const.UC_ARM_REG_R10) self.regs["r11"] = self.uc.reg_read(arm_const.UC_ARM_REG_R11) self.regs["r12"] = self.uc.reg_read(arm_const.UC_ARM_REG_R12) self.regs["sp"] = self.uc.reg_read(arm_const.UC_ARM_REG_R13) self.regs["lr"] = self.uc.reg_read(arm_const.UC_ARM_REG_R14) self.regs["pc"] = self.uc.reg_read(arm_const.UC_ARM_REG_R15) if self.fw_entry is not None and address & 0xfffffffe == self.fw_entry & 0xfffffffe: self.trace_init_state() if self.fw_entry is None and not self.trace_initialized: self.trace_init_state() if self.emulator_base_start is not None: if address >= self.emulator_base_start and address < self.emulator_base_stop: return self.coverage_pc.add(address) if address in self.coverage_activity: self.coverage_activity[address] += 1 else: self.coverage_activity[address] = 1 if address in self.watchpoints or address^1 in self.watchpoints: self.trace_state_change("Execute") @staticmethod def hook_mem_access(uc, access, address, size, value, self): pc = self.uc.reg_read(arm_const.UC_ARM_REG_R15) if self.emulator_base_start is not None: if pc >= self.emulator_base_start and pc < self.emulator_base_stop: return if access == UC_MEM_WRITE: self.write.add((pc, address, value)) if address in self.write_activity: self.write_activity[address] += 1 else: self.write_activity[address] = 1 else: self.read.add((pc, address)) if address in self.read_activity: self.read_activity[address] += 1 else: self.read_activity[address] = 1 if address in self.watchpoints: if access == UC_MEM_WRITE: self.trace_state_change("Write 0x%x" % address) else: self.trace_state_change("Read 0x%x" % address) """ For each tracepoint that was hit Dump Registers Do Memory Dump """ def trace_init_state(self): self.state = [] self.trace_initialized = True for name, addr, size in self.segments: data = self.uc.mem_read(addr, size) #data = list(map(chr, data)) self.state += [(addr, size, data)] """ Called if a tracepoint is hit Will save registers and analyzes changes made im memory """ def trace_state_change(self, reason): print(reason) new_state = [] memdiff = [] for addr, size, data in self.state: new_data = self.uc.mem_read(addr, size) #new_data = list(map(chr, new_data)) if data != new_data: new = old = "" for i in range(len(data)): if data[i] != new_data[i]: old += "%02x" % data[i] new += "%02x" % new_data[i] elif new != "": memdiff += [(i+addr-len(new), old, new)] new = old = "" new_state += [(addr, size, new_data)] #XXX memdif_rendered = self.render_mem_diff() sys.stderr.write(self.stderr) sys.stderr.write("\n"+memdif_rendered+"\n") self.state = new_state # disassemble current instruction try: pc = self.regs["pc"] md = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) instr = list(md.disasm(self.uc.mem_read(pc, 4), pc))[0] instr = instr.mnemonic + " " + instr.op_str except: import traceback; traceback.print_exc() instr = hexlify(self.uc.mem_read(pc, 4)) # Save tracepoint object tp = {} tp["reason"] = reason tp["regs"] = self.regs tp["instr"] = instr tp["memdiff"] = memdiff tp["memdif_rendered"] = memdif_rendered tp["stdout"] = self.stdout tp["stderr"] = self.stderr tp["resid"] = self.result_id self.results += [tp] self.stdout = "" self.stderr = "" self.result_id += 1 return [{"regs": self.regs, "memdiff": sorted(memdiff)}] def render_mem_diff(self, block_size=32): ret = "----------" + ("-"*(3*block_size+1)) + "\n" ret += " |\n" print_dots = False for addr, size, data in self.state: new_data = self.uc.mem_read(addr, size) #new_data = list(map(chr, new_data)) current_offset = 0 #print(len(data), len(new_data), size) #for each hexdump row while current_offset < size: old_row = data[current_offset: current_offset+block_size] new_row = new_data[current_offset: current_offset+block_size] #ugly equal comparison equal = True for x,y in zip(new_row, old_row): equal = equal and (x==y) if not equal: hex_new = "%8x | " % (addr + current_offset) hex_old = " | " symbols = "" #render diff for i in range(min(block_size, len(new_row))): if new_row[i] == old_row[i]: hex_new += "%02x " % new_row[i] hex_old += " " else: hex_new += "\033[;32m%02x\033[;00m " % new_row[i] hex_old += "\033[;31m%02x\033[;00m " % old_row[i] if (addr + current_offset + i) in self.watchpoints: symbols += " | " if len("Watchpoint") < 3*i - 1: symbols += " " * (3*i - len("Watchpoint") - 1) symbols += "\033[;33mWatchpoint ^^\033[;00m\n" else: symbols += " " * i symbols += "\033[;33m^^ Watchpoint\033[;00m\n" elif (addr + current_offset + i) in self.symbols_reverse: name = self.symbols_reverse[addr + current_offset + i] symbols += " | " if len(name) < 3*i - 1: symbols += " " * (3*i - len(name) - 1) symbols += "%s ^^\n" % name else: symbols += " " * i symbols += "^^ %s\n" % name ret += hex_new + "\n" + hex_old + "\n" if len(symbols) > 1: ret += symbols print_dots = True else: if print_dots: print_dots = False ret += " |\n" ret += " |" + ("-"*(3*block_size+1)) + "\n" ret += " |\n" current_offset += block_size #cleanup end split = ret.split("\n") if len(split) <= 3: return "" ret = "\n".join(split[:-3]) + "\n" ret += "----------" + ("-"*(3*block_size+1)) + "\n" return ret """ Run the Emulation """ def run(self, timeout=300): try: print("running until exit @ 0x%x" % self.symbols["exit"]) self.uc.emu_start(self.elf.header.e_entry, self.symbols["exit"], timeout=timeout*UC_SECOND_SCALE) self.trace_state_change("Exit") except KeyboardInterrupt: sys.exit(1) except Exception as e: self.exception = str(e) print(e) import traceback; traceback.print_exc() print(hex(self.uc.reg_read(arm_const.UC_ARM_REG_PC))) self.trace_state_change(str(e)) # Seems to be broken n lighthouse def get_drcov(self): drcov = b"DRCOV VERSION: 2\nDRCOV FLAVOR: drcov\n" drcov += b"Module Table: version 2, count %d\n" % len(self.state) drcov += b"Columns: id, base, end, entry, path\n" for i in range(len(self.state)): addr, size, _ = self.state[i] drcov += b"%d, 0x%x, 0x%x, 0x%x, %s\n" % (i, addr, addr+size+1, addr, os.path.basename(self.fname).encode()) drcov += b"BB Table: %d bbs\n" % len(self.coverage_bb) bb_table = b"" for address, size in self.coverage_bb: for module_id in range(len(self.state)): base_addr, module_size, _ = self.state[module_id] if address >= base_addr and address <= base_addr + module_size: bb_table += struct.pack("<Ihh", address - base_addr, size, module_id) break return drcov + bb_table def get_tracefile(self): trace = "" for address in self.coverage_pc: trace += "0x%x\n" % address return trace.encode()
INT_0x80 = b'\xCD\x80' X86_CODE32 = b'' trip0 = b'\0' * 3 from unicorn import Uc, UC_ARCH_X86, UC_MODE_32, UC_HOOK_INTR from kernels.linux.x86_32.d7_26_2020.L2_6 import linux_kernel_2_6 mu = Uc(UC_ARCH_X86, UC_MODE_32) # <-------| # | Mem_size = 4 * 1024 #<--------------------------------- These are specific to the functionality surrounding assembly parsing and execution. mu.mem_map( 0, Mem_size ) # | In this case, the kernel is emulated, the assembly pushs data onto a syscall that calls the kernel to mu.hook_add( UC_HOOK_INTR, linux_kernel_2_6 ) #<---| then call the kernel function with the data and parameters passed to syscall at the assembly level. def printf(chars_, *va_list): global mu, INT_0x80, trip0, linux_kernel_2_6 entry_point = 0x0a00 for a in chars_ % va_list: l = b'\xBA\1' + trip0 + b'\xB9' + eval( f"b'{chr(a)if hex(a)!='0xa'else''}'" ) + trip0 + b'\xBB\1' + trip0 + b'\xB8\4' + trip0 + INT_0x80 mu.mem_write(entry_point, l) mu.emu_start(entry_point, entry_point + len(l)) del l #del [list(map(lambda a: print(chr(a), end=''), chars_ % va_list)).pop()][0] # <-- This version of 'printf' is for when you don't need a kernel during runtime. Plus, it deletes itself after structuring and executing based on size of scope that needed to be addressed.
class CPU: def __init__(self, firmware: Firmware = None, state: CpuState = None, verbose=0, init=True): self.firmware = firmware self.uc = Uc(UC_ARCH_ARM, UC_MODE_THUMB) self.cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB) self.cs.detail = True self.state = state self.has_error = None self.last_addr = None self.ready = False self.context = None self.verbose = verbose if init: self.init() def init(self): if self.firmware: self.firmware.refresh() self.state.verify() self.init_memory() self.init_hook() self.init_firmware() self.context = self.uc.context_save() self.reset() self.ready = True def init_firmware(self): if not self.firmware: raise Exception("firmware missing error") addr = MemoryMap.FLASH.address self.uc.mem_write(addr, self.firmware.buffer) def reset(self): addr = MemoryMap.FLASH.address self.uc.context_restore(self.context) self.uc.reg_write(UC_ARM_REG_PC, from_bytes(self.uc.mem_read(addr + 4, 4))) def run(self): if not self.ready: raise Exception("init() does not called") INST_SIZE = 2 if self.firmware: self.last_func = self.firmware.text_map[self.uc.reg_read( UC_ARM_REG_PC)] if self.verbose >= 2: print(self.last_func) try: while self.step(): pass except UcError as e: print("ERROR:", e) addr = self.uc.reg_read(UC_ARM_REG_PC) self.debug_addr(addr - INST_SIZE * 8 - 2, count=7) print(">", end=" ") self.debug_addr(addr) self.debug_addr(addr + INST_SIZE, count=7) for reg in REGS: uc_value = self.uc.reg_read(reg) print(REGS_NAME[reg].ljust(5), hex32(uc_value), sep='\t') raise def step(self, count=None): addr = self.uc.reg_read(UC_ARM_REG_PC) cycle = self.state.cycle if count is not None: self.state.cycle = count try: self.uc.emu_start(addr | 1, MemoryMap.FLASH.address_until, 0, self.state.cycle) finally: if count is not None: self.state.cycle = cycle if self.has_error: raise UcError(0) return True def init_memory(self): for region in MemoryMap: # type: MemoryRegion self.uc.mem_map(region.address, region.size, region.uc_mode) def init_hook(self): peripheral = MemoryMap.PERIPHERAL self.uc.hook_add( UC_HOOK_MEM_READ, self.hook_peripheral_read, None, peripheral.address, peripheral.address_until, ) self.uc.hook_add(UC_HOOK_MEM_WRITE, self.hook_peripheral_write, None, peripheral.address, peripheral.address_until) self.uc.hook_add( UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, self.hook_unmapped) self.uc.hook_add( UC_HOOK_INTR, self.hook_intr, ) if self.verbose >= 2: self.uc.hook_add(UC_HOOK_CODE, self.hook_inst) def hook_intr(self, uc: Uc, intno, user_data): # self.debug_addr(uc.reg_read(UC_ARM_REG_PC) - 40, 40) if intno == 2: swi = from_bytes(uc.mem_read(uc.reg_read(UC_ARM_REG_PC) - 2, 1)) r0 = uc.reg_read(UC_ARM_REG_R0) r1 = uc.reg_read(UC_ARM_REG_R1) r2 = uc.reg_read(UC_ARM_REG_R2) r3 = uc.reg_read(UC_ARM_REG_R3) if swi == 0: print("done?") print(intno, swi, ":", uc.reg_read(UC_ARM_REG_R0), uc.reg_read(UC_ARM_REG_R1), uc.reg_read(UC_ARM_REG_R2), uc.reg_read(UC_ARM_REG_R3)) uc.reg_write(UC_ARM_REG_R0, 16) uc.reg_write(UC_ARM_REG_R1, 32) uc.reg_write(UC_ARM_REG_R2, 48) uc.reg_write(UC_ARM_REG_R3, 64) elif swi == 1: # TODO: address and size vaild required? buffer = uc.mem_read(r0, r1).decode('utf-8', 'replace') if self.state.write_to_stdout: print("API_REQ", buffer) self.api_response("hello") self.uc.emu_stop() else: self.has_error = True self.uc.emu_stop() def api_response(self, *args): bufs = json.dumps(args) buf = bufs.encode("utf-8") if self.state.write_to_stdout: print("API_RES", buf) self.uc.mem_write(MemoryMap.SYSCALL_BUFFER.address, buf) self.uc.mem_write(MemoryMap.SYSCALL_BUFFER.address + len(buf), b'\0') self.uc.reg_write(UC_ARM_REG_R0, MemoryMap.SYSCALL_BUFFER.address) self.uc.reg_write(UC_ARM_REG_R1, len(buf)) def hook_peripheral_read(self, uc: Uc, access, address, size, value, data): if address == PeripheralAddress.OP_CON_RAM_SIZE: uc.mem_write(address, to_bytes(self.state.ram_size)) elif address == PeripheralAddress.OP_IO_RXR: if self.state.input_buffer: uc.mem_write(address, to_bytes(self.state.input_buffer.pop(0))) else: uc.mem_write(address, to_bytes(0)) elif address == PeripheralAddress.OP_RTC_TICKS_MS: pass # uc.mem_write(address, to_bytes(int((time.time() - self.state.epoch) * 1000))) else: if self.verbose >= 1: print("read", access, hex(address), size, value, data) def hook_peripheral_write(self, uc: Uc, access, address, size, value, data): if address == PeripheralAddress.OP_CON_PENDING: if self.verbose >= 1: print("OPENPYTHON_CONTROLLER_PENDING", value) elif address == PeripheralAddress.OP_CON_EXCEPTION: if self.verbose >= 1: print("OPENPYTHON_CONTROLLER_EXCEPTION", value) elif address == PeripheralAddress.OP_CON_INTR_CHAR: if self.verbose >= 1: print("OPENPYTHON_CONTROLLER_INTR_CHAR", value) elif address == PeripheralAddress.OP_IO_TXR: self.state.output_storage.append(value) if self.state.write_to_stdout: print(chr(value), end="") sys.stdout.flush() else: if self.verbose >= 1: print("write", access, hex(address), size, value, data) def hook_unmapped(self, uc: Uc, access, address, size, value, data): print("unmapped:", access, hex(address), size, value, data) uc.emu_stop() self.has_error = True def hook_inst(self, uc: Uc, address, size, data): func = None if self.firmware: func = self.firmware.text_map[address] if func in HELPER_FUNCTIONS: return if self.last_func != func: self.last_func = func print("#inst", hex(address), func) self.last_addr = address def report_memory(self): total_size = 0 for mem_start, mem_end, perm in self.uc.mem_regions(): total_size += mem_end - mem_start print("memory:", hex(mem_start), hex(mem_end - mem_start), perm) print("memory total:", total_size / 1024, "kb") INST_SIZE = 2 def debug_addr(self, addr, count=1, *, end="\n"): INST_SIZE = 4 try: for inst in self.cs.disasm( self.uc.mem_read(addr, INST_SIZE * count), addr, count): # type: CsInsn if self.firmware: print(self.firmware.text_map[inst.address], end=" ") print(hex(inst.address), hex(from_bytes(inst.bytes)), inst.mnemonic, inst.op_str, end=end) except UcError as exc: if exc.errno == UC_ERR_READ_UNMAPPED: print("fail to read memory", hex(addr)) def debug_addr_bin(self, addr, count=1): INST_SIZE = 4 try: for inst in self.cs.disasm( self.uc.mem_read(addr, INST_SIZE * count), addr, count): # type: CsInsn if self.firmware: print(self.firmware.text_map[inst.address], end=" ") if len(inst.bytes) != 2: raise Exception( f"len(inst) != 2; {inst.bytes} => {inst.mnemonic} {inst.op_str}" ) bcode = bin(from_bytes(inst.bytes))[2:].zfill(16) print(hex(inst.address), bcode[0:4], bcode[4:8], bcode[8:12], bcode[12:16], inst.mnemonic, inst.op_str) except UcError as exc: if exc.errno == UC_ERR_READ_UNMAPPED: print("fail to read memory", hex(addr))