前言
最后一次炒冷饭,这次以拼某某为例分析arm指令下的ollvm去混淆
拼某某版本(3.66.0)
实验环境
1.Android真机(Android5.1)
2.ida7.5
3.win10
过程
与x86的反混淆类型,思路还是找出所有控制块,对真实块下断点,记录断点执行顺序,之后连接真实块.
以JNI_OnLoad为例.
找出所有控制块
控制块的特征比较明显,就是对内存基本上没有操作.
1.
2.
3.
4.
5.
6.
........
ida python脚本
这里对部分进行说明
def dbg_process_attach(self, pid, tid, ea, name, base, size):
print("Process attach pid=%d tid=%d ea=0x%x name=%s base=%x size=%x" % (pid, tid, ea, name, base, size))
self.pre_block=None
self.related_dict=dict()
self.block_addr_dict=dict()
so_base=idaapi.get_imagebase()
self.f_blocks = idaapi.FlowChart(idaapi.get_func(so_base+fun_offset), flags=idaapi.FC_PREDS)
for block in self.f_blocks:
start=block.start_ea
end=idc.prev_head(block.end_ea)
# 排除所有的控制块
if (end==start) or\
(idc.print_insn_mnem(start)=='LDR' and idc.print_insn_mnem(idc.next_head(start)).startswith('B')) or \
(idc.print_insn_mnem(start)=='LDR' and idc.print_insn_mnem(idc.next_head(start))=='CMP' and idc.print_insn_mnem(idc.next_head(idc.next_head(start)))=='MOV' and idc.print_insn_mnem(idc.next_head(idc.next_head(idc.next_head(start)))).startswith('B')) or\
(idc.print_insn_mnem(start)=='CMP' and idc.print_insn_mnem(idc.next_head(start))=='MOV' and idc.print_insn_mnem(idc.next_head(idc.next_head(start))).startswith('B')) or \
(idc.print_insn_mnem(start)=='MOV' and idc.print_insn_mnem(idc.next_head(start))=='CMP' and idc.print_insn_mnem(idc.next_head(idc.next_head(start))).startswith('B')) or \
(idc.print_insn_mnem(start)=='LDR' and idc.print_insn_mnem(idc.next_head(start))=='CMP' and idc.print_insn_mnem(idc.next_head(idc.next_head(start))).startswith('B')) or \
(idc.print_insn_mnem(start)=='CMP' and idc.print_insn_mnem(idc.next_head(start)).startswith('B')) or \
(idc.print_insn_mnem(start)=='LDR' and idc.print_insn_mnem(idc.next_head(start))=='CMP' and idc.print_insn_mnem(idc.next_head(idc.next_head(start)))=='MOV'\
and idc.print_insn_mnem(idc.next_head(idc.next_head(idc.next_head(start)))).startswith('LDR') \
and idc.print_insn_mnem(idc.next_head(idc.next_head(idc.next_head(idc.next_head(start))))).startswith('B')
) or\
(idc.print_insn_mnem(start)=='LDR' and idc.print_insn_mnem(idc.next_head(start))=='CMP' and idc.print_insn_mnem(idc.next_head(idc.next_head(start)))=='LDR'\
and idc.print_insn_mnem(idc.next_head(idc.next_head(idc.next_head(start)))).startswith('B')
):
continue
add_bpt(end,0,BPT_SOFT)
while start<block.end_ea: #对POP下断点 相当于x86的retn
if idc.print_insn_mnem(start).startswith('POP'):
add_bpt(start,0,BPT_SOFT)
break
start=idc.next_head(start)
这段代码是ida附加进程执行的代码,排除所有控制块代码,对真实块下断点
def dbg_process_exit(self, pid, tid, ea, code):#调试退出时执行
print("Process exited pid=%d tid=%d ea=0x%x code=%d" % (pid, tid, ea, code))
for ea in self.related_dict:
if len(self.related_dict[ea])==1:
if idc.print_insn_mnem(ea).startswith('B'):
disasm='B'+' '+hex(self.related_dict[ea].pop())
patcher.patch_code(ea,disasm,patcher.syntax,True,False)
else:
disasm='B'+' '+hex(self.related_dict[ea].pop())
patcher.patch_code(idc.next_head(ea),disasm,patcher.syntax,True,False)
else:
print(ea,self.related_dict[ea])#该真实块有两个后继真实块,要手动patch
这段代码是退出调试执行,连接所有真实块,对于有两个后继真实块的块需要自行手动修改.
动态调试
1.将ida文件夹dbgsrv的android_server传入手机端,改变文件权限
adb push android_server /data/local/tmp/android_server
adb shell
su
chmod 777 /data/local/tmp/android_server
/data/local/tmp/android_server
2.adb端口转发
adb forward tcp:23946 tcp:23946
3.打开ddms
4.ida载入python脚本,附加进程,,ddms运行
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8700
5.运行直到retn块后,结束调试
去混淆结果
F5后结果
写在最后
基于ida的ollvm有一定的局限性,比如要处理反调试,还需要可调试的真机.
较好的方案可以使用unidbg(动态调试)代替
github地址:
https://github.com/PShocker/de-ollvm-arm