APK是AndroidPackage的缩写,即Android安装包(apk),可以通过将APK文件直接传到Android模拟器或手机中执行即可安装。APK的本质是一个zip的压缩包,用压缩软件打开后就可以看到里面的文件以及结构。
APK的文件组成:
AndroidManifest.xml:清单文件,主要包含四大组件注册信息,应用权限,和元数据等信息,此文件列出了应用的名称、版本、访问权限和引用的库文件。
classes.dex:Dalvid虚拟机上运行的字节码文件,我们在将java代码编译成class字节码文件后,通过android SDK提供的工具将class转换成dex字节码,dex文件可能有多个。
META—INF: 包含 CERT.SF 和 CERT.RSA 签名文件,以及 MANIFEST.MF 清单文件。CERT.SF、MANIFEST.MF是对资源做的SHA1 hash处理,CERT.RSA包含有公钥证书和签名。
res:包含未编译到 resources.arsc 中的资源例如:布局xml、图片、动画等。
resources.arsc 打包工具会提取此 XML 内容,将其编译为二进制文件形式,并将相应内容进行归档。此内容包括语言字符串和样式,以及未直接包含在 resources.arsc 文件中的内容(例如布局文件和图片)的路径。
lib/:包含特定于处理器软件层的编译代码。此目录包含每种平台类型的子目录,如 armeabi、armeabi-v7a、arm64-v8a、x86、x86_64 和 mips
由上图可知,APK打包的流程主要有如下几个步骤:
工具aapt
输入
过程
输出
总结
这个主要是在aidl接口文件,通过SDK提供的AIDL工具生成相应的java文件,供程序调用,没有用到aidl的工程,并不会有这个过程
工具
输入
输出
总结:
工具
输入
过程
输出
android应用需要签名才能在设备上安装。
使用JDK自带签名工具的jarsigner:
对齐使用的是android-idk/tools目录下的zipalign工具,主要工作是将apk包中所有的资源文件起始偏移为4字节的整数倍,这样通过内存映射访问apk时的速度会更快。
经过这几步的编译,输出的apk文件,最终是一个具有特定文件格式的静态文件。如果拿到该文件,分析文件格式和逆向,最终是可以分析一个破解apk的业务和流程,这就必然对apk产生安全问题。
Android 支持以下三种应用签名方案:
从一开始,APK 签名就是Android 的一个有机部分。该方案基于签名的JAR。 但是v1方案存在以下一些不足:
签名不保护APK的某些部分,例如ZIP元数据。
APK验证程序需要处理大量不可信(尚未经过验证)的数据结构,然后会舍弃不受签名保护的数据,这会导致相当大的受攻击面。
APK 验证程序必须解压所有已压缩的条目,而这需要花费更多时间和内存。
由于这些不足导致apk签名后可以进行许多修改,可以移动甚至重新压缩文件,导致一些安全事件的发生。为了解决这些问题, google在Android 7.0以后引入了V2签名方案Android 7.0 中引入了APK 签名方案v2, Android 9之后引入v3(v2+)。
APK v2签名方案是一种全文件签名方案,该方案能够:
使用APK签名方案v2进行签名时,会在APK文件中插入一个APK签名分块,该分块位于“ZIP中央目录”部分之前并紧邻该部分。在“APK 签名分块”内,v2签名和签名者身份信息会存储在APK签名方案v2分块中。
搭载Android 7.0 及更高版本的设备支持APK 签名方案v2(v2方案)及更高版本的方案(在Android P(Android 9) 中,v2方案已更新为v3方案,以便在签名分块中包含其他信息,但在其他方面保持相同的工作方式)。该方案会对APK的内容进行哈希处理和签名,然后将生成的“APK签名分块”插入到APK中。
在验证期间,v2+方案会将APK文件视为Blob,并对整个文件进行签名检查。对APK进行的任何修改(包括对ZIP元数据进行的修改)都会使APK签名作废。这种形式的APK验证不仅速度要快得多,而且能够发现更多种未经授权的修改。新的签名格式向后兼容,因此,使用这种新格式签名的APK 可在更低版本的Android设备上进行安装(会直接忽略添加到APK 的额外数据),但前提是这些APK 还带有v1签名。
验证程序会对照存储在“APK签名分块”中的v2+签名对APK的全文件哈希进行验证。该哈希涵盖除“APK签名分块”(其中包含v2+签名)之外的所有内容。在“APK签名分块”以外对APK进行的任何修改都会使APK的v2+签名作废。v2+签名被删除的APK也会被拒绝,因为v1签名指明相应APK带有v2签名,所以Android 7.0及更高版本会拒绝使用v1签名验证APK。
APK反编译,就是把编译后的APK文件通过逆向工程手段获取到原始代码和资源的一个过程。由于APK文件是一个具有固定文件格式的静态文件,而且能够解析和执行的,所以理论上可以对一个APK文件进行逆向的分析进行反编译、破解。如果一个APK没有进行安全方面的防护措施,是可以通过很简单的几步给破解掉。
apktool+dex2jar+JD-GUI
得到class.dex文件,使用dex2jar工具反编译为jar文件。
使用以下命令:./dex2jar.sh classes.dex
通过JD-GUI打开classes_dex2jar.jar可以看到大部分源。
通过apktool直接反编译app.apk即可看到资源文件 ./apktool d app.apk -f -o ./app3
反编译后AndroidMenifest.xml:
反编译后layout:
反编译后color文件:
至此,一个apk中所有的资源文件、代码文件全部被反编译出来,所以作为一个app开发者,为了数据、业务的安全,必须要对app进行安全处理,防止对用户造成信息、财产方面的损失,以下是几个对apk进行安全加固的处理方法。
对于apk安全加固主要分为两个方面的安全防护:
混淆代码并不是让代码无法被反编译,而是将代码中的类、方法、变量等信息进行重命名,把它们改成一些毫无意义的名字。因为对于我们而言可能CellPhone类的call()方法意味着很多信息,而A类的a()方法则没有任何意义,但是对于计算机而言,它们都是平等的,计算机不会试图去理解CellPhone是什么意思,它只会按照设定好的逻辑来去执行这些代码。所以说混淆代码可以在不影响程序正常运行的前提下让破解者对代码逻辑的理解难度加大,从而大大提升了程序的安全性。
APK必须被签名才能够安装到手机中,没有被签名的apk是无法安装到设备中的。但是签名在反编译之后是获取不到的,所以只能用自己的签名文件去签名,但是在已经安装了应用设备中在安装一个签名不一致的应用会导致安装失败。
利用该原理,可以在程序启动的时候获取应用签名,然后和正确的签名值做比对,如果不符合就直接退出程序。
既然是在程序的入口进行签名比对,就可以反编译找到程序入口处相关的代码,进行处理删除或者把签名信息改成自己的签名值,重新打包即可解决这个问题。
- 加密的源apk - 自己的壳apk,负责解密apk,并动态加载apk的工作 - 加密工具,负责将源apk进行加密和壳dex合并成新的dex 复制代码
反调试检测是为了应对现在很多破解者使用IDA进行动态方式调试so文件,从而获取重要的信息,知道IDA进行so动态调试是基于进程的注入技术,然后使用Linux中的ptrace机制,进行调试目标进程的附加操作。ptrace机制有一个特点,如果一个进程被调试了,在它进程的status文件中有一个字段TracerPid会记录调试者的进程id值,如图所示:
查看文件:/proc/[myPid]/status,在第六行,有一个TracerPid字段,就是记录了调试者的进程id。那么就可以这么做来达到反调试的功效了:轮询遍历自己进程的status文件,然后读取TracerPid字段值,如果发现它大于0,就代表着自己的应用在被人调试,所以就立马退出程序。原理知道了,代码实现也很简单,这里用pthread创建一个线程,然后进行轮询操作: 使用pthreadcreate创建一个线程,线程启动之后执行threadfunction函数:
看看thread_funcation函数:
Dex-Java2C:将Java代码翻译为C代码,并实施Native层的代码混淆保护。
So文件加壳:对SO文件进行整体加壳保护,防止IDA Pro等工具逆向分析。
内存加密:防止内存数据被篡改或Dump,比如Dump解密后的Java代码。
自身虚拟化保护:专业版采用代码虚拟化技术对自身代码进行保护,防止逆向分析。
对于apk安全方面的技术,层出不穷,安全攻防拉锯技术不断改进不断提升。
apk的编译是把我们写的代码、资源、库文件打包成可以在Android设备上运行的过程,而为了代码、数据、业务安全诉求需要对apk进行安全防护工作。
安全攻防是魔高一尺、道高一丈,特别是金融类app需要高度重视的工作,在此与大家一起学习和分享。