java代码混淆之Allatori

前言

Allatori是一个Java 混淆器,它属于第二代的混淆器,因此它能够全方位的保护你的知识产权。
Allatori完全使用java编写,所以可以运行java代码的环境,都可以使用Allatori。
Allatori具有以下几种保护方式:命名混淆,流混淆,调试信息混淆,字符串混淆,以及水印技术。
对于教育和非商业项目来说这个混淆器是免费的。新版本支持war和ear文件格式,并且允许对需要混淆代码的应用程序添加有效日期。
点此查看官方文档

为什么要混淆代码?

Java是一种跨平台的、解释型语言,Java源代码编译成中间“字节码”存储于class文件中。
由于跨平台的需要,Java字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成Java源代码。
为了防止这种现象,我们可以使用Java混淆器对Java字节码进行混淆。

什么是名称混淆?

Allatori的名称混淆机制将有意义的类,字段、方法名称更改为无意义的字符串。
虽然Allatori是第二代Java混淆器,但它也可以执行出色的名称混淆。
与其他一些混淆器不同,它被设计为处理任何类型的依赖和继承方案。
Allatori生成的新名称非常短,因此它减少了字节代码的大小。
在名称混淆的字节代码中,包,类,字段和方法名称已重命名,并且永远不能恢复原始名称。
不幸的是,控制流程仍然清晰可见。
这就是为什么我们还需要使用Allatori的Flow Obfuscation(流混淆)。

什么是流混淆?

Allatori作为第二代Java混淆器,除了名称混淆之外,还执行流混淆。
比如使用了 ‘if’,’switch’,’while’或’for’ 等关键字的方法,会被进行执行流混淆。
它对字节码做了细微的修改,模糊了控制流,而不改变代码在运行时的行为。
通常情况下,选择和循环等逻辑构造会被更改,因此它们不再具有直接等效的Java源代码。
流模糊的字节码通常强制反编译器将一系列标签和非法的“goto”语句插入到它们生成的源代码中。
源代码有时会因为反编译错误而变得更加模糊。

什么是调试信息混淆?

Allatori使用所有“传统”混淆技术。它会混淆调试信息并重命名所有可能的方法和字段名称。
我理解的是Allatori混淆了执行流和类、方法的名称后就达到了调试信息混淆,因为混淆后的代码无法与源代码相对应。

什么是字符串混淆?

当竞争对手或黑客反编译混淆的应用程序时,他们会寻找任何他们感兴趣的信息。
比如嵌入在应用程序中的字符串文字提供了重要的线索。
这些文字可能是:
1.调试程序需要的输出。
2.错误消息的文本。
3.异常消息的文本。
在代码中对字符串的简单搜索将会显示其使用位置的确切位置,这意味着它将本地化​​必要的代码片段,从而使黑客更容易理解算法你的程序。
字符串文字可以成为探索整个应用程序的起点。
Allatori的字符串加密技术对存储在类文件的常量池中的字符串字面值进行加密。
在这种情况下,应用程序反编译后的字符串搜索不会给黑客带来任何好处。
Allatori将代码片段添加到类中,以便您的字符串在运行时被解密,这意味着表面上一切都将如常。

什么是软件水印?

软件水印可以用于将客户标识或版权信息隐藏到软件应用程序中,类似于使用隐写技术将其隐藏在诸如歌曲,电影和图像之类的其他数字内容中。
水印可用于识别软件的所有者或跟踪盗版副本的来源。

混淆器的优势

混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。
被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。
同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。
混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。
由于以上介绍的缩短变量和函数名以及丢失部分信息的原因, 编译后jar文件体积大约能减少25% ,这对当前费用较贵的无线网络传输是有一定意义的。

Allatori的使用

下载支持包

Allatori不需要安装,你需要在官网下载它的支持包。

下载后的包解压后它长这个样子。

它包含allatori的支持包,它在lib目录下,还有allatori的使用教程。
我们需要的是lib中的两个jar包,将它放到我们的工程目录中,这里使用maven 构建,你可以选择把它放到项目的根目录中。

编写allatori配置文件

在项目中新建一个allatori.xml文件,位置和名称不是固定的,看你心情。
以下是配置文件的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<config>
<input>
<jar in="allatori-1.0-SNAPSHOT.jar" out="new-allatori-1.0-SNAPSHOT.jar"/>
</input>

