Android加固调研

              简介
              基本概念
                     APK结构
                     Dex结构
                     APK打包过程
                     APK加载过程
                     Android JNI机制
              常用破解手段
                            静态破解
                                   反编译
                                   静态分析
                            动态破解
                            二次打包
                            本地数据窃取
                            通信抓取
              加固途径
                            防静态破解
                                   防反编译
                                   防静态分析
                            资源文件保护
                            防二次打包保护
                            防动态破解
                            SO文件保护
                            本地存储数据保护
                            ART模式下的加固
              常用第三方加固平台
                     腾讯云-乐固
                            简介
                            加固项目
                                   DEX文件加固
                                   资源文件保护
                                   防二次打包保护
                                   防调试器保护
                                   内存防dump保护
                                   高级内存保护
                                   so文件保护
                            使用客户
                            收费情况
                            加固过程
                            加固APK反编译测试
                                   SO
                                   DEX反编译
                            安全键盘
                                   接入效果
                            安全数据库
                            兼容性测试
              加固平台开发

简介

随着Android应用破解的愈发猖獗,Android应用安全越来越受到重视, Apk加固作为一种防止Apk被反编译的一种强力手段,越来越受到apk开发者的青睐。

 

说道APK加固,需要和混淆有所区别,由于Java代码在APK没有做过混淆处理时,很容易通过反编译得到源码的类,变量,函数名,数据结构等信息。Android应用的混淆是通过将Java代码中的类,变量,函数名用a,b,c等无意义的字母代替。这样逆向得到的代码变得不可读。从一定程度上保护源码

 

基本概念

APK结构

apk文件实际是一个压缩包,使用解压软件即可解压缩,解压后的文件一般如下表所示:

路径

作用

assets目录

存放一些配置文件,原assets目录

lib目录

armeabi子目录下存放so库

META-INF目录

签名信息和证书

res目录

资源文件

AndroidManifest

应用配置文件

classes.dex (classes2.dex)

dex执行文件(dalvik vm executes)

resources.arsc

记录资源文件和资源ID之间的映射关系

Dex结构

Dex文件结构如下:

 

 

其中header是dex文件的头部,包含了dex文件的检查签名,和数据结构偏移地址结构如下

typedef struct DexHeader {
    u1  magic[8];           /* includes version number */
    u4  checksum;           /* adler32 checksum */
    u1  signature[kSHA1DigestLen]; /* SHA-1 hash */
    u4  fileSize;           /* length of entire file */
    u4  headerSize;         /* offset to start of next section */
    u4  endianTag;          /*字节序标号*/
    u4  linkSize;
    u4  linkOff;
    u4  mapOff;
    u4  stringIdsSize;
    u4  stringIdsOff;
    u4  typeIdsSize;
    u4  typeIdsOff;
    u4  protoIdsSize;
    u4  protoIdsOff;
    u4  fieldIdsSize;
    u4  fieldIdsOff;
    u4  methodIdsSize;
    u4  methodIdsOff;
    u4  classDefsSize;
    u4  classDefsOff;
    u4  dataSize;
    u4  dataOff;
} DexHeader;

String table -Class Def table是偏移索引的区域。

Data区域是真正的数据保存位置。

这里面需要特殊说明的是Header的前几个量:

  • magic value是一个常量,用来识别dex文件,其值为:"dex\n035\0"
  • checksum 文件校验码 ,使用 alder32 算法校验文件除去 maigc ,checksum 外余下的所有文件区域 ,用于检查文件错误 。
  • signature , 使用 SHA-1 算法 hash 除去 magic ,checksum 和 signature 外余下的所有文件区域 ,用于唯一识别本文件 。
  • fileSize 文件大小

之后说的dex加壳之后重新打包的时候就需要将这几个值修改了。

 

APK打包过程

dex文件是供dalvik虚拟机运行的可执行文件,在编译的时候将java源码编译成标准的java字节码文件,然后通过DX工具转为dex文件,最后通过aapt(Android Asset Packaging Tool)工具将dex和其他资源文件打包为apk文件。

 

APK加载过程

Android系统zygote进程,通过fork新建一个Dalvik虚拟机实例,来执行apk,Dalvik虚拟机运行流程如下:

 

 

Android JNI机制

和其他Java虚拟机一样,Android也可以通过JNI机制调用native层代码,用来达到提高效率,复用代码,提高安全性等目的。

JNI代码的实现有两种方式:

  1. 传统的javah工具,生成一组带签名的函数,然后再native层实现这些函数。
  2. 在native的JNI_OnLoad方法中通过RegisterNatives动态注册native方法。

