Dexguard分析&钛备份破解
来源:互联网 发布:python 二叉树查找 编辑:程序博客网 时间:2024/04/30 14:25
菜鸟第一次分析,请大牛勿喷,会打击小菜的积极性的!
一、APKtools反编译
可能是Dexguard壳子利用了apktools的bug吧,反正我是没有反编译成功。AndroidManifest.xml里所有不在<intent-filter>与</intent-filter>的元素key值都没有反编译出来。
如图所示:
修复后如下:
但是还是无法打包。用apktools还是打包失败:
既然无法打包,很多方法就行不通了,log的方法也失效了,不过不要紧,反编译失败导致无法正常打包,那就用点原始的方法吧。不管那么多,先分析分析这个程序再说,程序分析透了,至少也算是对自身的一种提高,说干就干。(这个问题现在还没有解决,希望会的大牛告知一下,在下感激不尽。)
二、逆向分析
虽然说打包是没搞定,但主要是因为xml文件apktools分析出错的原因,还好不影响我们亲爱的smali文件的反编译。JEB载入,(这里要感谢SCZ大神的无私奉献,虽然说好像保存功能用不了,但是已经非常感谢了)随便翻了一下,看见一个这个:
我本来准备吐槽作者了,但是往后一翻,我觉得这个也不必怎么大惊小怪。因为对于第一次分析APK,发现如下的东西,我开始有点不淡定了。类名和函数的名称被加密成这样:
不过不管它多么花,也要干掉它!
那就要开始定位关键地方,进行破解了。
2.1.关键定位
我目前知道的一共有2种方法可以成功定位到关键地方。
第一种方法:搜Strings.
这是一种方法,当然可能搜到的不止一处,这就需要自己去甄别了。
第二种方法:这种方法在我之前的一篇学习笔记里也有讲到,里面的第四种方法,所谓的“遗留下的宝藏”。用到了DDMS 如图:
OK,真幸运,直接定位到了fs中。Nice。
2.2log分析
既然发现了验证的类,那就不能放过。发现大部分字符串都被加密过了,我们就从log开始着手吧。找了几个log如下:
发现了解密函数,把它Rename:
解密函数如下:
找到了解密函数,这就好办了,先把strings解密了再说,我写了一个python脚本来解密encryptStrings。Python脚本如下:
代码:
#Author Erickyimport sysimport osimport timefrom jeb.api import IScriptfrom jeb.api import EngineOptionfrom jeb.api.ui import Viewfrom jeb.api.dex import Dexfrom jeb.api.ast import Class, Field, Method, Call, Constant, StaticField, NewArrayencbytes = [69, 21, -111, -111, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -36, -45, -4, -1, -4, 4, -5, 83, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 42, 25, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -55, -12, -12, 2, 4, 2, -20, 10, -6, 6, 70, -71, -13, 2, 1, 76, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 68, -15, -1, -1, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -51, -20, -15, 2, 0, 0, -6, 13, 68, -74, -11, 82, -79, -2, -6, 83, -87, 20, -12, 2, 4, 67, -66, -14, -12, 11, -3, -4, 12, 54, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -51, -20, -15, 2, 0, 0, -6, 13, 68, -88, 13, -4, -1, 75, -84, -2, 10, -4, -1, 75, -83, 12, -9, 11, -9, -6, 77, -87, 20, -12, 2, 4, 67, -71, -10, -4, 81, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -51, -20, -15, 2, 0, 0, -6, 13, 68, -74, -11, 82, -84, -2, 10, -4, -1, 75, -87, 16, -14, -8, 88, -71, -13, 12, -15, 10, 57, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -52, -15, -22, 12, -6, 6, 70, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 42, 25, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -36, -31, -3, -6, 2, 68, -87, 20, -12, 2, 4, 36, 30, -30, 28, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -51, -20, -15, 2, 0, 0, -6, 13, 68, -88, 13, -4, -1, 75, -67, -4, 68, -83, 12, -2, -13, 12, -15, 10, 2, 0, 67, -67, -4, 1, 1, -21, 1, 13, 68, -80, -8, 16, -14, 81, -18, 1, -5, 0, 17, -80, 8, 69, -74, -12, 0, 82, -87, 20, -12, 2, 4, -6, -12, -6, 88, -69, -18, 2, 16, -20, 10, -7, 0, 77, -74, -11, 82, -70, -8, 10, -16, -4, 13, 0, 53, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -47, -34, 78, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 42, 25, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -37, -34, -8, 6, -16, 10, -6, 6, 70, -79, -2, 0, 64, -74, 20, -12, 2, 4, 67, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 42, 25]class Mydecypt(IScript): def run(self, jeb): self.jeb = jeb self.dex = self.jeb.getDex() self.cstbuilder = Constant.Builder(jeb) self.csig = 'fs' self.encbytes = encbytes self.mname_decrypt = None r = jeb.decompileClass(self.csig) decrypted_string = self.decrypt(66, 26, 0) #Here enter your encrypt strings print ' Decrypted string: %s' % repr(decrypted_string) def decrypt(self, length, curChar, pos): length = 93 - length pos = pos * 2 + 91 curChar = 378 - curChar r = '' for i in range(length): curChar +=1 r += chr(pos & 0xFF) if i >= len(self.encbytes): break curEncodedChar = self.encbytes[curChar] pos = pos - curEncodedChar -1 return r把字符串解密了,那就事半功倍了。具体的分析就比较简单了。分析解密后如下:
代码:
private static final BigInteger 大整数; private static final byte[] 加密字符串; public static boolean 普通版开关; public static boolean 专业版开关; public static hG HG类; private static final String 字符串常量; private static int 常数; private static boolean modaco隐藏版本开关; static { fs.加密字符串 = new byte[]{69, 21, -111, -111, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -36, -45, -4, -1, -4, 4, -5, 83, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 42, 25, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -55, -12, -12, 2, 4, 2, -20, 10, -6, 6, 70, -71, -13, 2, 1, 76, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 68, -15, -1, -1, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -51, -20, -15, 2, 0, 0, -6, 13, 68, -74, -11, 82, -79, -2, -6, 83, -87, 20, -12, 2, 4, 67, -66, -14, -12, 11, -3, -4, 12, 54, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -51, -20, -15, 2, 0, 0, -6, 13, 68, -88, 13, -4, -1, 75, -84, -2, 10, -4, -1, 75, -83, 12, -9, 11, -9, -6, 77, -87, 20, -12, 2, 4, 67, -71, -10, -4, 81, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -51, -20, -15, 2, 0, 0, -6, 13, 68, -74, -11, 82, -84, -2, 10, -4, -1, 75, -87, 16, -14, -8, 88, -71, -13, 12, -15, 10, 57, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -52, -15, -22, 12, -6, 6, 70, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 42, 25, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -36, -31, -3, -6, 2, 68, -87, 20, -12, 2, 4, 36, 30, -30, 28, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -51, -20, -15, 2, 0, 0, -6, 13, 68, -88, 13, -4, -1, 75, -67, -4, 68, -83, 12, -2, -13, 12, -15, 10, 2, 0, 67, -67, -4, 1, 1, -21, 1, 13, 68, -80, -8, 16, -14, 81, -18, 1, -5, 0, 17, -80, 8, 69, -74, -12, 0, 82, -87, 20, -12, 2, 4, -6, -12, -6, 88, -69, -18, 2, 16, -20, 10, -7, 0, 77, -74, -11, 82, -70, -8, 10, -16, -4, 13, 0, 53, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -47, -34, 78, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 42, 25, 14, -30, 5, -3, -10, -6, 9, -6, 6, 9, 60, -37, -34, -8, 6, -16, 10, -6, 6, 70, -79, -2, 0, 64, -74, 20, -12, 2, 4, 67, -68, 1, -3, -6, 2, 68, -71, -4, -4, 6, 42, 25}; fs.常数 = 245; boolean v0 = !fs.class.desiredAssertionStatus() ? true : false; fs.ʼ = v0; fs.字符串常量 = fs.class.getName(); fs.普通版开关 = false; fs.专业版开关 = false; fs.HG类 = null; fs.modaco隐藏版本开关 = false; fs.大整数 = BigInteger.ONE.shiftLeft(16).add(BigInteger.ONE);
值得注意的地方是这个函数:
代码:
private static void 关键函数(Runnable arg3, boolean arg4) { if(arg4) { fs.ˊ(true); fs.专业版开关 = true; fs.HG类 = new hG(); } else { fs.ˊ(fs.ˊ(MainApplication.ʾ)); boolean v0 = !fs.modaco隐藏版本开关 || !"95116f196c3b".equals(fs.HG类.get("keyId")) ? false : true; fs.普通版开关 = v0; } arg3.run();//很明显了吧 }
分析好的fs类我会打包在附件中。
三、破解
有3个开关,事情就变得很简单了,改几个字节就行了。找到类中的开关,打开交叉引用:
找到每一个地方,稍微分析一下,把需要改的置真即可。当然你不怕麻烦的话,可以每一处都置“1”,也是一样的。
看了吾爱的一篇文章说修改之后,会弹出版本不正确。打开APK中的so,你会看到一个mprotect的函数,根据名字我猜的话应该是so里面的MD5完整性校验,但是奇怪的是,由于我更改的是几个“开关”,貌似dexguard对开关,或者说是声明函数里的东西是不检查的,因此这种方法直接避过了dexguard 6.0的anti-tampering check.不仅省去了逆SO的时间和精力,动态加载也不用去担心,因为找到的这块是风水宝地啊,直接避开了dexguard的检测,当然也有可能是我直接改的dex文件里的opcode,可能dexguard对dex的检测只是依赖与dex自身的SHA-1值和签名值。
刚才说了打包不了,怎么办呢?我的方法是直接解压apk,用IDA load dex,然后在IDA中定位到具体的offset,然后用16进制工具直接找到offset直接修改dex的opcode即可。还是很期待有人指点下过dexguard反编译方法啦,我自己当然也会继续研究的。
如图:
四、总结
11.22号开始看丰生强的那本入门书,这个程序3天分析得差不多了,也算是对最近20天自己的一个交代。这个程序真的很有意思,除了表面上的2个版本之外后来还发现有一个隐藏的版本,真的是耐人寻味啊。从中加强了自己的逆向分析,同时也了解到了作者的一些验证思路。本来找到了keygen算法(第一个函数)想分析算法,patch之后做一个keygen出来,但是因为实力有限吧,最后还是放弃了。因为JEB不能保存的缘故,这一次fs里面分析的东西是临时做的,难免有疏漏,还望各位见谅。
在这里衷心谢谢非虫的书,越反复看觉得越写得好,虽然有的地方有一些冗长。也感谢论坛上的一些文章,认真读了,很有收获。接下来还是要继续学习吧,不过基础还是一定要打牢!
总的来说,收获还是挺大的,学到了很多东西。欢迎大家和我一起交流,一起进步。新手难免没有纰漏,失敬之处还望各位大侠多多指点。
(*^__^*)
By Ericky
2014.12.9
- Dexguard分析&钛备份破解
- [Android] Proguard And DexGuard
- [Android] Proguard And DexGuard
- Proguard/DexGuard混淆应用
- [备份]破解Xamarin
- 吾爱破解—破解备份
- androidroot权限破解分析
- UltraEdit破解分析
- 加密破解分析
- AliCrackme_1.apk破解分析
- 软件加密破解分析
- 加密破解分析2
- 钛备份 - Titanium Backup Pro v6.2.0-Test4 直装破解版
- MTK电话本联系人备份加密与破解
- Recover_mysql拷贝备份分析
- 物理备份死锁分析
- QQ2009 协议分析及破解
- android root权限破解分析
- Objective-C KVC - KVO
- 深入理解Java中为什么内部类可以访问外部类的成员
- 数据结构导论(2)
- python2.1-原理之琐碎技巧
- Objective - C 委托/协议
- Dexguard分析&钛备份破解
- JAVA static 关键字的作用
- 分享CodeIgniter上传图片成功的全过程
- Java-日期处理-时间戳转换-日期比较
- 【Leetcode】:Same Tree
- Java中equals和==的区别
- 非常全面的PHP header函数设置HTTP头的示例
- 图解用Wireshark进行Http协议分析
- Oracle的Timestamp数据类型与Mysql的Timestamp数据类型兼容的经验