三、Android安全机制之Apk防护

来源:互联网 发布:学校管理网络系统 编辑:程序博客网 时间:2024/04/28 23:38

一、代码和资源混淆

1. 代码混淆

    Android使用的ProGuard,起到压缩,混淆,预检,优化的作用,用法就是在build.gradle文件中minifyEnabled设置属性为true,然后在proguard-android.txt编写相应的规则,混淆是用“a b c”等字符替换程序类名、变量名和方法名,加大了反编译后代码的阅读难度,同时还有一个好处就是减少了Apk的体积。

2. 资源混淆

    资源混淆主要为了混淆资源ID长度,同时利用7z深度压缩,大大减少了安装包体积,提升了反破解难度。采用微信开源工具AndResGuard做Android资源混淆,资源混淆简单来说希望实现将res/drawable/icon,png变成res/drawable/a.png,甚至可以将文件路径也同时混淆,改成r/s/a.png。

资源混淆主要通过修改resources.arsc来实现的,所以首先需要对其文件格式有一定的了解resources.arsc一共有五种chunk类型,分别为TYPETABLE,TYPEPACKAGE,TYPESTRING ,TYPETYPE,TYPECONFIG。


--package,指的是一个package的开始,其实在resources.arsc是可以有多个package的。而packageID即是资源resID的最高八位,一般来说系统android的是1(0x01),普通的例如com.tencent.mm会是127(0x7f),剩下的是从2开始起步。当然这个我们在aapt也是可以指定的(1-127即八位的合法空间,一些混合编译就是改这个packageID)。

--string, 代表stringblock,我们一共有三种类型的stringblock。分别是table stringblock,typename stringblock, specsname stringblock。

