热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android应用篇app签名与加固原理分析

今天来写一下app的签名与加固的原理。签名的原理1.releaseapk的签名文件可以看到,签名打包好的apk中有一个META-INF目录,里面包括三

今天来写一下 app 的签名与加固的原理。

 

签名的原理

1. release apk 的签名文件

可以看到,签名打包好的 apk 中有一个 META-INF 目录,里面包括三个重要的文件:

  1. MANIFEST.MF
  2. CERT.SF
  3. CERT.RSA

来简单看下这三个文件的内容。

MANIFEST.MF:

Manifest-Version: 1.0
Built-By: Generated-by-ADT
Created-By: Android Gradle 2.3.0Name: res/layout/design_text_input_password_icon.xml
SHA1-Digest: YoJd6IrT+4Fi8q5etiV4CGfrcMg=Name: res/drawable-xxhdpi-v4/abc_ic_star_half_black_16dp.png
SHA1-Digest: EikVyBT5I7pmbJO2k8qF0V5hUc0=Name: solidity/ens/build/AbstractENS.bin
SHA1-Digest: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=Name: res/drawable/bg_top_bar_light_theme.xml
SHA1-Digest: G1WVIvrL6vVO7cHlz5sOJM2Yw9g=Name: res/layout/activity_transaction_send.xml
SHA1-Digest: WrZZ/cTwgm92KS+iW82elSy2ets=Name: res/drawable/bg_solid_selector_light_theme.xml
SHA1-Digest: CRQNmUrjsUhbW5g0TaJQTTTg49s=Name: res/drawable/notification_bg_low.xml
SHA1-Digest: T3aVUj+I2HyqpDuAMmdpIkSA/yY=Name: res/drawable-xhdpi-v4/abc_ic_star_black_48dp.png
SHA1-Digest: pUh1yzyRb6c3D0mL7cI4wKExeTI=Name: res/layout/activity_about_us.xml
SHA1-Digest: j58VkJ7ytUJJnCLv/u8bLBZSHLc=Name: res/drawable/btn_security_keyboard_delete_selector_light_theme.xml
SHA1-Digest: 2pcdMLjyn5er5D8lZSP2LE/dLFI=Name: res/drawable/abc_list_selector_background_transition_holo_light.xml
SHA1-Digest: AdlBQF7mCKx3VlJQ4HaRjQeNXyo=Name: res/drawable-xxhdpi-v4/ic_nav_wallet_switcher.png
SHA1-Digest: g8DLztAbX8rqkub9B0fD6rwsces=Name: assets/ShareSDK.xml
SHA1-Digest: spQnpYXJlxaiSoFJKoxJuqRGow0=Name: res/color/abc_primary_text_disable_only_material_dark.xml
SHA1-Digest: 4t4rkEo1veLwAALxNPcESWEUDcg=Name: META-INF/rxjava.properties
SHA1-Digest: +hrVyVtjCoOVFh3dKfYglbzj5S8=Name: res/drawable-xxhdpi-v4/ic_nav_delete.png
SHA1-Digest: mOta3HgN3CGFfmJ5Yrp0F7Ggv5E=Name: res/mipmap-hdpi-v4/ic_launcher.png
SHA1-Digest: 6r5KrzZ9OlIwT0RuvqKZoTI3DRI=Name: res/drawable-xhdpi-v4/ic_qrcode_scan_line.png
SHA1-Digest: 1hGLNSq7gfO1cX0Nbd59GOPwh+M=// ...

MANIFEST.MF 就是逐一遍历 apk 里面的所有条目,如果是目录就跳过,如果是一个文件,就用 SHA1 或者 SHA256 消息摘要算法提取出该文件的摘要然后进行 BASE64 编码后,作为 "SHA1-Digest" 属性的值写入到 MANIFEST.MF 文件中的一个块中。该块有一个 "Name" 属性,其值就是该文件在 apk 包中的路径。

CERT.SF:

