Android应用攻与防

安卓系统是由谷歌推出的一款移动终端,由于开源,所以国内出现了许多使用相关系统的厂商,比如小米,oppo,vivo,魅族等。
在国内,这些系统的用户群体甚多。我们日常使用的社交、游戏、工作等应用,很多都装在安卓系统上。
由于安卓系统的开源性,很多安全问题也随之而来。那么,在安卓系统上运行的应用又是如何保证自身安全的呢?
本文通过实战挖洞,展现一下Android应用的防守与攻击方式。

1
简介
为了避免应用被攻击,各种应用在投产前会用一些技术手段进行加固。但是在持续对抗下,总会出现各种反加固的手段。

部分开发者可能忽视应用的安全性,各大应用市场上也有很多未采取加固手段或进行简单加固的应用。
首先,我们来看一看一个未进行任何加固手段的应用,运行起来有多危险。
下图是开发工程师加密身份证号的AES加密算法。AES算法能保证身份证号以密文形式在网络上传输,不被他人窃取,将代码打包安装在我们手机中运行。

 

 

 


由于安卓应用包很容易获取,源码包被攻击者拿去反编译,反编译结果如下图:

 

 

 


对比开发者写的代码和攻击者反编译的代码,几乎完全一样,攻击者很容易分析出我们的代码逻辑,从而造成破坏。
如上面程序,拿到我们加密算法逻辑,对传输中的加密内容进行解密,来获取身份证号。
由此可见,没有做任何安全防护的安卓源码犹如“裸奔”。
2
防守原理
上面实例可见,没有加固的安卓应用,运行起来是相当危险的。下面介绍几种常见的应用加固方式。包括源码混淆、应用加壳、应用运行环境检测。
源代码混淆保护
混淆是将代码中的类、方法、变量等信息进行重命名,把它们改成一些如“a,b,c,d”这样毫无意义的名字,这样就增加了攻击者逆向难度。混淆效果图如下:

 

 

 


应用加壳保护
加壳是在二进制的程序中植入一段代码,在运行的时候优先取得程序的控制权,做一些额外的工作。
应用加固的一种手法对原始二进制原文进行隐藏。简单描述就是代码隐藏了,只有程序运行时才会还原。
我们将上面加密身份证号的应用加壳,然后使用工具反编译如下,已经看不到任何源码信息。
攻击者如果想要攻击,第一步就需要脱壳。我这里是只进行简单的加固,对安卓的关键dex文件整体加固,只有在运行的时候才会还原原有dex文件。当然还有其他不同类型的加壳

 

 


安卓应用加壳大致经历了四代:
第一代——壳Dex整体加密:
原理是对dex这个文件进行整体加密加壳,存放在APK的资源中,运行时将加密后的dex文件在内存中解密,并让Dalvik虚拟机动态加载执行。
第二代——壳Dex抽取:
通过将代码中的方法名和方法体进行分离,对分离的方法体进行加密处理,同时被加密的方法体中注入解密接口。运行加固包,执行被加密函数时,通过调用其中的解密接口,对当前方法进行解密。
第三代——壳混合加密:
结合第一、二代的加固优点,根据应用的实际情况采用不同的混合加固方式,从而达到加固效果和强度的最优化。
第四代——壳vmp保护:
VMP技术是将原来的可执行代码转换为自定义的字节码,这些自定义的字节码只能由自定义的虚拟机解释器执行。
应用运行环境检测保护
安卓应用如果不进行运行环境检测,就像你在一个装着针孔摄像头的房间睡觉,却不进行安全排查。显然,这样很容易引发很多安全问题。
安卓应用亦是如此。我们需要检测手机是不是有root权限、装了危险应用、设置了代理、开启了调试模式等等。

 

 


比如检测root。检测在常用目录下是否存在su命令,如存在,我们此时可以提示用户有风险或者强行退出。