通过第二种方法,可以实现运行时动态调整本地函数和Java函数直接的映射关系。

 

常用破解手段

静态破解

反编译

通过反编译工具将dex文件反编译为smali代码和java源码,然后通过阅读源码获取原应用的实现。

常用工具:

  1. apktool GOOGLE提供的APK编译工具,能够反编译及回编译apk
  2. Dex2jar 将dex文件反编译为java源码,生成的jar包通过jd-gui查看
  3. Smali2Java 将smali代码反编译成java代码的工具
  4. smali/backsmali 也是实现dex和smali直接的相互转化工具
  5. AXMLPrinter2 批量反编译xml文件

静态分析

静态分析就是直接对反编译的到的smali代码和java源码进行分析。

当然so库也可以进行反编译和汇编分析,不过其难度要比java层的分析要困难的多

动态破解

通过挂在debug调试器到运行的apk,来获取运行时的数据,

使用的工具比如:zjdroid,IDA,smalidea,Andbug

通过使用IDA,甚至可以在apk加载到内存后,从dump出内存中的dex文件

毕竟不管上层怎么加固加密,最终加载到内存的dex肯定不是加固的,通过IDA来动态调试libdvm.so中的dvmDexFileOpenPartial函数就获取内存中的dex内容。而这个dex文件就是完全没有加壳的源程序dex。

二次打包

修改APK中的资源文件,修改smali中的代码,插入自己的恶意代码或者导流代码,重新打包后发布。

本地数据窃取

窃取APK中的SharePreference和缓存文件,数据库数据。

通信抓取

通过代理抓包等方式,抓取apk运行过程中的通信数据

加固途径

防静态破解

防反编译

防静态反编译一般是根据反编译工具的流程找起漏洞

常用方式其中一种就是在使用花指令,比如利用jd-gui反编译时的原理,添加一些特殊代码,让反编译工具出错。

在类中加入如下字段:

private static final char[] wJ = "0123456789abcdef".toCharArray();
public static String imsi = "204046330839890";
public static String p = "0";
public static String keyword = "电话";public static String tranlateKeyword = "%E7%94%B5%E8%AF%9D";

就会让jd-gui反编译出错(最新版jd-gui已修复该问题)

 

另外一种方式就是对DEX加壳保护,让反编译得到的源码只是一堆无用的代码,真实的代码是加密的。

加壳原理:

加固过程中涉及3个对象:

  1. 需要加密的Apk(源Apk)
  2. 壳程序Apk(负责解密Apk)
  3. 加密工具(将源Apk进行加密和壳Dex合并成新的Dex)--加密工具可以用任意语言实现

流程:

  1. 通过加密工具,对源apk的dex进行加密,得到加密后的无意义内容
  2. 将加密后的内容append到壳程序apk的后面,并修改壳程序apk的头文件内容。得到最终apk。
  3. 壳程序中原有的dex内容是用来对源apk的加密内容进行解密。在运行时,通过DexClassLoader动态加载解密后的源apk,运行源apk内容。

Android加固调研_第1张图片

 

通过上面的流程得到的最终apk由于实际dex已经被加密,反编译的得到的只是壳apk 的内容。

不过反编译之后,他人可以得到壳apk中是如何对源apk进行解密的代码。所以实际上只是加大了反编译难度而已。为了进一步增加反编译难度。一般的壳程序中都会把解密部分放在native代码中。

防静态分析

代码混淆--代码混淆是Android常用的一种代码加密方式,混淆过后的代码中类名方法名等都会变成abc等无意义的字符。让阅读者没法直接从名称中直接分析出源码目的。增加静态分析难度。

资源文件保护

把资源放在assets目录下,必要情况对图片等资源的文件名和内容进行加密。

防二次打包保护

保护应用在被非法二次打包后不能正常运行:

  1. Java 代码中加入签名校验
  2. NDK 中加入签名校验
  3. 利用二次打包工具本身的缺陷阻止打包(manifest 欺骗,图片修改后缀等等)
  4. 通过Server认证校验。

但是实际上不论java层面或者ndk层面的签名校验,都可以通过修改smali或者native源码来绕过。

防动态破解

原理:当检测到apk被调试器连接时,终止apk的运行。

方式有如下几种:

1、多进程ptrace反调试---创建子进程。在子进程中获取父进程pid,并通过ptrace() 方法附着父进程。如果附着失败,说明进程已经被调试

2、获取/proc/$pid/status中TracerPid行,能够显示调试程序的pid, 可以写一个方法检查下这个值, 如果!=0就退出程序。参考Android Native反调试,用JNI实现APK的反调试。

3、检查代码执行的间隔时间。

