iOS 逆向之unicorn 模拟执行

IDA遇到hariki等等各种工具的混淆,字符串被加密,难以看出加密之后的字符串.如何得到该字符串.可以试着尝试unicorn模拟执行脚本.
1.用pip安装unicorn,注意pip版本必须是2.x+版本.如果没有请参照:pip安装教程安装.
pip install unicorn
2.根据unicorn,编写python脚本,这里在网上找了一个脚本:

#!/usr/bin/env python2 # -*- coding: utf-8 -*-# @Author: smartdone # @Date:2019-07-01 11:00import idaapi import idautils import idcfrom Simulator import * #from capstone import *sys.path.append('/usr/local/lib/python2.7/site-packages/')addr = 0x100075B00 PatchDword(addr,0) print(addr, 0)sim = Simulator() sim.sp = 4 sim.arch = UC_ARCH_ARM64 sim.mode = UC_MODE_ARMfor func in idautils.Functions(): func_name = idc.GetFunctionName(func) func_data = https://www.it610.com/article/idaapi.get_func(func) start = func_data.start_ea end = func_data.end_ea #print(func_name, hex(start), hex(end)) #if"ijm_obf_fun_globalstub_" in func_name : print(func_name, hex(start), hex(end)) start = 0x0000000100006AC8 end = 0x0000000100006B70 sim.emu_start(start, end) break#start=0x00000000002B8B7C #end=0x00000000002B9BA0sim.patch_segment('data')for seg in sim.segments: if "data" in seg['name']: # 把data段全部undefined print("MakeUnknown %s" % seg['name']) idc.MakeUnknown(seg['start'], seg['end'] - seg['start'], idaapi.DELIT_DELNAMES) # 调用ida重新解析data段 print("analyze area: 0x%x - 0x%x" % (seg['start'], seg['end'])) idaapi.analyze_area(seg['start'], seg['end']) # idaapi.clear_strlist() # idaapi.build_strlist()# 查询string的交叉引用,在引用位置添加备注 s = idautils.Strings(False) s.setup() for i, str_info in enumerate(s): if str_info: # print("%x: len=%dindex=%d-> '%s'" % (str_info.ea, str_info.length, i, str(str_info))) str_cont = str(str_info) refs = idautils.DataRefsTo(str_info.ea) for ref in refs: idc.MakeComm(ref, str_cont)

【iOS 逆向之unicorn 模拟执行】通过控制脚本里的 start和end地址 start = 0x0000000100006AC8、end = 0x0000000100006B70,就可以模拟执行对应的地址的代码,最终呈现效果对比图:
iOS 逆向之unicorn 模拟执行
文章图片

**解密前**

iOS 逆向之unicorn 模拟执行
文章图片

**解密后**