publicstatic boolean checkRootPathSU(){Filef=null;FinalStringkSuSearchPaths[]={"/system/bin/","/system/xbin/","/system/sbin/","/sbin/","/vendor/bin/"};try{for(inti=0;i<kSuSearchPaths.length;i++){f=newFile(kSuSearchPaths[i]+"su");if(f!=null&&f.exists()){Log.i(LOG_TAG,"findsu in : "+kSuSearchPaths[i]);returntrue;}}}catch(Exceptione){e.printStackTrace();}returnfalse;}


检测代理,发现我们在抓包,就断开连接。

publicstatic boolean isWifiProxy(Context context) {Finalboolean IS_ICS_OR_LATER = Build.VERSION.SDK_INT >=Build.VERSION_CODES.ICE_CREAM_SANDWICH;StringproxyAddress;intproxyPort;if(IS_ICS_OR_LATER) {proxyAddress= System.getProperty("http.proxyHost");StringportStr = System.getProperty("http.proxyPort");proxyPort= Integer.parseInt((portStr != null ? portStr : "-1"));}else {proxyAddress= android.net.Proxy.getHost(context);proxyPort= android.net.Proxy.getPort(context);}Log.i("代理信息","proxyAddress:"+proxyAddress + "prot : " proxyPort")return(!TextUtils.isEmpty(proxyAddress)) && (proxyPort != -1);}


3
实战攻击
国庆某SRC有三倍金币活动,在家正好挖到某个App逻辑漏洞,发现该App不用充钱,也能成为永久VIP会员。
特意写个过程分享一下。由于涉及到隐私问题,关键信息进行打码处理。仅提供了详细的攻击过程,给大家做个参考。
挖洞思路如下:
反编译—脱壳—抓包—绕过代理检测—静态分析—写脚本hook—动态修改—攻击成功。
拿到攻击目标应用安装包.apk文件。首先进行反编译,查看源码情况:

 

 


发现应用包已进行加壳处理。直接上工具进行脱壳。我这里使用frida_dexdump进行脱壳。
打印目标应用进程PID= 17460:

 

 


执行脱壳命令:

 

 


发现脱出来很多dex文件,为了方便分析,使用apktool进行解包,替换dex后重打包。
重打包apktoolb 原始版-oC:\Users\11.apk。

 

 


脱壳前后进行对比,发现脱出来很多源代码:

 

 


 

 


手机运行该应用,发现有个会员功能,会员需要付费购买。
此时,心里想该应用会员校验机制复杂不复杂?能不能绕过支付成为会员?
直接开搞。
手机设置代理,开启BurpSuite进行抓包。一打开目标软件就“网络异常”,说明这个应用检测了运行环境,检测到手机开了代理就断开连接。

 

 



查看脱壳后的源码,发现进行了代理检测:

 

 


既然有代理检测我们就不设置代理,采用一种透明代理模式。
在BurpSuite中监听80和443这两个端口,并且将其设置为透明代理模式:

 

 


手机连接电脑,以下命令:

 

 


再查看Burpsuite,此时已经能抓到包,成功绕过代理检测:

 

 


点击关于我的信息,查看返回包,发现有关于会员信息的关键字VIP,VipstartTime,VipendTime,isVIP等等。记住这些关键字,然后去源代码里面直接搜索,发现有相关信息:

 

 


静态分析跟踪调用逻辑关系,发现判断是不是会员用了isVip进行判定,该字段值是true就是会员,false是非会员:

 

 


编写Fridahook脚本,让判断会员的方法一直返回ture。把会员到期时间也进行更换,指定一个返回时间戳,后面还需要更换两个关键参数,这里就不放出来了。

 

 


手机端开启frida-server:

 

 


PC端执行我们的脚本:

 

 


进行了动态修改,此时查看手机页面,已经成为了会员,绕过成功,使用一下会员才有的功能,发现亦能使用。

到这里攻击已经完成,回顾一下破解思路:
首先拿到应用包,查看是否进行加壳——答案是已加壳。抓包发现进行了环境检测,使用透明代理模式进行绕过。
随后我们进行逆向分析代码,成功找到绕过支付成为VIP的逻辑漏洞。
使用frida进行hook动态修改,修改运行中的代码,成功成为会员,攻击成功。

posted @ 2021-11-17 14:45  Luminous~  阅读(328)  评论(0编辑  收藏  举报