ProGuard 破坏 JavaFX 应用程序

Posted

技术标签:

【中文标题】ProGuard 破坏 JavaFX 应用程序【英文标题】:ProGuard breaks JavaFX application 【发布时间】:2015-06-01 04:04:04 【问题描述】:

我试图混淆我的 JavaFX 应用程序,但它失败了。生成的结果不起作用,我不明白为什么。结果 jar 只是失败了,因为 fxml 文件无法再加载所有导入 (ClassNotFoundException)。

部署工作流程:

    构建可运行的 jar(在 IntelliJ 中被称为工件) 使用 ProGuard 混淆该 jar 修复该 jar 中 ProGuard 无法解决的一些问题

1) 最小的示例应用程序

示例应用程序“GuardTest”是一个由 3 个类组成的 IntelliJ 项目。

sample.Main:包含应用程序入口点并加载 GUI fxml 文件“sample.fxml” sample.Controller:“sample.fxml”的控制器类 controls.CustomControl:继承自 HBox 的简单 javafx 控件。这在“sample.fxml”中使用

'sample.fxml'的内容:

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>

<?import controls.CustomControl?>

<VBox fx:controller="sample.Controller"
          xmlns:fx="http://javafx.com/fxml">
    <children>
        <CustomControl>
            <children>
                <Button text="Test"></Button>
            </children>
        </CustomControl>
    </children>
</VBox>

2) 混淆

现在我将 ProGuard 用于从上述项目生成的 jar 文件。我使用以下设置:

-target 8

-injars ./out/artifacts/JavaFXApp/JavaFXApp.jar

-outjars ./out/obfuscated/Obfuscated.jar
-ignorewarnings

-printmapping ./out/obfuscated/proguard.map
-dontusemixedcaseclassnames
-dontshrink
-dontoptimize
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers

#-flattenpackagehierarchy
-repackageclasses 'p'
-allowaccessmodification

-libraryjars "<java.home>/lib/rt.jar"
-libraryjars "<java.home>/lib/javaws.jar"
-libraryjars "<java.home>/lib/ext/jfxrt.jar"