Signature-Version: 1.0
X-Android-APK-Signed: 2
SHA1-Digest-Manifest: 3PRb1OObhDRdv3ZpZ5FBNQzRGtw=
Created-By: 1.0 (Android)Name: res/layout/design_text_input_password_icon.xml
SHA1-Digest: YoJd6IrT+4Fi8q5etiV4CGfrcMg=Name: res/drawable-xxhdpi-v4/abc_ic_star_half_black_16dp.png
SHA1-Digest: EikVyBT5I7pmbJO2k8qF0V5hUc0=Name: solidity/ens/build/AbstractENS.bin
SHA1-Digest: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=Name: res/drawable/bg_top_bar_light_theme.xml
SHA1-Digest: G1WVIvrL6vVO7cHlz5sOJM2Yw9g=// ...

计算这个 MANIFEST.MF 文件的整体 SHA1 值,再经过 BASE64 编码后,记录在 CERT.SF 主属性块 (在文件头上) 的 "SHA1-Digest-Manifest" 属性值下。

然后,再逐条计算 MANIFEST.MF 文件中每一个块的 SHA1,并经过 BASE64 编码后,记录在 CERT.SF 中的同名块中,属性的名字是 "SHA1-Digest"。

CERT.RSA:

3082 0354 0609 2a86 4886 f70d 0107 02a0
8203 4530 8203 4102 0101 310b 3009 0605
2b0e 0302 1a05 0030 0b06 092a 8648 86f7
0d01 0701 a082 021f 3082 021b 3082 0184
a003 0201 0202 0457 5abd 4d30 0d06 092a
8648 86f7 0d01 0105 0500 3051 3110 300e
0603 5504 0713 0762 6569 6a69 6e67 3113
3011 0603 5504 0a13 0a73 7569 7368 6974
6563 6831 1330 1106 0355 040b 130a 7375
6973 6869 7465 6368 3113 3011 0603 5504
0313 0a73 7569 7368 6974 6563 6830 2017
0d31 3630 3631 3031 3331 3435 335a 180f
3330 3135 3130 3132 3133 3134 3533 5a30
// ...

把之前生成的 CERT.SF 文件,用私钥计算出签名,然后将签名以及包含公钥信息的数字证书一同写入 CERT.RSA 中保存。CERT.RSA 是一个满足 PKCS7 格式的文件,可以通过 openssl 工具来查看签名证书的信息。

 

2. 签名流程

  • 对 apk 中的每个文件做一次算法 (数据摘要 + BASE64 编码),保存到 MANIFEST.MF 文件中。
  • 对 MANIFEST.MF 整个文件做一次算法 (数据摘要 + BASE64 编码),存放到 CERT.SF 文件的头属性中,再对 MANIFEST.MF文件中各个属性块做一次算法 (数据摘要+Base64编码),存到到一个属性块中。
  • 用私钥对 CERT.SF 文件做签名,然后将签名以及包含公钥信息的数字证书一同写入 CERT.RSA 中保存。

 

3. 验证流程

Android 平台上所有应用程序安装都是由 PackageManangerService,Android 的安装流程非常复杂,与签名验证相关的步骤位于 下面两个类,有兴趣的可以看看:

frameworks/base/core/java/android/content/pm/PackageManagerService.java

安装应用时 PackageManagerService 会对 apk 进行签名检查,具体分为以下几步:

  • 1. 读取 CERT.RSA (证书信息包括公钥)、MANIFEST.MF、CERT.SF。
  • 2. 使用获取的公钥对 CERT.SF 解密,将解密结果和 MANIFEST.MF 进行比较,如果相同说明证书有效、MANIFEST.MF 未被更改。
  • 3. 对 apk 中所有文件内容分别进行消息摘要计算,将结果的 BASE64 编码和 MANIFEST.MF 里的相应内容进行比较,全部相同则 apk 的内容未被更改。

如何判断证书是否有效?

因为签名的时候是使用私钥对 MANIFEST.MF 进行加密保存在 CERT.SF 中,之后只需要用证书中的公钥对 CERT.SF 进行解密,将结果和 MANIFEST.MF 进行比较即可。注意,在证书正确的情况下如果更改的 apk 里面文件内容,此时以上判断还是不会通过。因为 MANIFEST.MF 保存的是 apk 里面所有文件的哈希值,只要改变了 apk 里文件内容,哈希值就会变化。


如何判断 apk 是否被更改?

在签名保证 MANIFEST.MF 有效的前提下,只要对当前 apk 所有文件的内容的哈希值的 BASE64 编码和 MANIFEST.MF 相应文件存的值进行比较即可。


