跳过内容

MASTG-TECH-0038: 打补丁

对 Android Manifest 或字节码进行小的更改通常是解决小烦恼的最快方法,这些小烦恼会妨碍您测试或逆向工程应用。 在 Android 上,尤其经常发生两个问题

  1. 您无法使用代理拦截 HTTPS 流量,因为应用采用了 SSL 锁定。
  2. 您无法将调试器附加到应用,因为 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 的类。 派生类实现方法 checkClientTrustedcheckServerTrustedgetAcceptedIssuers

要绕过锁定检查,请将 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 文件

  1. 使用 apktool 工具解包 APK 存档。
  2. 将文件 assets/index.android.bundle 的内容复制到临时文件中。
  3. 使用 JStillery 美化和反混淆临时文件的内容。
  4. 在临时文件中确定应修补代码的位置,并实施更改。
  5. 修补后的代码放在单行上,并将其复制到原始 assets/index.android.bundle 文件中。
  6. 使用 apktool 工具重新打包 APK 存档,并在将其安装在目标设备/模拟器上之前对其进行签名。