Closed pcdlrzxx closed 4 months ago
OK 高版本 JDK 的 BUG 我回去测试下
尝试用 JAVA8 执行下看看,是否有问题,我主要测试 JAVA 8
排查了下,报错原因和打开关闭配置无关,是 DLL 导出的问题,第一次嵌入的 DLL 好像没成功到处,然后 JNI 加载失败,UnsatisfiedLinkError,第二次跑已经导出了,可以直接加载
第二个报错,可以尝试用 JDK8 解决
因为 字节码加密 功能是基于 JNI 实现,无法保证通用性,我是基于 JDK 8 编译测试的
所以目前我修改了 字节码加密 功能必须 Java 8 才能用
https://github.com/jar-analyzer/jar-obfuscator/commit/90dd2afc3d7d54be5074d206b5070bde82d48b34
测试完了,来反馈一下:
1、关于高版本JDK,我在这里找到了类似的问题,大概意思就是JDK 12以后,禁止了以反射方式访问java.lang.ClassLoader
,不知道是不是这个原因,话说大佬后续会支持高版本JDK吗?
2、JDK 8下测试的结果是jar包都能正常运行,但是部分情况下混淆结果貌似不太对,3种情况:
a、当enableClassName: true & enablePackageName: false
或者enableClassName: false & enablePackageName: false
时,混淆结果正常,用JD-GUI打开看class文件都是// INTERNAL ERROR //
b、当enableClassName: false & enablePackageName: true
时,用JD-GUI打开看class文件虽然是// INTERNAL ERROR //
,但包名却没有正确的混淆,还是显示的原包名
c、当enableClassName: true & enablePackageName: true
时,用JD-GUI打开jar包虽然包名和类名正确混淆,但class文件却不是// INTERNAL ERROR //
,而是混淆加密后的代码形式
目前就发现这些,还有一个小问题就是,用了JNI本地方式加密后,执行jar包时会额外输出一些其他的日志信息,这个倒是影响不大
对了,补充问一下,用了JNI的方式加密后,是不是其他的类似enableClassName、enablePackageName、enableEncryptString等都可以关闭掉(毕竟代码都被保护到本地了),还是说即使是JNI方式,也是有办法反编译出源代码呢?
(1) 高版本的反射网上有办法可以绕过,之后我有计划看一下.
(2) a 混淆结果正常,但是反编译 // INTERNAL ERROR //
我觉得反而说明混淆的好,无法反编译哈哈
(2) b 这个情况可以处理下,但我觉得意义不大,因为很少有需求是 只混淆包名 不混淆类名 这样
(2) c 这个 INTERNAL ERROR
我不太明白为啥,可能是 JD-GUI
内部问题,我这没啥可处理的感觉
(3) JNI 输出信息 这个我是为了调试测试 可以去掉
(4) JNI 的加密,只要不泄露解密加密 dll
库,足够防止 99%
的人
你可以把 dll
当成钥匙,JAR
是宝箱,只有宝箱打不开,必须有钥匙;但是你的钥匙丢失了,别人拿到你的 dll
钥匙后,就会比较容易打开。由于我还设置了密码,所以会更难打开。不过如果别人有访问你系统的权限,可以拿到本地命令行启动的字符串,获得里面的 KEY 信息。
总之 JNI 的方式,尽了最大可能保护你的代码,如果愿意结合其他的混淆方式,效果是更好的
c、当
enableClassName: true & enablePackageName: true
时,用JD-GUI打开jar包虽然包名和类名正确混淆,但class文件却不是// INTERNAL ERROR //
,而是混淆加密后的代码形式(2) c 这个
INTERNAL ERROR
我不太明白为啥,可能是JD-GUI
内部问题,我这没啥可处理的感觉
关于这一点,貌似不是JD-GUI
的问题,我今天又用其他的反编译工具测试了下,也是同样的结果。感觉像是在这种情况下,JNI 加密方式没有起作用,代码并没有被保护到本地,大佬有时间可以再测试一下
.
(4) JNI 的加密,只要不泄露解密加密
dll
库,足够防止99%
的人 你可以把dll
当成钥匙,JAR
是宝箱,只有宝箱打不开,必须有钥匙;但是你的钥匙丢失了,别人拿到你的dll
钥匙后,就会比较容易打开。由于我还设置了密码,所以会更难打开。不过如果别人有访问你系统的权限,可以拿到本地命令行启动的字符串,获得里面的 KEY 信息。
如果需要把应用部署到客户的电脑上,让他们自己管理,这种情况下就得把dll
也给到他们,这样是不是就没法很好的保护代码了,像这种情况,有没有可能再对这个dll
进行保护啥的(虽然我觉得也没多大意义。。)
其实我很好奇,C/C++
写的的程序,大多数也是调用dll
,为什么他们的就很难反编译呢?
我可能没有理解,你的意思是 enableClassName: true & enablePackageName: true
且 enableSuperObfuscate: true
生成的 _encrypted.jar
文件其实是没有加密,可以被反编译观察到的吗
DLL 的混淆有更成熟稳定强大的技术,比如 OLLVM 和 VMP 等
- 我可能没有理解,你的意思是
enableClassName: true & enablePackageName: true
且enableSuperObfuscate: true
生成的_encrypted.jar
文件其实是没有加密,可以被反编译观察到的吗
是的,可以被反编译看到代码
2. DLL 的混淆有更成熟稳定强大的技术,比如 OLLVM 和 VMP 等
好吧,看来JAVA
在这块还是任重而道远啊
是否没有正确配置
superObfuscatePackage: me.n1ar4
字节码加密功能需要重新配置包名,而不是选择 obfuscatePackage
这个superObfuscatePackage
配置项有什么规则吗,我现在是将这项与obfuscatePackage
配置成了一样的(比如都配置成了 com.example)。而且我测试的结果是,只有enableClassName: true & enablePackageName: true
这一种情况下,JNI 加密会失效,只要这两项不同时为 true,其他的所有配置项都一样的情况下,JNI 加密都是有效的
superObfuscatePackage 是一个单独的配置,仅在 enableSuperObfuscate true 时生效,与其他配置无关
所以我不太清除原因,你可以在此测试看看
@4ra1n 大佬,我找到 JNI 字节码加密有时失效的原因了。这两天看了一下你的代码,发现在字节码加密前,会拿_obf.jar
里面的包名跟配置文件中的superObfuscatePackage
值进行比较,只有相同时才会执行字节码加密。但是如果开启了enablePackageName
,那_obf.jar
里的包名就已经是混淆后的,所以就无法匹配,导致没有执行加密。具体代码是PatchHelper
类的这一段:
if (name.toLowerCase().endsWith(ClassFile)) {
if (name.startsWith("BOOT-INF/classes/")) {
tempClassName = name.split("BOOT-INF/classes/")[1];
if (tempClassName.startsWith(packageName)) {
try {
bytes = CodeEncryptor.encrypt(bytes, bytes.length, key);
} catch (Exception e) {
logger.error("encrypt error: {}", e.toString());
return;
}
}
} else {
// encrypt target class
if (name.startsWith(packageName)) {
try {
bytes = CodeEncryptor.encrypt(bytes, bytes.length, key);
} catch (Exception e) {
logger.error("encrypt error: {}", e.toString());
return;
}
}
}
}
话说,这里需要比较这两个值吗,我感觉其实比较配置文件中的superObfuscatePackage
与obfuscatePackage
这两个值就行了,这样也不存在已经混淆了的情况
b、当
enableClassName: false & enablePackageName: true
时,用JD-GUI打开看class文件虽然是// INTERNAL ERROR //
,但包名却没有正确的混淆,还是显示的原包名(2) b 这个情况可以处理下,但我觉得意义不大,因为很少有需求是 只混淆包名 不混淆类名 这样
然后关于这点,我顺便改了下代码,自测也通过了,如果需要的话,我可以拉一个PR合并一下
好的,欢迎,我看下问题的原因(另外我测试是我的 JNI Util 有问题,简单改一下是支持高版本JDK的)
哦,那0.0.7已经支持高版本JDK了吗,还是说要在之后的版本呢
@4ra1n 大佬,我找到 JNI 字节码加密有时失效的原因了。这两天看了一下你的代码,发现在字节码加密前,会拿
_obf.jar
里面的包名跟配置文件中的superObfuscatePackage
值进行比较,只有相同时才会执行字节码加密。但是如果开启了enablePackageName
,那_obf.jar
里的包名就已经是混淆后的,所以就无法匹配,导致没有执行加密。具体代码是PatchHelper
类的这一段:if (name.toLowerCase().endsWith(ClassFile)) { if (name.startsWith("BOOT-INF/classes/")) { tempClassName = name.split("BOOT-INF/classes/")[1]; if (tempClassName.startsWith(packageName)) { try { bytes = CodeEncryptor.encrypt(bytes, bytes.length, key); } catch (Exception e) { logger.error("encrypt error: {}", e.toString()); return; } } } else { // encrypt target class if (name.startsWith(packageName)) { try { bytes = CodeEncryptor.encrypt(bytes, bytes.length, key); } catch (Exception e) { logger.error("encrypt error: {}", e.toString()); return; } } } }
话说,这里需要比较这两个值吗,我感觉其实比较配置文件中的superObfuscatePackage
与obfuscatePackage
这两个值就行了,这样也不存在已经混淆了的情况
我想了下,上面画线的说法不对,这样会将所有class文件都进行加密。emm,这个问题还是交给大佬你来处理吧,哈哈
OKOK 原来如此 我大概知道原因了
我解决了高版本不可用的问题,测试通过的,但还是建议使用 JAVA 8 来做字节码加密
对于这个问题,我决定如果开启包名混淆将不允许字节码加密
https://github.com/jar-analyzer/jar-obfuscator/commit/3ef2d0d8a02ba6b9b043aa2842955d83b874a96f
因为:每一个子包名都变成随机的了
例如 com.a.b 和 com.a.b.c 会变成两个不同的包,无法再通过一个参数 PACKAGE 指定 native 层的参数
完全加密感觉不可取,某些类如果被加密会出问题
这个问题是高版本 JNI 报错的问题,现在已经解决,我关闭了,有其他问题可以再提 issue
OK,辛苦大佬了,等新版本出来我再测试下高版本 JDK 下的加密功能。
我刚拉了个PR,就是简单处理下enableClassName: false & enablePackageName: true
时的混淆问题
在开启JVMTI加密字节码功能后,执行过程中出现了报错,有2种情况:
1、当
enableClassName
和enablePackageName
任意一个关闭或都关闭时,会出现以下报错:. 2、当
enableClassName
和enablePackageName
都打开时,会出现以下报错:此时如果再去执行生成的xxx_obf_encrypted.jar,则会产生下面的信息:
. 我这边的环境是win7_x64,jdk_17.0.9