Allatori 配置文件指南

写在前面

刚好最近在研究Java代码混淆,发现Allatori用的人还挺多的,但是相关文档比较少,就去把官网的文档顺手翻译了一下,这个应该适用最新版的Allatori,新版目前还没看到破解的,有些功能估计老版本用不了,等有空再研究研究

1 配置文件结构

1.1 Input 标签

Input 标签用于设置需要混淆的 jar(war、ear)文件或文件夹。该标签中至少应包含一个 jar 或 dir 标签,以指定输入和输出文件。

Input 标签有两个可选属性:

属性
basedir 可选属性。jar 文件的相对路径将相对于指定的目录进行解析。默认情况下,相对路径会相对于配置文件所在位置进行解析。
single-jar 可选属性。Allatori 会额外创建一个包含所有混淆后类的输出 jar 文件。

Jar 标签有两个必填属性:

属性
in 必填属性。需要混淆的 jar 文件名称。
out 必填属性。输出 jar 文件的名称。该名称可以与 in 属性的值相同,此时原 jar 文件会被混淆后的版本覆盖。

Jar 标签中可包含嵌套标签,用于混淆 war/ear 文件内部的嵌套 jar 文件:

<jar in="web.war" out="web-obf.war">
    <nested in="BOOT-INF/lib/some-library.jar"/>
    <nested in="BOOT-INF/lib/my*.jar"/>
</jar>

Dir 标签有两个必填属性:

属性
in 必填属性。包含需要混淆的类文件的目录名称。
out 必填属性。用于存放混淆后类文件的输出目录名称。为避免因配置中出现拼写错误而意外删除数据,输出目录在写入新文件前不会被清空。因此,在运行 Allatori 之前,应手动清空输出目录(例如,可使用 Ant 的 delete 任务)。

示例:

<input basedir="input-jars" single-jar="application.jar">
    <jar in="app.jar" out="app-obf.jar"/>
    <jar in="input/*.jar" out="output/*.jar"/>

    <jar in="web.war" out="web-obf.war">
        <nested in="BOOT-INF/lib/some-library.jar"/>
        <nested in="BOOT-INF/lib/my*.jar"/>
    </jar>

    <dir in="in-dir" out="out-dir"/>
</input>

1.2 Classpath 标签

Classpath 标签用于为待混淆的应用设置类路径,其内部包含嵌套的 jar 标签,这些标签指定了需要添加到类路径中的 jar 文件名称。虽然并非必须引用应用所需的所有库 jar 文件,但缺少类路径元素可能会导致混淆效果减弱。在混淆过程中,Allatori 会对所有缺失的类发出警告。

Classpath 标签有一个可选属性:

属性
basedir 可选属性。jar 文件的相对路径将相对于指定的目录进行解析。默认情况下,相对路径会相对于配置文件所在位置进行解析。

嵌套的 jar 标签有一个必填属性:

属性
name 必填属性。需要添加到类路径中的 jar 文件名称。支持通配符语法:* 匹配文件名中的任意字符;** 表示递归匹配子目录中的文件。

示例:

<classpath basedir="library-jars"> <!-- 将 library.jar 添加到类路径中 -->
    <jar name="library.jar"/> <!-- 将 lib 目录下的所有 jar 文件添加到类路径中 -->
    <jar name="lib/*.jar"/> <!-- 将 lib2 目录及其子目录下的所有 jar 文件添加到类路径中 -->
    <jar name="lib2/**/*.jar"/>
</classpath>

1.3 Keep-names 标签

Keep-names 标签用于设置在混淆过程中不应被重命名的类、方法和字段的名称。如果待混淆的应用是一个库,那么需要保留所有公共 API(应用程序编程接口)的名称;如果是独立应用,则至少需要保留主类的名称。此外,对于通过反射机制使用的类和方法的名称,也应进行保留。

可以使用注解对需要重命名的元素进行更精确的控制,且注解的设置会覆盖配置文件中的相关设置。

Keep-names 标签中可包含任意数量的以下嵌套标签:

  • field 标签:用于指定不应被重命名的字段。
  • method 标签:用于指定不应被重命名的方法。
  • class 标签:用于指定不应被重命名的类,该标签内部还可嵌套 field 和 method 标签。

这些嵌套标签用于设置匹配类、字段和方法名称的规则,符合规则的名称在混淆时不会被修改。所有这些标签都可以设置 access 或 template 属性。

access 属性根据访问级别来匹配元素,其可能的取值如下:

描述
private 匹配具有 private(私有)访问权限的类、字段或方法。
private+ 匹配具有 private 或更宽松访问权限(即包访问权限、protected、public)的类、字段或方法。
package 匹配具有 package(包)访问权限的类、字段或方法。
package+ 匹配具有 package 或更宽松访问权限(即 protected、public)的类、字段或方法。
protected 匹配具有 protected(受保护)访问权限的类、字段或方法。
protected+ 匹配具有 protected 或更宽松访问权限(即 public)的类、字段或方法。
public 匹配具有 public(公共)访问权限的类、字段或方法。

template 属性的语法与 Java 语言语法类似,针对 class、field 和 method 标签,其格式有所不同。

1.3.1 class 标签

class 标签用于匹配类,它具有以下属性:

属性
access 必选*。用于设置匹配规则,可能的取值如上表所示。
template 必选*。用于设置匹配规则,其格式如下文所述。
ignore 可选属性。若设置为 “true” 或 “yes”,则匹配到的类会被重命名,但该类内部嵌套的 method 和 field 标签仍会按正常规则处理。此属性可实现在不保留类名的情况下,保留部分字段和方法的名称。若设置为 “keep-if-members-match”,则只有当至少一个嵌套的 method 或 field 标签也能匹配时,才会保留该类的名称。
stop 可选属性。若设置为 “true” 或 “yes”,则 Allatori 对匹配到的类不再应用后续的规则。

*注:access 和 template 属性二者必填其一。

class 标签的 template 属性格式如下: [not] [@annotation] [modifiers] (class | interface) classname [extends classname] [implements classname] [instanceof classname]

在类名或类型名中,* 符号可匹配任意数量的字符。若名称以 regex: 开头,则会将其视为标准的正则表达式。

示例:

描述
class * 匹配所有类和接口。
interface * 匹配所有接口。
public class * 匹配所有公共类和公共接口。
protected+ class * 匹配所有受保护和公共的类与接口。
class abc 匹配所有全限定名中包含 “abc” 的类。
class com.abc.* 匹配 com.abc 包及其子包下的所有类。
class .abc. 匹配所有名称为 “abc” 的包及其子包下的所有类。
class * extends java.util.Enumeration 匹配所有继承自 java.util.Enumeration 的类。
class * extends *.Enumeration 匹配所有继承自 Enumeration(无论其所在包)的类。
class * instanceof java.io.Serializable 匹配所有属于 java.io.Serializable 实例的类(即实现了该接口的类)。
class * implements *.MouseListener 匹配所有实现了 MouseListener(无论其所在包)的类。
@java.lang.Deprecated class * 匹配所有被 @java.lang.Deprecated 注解标记的类。
class regex:com.package.(foo bar).*
class regex:com.package.(foo bar)..*
not class com.abc.* 匹配所有不在 com.abc 包及其子包下的类。

