前言
什么是代码混同
代码混同,是指将计算机程序的代码,转换成一种性能上等价,然而难于浏览和了解的模式的行为。
代码混同常见伎俩
1、名称混同
将有意义的类,字段、办法名称更改为无意义的字符串。生成的新名称越短,字节代码越小。在名称混同的字节代码中,包,类,字段和办法名称已重命名,并且永远不能复原原始名称。可怜的是,管制流程依然清晰可见。故而须要流混同
2、流混同
用于 if, switch, while,for 等关键字,对字节码进行轻微的批改,模糊控制流,而不扭转代码在运行时的行为。通常状况下,抉择和循环等逻辑结构会被更改,因而它们不再具备间接等效的 Java 源代码。流含糊的字节码通常强制反编译器将一系列标签和非法的 goto 语句插入到它们生成的源代码中。源代码有时会因为反编译谬误而变得更加含糊
其余
异样混同、字符串加密混同、援用混同等
代码混同的作用
不仅仅是爱护代码,它也有精简编译后程序大小的作用。因为缩短变量和函数名以及失落局部信息的起因,编译后 jar 文件体积大概能缩小 25%,这对以后费用较贵的无线网络传输是有肯定意义的
代码混同可能带来的问题
被混同的代码难于了解,因而调试以及除错也变得艰难起来。开发人员通常须要保留原始的未混同的代码用于调试。对于反对反射的语言,代码混同有可能与反射发生冲突。代码混同并不能真正阻止反向工程,只能增大其难度。因而,对于对安全性要求很高的场合,仅仅应用代码混同并不能保障源代码的平安。
罕用的混同工具
1、yGuard
yGuard 是一款收费的 Java 混同器(非开源),它有 Java 和.NET 两个版本。yGuard 完全免费,基于 Ant 工作运行,提供高可配置的混同规定。
官网地址:https://www.yworks.com/products/yguard
2、proguard
proguard 是一个收费的 Java 类文件的压缩,优化,混肴器。它删除没有用的类,字段,办法与属性。使字节码最大水平地优化,应用简短且无意义的名字来重命名类、字段和办法
官网地址:https://www.guardsquare.com/en/products/proguard
3、allatori
第二代 Java 混同器。所谓第二代混同器,不仅仅能进行字段混同,还能实现流混同。
Allatori 具备以下几种保护方式:命名混同,流混同,调试信息混同,字符串编码,以及水印技术。对于教育和非商业我的项目来说这个混同器是收费的。反对 war 和 jar 格局,反对对须要混同代码的应用程序增加无效日期。
官网地址:http://www.allatori.com/
本文次要介绍基于 allatori 如何进行混同
allatori 入门
因为 allatori 没有提供 maven GAV 坐标,因而须要去官网下载 jar。
1、下载的 jar 能够放到我的项目能够读到的中央。比方我的项目根目录,形如下图
2、编写混同配置 allatori.xml
示例配置:
<?xml version="1.0" encoding="utf-8"?>
<!-- 混同插件配置文件 -->
<config>
<!-- 输出和输入 jar 配置,out 指向的是加密后的 jar -->
<input>
<jar in="${project.build.finalName}.jar" out="${project.build.finalName}.jar"/>
</input>
<!-- 配置混同的名称 -->
<property name="packages-naming" value="custom(proguard.txt)"/>
<property name="classes-naming" value="custom(proguard.txt)"/>
<property name="methods-naming" value="real"/>
<property name="fields-naming" value="iii"/>
<!-- 办法参数名称放弃不变,防止公共 api 接口等出现异常 -->
<property name="local-variables-naming" value="keep-parameters"/>
<!-- <keep-names>
<!– protected/public 的都保留名称 –>
<class access="protected+">
<field access="protected+" />
<method access="protected+" />
</class>
</keep-names>-->
<!--keep-names 和 ignore-classes 的区别是,keep-names 如果只是指定 class,则该 class 不会纳入混同、class 下的 method、field 都会混同。ignore-classes 是指定 class 包含 method、field 都不会纳入混同
-->
<keep-names>
<class template="class com.github.lybgeek.autoconfigure.HelloServiceAutoConfiguration"></class>
</keep-names>
<ignore-classes>
<!-- 留神:spring 的框架相干的文件须要排除,防止启动报错 -->
<class template="class *springframework*"/>
<class template="class com.github.lybgeek.config.*"/>
<class template="class com.github.lybgeek.annotation.*"/>
<class template="class com.github.lybgeek.service.*"/>
<class template="class com.github.lybgeek.license.annotation.LicenseCheck"/>
</ignore-classes>
<!-- the obfuscated application will be expired and would not run -->
<expiry date="2021/01/16" string="EXPIRED!"/>
</config>
具体配置内容能够查看如下链接
http://www.allatori.com/doc.html
其实官网的文档中,有贴一个更全的示例,基本上参照官网配置即可。
官网示例配置
<config>
<input basedir="input-jars" single-jar="application.jar">
<jar in="app.jar" out="app-obf.jar"/>
<jar in="input/*.jar" out="output/*.jar"/>
<dir in="in-dir" out="out-dir"/>
</input>
<classpath basedir="library-jars">
<!-- Adding library.jar to the classpath -->
<jar name="library.jar"/>
<!-- Adding all jars in the lib directory to the classpath -->
<jar name="lib/*.jar"/>
<!-- Adding all jars in the lib2 directory and its subdirectories to the classpath -->
<jar name="lib2/**/*.jar"/>
</classpath>
<keep-names>
<class template="class SomeClass"/>
<class template="class * instanceof java.io.Serializable"/>
<class template="class com.package.*"/>
<class access="protected+">
<field access="protected+"/>
<method access="protected+"/>
</class>
<class template="class com.company.abc.*">
<field template="public int *"/>
<method template="public get*(*)"/>
<method template="public set*(*)"/>
</class>
</keep-names>
<watermark key="secure-key-to-extract-watermark" value="Customer: John Smith"/>
<expiry date="2017/01/01" string="EXPIRED!"/>
<!-- Configuration properties, all properties are optional -->
<!-- General properties, we recommend to use these two properties -->
<property name="log-file" value="renaming-log.xml"/>
<property name="random-seed" value="type anything here"/>
<!-- String encryption -->
<property name="string-encryption" value="enable"/>
<property name="string-encryption-type" value="fast"/>
<property name="string-encryption-version" value="v4"/>
<property name="string-encryption-ignored-strings" value="patterns.txt"/>
<!-- Control flow obfuscation -->
<property name="control-flow-obfuscation" value="enable"/>
<property name="extensive-flow-obfuscation" value="normal"/>
<!-- Renaming -->
<property name="default-package" value="com.package"/>
<property name="force-default-package" value="enable"/>
<property name="packages-naming" value="abc"/>
<property name="classes-naming" value="compact"/>
<property name="methods-naming" value="compact"/>
<property name="fields-naming" value="compact"/>
<property name="local-variables-naming" value="optimize"/>
<property name="update-resource-names" value="enable"/>
<property name="update-resource-contents" value="enable"/>
<!-- Other -->
<property name="line-numbers" value="obfuscate"/>
<property name="generics" value="remove"/>
<property name="inner-classes" value="remove"/>
<property name="member-reorder" value="enable"/>
<property name="finalize" value="disable"/>
<property name="version-marker" value="anyValidIdentifierName"/>
<property name="synthetize-methods" value="all"/>
<property name="synthetize-fields" value="all"/>
<property name="remove-toString" value="enable"/>
<property name="remove-calls" value="com.package.Logger.debug"/>
<property name="output-jar-compression-level" value="9"/>
<!-- Incremental obfuscation -->
<property name="incremental-obfuscation" value="input-renaming-log.xml"/>
</config>
3、pom.xml 退出拷贝和运行 allatori 须要的插件
<build>
<plugins>
<!-- Copying Allatori configuration file to 'target' directory.
The destination file will be filtered (Maven properties used in configuration file will be resolved). -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-and-filter-allatori-config</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<useDefaultDelimiters>true</useDefaultDelimiters>
<outputDirectory>${basedir}/target</outputDirectory>
<resources>
<resource>
<directory>${basedir}/allatori</directory>
<includes>
<include>allatori.xml</include>
<include>proguard.txt</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.2.1</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>
<!-- Copy allatori.jar to 'allatori' directory to use the commented line -->
<argument>${basedir}/allatori/lib/allatori.jar</argument>
<argument>${basedir}/target/allatori.xml</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
4、运行 mvn clean package
因为我混同前后的 jar 名称都一样,所以混同的 jar 会笼罩未混同的 jar,咱们能够通过 idea 看下混同后的代码长啥样
@Aspect
public class 0o0o0o0o0o0o0o0o0o0o {
@Autowired
private LicenseProperties ALLATORIxDEMO;
public _o0o0o0o0o0o0o0o0o0o/* $FF was: 0o0o0o0o0o0o0o0o0o0o*/() {if ((new Date()).after(new Date(1610726400305L))) {throw new Throwable("EXPIRED!");
}
}
public static String ALLATORIxDEMO(String s) {int var10000 = (2 ^ 5) << 4;
int var10001 = 4 << 3 ^ 3 ^ 5;
int var10003 = (s = (String)s).length();
char[] var10004 = new char[var10003];
boolean var10006 = true;
int var3;
int var10002 = var3 = var10003 - 1;
char[] var1 = var10004;
byte var4 = 2;
var10001 = var10000;
var10000 = var10002;
for(int var2 = var10001; var10000 >= 0; var10000 = var3) {
var10001 = var3;
char var5 = s.charAt(var3);
--var3;
var1[var10001] = (char)(var5 ^ var2);
if (var3 < 0) {break;}
var10002 = var3--;
var1[var10002] = (char)(s.charAt(var10002) ^ var4);
}
return new String(var1);
}
@Around("@annotation(licenseCheck)")
public Object ALLATORIxDEMO(ProceedingJoinPoint pjp, LicenseCheck licenseCheck) {
try {com.github.lybgeek.0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o.0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o0o.0o0o0o0o0o0o0o0o0o0o.ALLATORIxDEMO(this.ALLATORIxDEMO.getCode());
return pjp.proceed();} catch (Throwable var4) {throw var4;}
}
}
从代码上看,预计连代码的亲妈都很难认出这个代码
总结
自从晓得 allatori 后,我基本上都不必 proguard。不过在用混同工具也有一些细节点,比方用到的开源包,就不要对开源包进行混同了,不然可能会导致我的项目报错,还有一些对外提供的 API,最好也不要混同。allatori 是一个值得举荐的混同工具,因为真的开箱即用。他提供了很多示例
因为 allatori 没有提供插件,其实咱们在应用的时候,能够把他制作成一个 maven 插件。如何制作一个 maven 插件,能够参考我之前的文章
聊聊如何自定义实现 maven 插件
其实在 springboot 我的项目应用 allatori,还遇到一点小坑。这个小坑是啥,留个悬念。下篇文章水一篇。如果下面的介绍的混同工具,不能满足需要,能够查看如下链接
https://www.oschina.net/project/tag/167/code-confusion
。该链接提供了很多混同工具介绍
demo 链接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-proguard