美文网首页
聊聊如何进行代码混淆

聊聊如何进行代码混淆

作者: linyb极客之路 | 来源:发表于2022-01-26 09:20 被阅读0次

    前言

    什么是代码混淆

    代码混淆,是指将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为。

    代码混淆常见手段

    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可以放到项目可以读到的地方。比如项目根目录,形如下图

    image

    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>
            &lt;!&ndash; protected/public的都保留名称 &ndash;&gt;
            <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看下混淆后的代码长啥样


    image
    @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是一个值得推荐的混淆工具,因为真的开箱即用。他提供了很多示例

    image
    因为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

    相关文章

      网友评论

          本文标题:聊聊如何进行代码混淆

          本文链接:https://www.haomeiwen.com/subject/jmxnhrtx.html