1.3.2 field 标签

field 标签用于匹配字段。若该标签嵌套在 class 标签内部,则仅对 class 标签匹配到的类生效;若其父标签为 keep-names,则对所有类生效。field 标签具有以下属性:

属性
access 必选*。用于设置匹配规则,可能的取值如上表所示。
template 必选*。用于设置匹配规则,其格式如下文所述。

*注:access 和 template 属性二者必填其一。

field 标签的 template 属性格式如下: [not] [@annotation] [modifiers] [type] fieldname [instanceof classname]

在字段名或类型名中,* 符号可匹配任意数量的字符。若名称以 regex: 开头,则会将其视为标准的正则表达式。

示例:

描述
* 匹配所有字段。
private * 匹配所有私有字段。
private+ * 匹配所有字段(包括私有、包访问权限、受保护、公共字段)。
protected+ * 匹配所有受保护和公共字段。
static * 匹配所有静态字段。
public static * 匹配所有公共静态字段。
public int * 匹配所有公共的 int 类型字段。
java.lang.String * 匹配所有 String 类型字段。
java.lang.* * 匹配所有类型属于 java.lang 包的字段。
abc* 匹配所有名称以 “abc” 开头的字段。
private abc* 匹配所有名称以 “abc” 开头的私有字段。
* instanceof java.io.Serializable 匹配所有可序列化(属于 java.io.Serializable 实例)的字段。
@java.lang.Deprecated * 匹配所有被 @java.lang.Deprecated 注解标记的字段。
regex:(a b).*
not abc* 匹配所有名称不以 “abc” 开头的字段。

1.3.3 method 标签

method 标签用于匹配方法。若该标签嵌套在 class 标签内部,则仅对 class 标签匹配到的类生效;若其父标签为 keep-names,则对所有类生效。method 标签具有以下属性:

属性
access 必选*。用于设置匹配规则,可能的取值如上表所示。
template 必选*。用于设置匹配规则,其格式如下文所述。
parameters 可选属性。若设置为 “keep”,则方法参数的名称不会被修改,此属性对公共 API 方法非常有用。

*注:access 和 template 属性二者必填其一。

method 标签的 template 属性格式如下: [not] [@annotation] [modifiers] [type] methodname(arguments)

在方法名或类型名中,* 符号可匹配任意数量的字符;在参数列表中,* 匹配单个任意参数,** 则匹配任意数量的参数。若名称以 regex: 开头,则会将其视为标准的正则表达式。

示例:

描述
*(**) 匹配所有方法。
private *(**) 匹配所有私有方法。
private+ *(**) 匹配所有方法(包括私有、包访问权限、受保护、公共方法)。
protected+ *(**) 匹配所有受保护和公共方法。
private+ () 匹配所有仅有一个参数的方法。
private+ (,*) 匹配所有有两个参数的方法。
private+ *(java.lang.String) 匹配所有仅有一个 String 类型参数的方法。
private+ *(java.lang.String,**) 匹配所有第一个参数为 String 类型的方法。
private+ (java.lang.) 匹配所有仅有一个参数且该参数类型属于 java.lang 包的方法。
public get*(**) 匹配所有名称以 “get” 开头的公共方法(通常为 getter 方法)。
public abc(**) 匹配所有名称中包含 “abc” 的公共方法。
private+ int *(**) 匹配所有返回值类型为 int 的方法。
@java.lang.Deprecated *(**) 匹配所有被 @java.lang.Deprecated 注解标记的方法。
public regex:(g s)et.*(**)
not abc(**) 匹配所有名称中不包含 “abc” 的方法。

示例:

<keep-names> 
    <!-- 对 com.company.abc 包下的类不再应用后续规则,因此该包下的所有类、方法和字段都会被重命名 -->
    <class template="class com.company.abc.*" stop="true"/> 
    <!-- 后续规则指示 Allatori 不对匹配到的元素进行重命名 -->

    <!-- 匹配任意包下名称为 "Main" 的类 -->
    <class template="class *.Main"/> 
    <!-- 匹配名称以 "Bean" 结尾的类 -->
    <class template="class *Bean"> 
        <!-- 匹配所有字段 -->
        <field access="private+"/> 
        <!-- 匹配公共的 int 类型字段 -->
        <field template="public int *"/> 
        <!-- 匹配所有静态字段 -->
        <field template="static *"/> 
        <!-- 匹配受保护和公共的 String 类型字段 -->
        <field template="protected+ java.lang.String *"/> 
        <!-- 匹配所有方法 -->
        <method template="private+ *(**)"/> 
        <!-- 匹配所有 getter 方法 -->
        <method template="private+ get*(**)"/> 
        <!-- 匹配所有带有 String 类型参数的方法,且这些方法的参数名称不会被修改 -->
        <method template="private+ *(java.lang.String)" parameters="keep"/>
    </class> 
    <!-- 匹配序列化相关的成员 -->
    <class template="class * instanceof java.io.Serializable">
        <field template="static final long serialVersionUID"/>
        <method template="void writeObject(java.io.ObjectOutputStream)"/>
        <method template="void readObject(java.io.ObjectInputStream)"/>
        <method template="java.lang.Object writeReplace()"/>
        <method template="java.lang.Object readResolve()"/>
    </class> 
    <!-- 匹配小程序(Applet)相关类 -->
    <class template="class * instanceof java.applet.Applet"/> 
    <!-- 匹配 Servlet 相关类 -->
    <class template="class * instanceof javax.servlet.Servlet"/> 
    <!-- 匹配 MIDlet 相关类(移动应用程序) -->
    <class template="class * instanceof javax.microedition.midlet.MIDlet"/>
</keep-names>

1.4 Watermark 标签

Watermark 标签用于设置水印的密钥(key)和值(value),该标签有两个属性:

属性
key 必填属性。用于通过隐写术将水印嵌入应用程序的密钥。
value 添加水印时必填属性。将嵌入到应用程序 jar 文件中的任意字符串,可包含版权信息、客户名称、公司名称或其他能唯一标识该构建版本的信息。水印可用于识别软件的所有者,或追踪盗版副本的来源。

示例:

<watermark key="secure-key-to-extract-watermark" value="Customer: John Smith"/>

水印也可在不进行混淆的情况下,添加到已混淆或未混淆的 jar 文件中。有关添加和提取水印的完整示例,可在 Allatori 发行版附带的教程中找到。