如何防范被重新签名?

通过反编译工具 (apktool) 可以轻易的对 apk 进行重新签名,针对这种情况可以通过以下几个方法加强被成功重新签名的难度:

  • 对 apk 进行加壳处理,增加反编译难度。
  • 程序中对签名自行验证,可配合服务端进行。

不过没有什么是绝对安全的,只是破解的成本和收益的博弈。比如方法一可以通过出壳进行破解,只是增加了出壳的成本。方法二可以在反编译的时候通过更改相关 smali 代码绕过签名验证的地方,只是增加了反编译成本。

 

 

加固的原理

1. 加固原理解析

在该过程中涉及到3个对象,分别如下:

  • 源程序

源程序也就是我们的要加固的对象,这里面主要修改的是原 apk 文件中的 classes.dex 文件和 AndroidManifest.xml 文件。

  • 壳程序

壳程序主要用于解密经过加密了的 dex 文件,并加载解密后的原 dex 文件,并正常启动原程序。

  • 加密程序

加密程序主要是对原 dex 文件进行加密,加密算法可以是简单的异或操作、反转、RC4、DES、RSA 等加密算法。

加固过程可以分为如下4个阶段:

  1. 加密阶段。
  2. 合成新的 dex 文件。
  3. 修改原 apk 文件并重打包签名。
  4. 运行壳程序加载原 dex 文件。

下面来分别分析这4个阶段。

 

2. 加密阶段

加密阶段主要是讲把原 apk 文件中提取出来的 classes.dex 文件通过加密程序进行加密。加密的时候如果使用 DES 对称加密算法,则需要注意处理好密钥的问题。同样的,如果采用非对称加密,也同样存在公钥保存的问题。

顺便来说下 dex 的结构:

这就是 dex 的文件格式了,那么 Header 中存储了什么内容呢?

 

3. 合成新的 dex 文件

这一阶段主要是讲上一步生成的加密的 dex 文件和壳 dex 文件合并,将加密的 dex 文件追加在壳 dex 文件后面,并在文件末尾追加加密 dex 文件的大小数值。

在壳程序里面,有个重要的类:ProxyApplication 类,该类继承 Application 类,也是应用程序最先运行的类。所以,我们就是在这个类里面,在原程序运行之前,进行一些解密 dex 文件和加载原 dex 文件的操作。

 

4. 修改原 apk 文件并重打包签名

在这一阶段,我们首先将 apk 解压,修复 classes.dex 和 AndroidManifest.xml 这两个文件,其他文件和文件加都不需要改动。首先,我们把解压后 apk 目录下原来的 classes.dex 文件替换成合成的新的 classes.dex 文件。然后,由于我们程序运行的时候,首先加载的其实是壳程序里的 ProxyApplication 类。所以,我们需要修改 AndroidManifest.xml 文件,指定 application 为ProxyApplication,这样才能正常找到识别 ProxyApplication 类并运行壳程序。

5. 运行壳程序加载原 dex 文件

Dalvik/ART 虚拟机会加载我们经过修改的新的 classes.dex 文件,并最先运行 ProxyApplication 类。在这个类里面,有2个关键的方法:attachBaseContext() 和 onCreate() 方法。ProxyApplication 先运行 attachBaseContext() 再运行 onCreate() 方法。

在 attachBaseContext() 方法里,主要做两个工作:

  1. 读取 classes.dex 文件末尾记录加密 dex 文件大小的数值,则加密 dex 文件在新 classes.dex 文件中的位置为:len (新classes.dex 文件) – len (加密 dex 文件大小)。然后将加密的 dex 文件读取出来,加密并保存到资源目录下。
  2. 然后使用自定义的 DexClassLoader 加载解密后的原 dex 文件。

在 onCreate() 方法中,主要做两个工作:

  1. 通过反射修改 ActivityThread 类,并将 Application 指向原 dex 文件中的 Application。
  2. 创建原 Application 对象,并调用原 Application 的 onCreate() 方法启动原程序。

 

6. 加固的优缺点

优点:

  1. 保护自己核心代码算法,提高破解/盗版/二次打包的难度。
  2. 缓解代码注入/动态调试/内存注入攻击。

