Shocker 发表于 2021-9-12 11:34

基于IDA Python的OLLVM反混淆(四) 实战拼某某混淆

## 前言
最后一次炒冷饭,这次以拼某某为例分析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)==1:
                if idc.print_insn_mnem(ea).startswith('B'):
                  disasm='B'+' '+hex(self.related_dict.pop())
                  patcher.patch_code(ea,disasm,patcher.syntax,True,False)
                else:
                  disasm='B'+' '+hex(self.related_dict.pop())
                  patcher.patch_code(idc.next_head(ea),disasm,patcher.syntax,True,False)
            else:
                print(ea,self.related_dict)#该真实块有两个后继真实块,要手动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

huhuf6 发表于 2021-9-12 13:28

Shocker 发表于 2021-9-12 13:22
对,这个方法有一定的局限性.但相比其他大神的反混淆脚本,这个应该是最通俗易懂的.

目前我是用angr实现了armv7的fla还原,但是bcf还没有怎么研究,楼主对bcf了解吗,我想把这两个弄在一个脚本里还原

Shocker 发表于 2021-9-12 13:22

huhuf6 发表于 2021-9-12 13:13
楼主看了一下你都样本,是纯arm32不带thumb指令的,如果带thumb指令的话应该还有蛮多坑的,如果是在动态调 ...

对,这个方法有一定的局限性.但相比其他大神的反混淆脚本,这个应该是最通俗易懂的.{:1_918:}

meichangsu 发表于 2021-9-12 12:23

谢谢楼主分享

诅咒者之魂 发表于 2021-9-12 12:45

感谢楼主分享

huhuf6 发表于 2021-9-12 13:13

楼主看了一下你都样本,是纯arm32不带thumb指令的,如果带thumb指令的话应该还有蛮多坑的,如果是在动态调试的时候执行patch的话,是不是就原本没有执行的块就被nop掉了?

chenjingyes 发表于 2021-9-12 18:57

谢谢楼主分享:lol

yyb414 发表于 2021-9-12 19:00


谢谢楼主分享

Forgo7ten2020 发表于 2021-9-13 10:15

请问楼楼idpython有没有什么好的教程?官方的api文档比较难查阅啊

Shocker 发表于 2021-9-13 10:41

Forgo7ten2020 发表于 2021-9-13 10:15
请问楼楼idpython有没有什么好的教程?官方的api文档比较难查阅啊

你可以看看这篇文章
基于python3.x IDAPython第二讲 段 函数 汇编指令等操作
页: [1] 2
查看完整版本: 基于IDA Python的OLLVM反混淆(四) 实战拼某某混淆