1.5 Expiry 标签

Expiry 标签用于为应用程序设置过期日期。过期日期检查会被插入到多个方法中,而非仅在主方法中,因此难以轻易移除该功能。此特性也可用于混淆那些甚至没有主方法的库。该标签有两个必填属性:

属性
date 必填属性。过期日期,格式为 yyyy/mm/dd。
string 必填属性。当应用程序在指定过期日期之后运行时,抛出异常所携带的字符串信息。

示例:

<expiry date="2017/01/01" string="EXPIRED!"/>

有关使用过期日期的完整示例,可在 Allatori 发行版附带的教程中找到。

1.6 Property 标签

Property 标签用于设置各种混淆属性,该标签有两个必填属性:name(属性名)和 value(属性值),格式如下:

<property name="property-name" value="property-value"/>

1.6.1 通用属性(General properties)

1.6.1.1 log-file
描述
filename Allatori 会将混淆日志写入指定的文件。若未设置该属性,则不会创建日志文件。相对路径会相对于配置文件所在位置进行解析。

日志文件用于从混淆后的堆栈跟踪信息恢复原始的堆栈跟踪,它保存了混淆后名称与原始名称、行号之间的映射关系。

示例:

<property name="log-file" value="log.xml"/>
<property name="log-file" value="logs/file.xml"/>

可使用堆栈跟踪工具(Stack trace utility)结合日志文件恢复原始名称,命令如下:

java -cp allatori.jar com.allatori.StackTrace2 log.xml input.txt output.txt
1.6.1.2 random-seed
描述
任意字符串 用于初始化随机数生成器的字符串。

该属性的默认值为当前时间(以毫秒为单位)。

默认情况下,若对相同的输入 jar 文件两次运行 Allatori,生成的输出 jar 文件会有所不同。被重命名的类、字段和方法会使用不同的名称,字段和方法的顺序也会不同,这样做是为了增加分析两个不同版本混淆应用程序的难度。若需要在多次连续运行 Allatori 时生成相同的输出 jar 文件,则需设置随机种子(random-seed)。建议为应用程序的不同公开版本设置不同的随机种子。

示例:

<property name="random-seed" value="any text here"/>

1.6.2 字符串加密属性(String encryption properties)

1.6.2.1 string-encryption
描述
enable (默认值)所有可安全替换为加密值的字符串常量都会被加密,Allatori 会添加一个在运行时解密字符串的方法。
disable 禁用字符串加密功能。
maximum 对所有字符串常量进行加密,具体限制见下文说明。
maximum-with-warnings 对所有字符串常量进行加密。对于使用 == 运算符进行的字符串比较,都会产生警告,用户可将这些比较替换为 equals() 方法调用。

在某些情况下,字符串可能会按以下方式进行比较:

String myString = "Hello";
...
public boolean test() {
    return myString == "Hello";
}

尽管使用 == 运算符而非 equals 方法比较字符串是一种不良实践,但上述示例中的方法会返回 true,因为 JVM 会缓存 String 对象,以便在同一个类中重复使用。然而,经过字符串加密后,该方法会变为如下形式:

public boolean test() {
    return myString == new String("Hello");
    // 为使示例更清晰,此处未对 "Hello" 字符串进行加密
}

在这个版本的方法中,由于比较的是不同的对象,因此会返回 false

若将 string-encryption 属性设置为 enable,Allatori 不会对使用 == 运算符比较的字符串进行加密,从而确保应用程序能正常运行。

若始终使用 equals 方法比较字符串,则可将 string-encryption 属性设置为 maximum

示例:

<property name="string-encryption" value="enable"/>

可通过注解或 apply2class 属性,对指定类启用或禁用字符串加密功能。其中,apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<!-- 对 com.abc 包下的类禁用字符串加密 -->
<property name="string-encryption" value="disable" apply2class="class com.abc.*"/> 
<!-- 对其他所有类启用字符串加密 -->
<property name="string-encryption" value="enable"/>
1.6.2.2 string-encryption-type
描述
fast (默认值)Allatori 会使用速度极快的字符串加密算法。
strong Allatori 会使用强度高且复杂的字符串加密算法,但该算法速度相对较慢。
custom (package.EncryptClassName.encryptMethodName, package.DecryptClassName.decryptMethodName) Allatori 会使用指定的字符串加密/解密方法,详情见下文说明。

该属性需在字符串加密功能已启用的前提下才能生效。

示例:

<property name="string-encryption-type" value="strong"/>

可通过注解或 apply2class 属性,为指定类设置字符串加密类型。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<!-- 为 com.abc 包下的类设置高强度字符串加密类型 -->
<property name="string-encryption-type" value="strong" apply2class="class com.abc.*"/> 
<!-- 为其他所有类设置快速字符串加密类型 -->
<property name="string-encryption-type" value="fast"/>

自定义字符串加密: 有关使用示例,可在 tutorial/step15-custom-string-encryption 文件夹中查找。

加密方法仅在混淆过程中需要,应用程序运行时无需该方法,它可位于不包含在发行版本中的单独 jar 文件中。解密方法在应用程序运行时需要,可放置在应用程序的任意类中。

可将自定义字符串加密与 Allatori 自带的字符串加密结合使用:

<property name="string-encryption-type"
value="custom(package.EncryptClassName.encryptMethodName, package.DecryptClassName.decryptMethodName)"
apply2class="class com.some.package.*"/>

也可使用多个自定义字符串加密方法:

<property name="string-encryption-type"
value="custom(EncryptClassName1.encryptMethodName1, DecryptClassName1.decryptMethodName1)"
apply2class="class com.some.package.*"/>
<property name="string-encryption-type"
value="custom(EncryptClassName2.encryptMethodName2, DecryptClassName2.decryptMethodName2)"
apply2class="class com.some.other.package.*"/> 
<!-- 为未匹配上述规则的类设置的方法 -->
<property name="string-encryption-type"
value="custom(EncryptClassName3.encryptMethodName3, DecryptClassName3.decryptMethodName3)"/>

由于自定义字符串加密特性会在运行时替换字符串,因此它也可用于国际化,而非仅用于加密。在混淆过程中(若字符串加密设置为 maximum),所有字符串都会调用加密方法,因此可利用该方法记录所有字符串;而解密方法则可在运行时将字符串替换为其国际化版本。

1.6.2.3 string-encryption-version
描述
v4 (默认值)使用新的字符串加密算法。
v3 Allatori 会使用 3.X 版本中的字符串加密算法。

该属性需在字符串加密功能已启用的前提下才能生效。

这并不意味着 v3 版本的算法已过时,我们会不时对 v3 和 v4 版本的算法进行更新,以保证其安全性。引入该属性是因为 v3 和 v4 版本算法的核心思路存在显著差异。

示例:

