MASTG-TECH-0038: 打补丁
对 Android Manifest 或字节码进行小的更改通常是解决小烦恼的最快方法,这些小烦恼会妨碍您测试或逆向工程应用。 在 Android 上,尤其经常发生两个问题
- 您无法使用代理拦截 HTTPS 流量,因为应用采用了 SSL 锁定。
- 您无法将调试器附加到应用,因为 Android Manifest 中的
android:debuggable
标志未设置为"true"
。
在大多数情况下,可以通过对应用进行细微更改(即打补丁),然后重新签名和重新打包来解决这两个问题。运行默认 Android 代码签名之外的其他完整性检查的应用是例外。 在这些情况下,您还必须修补其他检查。
第一步是使用 apktool
解包和反汇编 APK
apktool d target_apk.apk
注意:为了节省时间,如果只想解包 APK 而不反汇编代码,可以使用标志
--no-src
。 例如,当您只想修改 Android Manifest 并立即重新打包时。
补丁示例:禁用证书锁定¶
对于出于合法原因想要拦截 HTTPS 通信的安全测试人员来说,证书锁定是一个问题。 修补字节码以停用 SSL 锁定可以对此有所帮助。 为了演示绕过证书锁定,我们将演练示例应用中的实现。
解包和反汇编 APK 后,就可以在 Smali 源代码中查找证书锁定检查。 在代码中搜索“X509TrustManager”之类的关键字应该会引导您朝着正确的方向前进。
在我们的示例中,搜索“X509TrustManager”会返回一个实现自定义 TrustManager 的类。 派生类实现方法 checkClientTrusted
、checkServerTrusted
和 getAcceptedIssuers
。
要绕过锁定检查,请将 return-void
操作码添加到每个方法的第一行。 此操作码会导致检查立即返回。 通过此修改,不会执行任何证书检查,并且应用程序接受所有证书。
.method public checkServerTrusted([LJava/security/cert/X509Certificate;Ljava/lang/String;)V
.locals 3
.param p1, "chain" # [Ljava/security/cert/X509Certificate;
.param p2, "authType" # Ljava/lang/String;
.prologue
return-void # <-- OUR INSERTED OPCODE!
.line 102
iget-object v1, p0, Lasdf/t$a;->a:Ljava/util/ArrayList;
invoke-virtual {v1}, Ljava/util/ArrayList;->iterator()Ljava/util/Iterator;
move-result-object v1
:goto_0
invoke-interface {v1}, Ljava/util/Iterator;->hasNext()Z
此修改会破坏 APK 签名,因此在重新打包后,您还必须重新签署更改后的 APK 存档。
补丁示例:使应用可调试¶
每个启用调试器的进程都会运行一个额外的线程来处理 JDWP 协议数据包。 仅对于在其清单文件的 <application>
元素中设置了 android:debuggable="true"
标志的应用,才会启动此线程。 这是运送给最终用户的 Android 设备的典型配置。
在对应用进行逆向工程时,您通常只能访问目标应用的发布版本。 发布版本并非旨在进行调试,这是调试版本的目的。 如果系统属性 ro.debuggable
设置为“0”,则 Android 不允许对发布版本进行 JDWP 和本机调试。 尽管这很容易绕过,但您仍然可能会遇到限制,例如缺少行断点。 然而,即使是不完美的调试器仍然是一个宝贵的工具,能够检查程序的运行时状态可以使理解程序容易得多。
要将发布版本转换为可调试版本,您需要修改 Android Manifest 文件 (AndroidManifest.xml) 中的标志。 解包应用(例如 apktool d --no-src UnCrackable-Level1.apk
)并解码 Android Manifest 后,使用文本编辑器将 android:debuggable="true"
添加到其中
<application android:allowBackup="true" android:debuggable="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:name="com.xxx.xxx.xxx" android:theme="@style/AppTheme">
即使我们没有更改源代码,此修改也会破坏 APK 签名,因此您还必须重新签署更改后的 APK 存档。
修补 React Native 应用¶
如果 React Native 框架已用于开发,则主应用程序代码位于文件 assets/index.android.bundle
中。 此文件包含 JavaScript 代码。 大多数情况下,此文件中的 JavaScript 代码是经过最小化的。 通过使用工具 JStillery 可以检索该文件的人类可读版本,从而允许进行代码分析。 应该首选 JStillery 的 CLI 版本或本地服务器,而不是使用在线版本,否则源代码将被发送并披露给第三方。
可以使用以下方法来修补 JavaScript 文件
- 使用
apktool
工具解包 APK 存档。 - 将文件
assets/index.android.bundle
的内容复制到临时文件中。 - 使用
JStillery
美化和反混淆临时文件的内容。 - 在临时文件中确定应修补代码的位置,并实施更改。
- 将修补后的代码放在单行上,并将其复制到原始
assets/index.android.bundle
文件中。 - 使用
apktool
工具重新打包 APK 存档,并在将其安装在目标设备/模拟器上之前对其进行签名。