--type,这里讲的是typename stringblock里面我们用到的各种type(用到多少种类型的type,就有多少个type chunk,例如attr, drawable, layout, id, color, anim等,Type ID是紧跟着Package ID。

--config, 即是Android用来描述资源维度,例如横竖屏,屏幕密度,语言等。对于每一种type,它定义了多少种config,它后面就紧跟着多少个config chunk,例如我们定义了drawable-mdpi,drawable-hdpi,那后面就会有两个config。

--entry,尽管没有entry这个chunk,但是每个config里面都会有很多的entry,例如drawable-mdpi中有icon1.png,icon2.png两个drawable,那在mdpi这个config中就存在两个entry。

 

typename stringblock,--type,--config,--entry的直观的关系树状图如下:



简单来说方案为:


reference:

http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=208135658&idx=1&sn=ac9bd6b4927e9e82f9fa14e396183a8f#rd

二、Apk加固

      即是给Dex或者So加壳,一般附带有反调试,防二次打包的功能,目前提供这方面服务的厂商还是很多的,BAT,网易,360,爱加密,梆梆,娜迦,通付盾,网秦,顶象,几维等等,两年前我了解的时候,也只有百度,爱加密和梆梆有这方面服务,当时的加固也就是第一代加固了。

      第一代加固技术原理是对dex加密,运行时动态解密,因为加载到内存中是完整的dex,所以这种方式容易被脱壳,只要hook住系统加载dex的函数,如dexFileParse,即可导出解密后的dex,这方面的脱壳工具也是蛮多的,比如dexHunter,drizzleDumper,ZjDroid,DexExtractor。

      第二代加固技术是在第一代的基础上加了函数级别的加密,主要的流程是:发布阶段将原始DEX内的函数内容清除,单独移除到一个加密文件中,虚拟机读取DEX文件后,内部的每一个函数有一个结构体,这个结构体上有一个指针指向函数内容,可以通过修改这个指针指向原函数内容。或者拦截虚拟机内与查找执行代码相关的函数,返回解密后的函数内容。强度再高一点的就是直接把dex函数标记为native,内容被抽离并转换成jni动态库,举个例子,比如目前的360加固,脱壳出来的dex的onCreate函数被动态注册为native函数,在未修复前是无法还原和安装运行的。

      未来的第三代加固技术是VMP壳,由windows平台演变而来,主要是保护Native代码。本人暂未研究,就不多介绍了。

      Apk加固能起到比较大的安全作用,对逆向来说就多了一道门坎,就是脱壳,对于有加固的Apk,逆向之前首要的任务就是脱壳,而且脱壳也是一件费时费力的事情,非专业人士一看到加固的Apk基本就知难而退了。当然加固也有一些弊端就是无法保证加固后的Apk在所有厂商订制平台上运行良好,所以一些大厂的Apk基本是没做加固的。

三、签名校验

      签名校验,被重编译其签名肯定是不一样的,所以目前最常用的防止重编译的方式也就是签名校验。通过获取应用自身的签名然后进行比对,如果不一致则说明是破解版,禁止下一步的操作。但是现在这种签名校验的方式已经不安全了,通过Hook技术可以轻松的破解。

      Hook技术在Java层一般是采用反射和动态代理实现,也有通过方法替换来实现的,比如Legend,YAHFA,ArtHook等,但是对系统版本都有所限制。

      在Native层的话,主要是通过解析映射到内存中的elf的结构,解析出GOT表,GOT表中存储的是elf调用外部函数地址,通过Hook替换之。代表项目是AllHookInOne,ELF文件格式提供了两种视图,分别是链接视图和执行视图,链接视图是以节(section)为单位,执行视图是以段(segment)为单位。动态链接库在加载的过程中,linker只关注ELF中的段(Segment)信息。因此ELF中的节信息被完全篡改或者甚至删除掉,并不会影响linker的加载过程,所以这里是基于执行视图(Execution View)进行符号解析。

      在root的情况下,Xposed通过替换/system/bin/app_process程序控制zygote进程,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持,可以达到hook整个系统中所有进程内存里面的对象的目的。

      Hook应用获取签名的方法是采用Java层反射和动态代理实现,大概思想就是通过接口生成一个代理对象,通过反射替换内存中的接口对象,并持有原对象的引用,这样每次对原对象操作的时候都会先经过代理对象,代理对象就可以修改传入参数,或者直接返回结果。


      获取签名一般是通过PackageManager类的getPackageInfo方法,其底层ActivityThread是通过AIDL接口IPackageManager来与系统的PackageManagerService进行交互的,所以我们只要hook我们应用内的IPackageManager就可以达到修改PMS的一些服务目的,通过hookPms来修改应用内签名获取的方法,反编译的时候,遇到反编译的APP有签名校验的时候,可以用事先获取到的真实签名数据来替换APP内获取的签名,从而达到破解签名校验的目的。

      可见通过PackageManager获取签名进行校验已经不安全,不过可以通过解压APK里的文件来进行MD5校验,判断是否被篡改,比如判断dex或者签名文件里的RSA文件都可以,需要注意的是,每次编译后,其值都会改变,所以无法放在代码里存储,一般存云端。

四、其他

1,重要逻辑代码用C/C++实现,因为相比逆向Java代码,逆向NDK程序的汇编代码是一件及其枯燥和艰难的事情,能坚持下来的人少之又少,没有汇编基础的人更是极易放弃。

2,防debug,AndroidMainfest的Application标签的debug属性设为false,当然release版本自动会帮你加上此属性为false,但是此属性是很容易被逆向修改的,所以最好在代码再做一层判断。再者就是NDK层防调试的方法一般是轮训检查进程的TracerPid值,如果非0说明被调试了,因为如果应用被调试了,那么它的TracerPid值就是调试进程的pid值。

3,关闭log等调试信息,如果log信息没关闭,那么很可能就会被利用。如果破解者想知道软件中某个功能的实现流程,并且执行此功能的时候有log输出,那么就可以根据log打印的信息定位到具体某处代码,在此处打印堆栈信息就可以知道代码的流程。

4,防Activity劫持,Activity劫持的概念:恶意的应用在后台检测到目标进程的Activity在运行,从而弹出一个页面相仿的钓鱼Activity,欺骗用户输入敏感信息从而进行窃取。防Activity劫持目前并没有太有效的手段,一般处理方式就是在Activity的onStop方法里加上“应用已在后台运行”的提示,用于警示用户。
5,防双开软件,双开软件利用插件技术给App创造了一个虚拟沙盒,隔离了App与Android系统的交互,使App进程成为了双开软件的子进程,从而拥有了子App的访问权限,进而导致这个App内的数据在非root的情况下也可以被随意地获取和修改。比如修改某些变量的值,修改某些方法的参数或返回值,或者直接替换整个方法的代码逻辑等。预防双开软件的方法可以检测自己应用的进程名,如果检测到不是自己应用的进程名的话,那么就毫不留情地终止应用吧。

6,防截屏,获取了root权限的恶意应用是可以通过连续地调用系统截屏方式来窃取用户输入的密码的,所有应该在密码输入页面所属的Activity添加一项getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE),即可禁止掉截屏。微信的登录页面也做了此项防护措施。