<property name="string-encryption-version" value="v3"/>
1.6.2.4 string-encryption-ignored-strings
描述
filename 包含应排除在混淆之外的字符串常量的文本文件,这些字符串不会被加密。

指定的文本文件中包含字符串模板,每一行代表一个新的模板,* 可匹配任意数量的任意字符。若模板行以 regex: 开头,则会按正则表达式进行处理,示例如下:

Copyright*
All Rights Reserved*
*CompanyName*
regex:\d+

示例:

<property name="string-encryption-ignored-strings" value="patterns.txt"/>

1.6.3 控制流混淆属性(Control flow obfuscation properties)

1.6.3.1 control-flow-obfuscation
描述
enable (默认值)Allatori 会修改方法的代码,在不改变应用程序运行时行为的前提下,大幅增加反编译的难度。通常,控制流混淆还能使应用程序体积更小、运行速度更快。
disable 禁用控制流混淆功能。

示例:

<property name="control-flow-obfuscation" value="enable"/>

可通过注解或 apply2class 属性,对指定类启用或禁用控制流混淆功能。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<!-- 对 com.abc 包下的类禁用控制流混淆 -->
<property name="control-flow-obfuscation" value="disable" apply2class="class com.abc.*"/> 
<!-- 对其他所有类启用控制流混淆 -->
<property name="control-flow-obfuscation" value="enable"/>
1.6.3.2 extensive-flow-obfuscation
描述
normal (默认值)Allatori 会使用控制流混淆技术,这会使混淆后的应用程序体积略微增大、运行速度略微降低,但 Allatori 会尽量减少此类代码转换的次数。
disable 禁用深度控制流混淆功能。
maximum Allatori 会充分使用控制流混淆技术,这会使混淆后的应用程序体积略微增大、运行速度略微降低。

该属性需在控制流混淆功能已启用的前提下才能生效。

示例:

<property name="extensive-flow-obfuscation" value="maximum"/>

可通过注解或 apply2class 属性,为指定类设置深度控制流混淆。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<!-- 为 com.abc 包下的类设置 "maximum" 级别的深度控制流混淆 -->
<property name="extensive-flow-obfuscation" value="maximum" apply2class="class com.abc.*"/> 
<!-- 为其他所有类设置 "normal" 级别的深度控制流混淆 -->
<property name="extensive-flow-obfuscation" value="normal"/>

1.6.4 重命名属性(Renaming properties)

1.6.4.1 default-package
描述
包名 完整的包名,可以是已存在的包名,也可以是新的包名。

若某个包中的所有类都被重命名,Allatori 会将这些类移动到默认包(default package)中。若要将所有被重命名的类都移动到默认包,则需启用 force-default-package 属性。将默认包设置为空字符串("")可减小生成的 jar 文件体积。

示例:

<property name="default-package" value=""/>
<property name="default-package" value="com.company.product"/>
1.6.4.2 force-default-package
描述
disable (默认值)仅将那些所在包中所有类都被重命名的类移动到默认包。
enable 将所有被重命名的类都移动到默认包。

该属性需在已设置默认包(default-package)的前提下才能生效。

示例:

<property name="force-default-package" value="enable"/>
1.6.4.3 packages-naming
描述
abc (默认值)包名会被重命名为 ‘a’、‘b’、‘c’、’d’……‘aa’、‘ab’ 等,且仅使用小写字母。
ABC 包名会被重命名为 ‘A’、‘B’、‘C’、‘D’……‘AA’、‘AB’ 等,且仅使用大写字母。
123 包名会被重命名为 ‘1’、‘2’、‘3’……‘00’、‘01’ 等。
keep 保留包的原始名称。
custom(filename.txt) 包名会根据指定的文本文件生成,文件的每一行代表一个单独的名称元素。例如,若文件中有两行内容分别为 ‘0’ 和 ‘1’,则生成的包名会是 ‘0’、‘1’、‘00’、‘01’、‘10’、‘11’、‘000’ 等。

示例:

<property name="packages-naming" value="abc"/>
1.6.4.4 classes-naming
描述
compact (默认值)Allatori 会尽可能使用单字符作为类名,以减小生成的 jar 文件体积。类名可能包含大小写字母,且仅大小写不同(例如 a.class 和 A.class)。Jar 文件支持大小写不同的文件名,但 Windows 文件系统不支持,因此在 Windows 系统中解压部分类文件时可能会出现问题(a.class 会覆盖 A.class)。不过,包含此类文件名的 jar 文件在所有平台(包括 Windows)上都能正常运行。
iii 所有类名都具有相同的长度,且仅大小写不同,例如 iiii、iiiI、iiIi 等。与其他重命名选项相比,生成的 jar 文件体积会更大。
lll 所有类名都由字符 ’l’(小写字母 L)和 ‘1’(数字 1)组合而成,且具有相同的长度,例如 llll、lll1、ll1l 等。与其他重命名选项相比,生成的 jar 文件体积会更大。
abc 类名会被重命名为 ‘a’、‘b’、‘c’、’d’……‘aa’、‘ab’ 等,且仅使用小写字母。
ABC 类名会被重命名为 ‘A’、‘B’、‘C’、‘D’……‘AA’、‘AB’ 等,且仅使用大写字母。
123 类名会被重命名为 ‘1’、‘2’、‘3’……‘00’、‘01’ 等。
windows Allatori 会使用 Windows 系统中禁止使用的名称(如 ‘con’、‘prn’、‘aux’、’nul’ 等)作为类名。在 jar 文件中包含 con.class 是允许的,但在 Windows 系统中无法解压该类文件。类名也可能包含大小写字母且仅大小写不同,包含此类文件名的 jar 文件在所有平台(包括 Windows)上都能正常运行。与 compact 或 abc 命名方式相比,该选项生成的 jar 文件体积会更大。
custom(filename.txt) 类名会根据指定的文本文件生成,文件的每一行代表一个单独的名称元素。例如,若文件中有两行内容分别为 ‘0’ 和 ‘1’,则生成的类名会是 ‘0’、‘1’、‘00’、‘01’、‘10’、‘11’、‘000’ 等。
unique 所有被重命名的类都将拥有唯一的名称,不同包中不会出现相同的类名。该选项可与其他类命名选项结合使用。
keep-$-sign 被重命名的类会保留 Java 内部类的命名格式,例如类 Foo 和 Foo$Bar 会被重命名为 a 和 a$b。默认情况下,Allatori 会将 Foo 和 Foo$Bar 重命名为 a 和 b。该选项可与其他类命名选项结合使用。

示例:

<property name="classes-naming" value="abc"/>
1.6.4.5 methods-naming
描述
compact (默认值)Allatori 会尽可能使用单字符作为方法名,以减小生成的 jar 文件体积。
iii 所有方法名都具有相同的长度,且仅大小写不同,例如 iiii、iiiI、iiIi 等。与其他重命名选项相比,生成的 jar 文件体积会更大。
lll 所有方法名都由字符 ’l’(小写字母 L)和 ‘1’(数字 1)组合而成,且具有相同的长度,例如 llll、lll1、ll1l 等。与其他重命名选项相比,生成的 jar 文件体积会更大。
abc 方法名会被重命名为 ‘a’、‘b’、‘c’、’d’……‘aa’、‘ab’ 等。
ABC 方法名会被重命名为 ‘A’、‘B’、‘C’、‘D’……‘AA’、‘AB’ 等。
123 方法名会被重命名为 ‘1’、‘2’、‘3’……‘00’、‘01’ 等。
keywords Allatori 会使用 Java 保留关键字(如 ‘if’、‘for’、‘int’ 等)作为方法名。这种命名方式在类文件格式中是合法的,但会对许多反编译器造成干扰。不过,与 compact 命名方式相比,该选项生成的 jar 文件体积会更大。
real 通常,根据配置规则,部分方法不会被重命名。Allatori 会将这些未被重命名的方法名分配给被重命名的方法,使得新方法名与原始方法名之间的差异难以辨别。该选项可与其他方法命名选项结合使用(若该选项的名称用尽,则会使用第二个选项的名称)。
custom(filename.txt) 方法名会根据指定的文本文件生成,文件的每一行代表一个单独的名称元素。例如,若文件中有两行内容分别为 ‘0’ 和 ‘1’,则生成的方法名会是 ‘0’、‘1’、‘00’、‘01’、‘10’、‘11’、‘000’ 等。
unique 是 unique-renaming 属性的快捷方式,可与其他方法命名选项结合使用。若两个方法的名称和签名相同,则它们会被重命名为相同的新名称;若两个方法的名称或签名不同,则重命名后的名称也会不同。这能确保在后续的增量混淆过程中保持一致性。

示例:

<property name="methods-naming" value="keywords"/>
1.6.4.6 fields-naming
描述
compact (默认值)Allatori 会尽可能使用单字符作为字段名,以减小生成的 jar 文件体积。
iii 所有字段名都具有相同的长度,且仅大小写不同,例如 iiii、iiiI、iiIi 等。与其他重命名选项相比,生成的 jar 文件体积会更大。
lll 所有字段名都由字符 ’l’(小写字母 L)和 ‘1’(数字 1)组合而成,且具有相同的长度,例如 llll、lll1、ll1l 等。与其他重命名选项相比,生成的 jar 文件体积会更大。
abc 字段名会被重命名为 ‘a’、‘b’、‘c’、’d’……‘aa’、‘ab’ 等。
ABC 字段名会被重命名为 ‘A’、‘B’、‘C’、‘D’……‘AA’、‘AB’ 等。
123 字段名会被重命名为 ‘1’、‘2’、‘3’……‘00’、‘01’ 等。
keywords Allatori 会使用 Java 保留关键字(如 ‘if’、‘for’、‘int’ 等)作为字段名。这种命名方式在类文件格式中是合法的,但会对许多反编译器造成干扰。不过,与 compact 命名方式相比,该选项生成的 jar 文件体积会更大。
real 通常,根据配置规则,部分字段不会被重命名。Allatori 会将这些未被重命名的字段名分配给被重命名的字段,使得新字段名与原始字段名之间的差异难以辨别。该选项可与其他字段命名选项结合使用(若该选项的名称用尽,则会使用第二个选项的名称)。
custom(filename.txt) 字段名会根据指定的文本文件生成,文件的每一行代表一个单独的名称元素。例如,若文件中有两行内容分别为 ‘0’ 和 ‘1’,则生成的字段名会是 ‘0’、‘1’、‘00’、‘01’、‘10’、‘11’、‘000’ 等。
unique 所有被重命名的字段都将拥有唯一的名称,该选项可与其他字段命名选项结合使用。

示例:

<property name="fields-naming" value="keywords"/>
1.6.4.7 classes-naming-prefix
描述
任意字符串 指定的字符串会作为所有被重命名类的名称前缀。

示例:

<property name="classes-naming-prefix" value="c_"/>

该属性的一种可能用法是将 MainClass$ 设为前缀:

<property name="classes-naming-prefix" value="MainClass$"/>

此时,部分反编译器会将被重命名的类视为 MainClass 的内部类。

1.6.4.8 methods-naming-prefix
描述
任意字符串 指定的字符串会作为所有被重命名方法的名称前缀。

示例:

<property name="methods-naming-prefix" value="m_"/>
1.6.4.9 fields-naming-prefix
描述
任意字符串 指定的字符串会作为所有被重命名字段的名称前缀。

示例:

<property name="fields-naming-prefix" value="f_"/>
1.6.4.10 local-variables-naming
描述
optimize (默认值)Allatori 会对方法中的局部变量进行优化,以减少局部变量的总数。剩余的局部变量会使用相同的名称(单名称重命名选项),这是默认且推荐的选项。
single-name 几乎所有局部变量都会使用相同的名称,这种方式符合 Java 虚拟机的规范,但会对许多反编译器造成干扰。
abc 局部变量会被重命名为唯一的名称,如 ‘a’、‘b’、‘c’、’d’ 等。
remove 移除局部变量的原始名称,这有助于减小生成的 jar 文件体积。
keep-parameters 保留参数名称不变,其他局部变量会被重命名。该选项对属于公共 API 的方法非常有用。此外,也可通过 keep-names 部分中的 method 标签,仅对指定方法保留参数名称。
keep 保留所有局部变量的原始名称,不推荐使用该选项。

示例:

<property name="local-variables-naming" value="single-name"/>

对于 single-name 和 optimize 重命名选项,默认的单名称为 ‘a’,可通过以下任意一行代码修改该默认名称:

<property name="local-variables-naming" value="optimize:ANY_OTHER_NAME"/>
<property name="local-variables-naming" value="optimize:int"/>
<property name="local-variables-naming" value="single-name:4"/>
1.6.4.11 skip-renaming
描述
disable (默认值)Allatori 会根据 keep-names 规则对类、方法和字段进行重命名。
enable 所有类、方法和字段都不会被重命名。局部变量的命名由 local-variables-naming 属性单独控制。字符串加密、控制流混淆等功能会根据配置文件中的设置正常应用。

示例:

<property name="skip-renaming" value="enable"/>
1.6.4.12 update-resource-names
描述
disable (默认值)不修改资源文件的名称。
enable 资源文件的名称会根据类名的变更进行修改。若资源文件的名称基于某个类名,且该类被重命名,则对应的资源文件也会被重命名。

示例:

<property name="update-resource-names" value="enable"/>
1.6.4.13 update-resource-contents
描述
disable (默认值)不修改资源文件的内容。
enable 资源文件的内容会根据类名的变更进行更新。
enable:ENCODING 使用指定的编码格式,根据类名的变更更新资源文件的内容,默认编码格式为 UTF-8。