4、检测手机上的一些硬件信息,判断是否在模拟器中,如果是模拟器则无法运行。

 

SO文件保护

对so文件进行优化压缩、源码加密隐藏、防止调试器逆向分析

某种so加密结构:

Android加固调研_第2张图片

 

 

 

应用加载过程:

Android加固调研_第3张图片

 

 

本地存储数据保护

本地存储数据的保护一般是对本地数据内容进行加密,读取时进行解密,数据库和SP存储都有一些开源方案。

数据库保护:SQLCipher

SharePreference:Secure-Preferences

文件:对称加密,或者底层对文件操作加一层,对所有文件操作都进行统一加解密

 

ART模式下的加固

上面提到过的apk加固方案实际是针对dex的,但是在Android4.4之后,Android引入了新的运行模式ART,ART模式相比Dalvik,引入了AOT(ahead of time)编译,改进了垃圾回收机制,对取样分析的支持等。在安装时,ART模式使用dex2oat将apk的dex文件重新编译成oat文件。oat文件是一种可执行文件(ELF)。这样ART模式就可以在应用安装时,将apk转为机器码的可执行文件保存在本地,在每次启动时加快启动速度。

Android5.0之后,ART模式就是默认的运行模式。其流程

这就导致了原有的加固方案只能准对Dalvik虚拟机的运行模式,无法再ART模式下使用。

Art模式下的加固,需要用到Hook技术,所谓hook就是通过一些手段改变一个函数的执行逻辑,比如在函数调用前更改一下参数或者在调用后修改返回值,甚至直接返回,不执行原函数。

在ART中类方法的执行要比在Dalvik中要复杂得多,Dalvik如果除去JIT部分,可以理解为是一个解析执行的虚拟机,而ART则同时包含本地指令执行和解析执行两种模式,同时所生成的oat文件也包含两种类型,分别是portable和quick。portable和quick的主要区别是对于方法的加载机制不相同,quick大量使用了Lazy Load机制,因此应用的启动速度更快,但加载流程更复杂。其中quick是作为默认选项,这里涉及的技术分析都是基于quick类型的。

由于ART存在本地指令执行和解析执行两种模式,因此类方法之间并不是能直接跳转的,而是通过一些预先定义的bridge函数进行状态和上下文的切换,如下图:

 

Android加固调研_第4张图片

 

Hook方法涉及到ART底层的C代码和汇编代码,这里不列举分析了,其基本原理是:

修改ArtMethod中的entrypoint,通过中转处理,让ART中间执行一段我们的自定义代码,然后再继续执行原代码。而自定义代码部分可以做一些加密解密等操作。

常用第三方加固平台

市面上的第三方加固平台很多,加固用到的手段基本上已经涵盖了上文提到的所有手段,并且还有一些其他保密的加固方式,毕竟对于破解者来说,如果知道了其加固或者加密的方法,就更加容易找到相应的破解方法。

市面上比较常见的加固平台:

  • 爱加密
  • 梆梆加固
  • 360加固保
  • 腾讯云应用乐固
  • 阿里聚安全

时间关系,暂时我并没有针对性的使用各个加固平台进行一个对比测试,下面放一个其他人做的几个平台的对比测试:

 

加固前

360

阿里

腾讯

梆梆

加固时间

 

1'20"

35"

1'1"

2'14"

apk体积

16M

16M

15M

17M

16.5M

启动速度

2.52s

1.77s

5.02

3.48

5.57

兼容性

100%

99%

100%

100%

99%

 

腾讯云-乐固

简介

应用乐固(Cloud Reinforcement)是移动应用一站式安全服务平台,涵盖应用加固、安全评测、渠道监控、安全SDK、适配分析、质量跟踪等服务。可防止应用被盗版破解、及时发现应用漏洞、监控应用正盗版分发等,有效捍卫应用开发者利益。

加固项目

DEX文件加固

对DEX文件进行专业加壳加花保护,防止利用调试器对应用进行逆向破解。

资源文件保护

资源文件被非法篡改、删除后,程序将无法正常运行。

防二次打包保护

应用内任意文件被修改、替换后,都将无法正常运行。

防调试器保护

防止使用各类静、动态调试工具影响应用运行。

内存防dump保护

防止通过动态调试、dump形式获取应用部分代码。

高级内存保护

可对内存数据进行高强度防护,有效防止内存调试、内存dump等方式窃取源码。

so文件保护

可对指定so文件进行安全防护,防止被逆向工具破解,暴露核心敏感逻辑。

使用客户

大众点评,驾考宝典,饿了么,四维图新

收费情况