就得到字符串的名字
注意:如果汇编代码里有if判断,如果不满足条件,if块里的汇编代码不会执行,会导致if后的代码都不执行,所以要找到if块里的代码单独执行.
以上python代码依赖于Simulator脚本,脚本源码如下:
#!/usr/bin/env python2 # -*- coding: utf-8 -*-# @Author: smartdone # @Date:2019-07-01 11:00import idaapi import idc import idautils import syssys.path.append('/usr/local/lib/python2.7/site-packages/')from unicorn import * from unicorn.x86_const import * from unicorn.arm_const import * from unicorn.arm64_const import *IMAGE_BASE = idaapi.get_imagebase() DEBUG = Truedef hook_code(uc, address, size, user_data): instruction = uc.mem_read(address, size) if instruction == b'\xc3': uc.emu_stop()if address == 0: uc.emu_stop()if address != 0 and address != IMAGE_BASE: idc.set_color(address, idc.CIC_ITEM, 0xFFB6C1)if DEBUG: _code = idc.GetDisasm(address) print("0x%016x \t%s" % (address, _code))class Simulator(object): def __init__(self): self.segments = [] self.mem_map = []self.ph_flag = None self.ph_id = Noneself.arch = None self.mode = Noneself.sp = Noneself.stack_base = 0 self.stack_length = 1024 * 1024 * 2self.get_segments() self.get_arch() self.get_unicorn_mem_pages()def get_segments(self): if len(self.segments) == 0: for seg in idautils.Segments(): name = idc.SegName(seg) start = idc.SegStart(seg) end = idc.SegEnd(seg) d = idc.GetManyBytes(start, end - start) d = [ord(item) for item in list(d)] seg_data = https://www.it610.com/article/{"name": name, "start": start, "end": end, "data": d} self.segments.append(seg_data) return self.segmentsdef get_arch(self): self.ph_id = idaapi.ph.id self.ph_flag = idaapi.ph.flagif self.ph_id == idaapi.PLFM_386 and self.ph_flag & idaapi.PR_USE64: self.arch = UC_ARCH_X86 self.mode = UC_MODE_64 self.sp = UC_X86_REG_RSP elif self.ph_id == idaapi.PLFM_386 and self.ph_flag & idaapi.PR_USE32: self.arch = UC_ARCH_X86 self.mode = UC_MODE_32 self.sp = UC_X86_REG_RSP elif self.ph_id == idaapi.PLFM_ARM and self.ph_flag & idaapi.PR_USE32: self.arch = UC_ARCH_ARM self.mode = UC_MODE_ARM self.sp = UC_ARM_REG_SP elif self.ph_id == idaapi.PLFM_ARM and self.ph_flag & idaapi.PR_USE64: self.arch = UC_ARCH_ARM64 self.mode = UC_MODE_ARM self.sp = UC_ARM64_REG_SPdef is_thumb_ea(self, ea): if self.ph_id == idaapi.PLFM_ARM and not self.ph_flag & idaapi.PR_USE64: if idaapi.IDA_SDK_VERSION >= 700: try: t = idaapi.get_sreg(ea, 20) except: t = idaapi.get_sreg(ea, "T") else: t = idaapi.get_segreg(ea, 20) return t is not idaapi.BADSEL and t is not 0 else: return Falsedef emu_start(self, func_start, func_end): if self.arch == UC_ARCH_ARM: if self.is_thumb_ea(func_start): print("thumb mode") self.mode = UC_MODE_THUMB mu = Uc(self.arch, self.mode)for item in self.mem_map: Simulator.map_memory(mu, item['start'], item['length'])# 给栈分配内存 Simulator.map_memory(mu, self.stack_base, self.stack_length)# 写入数据 for item in self.segments: Simulator.write_memory(mu, item['start'], item['data'])# 配置寄存器 mu.reg_write(self.sp, self.stack_base + 1024 * 1024)mu.hook_add(UC_HOOK_CODE, hook_code)try: # 开始执行 if self.mode == UC_MODE_THUMB: mu.emu_start(func_start + 1, func_end) else: mu.emu_start(func_start, func_end) except Exception as e: print("Err: %s. Execution function failed.(The function address is 0x%x)" % (e, func_start))# 读取数据 for item in self.segments: _data = https://www.it610.com/article/Simulator.read_memory(mu, item['start'], item['end']) self.replace_data(item['start'], _data)# unmap memory for item in self.mem_map: Simulator.unmap_memory(mu, item['start'], item['length'])Simulator.unmap_memory(mu, self.stack_base, self.stack_length)def replace_data(self, start, data): for i in range(len(self.segments)): if self.segments[i]['start'] == start: self.segments[i]['data'] = data@staticmethod def write_memory(mu, start, data): if isinstance(data, list): data = https://www.it610.com/article/bytearray(data) mu.mem_write(start, bytes(data))@staticmethod def read_memory(mu, start, end): _length = end - start _data = mu.mem_read(start, _length) return bytearray(_data)@staticmethod def map_memory(mu, start, _length): mu.mem_map(start, _length) print("map memory: offset 0x%x, size: 0x%x" % (start, _length))@staticmethod def unmap_memory(mu, start, _length): mu.mem_unmap(start, _length) print("unmap memory: offset 0x%x, size: 0x%x" % (start, _length))@staticmethod def get_base_and_len(base, length): _base = base - (base % (1024 * 1024)) _length = (length / (1024 * 1024) + 1) * 1024 * 1024 return _base, _lengthdef get_unicorn_mem_pages(self): if len(self.segments) == 0: return Noneif len(self.mem_map) == 0: seg = None pages = [] for item in self.segments: if not seg: seg = {'start': item['start'], 'end': item['end']} else: if item['start'] - seg['end'] > (1024 * 1024 * 2): pages.append(seg) seg = {'start': item['start'], 'end': item['end']} else: seg['end'] = item['end'] pages.append(seg)for item in pages: start, length = Simulator.get_base_and_len(item['start'], item['end'] - item['start']) self.mem_map.append({"start": start, "length": length})for item in self.mem_map: if self.stack_base < item['start'] + item['length']: self.stack_base = item['start'] + item['length']return self.mem_mapdef patch_segment(self, seg_name): for seg in self.segments: if seg_name in seg['name']: print("patch %s: 0x%x - 0x%x" %(seg['name'], seg['start'], seg['end'])) for i in range(len(seg['data'])): idc.patch_byte(seg['start'] + i, seg['data'][i])

    推荐阅读