<classpath>
<jar name="/Users/choice/.m2/repository/**/*.jar"/>
</classpath>

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

<keep-names>
<class template="class * instanceof java.io.Serializable"/>
<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>

<class access="protected+">
<field access="protected+"/>
<method access="protected+"/>
</class>
</keep-names>

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

<ignore-classes>
<class template="class *springframework*" />
</ignore-classes>

</config>

配置文件中的内容下面会一一讲解。

添加Allatori的maven配置

将以下内容添加到 pom.xml 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<build>
<plugins>
<!-- 配置你项目的编译方式 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Allatori config 将Allatori配置文件复制到“目标”目录。目标文件将被过滤(配置文件中使用的Maven属性将被解析)。-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-and-filter-allatori-config</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target</outputDirectory>
<resources>
<resource>
<directory>${basedir}/allatori</directory>
<includes>
<include>allatori.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- Running Allatori -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>run-allatori</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>java</executable>
<arguments>
<argument>-Xms128m</argument>
<argument>-Xmx512m</argument>
<argument>-jar</argument>
<argument>${basedir}/../lib/allatori.jar</argument>
<argument>${basedir}/target/allatori.xml</argument>
</arguments>
</configuration>
</plugin>
<!-- Allatori plugin end -->
</plugins>
</build>

常用的Allatori标签

以下是常用的配置标签,如想看完整的标签,请查看官网文档

config –> input tag

input 标签是用来设置混淆源(JAR,WAR,EAR)文件。
它应该包含至少一个嵌套的jar或dir标记来设置输入和输出文件。
input 标签有两个可选属性:
    basedir         可选属性。将根据指定的目录解析jar文件的相对路径。默认情况下,将根据配置文件位置解析相对路径。
    single-jar      可选属性。Allatori将创建一个包含所有混淆类的附加输出jar文件。

config –> input –> jar tag

jar标签有两个必需的属性:
    in          要混淆的jar文件的名称。
    out         输出jar文件的名称。它可以与in相同,在这种情况下,jar将被其混淆版本覆盖。

config –> classpath tag

classpath 标签是用来设置的类路径混淆应用。
它包含带有jar文件名称的嵌套jar标签。
没有必要引用应用程序所需的所有库jar,但缺少类路径元素可能会导致模糊处理较弱。
Allatori会在混淆过程中警告你所有缺失的class。
classpath 标签有一个可选属性:
    basedir         将根据指定的目录解析jar文件的相对路径。默认情况下,将根据配置文件位置解析相对路径。

config –> classpath –> jar tag

classpath 中的 jar标签有一个必需属性:
    name    要添加到类路径的jar文件的名称。
            允许使用通配符语法:“*”匹配文件名中的任何字符; “**”递归到子目录。

config –> expiry tag

expiry 标记用于到期日设置为你的应用程序。
有效期限检查被插入到许多方法中,而不仅仅是主要方法,因此不能轻易删除。
此功能可用于混淆甚至没有主方法的库。
expiry 标签有两个必需的属性:
    date            必需的。有效期为yyyy/mm/dd格式。
    string            必需的。如果应用程序在指定的过期日期之后运行,则抛出异常的任何字符串消息。

config –> keep-names tag

keep-names 标签用于设置不应该在混淆过程被重新命名类,方法和字段名。
如果混淆的应用程序是库,那么您应该保留所有公共API。
对于独立应用程序,您应至少保留主类的名称。
您还应该保留通过反射使用的类和方法的名称。
keep-name 有下列嵌套的标签:
    field           用于指定不重命名的范围。
    method          用于指定不应重命名的方法;
    class           用于指定不应重命名的类。反过来,可以包含嵌套的字段和方法标记。

config –> keep-names –> class tag

class 标签用于匹配的类。
class 它具有以下属性:
access        设置匹配规则,如下:
    private         匹配具有 private 访问权限的类,字段或方法。
    private+        匹配具有 private 或更高访问权限的类,字段或方法。
    package         使用包访问匹配类,字段或方法。
    package+        匹配包及子包的类,字段或方法。
    protected       匹配具有 protected 访问权限的类,字段或方法。
    protected+      匹配具有 protected 或更改访问权限的类,字段或方法。
    public          匹配具有 public 访问权限的类,字段或方法。
template    设置匹配规则。如下:
    class *         匹配所有类和接口。
    interface *     匹配所有接口。
    public class *        匹配所有public的类和接口。
    protected+ class *  匹配所有protected的类和接口。
    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 *            匹配所有已弃用的类。