示例:

<property name="update-resource-contents" value="enable"/>
<property name="update-resource-contents" value="enable:UTF-8"/>

可通过 apply2file 属性,将该属性应用于指定的文件:

<property name="update-resource-contents" value="enable" apply2file="*.xml"/>

1.6.5 其他属性(Other properties)

1.6.5.1 line-numbers
描述
obfuscate (默认值)调试信息会被混淆,不经过进一步处理无法使用。Allatori 提供了一个专用工具,可借助混淆后的堆栈跟踪信息重建原始的堆栈跟踪。混淆后报告的堆栈跟踪如下所示:java.lang.NullPointerExceptionat com.company.c.a(m:61)at com.company.b.b(w:94)at com.company.b.a(w:83)at com.company.a.a(n:75)使用 Allatori 堆栈跟踪工具转换后,堆栈跟踪会变为:java.lang.NullPointerExceptionat com.company.Util.createTestException(Util.java:38) at com.company.TraceTest.testNullObject(TraceTest.java:53)at com.company.TraceTest.allTraceTests(TraceTest.java:14)at com.company.Main.runTest(Main.java:27)使用该选项可减小生成的 jar 文件体积。
remove 当应用程序的体积至关重要时,可使用该选项。移除行号后报告的堆栈跟踪如下所示:java.lang.NullPointerExceptionat com.company.c.a(Unknown Source)at com.company.c.b(Unknown Source)at com.company.c.c(Unknown Source)at com.company.c.d(Unknown Source)
keep 保留调试信息,不做任何修改。该选项在应用程序内部测试时可能有所帮助,在其他情况下,建议选择其他选项。保留行号后报告的堆栈跟踪如下所示:java.lang.NullPointerExceptionat com.company.c.a(Util.java:38)at com.company.b.a(TraceTest.java:14)at com.company.a.a(Main.java:27)

(默认值)调试信息会被混淆,不经过进一步处理无法使用。Allatori 提供了一个专用工具,可借助混淆后的堆栈跟踪信息重建原始的堆栈跟踪。混淆后报告的堆栈跟踪如下所示:```java java.lang.NullPointerException at com.company.c.a(m:61) at com.company.b.b(w:94) at com.company.b.a(w:83) at com.company.a.a(n:75)

示例:
​```xml
<property name="line-numbers" value="obfuscate"/>
1.6.5.2 generics
描述
keep (默认值)若使用反射机制确定泛型类型,或需要使用混淆后的 jar 文件作为库来编译其他类,则需保留泛型类型签名。
remove 移除泛型类型信息,例如字符串向量 Vector 会被视为普通的 Vector。该选项不影响应用程序的性能,还能减小 jar 文件体积,是推荐选项。

示例:

<property name="generics" value="remove"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<!-- 保留 com.abc 包下类的泛型类型信息 -->
<property name="generics" value="keep" apply2class="class com.abc.*"/> 
<!-- 移除其他所有类的泛型类型信息 -->
<property name="generics" value="remove"/>
1.6.5.3 inner-classes
描述
keep (默认值)Java 编译器会添加包含内部类名称的信息属性,这些属性在混淆后的类中会被保留。
remove 移除这些信息属性,使得类的层次结构更难被还原。该选项不影响应用程序的性能,还能减小 jar 文件体积,是推荐选项。

示例:

<property name="inner-classes" value="remove"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<!-- 保留 com.abc 包下类的内部类信息 -->
<property name="inner-classes" value="keep" apply2class="class com.abc.*"/> 
<!-- 移除其他所有类的内部类信息 -->
<property name="inner-classes" value="remove"/>
1.6.5.4 throws-clause
描述
keep (默认值)保留方法的 throws 声明(异常声明)。
remove Java 虚拟机允许抛出未在方法中通过 throws 关键字声明的异常。此思路与 Lombok 的 sneaky throws(隐式抛出异常)类似,但此处用于混淆目的。

示例:

<property name="throws-clause" value="remove"/>

可通过 apply2class 和/或 apply2method 属性,为指定类/方法设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,apply2method 属性的格式与 method 标签的 template 属性格式相同,示例如下:

<!-- 移除所有私有方法的 throws 声明 -->
<property name="throws-clause" value="remove" apply2method="private *(**)"/> 
<!-- 移除 com.abc 包下类的 throws 声明 -->
<property name="throws-clause" value="remove" apply2class="class com.abc.*"/>
1.6.5.5 member-reorder
描述
enable (默认值)通常,开发人员会在源文件中按逻辑将相关的方法和字段排列在一起,这种顺序在编译后会被保留。Allatori 会打乱字段和方法的顺序。
random 与 enable 功能相同,即随机打乱字段和方法的顺序。
alphabetical 按字母顺序排列字段和方法。
reverse-alphabetical 按字母逆序排列字段和方法。
disable 禁用成员重排功能。

示例:

<property name="member-reorder" value="random"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<!-- 对 com.abc 包下的类进行成员重排 -->
<property name="member-reorder" value="random" apply2class="class com.abc.*"/> 
<!-- 不对其他所有类进行成员重排 -->
<property name="member-reorder" value="disable"/>
1.6.5.6 finalize
描述
disable (默认值)禁用类的 final 修饰(即不将类声明为最终类)。
enable 没有子类的类(叶子类)会被声明为 final(最终类)。该特性仅应用于混淆独立应用程序,使用后可能使应用程序运行速度更快。

示例:

<property name="finalize" value="enable"/>
1.6.5.7 version-marker
描述
有效的 Java 标识符 Allatori 会将给定的标识符用作部分被重命名方法和字段的名称,以此为混淆后的类文件添加标记。该属性可用于标记产品的演示版本,例如 Allatori 演示版本会使用 “ALLATORI_DEMO” 字符串进行标记。请注意,Allatori 演示版本会对混淆后的 jar 文件进行标记,并在该属性值前添加 “ALLATORI_DEMO_” 前缀。

示例:

<property name="version-marker" value="THIS_IS_DEMO_VERSION"/>
1.6.5.8 synthetize-methods
描述
private (默认值)所有私有方法都会被标记为 synthetic(合成方法)。
all 所有方法都会被标记为 synthetic。
package 所有具有包访问权限的方法都会被标记为 synthetic。
protected 所有受保护方法都会被标记为 synthetic。
public 所有公共方法都会被标记为 synthetic。
disable Allatori 不会将任何方法标记为 synthetic。

部分反编译器不会显示 synthetic 方法。

示例:

<property name="synthetize-methods" value="all"/>

该属性可多次使用:

