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,就可以模拟执行对应的地址的代码,最终呈现效果对比图:
文章图片
**解密前**
文章图片
**解密后**
就得到字符串的名字
注意:如果汇编代码里有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])
推荐阅读
- PMSJ寻平面设计师之现代(Hyundai)
- 太平之莲
- 闲杂“细雨”
- 七年之痒之后
- 深入理解Go之generate
- 由浅入深理解AOP
- 期刊|期刊 | 国内核心期刊之(北大核心)
- 生活随笔|好天气下的意外之喜
- 感恩之旅第75天
- 2020-04-07vue中Axios的封装和API接口的管理