ignore        如果设置为“true”或“yes”,则将重命名匹配的类,但将像往常一样处理嵌套的方法和字段标记。
                它允许保留某些字段和方法的名称,而不保留类的名称。
stop        如果设置为“true”或“yes”,则Allatori将停止对匹配的类应用任何进一步的规则。

config –> keep-names –> class –> field tag

field 该标签用于匹配字段。
field 标签具有以下属性:
    access属性设置匹配规则,如下:
        private                                    匹配具有 private 访问权限的类,字段或方法。
        private+                                匹配具有 private 或更高访问权限的类,字段或方法。
        package                                    使用包访问匹配类,字段或方法。
        package+                                匹配包及子包的类,字段或方法。
        protected                                匹配具有 protected 访问权限的类,字段或方法。
        protected+                                匹配具有 protected 或更高访问权限的类,字段或方法。
        public                                    匹配具有 public 访问权限的类,字段或方法。
    template属性具有以下格式:
        *                                        匹配所有字段。
        private *                                匹配所有私有字段。
        private+ *                                匹配所有字段。
        protected+ *                            匹配所有受保护和公共字段。
        static *                                匹配所有静态字段。
        public static *                            匹配所有公共静态字段。
        public int *                            匹配所有公共整数字段。
        java.lang.String *                        匹配所有String字段。
        java.lang.* *                            匹配java.lang包中所有类型的字段。
        abc*                                    匹配名称以“abc”开头的所有字段。
        private abc*                            匹配名称以“abc”开头的所有私有字段。
        * instanceof java.io.Serializable        匹配所有可序列化的字段。
        @java.lang.Deprecated *                    匹配所有已弃用的字段。

config –> keep-names –> class –> method tag

method 标签用于匹配的方法。
如果 method 标签嵌套在 class 标签中,则它仅应用于父类标记匹配的类。
如果父标记是 keep-names,则它将应用于所有类。
method 标签具有以下属性:
    access                                            设置匹配规则,规则同行 access 属性
    method标签的template属性具有以下格式:
    [@annotation] [modifiers] [type] methodname(arguments)
    template
    *(**)                                        匹配所有方法。
    private *(**)                                匹配所有私有方法。
    private+ *(**)                                匹配所有方法。
    protected+ *(**)                            匹配所有受保护和公共方法。
    private+ *(*)                                只用一个参数匹配所有方法。
    private+ *(*,*)                                使用两个参数匹配所有方法。
    private+ *(java.lang.String)                使用String类型的一个参数匹配所有方法。
    private+ *(java.lang.String,**)                使用String匹配所有方法作为第一个参数。
    private+ *(java.lang.*)                        匹配所有方法只有一个参数,类型在java.lang包中。
    public get*(**)                                匹配名称以“get”开头的所有公共方法。
    public *abc*(**)                            匹配名称包含“abc”的所有公共方法。
    private+ int *(**)                            匹配int返回类型的所有方法。
    @java.lang.Deprecated *(**)                    匹配所有已弃用的方法。
    parameters                                    如果设置为“keep”,则不会更改方法参数的名称。对公共API方法很有用。  

config –> ignore-classes tag

ignore-classes标签用于从混淆过程中完全排除一些类。
这些类是“原样”复制的,对输出jar文件没有任何更改。
注意,被忽略的类将引用其他类的原始名称,不能重命名被忽略类引用的类/方法。

config –> ignore-classes –> class tag

与 config --> ignore-classes --> class tag 中的规则相同。

config –> property tag