<property name="synthetize-methods" value="private"/>
<property name="synthetize-methods" value="package"/>
<property name="synthetize-methods" value="protected"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<property name="synthetize-methods" value="all" apply2class="class com.abc.*"/>
<property name="synthetize-methods" value="private"/>
1.6.5.9 synthetize-fields
描述
disable (默认值)Allatori 不会将任何字段标记为 synthetic(合成字段)。
all 所有字段都会被标记为 synthetic。
private 所有私有字段都会被标记为 synthetic。
package 所有具有包访问权限的字段都会被标记为 synthetic。
protected 所有受保护字段都会被标记为 synthetic。
public 所有公共字段都会被标记为 synthetic。

部分反编译器不会显示 synthetic 字段。

示例:

<property name="synthetize-fields" value="all"/>

该属性可多次使用:

<property name="synthetize-fields" value="private"/>
<property name="synthetize-fields" value="package"/>
<property name="synthetize-fields" value="protected"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<property name="synthetize-fields" value="all" apply2class="class com.abc.*"/>
<property name="synthetize-fields" value="private"/>
1.6.5.10 set-methods-to-public
描述
disable (默认值)Allatori 不会将任何方法的访问权限修改为 public(公共)。
all 所有方法的访问权限都会被修改为 public。需注意,将私有方法修改为公共方法可能会破坏应用程序的功能。
private 所有私有方法的访问权限都会被修改为 public。将私有方法修改为公共方法可能会破坏应用程序的功能。
package 所有具有包访问权限的方法的访问权限都会被修改为 public。
protected 所有受保护方法的访问权限都会被修改为 public。

示例:

<property name="set-methods-to-public" value="all"/>

该属性可多次使用:

<property name="set-methods-to-public" value="package"/>
<property name="set-methods-to-public" value="protected"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<property name="set-methods-to-public" value="all" apply2class="class com.abc.*"/>
<property name="set-methods-to-public" value="protected"/>
1.6.5.11 set-fields-to-public
描述
disable (默认值)Allatori 不会将任何字段的访问权限修改为 public(公共)。
all 所有字段的访问权限都会被修改为 public。
private 所有私有字段的访问权限都会被修改为 public。
package 所有具有包访问权限的字段的访问权限都会被修改为 public。
protected 所有受保护字段的访问权限都会被修改为 public。

示例:

<property name="set-fields-to-public" value="all"/>

该属性可多次使用:

<property name="set-fields-to-public" value="package"/>
<property name="set-fields-to-public" value="protected"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<property name="set-fields-to-public" value="all" apply2class="class com.abc.*"/>
<property name="set-fields-to-public" value="protected"/>
1.6.5.12 remove-toString
描述
disable (默认值)不删除 toString 方法。
enable Allatori 会删除混淆类中的 toString() 方法。toString 方法可能会泄露类的相关信息,且通常仅用于调试,因此可以将其删除。

示例:

<property name="remove-toString" value="enable"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<property name="remove-toString" value="enable" apply2class="class com.abc.*"/>
<property name="remove-toString" value="enable" apply2class="class com.xyz.*"/>
1.6.5.13 remove-calls
描述
ClassName.methodName Allatori 会移除对指定方法的调用,该属性可用于移除调试日志相关的调用。ClassName(类名)和 methodName(方法名)中可包含 *,以匹配多个类/方法。若被移除调用的方法有返回值,则该返回值会被替换为 null(数值类型替换为 0,布尔类型替换为 false),且 Allatori 会在混淆过程中输出警告信息。

示例:

<property name="remove-calls" value="android.util.Log.d"/>
<property name="remove-calls" value="android.util.Log.*"/>

// 若 methodOne() 方法的调用被移除,则以下代码行
int i = methodOne(); 
// 会被修改为
int i = 0;

// 若 methodTwo() 方法的调用被移除,则以下代码行
Object o = methodTwo();
// 会被修改为
Object o = null;

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<!-- 移除 com.abc 包下类中对 Logger.debug 方法的调用 -->
<property name="remove-calls" value="com.package.Logger.debug" apply2class="class com.abc.*"/>
1.6.5.14 remove-annotations
描述
AnnotationClassName Allatori 会移除指定的 AnnotationClassName(注解类名)对应的注解,该属性可用于移除不必要的注解或调试用注解。AnnotationClassName 中可包含 *,以匹配多个注解类。

示例:

<property name="remove-annotations" value="kotlin.Metadata"/>
<property name="remove-annotations" value="com.package.annotations.*"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<!-- 移除 com.abc 包下类中的 com.package.Annotation 注解 -->
<property name="remove-annotations" value="com.package.Annotation" apply2class="class com.abc.*"/>
1.6.5.15 output-jar-compression-level
描述
0-9 设置输出 jar 文件的压缩级别,0 表示不压缩,9 表示最高压缩级别,该属性值直接映射到 ZipOutputStream.setLevel(int) 方法的参数。

示例:

<property name="output-jar-compression-level" value="9"/>
1.6.5.16 output-jar-duplicate-name-entries
描述
remove (默认值)若输入 jar 文件的同一文件夹中包含多个名称相同的文件,Allatori 仅会在输出 jar 文件中保留一个文件。
keep Allatori 会将所有名称重复的 jar 条目写入输出 jar 文件。Zip(jar)文件格式允许存在名称重复的条目,但标准 Java API 不支持写入此类条目。若要在 Java 16+ 版本中运行 Allatori 以实现此功能,需在运行 JVM 时添加 --add-opens java.base/java.util.zip=ALL-UNNAMED 选项;在旧版本 Java 中,则需添加 --illegal-access=permit 选项。

示例:

<property name="output-jar-duplicate-name-entries" value="keep"/>

1.6.6 增量混淆属性(Incremental obfuscation properties)

1.6.6.1 incremental-obfuscation
描述
先前创建的日志文件名称 指定上一次 Allatori 运行生成的日志文件名称,相对路径会相对于配置文件所在位置进行解析。增量混淆用于为应用程序创建补丁或附加组件,在这种情况下,需要确保类、方法和字段的新名称与之前混淆版本中的名称保持一致。将上一次 Allatori 运行生成的日志文件作为下一次混淆的输入,可使这两个版本完全兼容,从而让补丁或附加组件能无缝集成到之前部署的应用程序中。使用增量混淆时,即使只需分发部分类,也需将所有类纳入混淆过程。

示例:

<property name="incremental-obfuscation" value="input-renaming-log.xml"/>
1.6.6.2 unique-renaming
描述
disable (默认值)禁用唯一重命名功能。
enable 若两个方法的名称和签名相同,则它们会被重命名为相同的新名称;若两个方法的名称或签名不同,则重命名后的名称也会不同。该属性可确保在后续的增量混淆过程中保持一致性。

示例:

<property name="unique-renaming" value="enable"/>

可通过 apply2class 属性,为指定类设置该属性。apply2class 属性的格式与 class 标签的 template 属性格式相同,示例如下:

<property name="unique-renaming" value="enable" apply2class="class com.abc.*"/>