目前乐固在线提供的基础服务免费,定制化需求需要和客服沟通

加固过程

两种方式:

1,在线上传应用加固

上传目标apk进行加固,加固完成后,只需要将加固后的apk,使用乐固提供的签名工具,使用原应用的签名重新进行一次签名即可。同时支持多渠道打包功能。

Android加固调研_第5张图片

 

 

 

2,下载使用PC加固工具

使用加固PC工具能够一站式的加固应用,本地首先需要配置好签名信息。然后选择应用上传进行加固。

Android加固调研_第6张图片

 

 

 

 

文件上传成功并加固完成后,会自己根据配置中的签名,进行签名并输出一个加固后的包

 

加固APK反编译测试

SO

对网游开发平台中下载的网游sdk demo进行加固测试,测试前后的apk进行解压后对比可以发现:

加固后的lib库中,增加了几个so文件

其中libbugly.so应该腾讯的bugly异常上报库,用来监控应用的crash,anr等错误。

liblegudb.so和libshella.so则

 

Android加固调研_第7张图片

 

其中liblegudb.so看起来是数据库加密的native库

生成的数据库如下图所示:(注意,该数据库和应用本身的数据库并不相同,原应用数据库内容依旧是明文的)

进入某一个具体的数据表,看到的数据也是加密后的。

Android加固调研_第8张图片

 

 

 

libshella.so则是解dex壳的native库,使用ida反编译时,ida报错无法反编译。

 

DEX反编译

加固后的apk的dex文件相比原dex文件大小略有增加,应该是增加了壳dex导致的。

对加固后的dex进行反编译后得到的只是壳程序的smali代码,而没有原应用dex文件的内容,这也符合上文中的加壳保护的现象。

Android加固调研_第9张图片

 

大部分的代码进行了混淆操作,对以上的smali文件内容进行初步分析可知:

TxAppEntry:继承了Application类,其实现对乐固框架的初始化,我们从Manifest也可以看到,application已经改成了TxAppEntry。其attachBaseContext中做了如下事情:

 protected void attachBaseContext(Context paramContext)
  {
    super.attachBaseContext(paramContext);
    Log.d("SecShell", "attach start");
    if (!b(this)) {//将libshella,libshellb,libshellc三个so库的路径初始化给静态全局变量
      return;
    }
    LeguDB.init(this, null, null);//加载liblegudb.so
    d(paramContext);//加载nfix和ufix SO库,修复本地资源文件和Unity资源文件
    this.h = new Handler(getMainLooper());
    paramContext = new CrashReport.UserStrategy(this);
    paramContext.setAppVersion("2.10.1");
    CrashReport.setSdkExtraData(this, "900015015", "2.10.1");
    CrashReport.initCrashReport(this, "900015015", false, paramContext);
    new Thread(new b(this)).start();
    a(this);
    paramContext = new IntentFilter(TxReceiver.TX_RECIEVER);
    registerReceiver(new TxReceiver(), paramContext);
    Log.d("SecShell", "attach end");
  }

LeguModuleInitializer:通用的so加载器

LeguDB:数据库so加载初始化,调用LeguModuleInitializer进行本地库加载

 

正常的一个反编译流程,应该是获知shell程序是如何从加壳后的dex文件中,获取原始dex的解密流程。然后写一个小程序用同样的方法,解析出原dex。

不同于上文“防反编译”章节中对dex整体进行简单整体加密,目前很多的加密手段是对原dex进行解析,并针对每个方法都进行不同内容的插入,从而增加解壳部分的复杂度。

同时也会在解壳代码的加载过程,增加其复杂度,从静态分析中很难看到其解壳代码。

 

由于并不擅长反编译相关的手段和知识,对乐固加固后的应用和原应用的对比也仅限于此。

 

安全键盘

一般来说,通过Android系统自带的键盘可以通过以下几种方式窃取用户信息:

  1. 键盘记录--通过读取dev/input/event1(需要root) 中的信息获取到手机屏幕事件,从而获取输入的点击,进而得到输入内容。
  2. 屏幕录像--屏幕录像同样需要root权限,然后根据录像中的按钮点击动作来获取输入内容
  3. 屏幕截屏--类似屏幕录像
  4. 第三方输入法漏洞--利用第三方输入法的漏洞截取输入

乐固本身提供了一个安全键盘的sdk,该sdk使用如下方式来保证输入安全:

  • 使用随机分布的数字键盘,对底层获取按键事件位置的方式进行防护,即使获取到点击位置,也无法获取按键信息
  • 使用自定义的键盘和EditText输入空间,避免调用系统的键盘,这样可以避免系统键盘或者第三方键盘有漏洞的影响
  • 防屏幕截屏--这个就是在用户点击输入的时候,不回显输入的数据
  • 密码全程动态加密-对每一次用户输入和删除操作都会对密码动态加密,内存中不会出现明文密码
  • 高强度加密算法
  • sdk的自身防护---自研保护+so保护机制,防止键盘本身被攻击

