一、前言
最近遇到一个需求根据word模板导出word文档,调研了一圈发现aspose比较好用,但缺点就是aspose需要收费.如果不付费就会出现水印.于是按照免费即是最好的,最好有破解的想法,尝试了一下如果绕过aspose的license验证.
二、分析
License aposeLic = new License();
FileInputStream stream = new FileInputStream("license.xml");//许可xml
aposeLic.setLicense(stream);
boolean licensed = aposeLic.isLicensed();
System.out.println(licensed);
根据以上的代码就可以根据aposeLic.setLicense(stream);
这句话追踪进去.
//反编译出
//License.class
public void setLicense(InputStream stream) throws Exception {
if (stream == null) {
throw new NullPointerException("stream");
} else {
(new zzZLD()).zzW(stream);
}
}
//进入new zzZLD()).zzW(stream) 这个方法
//反编译出
//zzZLD.class final void zzW(InputStream var1)
final void zzW(InputStream var1) throws Exception {
if (var1 == null) {
throw new NullPointerException("stream");
} else {
.......
} else {
} else if ((new Date()).after(this.zzYPO)) {
throw new IllegalStateException("The license has expired.");
} else {
this.zzYPN = 1;
zzYPM = this;
}
}
} else {
throw new IllegalStateException("This license is disabled, please contact Aspose to obtain a new license.");
}
}
}
可以从zzW方法中看出,有部分的验证逻辑在里面,如果最终通过验证会修改zzYPN
和zzYPM
这两个变量,先把这个几下,看看这两变量有啥用.
在zzZLD.class
中可以看到这两个变量在zzZoR
方法和zzZoQ
方法中参与运算.
//反编译出
//zzZLD.class
static int zzZoR() {
boolean var0 = zzYPM == null || zzYPM.zzYPN == 0 || (new Date()).after(zzYPM.zzYPO) || zzYRK.zzYEL() == 4096;
boolean var1 = zzZGS.zzZg4() == 0;
return var0 && var1 ? 0 : 1;
}
static int zzZoQ() {
boolean var0 = zzYPM == null || zzYPM.zzYPN == 0 || (new Date()).after(zzYPM.zzYPO) || zzYRK.zzYEL() == 4096;
boolean var1 = zzZGS.zzZg4() == 0;
return var0 && var1 ? 0 : 1;
}
从以上两个方法返回值可以看出是验证license是否有效的方法.
//反编译出
//License.class
public boolean isLicensed() {
return zzZLD.zzZoQ() == 1;
}
看到isLicensed
这个方法我就更加确定了,但是这两个方法,看起来逻辑完全一模一样.emmmmmm.那么怎么破解呢?
三、破解
破解思路是利用javassist
修改掉zzZLD
类中的zzW
方法的验证逻辑.
CtClass zzZLDClass = ClassPool.getDefault().getCtClass("com.aspose.words.zzZLD");
CtMethod zzW = zzZLDClass.getDeclaredMethod("zzW");
zzW.setBody("{this.zzYPO = new java.util.Date(Long.MAX_VALUE);this.zzYPN = 1;zzYPM = this;}");
zzZLDClass.writeFile("~/project/aspose");
这里设置Date(Long.MAX_VALUE)
是有原因的,因为在zzZoR
方法和zzZoQ
方法中会进行一个after的一个判断,只要var0
变量为false那么就会返回1,这样验证就通过了.
emmmm,但是仅仅是这样还不够,因为在验证的条件中还有一个zzYRK.zzYEL() == 4096
条件,如果不直接改zzZoR
方法和zzZoQ
方法的话就需要修改zzYEL()
方法的返回值为256即可.
CtClass zzYRKClass = ClassPool.getDefault().getCtClass("com.aspose.words.zzYRK");
CtMethod zzYELMethod = zzYRKClass.getDeclaredMethod("zzYEL");
zzYELMethod.setBody("{return 256;}");
zzYRKClass.writeFile("~/project/aspose");
Ok.以上就搞定了,把修改好的class覆盖掉jar中的class就行了.
对了,还要删除jar中的META-INF文件夹,因为aspose会进行一个文件指纹验证.