1.7 Ignore-classes 标签

Ignore-classes 标签用于将部分类完全排除在混淆过程之外,这些类会原封不动地复制到输出 jar 文件中。需注意,被排除的类会以原始名称引用其他类,因此无法对被排除类所引用的类/方法进行重命名。

该标签包含嵌套的 class 标签,这些标签与 keep-names 部分中的 class 标签用法相同。

示例:

<ignore-classes>
    <class template="class com.company.abc.*"/>
    <class template="class com.company.xyz.SomeClass"/>
</ignore-classes>

2 注解(Annotations)

注解类位于 allatori-annotations.jar 文件中。在混淆过程中,所有 Allatori 注解都会被移除,因此应用程序运行时无需该 jar 文件。注解用于更便捷、更精确地配置混淆器。

以下是 com.allatori.annotations 包中所有可用的注解:

注解 适用对象 描述
Rename 类、方法和字段 指示 Allatori 对带注解的元素进行重命名,该注解会覆盖配置文件中的设置。
DoNotRename 类、方法和字段 指示 Allatori 不对带注解的元素进行重命名,该注解会覆盖配置文件中的设置。
StringEncryption 可能的取值如下:StringEncryption.ENABLEStringEncryption.DISABLEStringEncryption.MAXIMUMStringEncryption.MAXIMUM_WITH_WARNINGS该注解会覆盖 string-encryption 属性的设置,示例:@StringEncryption(StringEncryption.ENABLE)
StringEncryptionType 可能的取值如下:StringEncryptionType.FASTStringEncryptionType.STRONG该注解会覆盖 string-encryption-type 属性的设置,示例:@StringEncryptionType(StringEncryptionType.STRONG)
ControlFlowObfuscation 类和方法 可能的取值如下:ControlFlowObfuscation.ENABLEControlFlowObfuscation.DISABLE该注解会覆盖 control-flow-obfuscation 属性的设置,示例:@ControlFlowObfuscation(ControlFlowObfuscation.ENABLE)
ExtensiveFlowObfuscation 类和方法 可能的取值如下:ExtensiveFlowObfuscation.DISABLEExtensiveFlowObfuscation.NORMALExtensiveFlowObfuscation.MAXIMUM该注解会覆盖 extensive-flow-obfuscation 属性的设置,示例:@ExtensiveFlowObfuscation(ExtensiveFlowObfuscation.MAXIMUM)

3 Android 混淆(Android obfuscation)

Allatori 支持对 Android 应用程序进行全功能混淆,且能轻松集成到构建流程中。我们已为 Android 项目创建了一个典型的配置文件,极大简化了 Android 应用程序的混淆操作。有关配置文件(allatori.xml)和示例构建文件(build.gradle 和 build.xml),可在 Allatori 发行版附带的教程中找到。

3.1 使用 Android Studio

在 Android Studio 项目中设置 Allatori 需执行以下三个步骤:

  1. 在项目的 rootDir(根目录)文件夹中创建 allatori 文件夹,并将 allatori.jar 文件复制到该文件夹中;
  2. 从教程中复制 allatori.xml 文件到项目的 projectDir 文件夹(项目的 build.gradle 文件所在目录);
  3. 编辑 build.gradle 文件:
android {
    ...
    // 针对应用程序(application 类型项目)
    applicationVariants.all { variant ->
        variant.javaCompileProvider.get().doLast {
            runAllatori(variant)
        }
    }
    // 针对库(library 类型项目)
    // libraryVariants.all { variant ->
    //    variant.javaCompileProvider.get().doLast {
    //        runAllatori(variant)
    //     }
    // }
}

def runAllatori(variant) {
    copy {
        from "$projectDir/allatori.xml"
        into "$buildDir/intermediates/classes/"
        expand(classesRoot: variant.javaCompileProvider.get().destinationDir,
                kotlinRoot: "${buildDir}/tmp/kotlin-classes/${variant.name}",
                androidJar: "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar",
                classpathJars: variant.javaCompileProvider.get().classpath.getAsPath(),
                logFile: "allatori-log-${variant.name}.xml")
        rename('allatori.xml', "allatori-${variant.name}.xml")
    }

    new File("${variant.javaCompileProvider.get().destinationDir}-obfuscated").deleteDir()
    javaexec {
        main = 'com.allatori.Obfuscate'
        classpath = files("$rootDir/allatori/allatori.jar")
        args "$buildDir/intermediates/classes/allatori-${variant.name}.xml"
    }
    new File("${variant.javaCompileProvider.get().destinationDir}").deleteDir()
    new File("${variant.javaCompileProvider.get().destinationDir}-obfuscated").renameTo(new File("${variant.javaCompileProvider.get().destinationDir}"))

    // Kotlin 支持(若项目使用 Kotlin)
    // new File("${buildDir}/tmp/kotlin-classes/${variant.name}").deleteDir()
    // new File("${buildDir}/tmp/kotlin-classes/${variant.name}-obfuscated").renameTo(new File("${buildDir}/tmp/kotlin-classes/${variant.name}"))
}

3.2 使用 Ant

在 Android Ant 项目中设置 Allatori 需执行以下三个步骤:

  1. 在项目文件夹中创建 allatori 文件夹,并将 allatori.jar 文件复制到该文件夹中;
  2. 从教程中复制 allatori.xml 文件到项目文件夹;
  3. 在 build.xml 文件中添加以下目标(target):
<target name="-obfuscate" unless="do.not.compile">
    <taskdef name="allatori" classname="com.allatori.ant.ObfuscatorTask"
             classpath="allatori/allatori.jar"/>
    <delete dir="${out.classes.absolute.dir}-obfuscated"/>
    <allatori config="allatori.xml"/>
    <property name="out.dex.input.absolute.dir"
              value="${out.classes.absolute.dir}-obfuscated"/>
</target>

可能需要根据项目实际情况进行进一步配置。在 Allatori 配置文件中,可使用标准 Ant 语法 ${PropertyName} 引用 Ant 构建文件中定义的属性。

4 Eclipse IDE 插件(Eclipse IDE plugin)

Allatori 可轻松与 Eclipse IDE 配合使用,操作步骤如下:

  1. 将 allatori.jar 文件复制到 eclipse/dropins 文件夹;
  2. 在 Eclipse 中右键单击项目名称,在弹出的菜单中选择“Configure -> Add Allatori Builder”(配置 -> 添加 Allatori 构建器)(参见截图);
  3. 执行“Clean rebuild”(清理并重新构建)操作,混淆过程仅在清理构建时运行。

首次运行时,会在项目的根文件夹中创建默认的 allatori.xml 配置文件。

在配置文件中,可使用 ${eclipse-input}${eclipse-classpath} 属性:

<input>
    ${eclipse-input}
</input>

<classpath>
    ${eclipse-classpath}
</classpath>