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:00

import idaapi
import idautils
import idc

from 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_ARM


for func in idautils.Functions():
    func_name = idc.GetFunctionName(func)
    func_data = 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=0x00000000002B9BA0
    
   

sim.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=%d  index=%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)

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

                                **解密前**

iOS 逆向之unicorn 模拟执行_第2张图片

                                **解密后**

就得到字符串的名字
注意:如果汇编代码里有if判断,如果不满足条件,if块里的汇编代码不会执行,会导致if后的代码都不执行,所以要找到if块里的代码单独执行.
以上python代码依赖于Simulator脚本,脚本源码如下:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

# @Author: smartdone
# @Date:   2019-07-01 11:00

import idaapi
import idc
import idautils
import sys

sys.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 = True


def 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 = None

        self.arch = None
        self.mode = None

        self.sp = None

        self.stack_base = 0
        self.stack_length = 1024 * 1024 * 2

        self.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 = {"name": name, "start": start, "end": end, "data": d}
                self.segments.append(seg_data)
        return self.segments

    def get_arch(self):
        self.ph_id = idaapi.ph.id
        self.ph_flag = idaapi.ph.flag

        if 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_SP

    def 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 False

    def 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 = 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 = 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, _length

    def get_unicorn_mem_pages(self):
        if len(self.segments) == 0:
            return None

        if 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_map

    def 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])

你可能感兴趣的