首页
论坛
专栏
课程

[原创]还原数字公司dex加固opcode映射关系

lpcdma 2018-3-9 18:20 3608
前言
昨天又去测试了下数字公司的加固,顺便试了下脱壳。
按照以前的办法没搞定。
搜索现成的文章,
看过三篇文章,讲述数字公司这种加壳的还原方法。
基本流程都是过反调试,找入口,找到加密后的code_item,解密code_item,
然后找到opcode的映射关系,还原出原始的opcode。

我对这种加固的理解是:类似cpu的多核执行的方式。也想过不用分析,
直接的通用的还原办法,不过目前没想到,似乎也没法,因为这个的确是在
自己实现的代码中执行dalvik opcode。除非能跟到dex_pc,

一、找入口
1.构造demo
按照官方文档写了一段这样的代码

对应的code_item是

android手机目前是andorid 5所以基于5的art来弄,

enter的值就是注册jni函数的入口了,没加固的时候是0x00
注:不同rom偏移偏移不一定是10*4,具体参考源码
原理是art_method jni 的入口entry_point_from_jni_
对应的源码在
http://androidxref.com/5.0.0_r2/xref/art/runtime/mirror/art_method.h
注:与dvm dvmUseJNIBridge 方法类似只是不用去hook
上传加固后得到的入口是0xa369415f
2.调试
目的是找opcode对照表,所以采用白盒的方式,不用启动的时候附加,
所以不过反调试。
打开app直接AS附加成功,br s -a 0xa369415e 设置断点后触发,
结果直接挂了,这里折腾半天,以为遇到了什么反调试,结果是lldb
在识别arm,thumb有问题。所以放弃AS还是用ida来调。
ida调试的时候需要先启动应用,再启动android_server
附加后成功下段到


二、找 code_item
这个地方本来不想dump so的,结果还是需要dump下方便分析
也根据前人的文章有个内存加载的so
1.dump so
找到debug006的开头,加载这个是1k对齐的,所以直接找很快。

找到这个内存加载文件的头了,直接可以肉眼解密,与或0x52
dump脚本,大小是猜就行了。
import idautils
import idc
import idaapi
import struct


def main(ea_start, ea_end, save_file):    
    print '[*]begin to dump segment'

    count = 0
    
    handle_f = open(save_file, 'wb')
    for byte_addr in range(ea_start, ea_end):
        count += 1
        byte_value = idaapi.get_byte(byte_addr)
        if count <= 340 :
            handle_f.write(struct.pack('B',byte_value ^ 0x52))
        else :
            handle_f.write(struct.pack('B',byte_value))
    
    handle_f.close()
    hooks = idaapi.DBG_Hooks()
    hooks.hook()
          
    print '[*]script by freakish, enjoy~~'       
    print '[*]script finish'


ea_start = 0xA3687000
ea_end = 0xa3729408
save_file = 'd:/text.so'

main(ea_start, ea_end, save_file)
代码改自https://raw.githubusercontent.com/freakishfox/xAnSo/master/IDAScript/IDADumpMemory.py
另外一种办法是去/data/data/com.a360.reftest/.jiagu/classes.oat中去找,理论上是可以的。
3.找虚拟机入口
根据参数和返回值,sub_D930动态调试的时候的unk_A3694930就是这个虚拟机的入口了

这个函数有点大,折腾许久,根据调试和参考之前那三篇文章定位到sub_3FE74应该就是解释器入口

看到这个函数不管前面在干嘛,直接定位sub_3FF66函数了。
4.找被加密的code_item
动态调试下段看参数情况。

断在这里几次调试查看r0,r1,r2,r3的内容,没有什么发现,然后看看其他寄存器内容
这里r7的数据看着有些异样。
提出来看看,同样肉眼解密

再看看构造demo中的smali代码

6个操作对应6个opcode这样就对上了,
至此可以看出opcode没有对上外操作数已经对应上。
在这里我以为f5->12->const/4
然后我又去加了一次结果发现,还对opcode也做了加密
三、解密code_item
第二次加密code_item变成了

那这种映射显然就不对了。
然后继续跟代码。

r1+0x5c的值应该是一个数组,里面存放的是这个解释器真正的opcode,

通过f5或者16去索引得到改解释器的opcode
调试两次加密后的结果均为
14 e1 2d 92 6c ea 
这样这6个操作映射关系就正确了。
xvm  vm   desc
14    12    const/4
e1    D8    add-int/lit8
2d    d7    xor-int/lit16
92   90    add-int
6c   d0    add-int/lit16
ea   0f    return
剩下的就是构造比较全的demo了,找到所有的映射关系。
总结
这种加壳的思路能做到兼容性这么好,确实值得跪拜。

参考
http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html
https://bbs.pediy.com/thread-223699.htm 这个我没权限看
https://bbs.pediy.com/thread-223796.htm



[防守篇]2018看雪.TSRC CTF 挑战赛(团队赛)11月1日征题开启!

最新回复 (12)
七少月 2018-3-9 19:04
2

0

思路很不错,但现在vmp最大问题不是分析,而是怎么批量解决,当然找到了映射关系,或者可以对这种置换表批量,但像DX那种,连数据都是CPP  ollvm后,硬编码搞的,手工还原起来不适合业务需求
开花的水管 2018-3-10 11:11
3

0

感谢楼主分享!厉害
bluth 2018-3-10 11:16
4

0

数字的壳,兼容性确实不错
luochangwe 2018-3-10 14:41
5

0

学习一下  感谢分享!
vVv一 2018-3-11 12:46
6

0

学习一下    感谢分享!
Roselia 1 2018-3-12 08:43
7

0

感谢分享
baichisi 2018-3-12 10:34
8

0

学习一下        感谢分享!
gtict 2018-3-15 16:37
9

0

问下mid+10*4这个10*4是怎么得来的。谢谢。代码完全看不出偏移
lpcdma 2018-3-15 16:56
10

0

gtict 问下mid+10*4这个10*4是怎么得来的。谢谢。代码完全看不出偏移
class  Box
{
      public:
            double  length;      //  盒子的长度
            double  breadth;    //  盒子的宽度
            double  height;      //  盒子的高度
};
~=
struct  Box
{
    double  length;      //  盒子的长度
            double  breadth;    //  盒子的宽度
            double  height;      //  盒子的高度
};
gtict 2018-3-15 17:24
11

0

lpcdma class Box { public: double length; // 盒子的长度 double breadth; // 盒子的宽度 do ...
我意思是entry_point_from_jni_入口地址跟add地址在代码里面没有体现出来,,不是具体计算。
gtict 2018-3-15 17:30
12

0

YaoJinAa 1 2018-3-20 19:44
13

0

厉害,收藏
返回