缺点:

  1. 影响兼容性。
  2. 影响程序运行效率。
  3. 部分流氓、病毒也会使用加壳技术来保护自己。
  4. 部分应用市场会拒绝加壳后的应用上架。

 


推荐阅读
  • POJ 2482 星空中的星星:利用线段树与扫描线算法解决
    在《POJ 2482 星空中的星星》问题中,通过运用线段树和扫描线算法,可以高效地解决星星在窗口内的计数问题。该方法不仅能够快速处理大规模数据,还能确保时间复杂度的最优性,适用于各种复杂的星空模拟场景。 ... [详细]
  • 2023年最新Linux环境下Android开发环境搭建指南
    2023年最新Linux环境下Android开发环境搭建指南,帮助Android开发者在Linux系统上快速搭建开发环境,解决常见的配置问题。 ... [详细]
  • 在1995年,Simon Plouffe 发现了一种特殊的求和方法来表示某些常数。两年后,Bailey 和 Borwein 在他们的论文中发表了这一发现,这种方法被命名为 Bailey-Borwein-Plouffe (BBP) 公式。该问题要求计算圆周率 π 的第 n 个十六进制数字。 ... [详细]
  • 本文介绍了一种通过设置主题(Theme)来实现快速启动的Android引导页,并详细说明了如何避免因不同屏幕分辨率导致的图片拉伸问题。 ... [详细]
  • 在 Android 开发中,`android:exported` 属性用于控制组件(如 Activity、Service、BroadcastReceiver 和 ContentProvider)是否可以被其他应用组件访问或与其交互。若将此属性设为 `true`,则允许外部应用调用或与之交互;反之,若设为 `false`,则仅限于同一应用内的组件进行访问。这一属性对于确保应用的安全性和隐私保护至关重要。 ... [详细]
  • 探讨了在HTML表单中使用元素代替进行表单提交的方法。 ... [详细]
  • 问题概述:在本地环境中代码运行正常,但部署到服务器后出现错误。具体表现为NCManager和SAXBuilder无法解析为类型。 ... [详细]
  • OBS Studio自动化实践:利用脚本批量生成录制场景
    本文探讨了如何利用OBS Studio进行高效录屏,并通过脚本实现场景的自动生成。适合对自动化办公感兴趣的读者。 ... [详细]
  • importjava.io.*;importjava.util.*;publicclass五子棋游戏{staticintm1;staticintn1;staticfinalintS ... [详细]
  • 本文介绍了如何在 Node.js 中使用流(Stream)进行数据读取与写入,包括创建可读流与可写流的基本方法,并提供了具体的代码示例。 ... [详细]
  • JavaScript 实现图片文件转Base64编码的方法
    本文详细介绍了如何使用JavaScript将用户通过文件输入控件选择的图片文件转换为Base64编码字符串,适用于Web前端开发中图片上传前的预处理。 ... [详细]
  • 本文介绍了如何在Android应用中通过Intent调用其他应用的Activity,并提供了详细的代码示例和注意事项。 ... [详细]
  • 本文详细介绍了在编写jQuery插件时需要注意的关键要点,包括模块化支持、命名规范和性能优化等内容,旨在帮助开发者提高插件的质量和可维护性。 ... [详细]
  • 在安装 iOS 开发所需的 CocoaPods 时,用户可能会遇到多种问题。其中一个常见问题是,在执行 `pod setup` 命令后,系统无法连接到 GitHub 以更新 CocoaPods/Specs 仓库。这可能是由于网络连接不稳定、GitHub 服务器暂时不可用或本地配置错误等原因导致。为解决此问题,建议检查网络连接、确保 GitHub API 限制未被触发,并验证本地配置文件是否正确。 ... [详细]
  • 在Ubuntu上安装MySQL时解决缺少libaio.so.1错误及libaio在MySQL中的重要性分析
    在Ubuntu系统上安装MySQL时,遇到了缺少libaio.so.1的错误。本文详细介绍了如何解决这一问题,并深入探讨了libaio库在MySQL性能优化中的重要作用。对于初学者而言,理解这些依赖关系和配置步骤是成功安装和运行MySQL的关键。通过本文的指导,读者可以顺利解决相关问题,并更好地掌握MySQL在Linux环境下的部署与管理。 ... [详细]
author-avatar
php小学生
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有