Flink 代码混淆

Posted 文大侠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flink 代码混淆相关的知识,希望对你有一定的参考价值。

原理

Java 默认编译完成,很容易反编译出源码逻辑
混淆本质上就是对于生成的class文件中敏感的部分替换,包括包名、类名、类成员变量、类成员函数、函数参数等

最常用的java混淆就是proguard混淆
对应仓库 https://github.com/Guardsquare/proguard

Flink代码混淆

这样指的是混淆提交给flink运行的业务代码,不是混淆flink的框架代码
通常可能是scala+java的混编代码,有大量的序列化逻辑存在,因此需要注意proguard的配置
通常的java工程和spring的混淆配置不适用

maven配置

借助maven插件 https://github.com/wvengen/proguard-maven-plugin
完成编译完成自动混淆

增加如下配置

<properties>
<proguard-maven-plugin.version>2.5.1</proguard-maven-plugin.version>
    <proguard.version>7.1.1</proguard.version>
</properties>

<!--proguard混淆插件 在build中指定插件-->
<plugin>
    <groupId>com.github.wvengen</groupId>
    <artifactId>proguard-maven-plugin</artifactId>
    <version>$proguard-maven-plugin.version</version>
    <executions>
        <execution>
            <!--打包的时候开始混淆-->
            <phase>package</phase>
            <goals>
                <goal>proguard</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <!-- 开启混淆 配置proguard的版本和对应配置文件 -->
        <obfuscate>true</obfuscate>
        <proguardVersion>$proguard.version</proguardVersion>
        <proguardInclude>$basedir/script/obfuscate/proguard.cfg</proguardInclude>

        <!-- 输入输出和对应依赖jar -->
        <!-- -injars -->
        <injar>$project.build.finalName.jar</injar>
        <!-- -outjars -->
        <outjar>$project.build.finalName.jar</outjar>
        <!-- -libraryjars = libs+project dependencies -->
        <libs>
            <lib>$java.home/lib/rt.jar</lib>
            <lib>$java.home/lib/jsse.jar</lib>
            <lib>$java.home/lib/jce.jar</lib>
        </libs>

        <!--增量迭代-->
        <applyMappingFile>$basedir/script/obfuscate/proguard_map.txt</applyMappingFile>

        <!--指定定输出新旧元素名的对照表的文件-->
        <mappingFileName>proguard_map.txt</mappingFileName>
        <!--配置匹配的类或者类成员的详细列表-->
        <seedFileName>proguard_seed.txt</seedFileName>

        <!-- 其它proguard命令配置 -->
        <options>
            <!--默认开启,不做收缩(删除注释、未被引用代码)-->
            <option>-dontshrink</option>
            <!--默认是开启的,这里关闭字节码级别的优化-->
            <option>-dontoptimize</option>

            <!--对于类成员的命名的混淆采取唯一策略,用于mapping文件增量更新-->
            <option>-useuniqueclassmembernames</option>
            <!--混淆时不生成大小写混合的类名,默认是可以大小写混合,保证windows等大小写敏感系统正常-->
            <option>-dontusemixedcaseclassnames </option>

            <!--忽略note消息-->
            <option>-dontnote</option>
        </options>

    </configuration>
    <dependencies>
        <dependency>
            <groupId>com.guardsquare</groupId>
            <artifactId>proguard-base</artifactId>
            <version>$proguard.version</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</plugin>

几点注意说明

  1. 这里打包默认会生成proguard_map.txt和proguard_seed.txt,前者表示具体的混淆列表,后者表示对应命中proguad的keep规则的列表
  2. 混淆后的java包如果报异常,默认我们看到的堆栈中各种名称是混淆后的,如果需要看到对应的混淆前的堆栈,需要利用proguard_map.txt反混淆,具体过程参考文档
    https://www.guardsquare.com/manual/tools/retrace
  3. proguard_map.txt管理需要注意,每次编译后丢失了就很难找回,默认混淆是随机混淆,每次编译混淆结果都不一样。
    一种方式是每次编译的时候都把当前包对应的proguard_map.txt保存起来,比如cicd中上传到指定位置
    另一种是这里的applyMappingFile,配置的是增量map,每次编译时指定映射,这样同一样的代码,只要指相同的proguard_map.txt,混淆结果相同。这里需要初始化时创建这个目录,默认为空文件,每次生成完成后手动替换新的proguard_map.txt到这里的目录

shade混编处理

代码中通常会遇到,需要把所有依赖包一起提交运行的需求,通常会使用maven-shade-plugin插件。
所以如上的配置需要保证混淆前后的包名不变,这样只需要再混淆插件后增加maven-shade-plugin插件,即可基于前一步的混淆结果继续打依赖包。

proguard配置

主要是要保证scala的运行和flink的序列化正常,当前所用配置供参考

-verbose

# 只混淆公司的包
-keep class !com.xxx.xx.**

# scala 参考 https://github.com/Guardsquare/proguard/blob/v7.2.0/examples/standalone/scala.pro

-renamesourcefileattribute SourceFile
-keepattributes Signature,Exceptions,*Annotation*,
                InnerClasses,PermittedSubclasses,EnclosingMethod,
                Deprecated,SourceFile,LineNumberTable

# Preserve all public applications.

-keepclasseswithmembers public class * 
    public static void main(java.lang.String[]);


# Preserve some classes and class members that are accessed by means of
# introspection.

-keep class * implements org.xml.sax.EntityResolver

-keepclassmembers class * 
    ** MODULE$;


# Preserve all native method names and the names of their classes.

-keepclasseswithmembernames,includedescriptorclasses class * 
    native <methods>;


# Preserve the special static methods that are required in all enumeration
# classes.

-keepclassmembers,allowoptimization enum * 
    public static **[] values();
    public static ** valueOf(java.lang.String);


# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
# You can comment this out if your application doesn't use serialization.
# If your code contains serializable classes that have to be backward
# compatible, please refer to the manual.

-keepnames class * implements java.io.Serializable

-keepclassmembers class * implements java.io.Serializable 
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();


# 对于flink 需要处理序列化lambda 问题
# 参考 https://www.guardsquare.com/manual/configuration/examples#serializable
-keepclassmembers class * 
    private static synthetic java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda);


-keepclassmembernames class * 
    private static synthetic *** lambda$*(...);


参考

proguard 官方配置

  • https://www.guardsquare.com/manual/configuration/usage
  • https://www.jianshu.com/p/60e82aafcfd0

proguard maven插件配置

  • https://wvengen.github.io/proguard-maven-plugin/
  • https://langyastudio.blog.csdn.net/article/details/123783160

以上是关于Flink 代码混淆的主要内容,如果未能解决你的问题,请参考以下文章

Flink 代码混淆

apache flink 的联合类型混淆?

译文《Apache Flink官方文档》 Apache Flink介绍

[Essay] Apache Flink:十分可靠,一分不差

如何获取 NSPropertyDescription 属性的新旧值

实战:第十九章:存入Long类型对象,在代码中使用Long类型接收,结果报类型转换错误