property 标签用于设置不同的混淆属性。
property 标签有两个必需的属性 name 和 value:
    普通属性
    log-file            
        filename                        Allatori将把混淆日志写入指定的文件。如果没有设置属性,则不会创建日志文件。
                                        相对路径根据配置文件位置进行解析。
    随机种子
    random-seed         
        any string                      用于初始化随机数生成器的字符串。
    字符串加密属性
    string-encryption
        enable                          (默认情况下)所有可以安全更改加密值的字符串字面值都将被加密。
                                        Allatori将在运行时添加一个方法来解密字符串。
        disable                         字符串加密被禁用。
        maximum                         所有字符串文字将被加密。请参阅下面的限制。
        maximum-with-warnings           所有字符串文字都将被加密。
                                        使用==运算符的每个字符串比较都会产生警告,您将能够用equals()调用替换这些比较。
    string-encryption-type  
        fast                            (默认)Allatori将使用非常快的字符串加密算法。
        strong                          Allatori将使用强大和棘手的字符串加密算法,不过,速度要慢一些。
    控制流混淆属性
    control-flow-obfuscation
        enable                          Allatori将修改方法的代码。它不会在运行时改变应用程序的行为,但会使反编译过程更加困难。
                                        通常,控制流混淆也使应用程序更小、更快。
        disable                         控制流混淆被禁用。
    extensive-flow-obfuscation
        normal                          (默认情况下)Allatori将使用控制流混淆技术,这将使混乱的应用程序变得更大、更慢。
                                        然而,Allatori将最小化此类代码转换的数量。
        disable                         禁用广泛的控制流混淆。
        maximum                         Allatori将完全使用控制流混淆技术,这使得混淆应用程序变得更大、更慢。

混淆前后效果

混淆前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class App {
public App() {
}

public static void main(String[] args) {
System.out.println("Hello World!");
Integer aaaaaaa = 127;
Integer aaaaaaa1 = 127;
System.out.println(aaaaaaa == aaaaaaa1);
Integer bbbbbbb = 128;
Integer bbbbbbb1 = 128;
System.out.println(bbbbbbb == bbbbbbb1);
int score = 10;
if (score >= 60) {
System.out.println("及格");
} else if (score >= 80) {
if (score < 85) {
System.out.println("良好");
} else if (score >= 85) {
System.out.println("优秀");
}
}

}
}

混淆后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class App {
public App() {
}

public static String ALLATORIxDEMO(String s) {
int var10000 = 5 << 4 ^ 3 << 2 ^ 3;
int var10001 = (2 ^ 5) << 4 ^ 2 << 2 ^ 3;
int var10002 = 4 << 4 ^ 4 << 1;
int var10003 = s.length();
char[] var10004 = new char[var10003];
boolean var10006 = true;
int var5 = var10003 - 1;
var10003 = var10002;
int var3;
var10002 = var3 = var5;
char[] var1 = var10004;
int var4 = var10003;
var10000 = var10002;

for(int var2 = var10001; var10000 >= 0; var10000 = var3) {
var10001 = var3;
char var6 = s.charAt(var3);
--var3;
var1[var10001] = (char)(var6 ^ var2);
if (var3 < 0) {
break;
}

var10002 = var3--;
var1[var10002] = (char)(s.charAt(var10002) ^ var4);
}

return new String(var1);
}

public static void main(String[] var0) {
System.out.println(ALLATORIxDEMO("qkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXBXh[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[kqk[h[h[h[hXk[k[h[k[h[hXk[kXk[kXk[kXh[kXk[h[h[h[hXBXh[h[h[hXhXhXh[hXh[hXhXh[k[hXhXhXhXh[k[h[h[h[h[kqk[h[h[h[kXk[k[h[k[h[kXk[hXh[k[k[kXh[hXh[h[h[h[hXBXh[h[h[hXhXhXkXhXkXhXhXh[k[hXkXhXhXhXkXh[h[h[h[kqk[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[hXBXh4*\u001d=\b+\u001a<\u0012'\u0015h\u00191[\t\u0017$\u001a<\u0014:\u0012h4*\u001d=\b+\u001a<\u0014:[>MfMh?\r6\u0007[kqk[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[hXBXh[h[h[h[h[h\u0013<\u000f8AgT?\f?U)\u0017$\u001a<\u0014:\u0012f\u0018'\u0016h[h[h[h[h[h[kqk[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[h[hXBXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkXkq"));
System.out.println(ALLATORIxDEMO("\u0000\u001e$\u0017'[\u001f\u0014:\u0017,Z"));
Integer a = 127;
Integer a = 127;
System.out.println(a == a);
Integer a = 128;
Integer a = 128;
System.out.println(a == a);
int a = 10;
if (10 >= 60) {
System.out.println(ALLATORIxDEMO("厂桇"));
} else {
if (a >= 80) {
if (a < 85) {
System.out.println(ALLATORIxDEMO("舧夆"));
return;
}

if (a >= 85) {
System.out.println(ALLATORIxDEMO("佐离"));
}
}

}
}
}

Choice wechat
关注公众号,获取文章更新通知。
-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!