-adaptresourcefilecontents **.fxml,**.properties,META-INF/MANIFEST.MF,images/*.jar,publicCerts.store,production.version

-keepattributes javafx.fxml.FXML,Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod
-keepclassmembers class * 
    @javafx.fxml.FXML *;


-keepclassmembernames public class com.javafx.main.Main, com.nywelt.sharkload.application.Main 
    public static void main(java.lang.String[]);

-keepclasseswithmembers public class com.javafx.main.Main, com.product.main.EntryFX, net.license.LicenseEntryPoint 
    public *; public static *;

3) 修复一些(明显的)ProGuard 故障

生成的 jar 文件“Obfuscated.jar”具有以下结构:

**Obfuscated.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
- sample
--> sample.fxml

主类通过加载具有以下行的“sample.fxml”文件来启动 GUI:

Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));

因此,我还必须将“sample.fxml”文件移动到文件夹 p 中,以使上述行再次工作。我还修复了 fxml 文件中 ProGuard 忘记更改(现在被混淆的)类名的一些问题。

现在结构看起来像这样:

**Obfuscated_fixed.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
--> sample.fxml

sample.fxml 文件现在如下所示:

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>

<?import p.a?>

<VBox fx:controller="p.b"
          xmlns:fx="http://javafx.com/fxml">
    <children>
        <a>
            <children>
                <Button text="Test"></Button>
            </children>
        </a>
    </children>
</VBox>

问题

现在这个 jar 应该真的可以再次工作了,因为一切都正常了。但事实并非如此! fxml 加载器无法加载 CustomControl(现在命名/混淆为“a.class”)。这是为什么呢?

启动 jar 文件时出现以下错误输出(我运行的是 java 版本 1.8.0_40):

E:\Eigene Programme\GuardTest\out\obfuscated>java -jar Obfuscated_fixed.jar
Exception in Application start method
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
        at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown So
urce)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152(
Unknown Source)
        at com.sun.javafx.application.LauncherImpl$$Lambda$49/849460928.run(Unkn
own Source)
        at java.lang.Thread.run(Unknown Source)
Caused by: javafx.fxml.LoadException:
file:/E:/Eigene%20Programme/GuardTest/out/obfuscated/Obfuscated_fixed.jar!/p/sam
ple.fxml

        at javafx.fxml.FXMLLoader.constructLoadException(Unknown Source)
        at javafx.fxml.FXMLLoader.importClass(Unknown Source)
        at javafx.fxml.FXMLLoader.processImport(Unknown Source)
        at javafx.fxml.FXMLLoader.processProcessingInstruction(Unknown Source)
        at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
        at javafx.fxml.FXMLLoader.load(Unknown Source)
        at p.c.start(Main.java:13)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159
(Unknown Source)
        at com.sun.javafx.application.LauncherImpl$$Lambda$52/663980963.run(Unkn
own Source)
        at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(Unknown
 Source)
        at com.sun.javafx.application.PlatformImpl$$Lambda$46/410424423.run(Unkn
own Source)
        at com.sun.javafx.application.PlatformImpl.lambda$null$170(Unknown Sourc
e)
        at com.sun.javafx.application.PlatformImpl$$Lambda$48/1149216748.run(Unk
nown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(Unknown S
ource)
        at com.sun.javafx.application.PlatformImpl$$Lambda$47/1963387170.run(Unk
nown Source)
        at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$null$145(Unknown Source)
        at com.sun.glass.ui.win.WinApplication$$Lambda$36/237061348.run(Unknown
Source)
        ... 1 more
Caused by: java.lang.ClassNotFoundException
        at javafx.fxml.FXMLLoader.loadType(Unknown Source)
        ... 26 more

E:\Eigene Programme\GuardTest\out\obfuscated>Pause
Drücken Sie eine beliebige Taste . . .

在主类中设置默认的类加载器

FXMLLoader.setDefaultClassLoader(this.getClass().getClassLoader());

也无济于事。

项目文件

您可以在此处找到示例项目 (IntelliJ): https://www.dropbox.com/s/ot51spvwk6lzo4k/GuardTest.zip?dl=0

IntelliJ 生成的 jar 工件被编译为: ./out/artifacts/JavaFXApp/JavaFXApp.jar

混淆后的 Jar 位于: ./out/obfuscated/Obfuscated.jar

如上所述的混淆但固定(至少应该是)jar: ./out/obfuscated/Obfuscated_fixed.jar

为了显示“sample.fxml”文件中的导入语句导致问题,我从 fxml 文件中删除了我的自定义控件并将其保存到(工作)jar 中: ./out/obfuscated/Obfuscated_fixed_work.jar

对于这个冗长的问题,我深表歉意。无论如何,我希望你能帮助我:)

【问题讨论】:

【参考方案1】:

我找到了解决办法!问题是 FXML 无法导入不以大写字母开头的类。因此,必须提供 ProGuard 用于混淆的可用名称列表。这是由以下人员完成的:

-classobfuscationdictionary obfuscationClassNames.txt

使用 obfuscationClassNames.txt 包含可用类名的行分隔列表:

A
B
C
D
...

【讨论】:

谢谢!也为我工作。我认为 ProGuard 会有一些更简单的选择 很好,但是我在哪里保存这个文件?或者我如何将它链接到在 proguardGui 中启动的混淆过程?【参考方案2】:

我使用了相同的步骤。我可以看到导入已更新到 fxml 文件中,但它们的使用没有。

获取异常:

javafx.fxml.LoadException: MenuBarControl 不是有效类型。

但如果我看到 fxml 文件导入已更新,但没有使用。

< ? import q.A ? >
< ? import r.A ? >

 <VBox fx:id="top">
    <MenuBarControl fx:id="menuBarControl"/>
  </VBox>

【讨论】:

当我没记错时,这是 ProGuard 的另一个问题。您需要配置 ProGuard 以使用选项“-printmapping”输出映射文件(哪个类具有哪个混淆名称),并在混淆后手动更改。 这可以通过避免&lt;?import ... &gt; 语句并在XML 标记中写入完整的类名来轻松实现。我目前的问题是fx:ids 没有应用于我的控制器类,所以我得到 NullPointerExceptions... 有什么解决方案吗? @Crine 您是否尝试将 -keepattributes 和 -keepclassmembers 添加到 ProGuard 设置中,如我上面的原始问题所示?这应该可以解决问题,因为注释为 fxml 的类成员不应该被混淆。

以上是关于ProGuard 破坏 JavaFX 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

带有自定义控件和自定义 StringProperty 的 Proguard

Proguard 混淆正在破坏 simplexml

Gradle badass-runtime-plugin 和 ProGuard Gradle 插件

为啥 javafx 会破坏我的半透明游标?

Proguard Maven 插件不使用指定的 Proguard 版本

应用程序使用 proguard 崩溃