接入效果

Android加固调研_第10张图片

 

通过接入该sdk,在需要输入加密内容的部分使用SafeEditText代替EditText,在输入内容时,SafeEditText并不会显示你输入的内容(连闪一下也没有),规避截屏获取密码的可能。下方的键盘是自定义的密码键盘。

不过看起来这个键盘有个小bug:调起键盘界面时,下面的导航栏变成透明,并且和键盘下面的部分按键冲突。

Android加固调研_第11张图片

 

 

安全数据库

目前乐固还没有上线安全数据库的sdk,并且乐固加固过程本身也是不会对原应用的数据库进行什么加密操作。所以这里不存在替换加固方案后,数据库升级问题。

 

单就数据库来说:

由于sqlite数据库是明文存储,root用户很容易拿到数据库的db文件,并查看其中保存的数据内容:

下图是查看乐固加固后的应用的数据库的数据:

Android加固调研_第12张图片

 

 

其数据内容和原数据没有区别,并且都是明文的。

所以本地存储安全--包括数据库安全,sharepreerence安全和本地缓存文件安全需要应用本身去实现。这块可以使用已有的解决方案如:

数据库保护:SQLCipher---一方面对数据通过用户提供的key进行加密,另一方面其加密后的数据库无法打开无法看到其中存储的已加密数据

SharePreference:Secure-Preferences---其对保存的SP数据对,通过AES进行加密,加密的密钥是一个生成的伪随机数。加密后的数据

当然也可以自己选择加密算法,在写入本地存储前进行加密,读取时进行解密。

 

兼容性测试

从乐固加固升级到

覆盖安装

启动

数据库读写

SP读写

原应用

正常

正常

正常

正常

360加固

正常

正常

正常

正常

某开源加固方案

正常

正常

正常

正常

不过覆盖安装后,原有的乐固加固本身创建的bugly的数据依旧存在,因为升级安装本身并不会删除原因的data目录下文件,只会新增或者升级。

同样的,从乐固升级到360加固保。则应用的data目录下,就会存在两个平台产生的文件(360并没有新的数据库,但是有新的sp文件)

覆盖安装后原有的lib目录会被删除并覆盖,不会遗留原有的so文件,但是data下本应用的根目录还残留有乐固加固使用的文件(mix.dex,mix.so,tx_shell目录)。不过由于升级后的应用使用其他加固平台或者自由加固,这些文件只是会增加包体积而已,并不会从代码层面产生影响。

上表中的开源加固方案,使用ApkToolPlus集成的

。比第三方加固方式要简单得多。仅为测试加固兼容性需要。

综上可以看出:

各种加固方式基本是针对apk本身做的反编译,反调试等做的加固。从应用启动过程来看,覆盖安装后,启动过程就由新的应用所控制,原加固手段中在Application中所作的解密,hook等操作都已无效。只要新覆盖安装的apk的启动过程中需要的文件不存在异常即可。

加固本身并没有干涉应用本身内部使用的缓存文件,数据库,sp等(实际上如果不植入加固平台的sdk的话,加固平台是没法干涉这些代码层的操作的)。

 

加固平台开发

安全领域一句比较有名的观点是---“未知攻焉知防”。

对Android加固来说,为了其加固效果,开发者首先需要对常用的破解工具和破解手段比较熟悉。这也是为何大多数的安全专家本身就具有很高的软件破解能力的原因。

在对各种破解手段有一定的实践基础上,再针对性的对每一种破解手段进行防护,就是一个加固平台需要完成的任务。下表总结了针对市面上常规的破解方式的几种加固方式及其开发的难点。

项目

方式

难点

dex文件加固

加壳

ART模式下的兼容,各Android版本的适配,解壳算法的隐藏

 

加花指令

花指令的获取收集

资源文件保护

资源文件加密

 

 

资源文件篡改识别

 

反调试

防止调试器挂载调试

针对各种调试器的反调试实践

 

防止内存dump

调试器dump时如何触发应用退出

防二次打包

java层签名校验

 

 

native层签名校验

 

 

利用二次打包工具缺陷

 

 

服务器校验

 

so保护

针对某so文件进行防护

加密后so文件的加载时机

本地数据保护

数据库保护

数据库整体的防加载

 

缓存文件保护

 

 

SP内容保护

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Android应用)