什么是代码混淆?

#

代码混淆是修改应用程序的二进制文件,使其更难被人理解的过程。混淆会隐藏编译后的 Dart 代码中的函数和类名,用另一个符号替换每个符号,从而使攻击者难以对你的专有应用程序进行逆向工程。

限制和警告

#

Flutter 的代码混淆仅适用于发布版本构建

混淆代码**不会**加密资源,也不会阻止逆向工程。它只会用更模糊的名称重命名符号。

Web 应用不支持混淆。Web 应用可以被缩小化,这会提供类似的结果。当你构建 Flutter Web 应用的发布版本时,Web 编译器会缩小化该应用。要了解更多信息,请参阅构建和发布 Web 应用

支持的目标

#

以下构建目标支持本页描述的混淆过程

  • aar
  • apk
  • appbundle
  • ios
  • ios-framework
  • ipa
  • linux
  • macos
  • macos-framework
  • windows

有关构建目标可用的命令行选项的详细信息,请运行以下命令。输出中应列出 --obfuscate--split-debug-info 选项。如果未列出,你需要安装更新的 Flutter 版本才能混淆代码。

flutter build <build-target> -h
  • <build-target>:构建目标。例如,apk

混淆你的应用

#

要混淆你的应用并创建符号映射,请在发布模式下使用 flutter build 命令以及 --obfuscate--split-debug-info 选项。如果你将来想要调试混淆后的应用,你将需要符号映射。

  1. 运行以下命令以混淆你的应用并生成一个 SYMBOLS 文件

    flutter build <build-target> \ 
       --obfuscate \ 
       --split-debug-info=/<symbols-directory>
    • <build-target>:构建目标。例如,apk
    • <symbols-directory>:SYMBOLS 文件应放置的目录。例如,out/android
  2. 一旦你混淆了二进制文件,**请备份 SYMBOLS 文件**。如果你丢失了原始 SYMBOLS 文件并希望解混淆堆栈跟踪,你可能需要它。

读取混淆的堆栈跟踪

#

要调试由混淆应用生成的堆栈跟踪,请使用以下步骤使其易于阅读

  1. 找到匹配的 SYMBOLS 文件。例如,来自 Android arm64 设备的崩溃需要 app.android-arm64.symbols

  2. 将堆栈跟踪(存储在文件中)和 SYMBOLS 文件都提供给 flutter symbolize 命令。

    flutter symbolize \
       -i <stack-trace-file> \
       -d <obfuscated-symbols-file>
    • <stack-trace-file>:堆栈跟踪的文件路径。例如,???
    • <obfuscated-symbols-file>:包含混淆符号的符号文件的文件路径。例如,out/android/app.android-arm64.symbols

    有关 symbolize 命令的更多信息,请运行 flutter symbolize -h

读取混淆的名称

#

你可以生成一个包含混淆映射的 JSON 文件。混淆映射是一个 JSON 数组,其中包含原始名称和混淆名称的对。例如,["MaterialApp", "ex", "Scaffold", "ey"],其中 exMaterialApp 的混淆名称。

要生成混淆映射,请使用以下命令

flutter build <build-target> \
   --obfuscate \
   --split-debug-info=/<symbols-directory> \
   --extra-gen-snapshot-options=--save-obfuscation-map=/<obfuscation-map-file>
  • <build-target>:构建目标。例如,apk
  • <symbols-directory>:符号应放置的目录。例如,out/android
  • <obfuscation-map-file>:JSON 混淆映射应放置的文件路径。例如,out/android/map.json

注意事项

#

在编写最终将成为混淆二进制文件的应用程序时,请注意以下事项。

  • 依赖于匹配特定类、函数或库名称的代码将失败。例如,以下对 expect() 的调用在混淆的二进制文件中将不起作用

    dart
    expect(foo.runtimeType.toString(), equals('Foo'));
  • 枚举名称目前未被混淆。