详细介绍如何通过修改字节码的方式破解Aspose系列jar包的授权验证,包含完整的代码示例。
授人以鱼 不如授人以渔
调用授权方法
``java InputStream is = new FileInputStream("..license.xml"); License license = new License(); license.setLicense(is); ``
license.xml文件内容
```xml
```
分析License类的setLicense方法找到关键代码
```java public void setLicense(String licenseName) throws Exception { if (licenseName == null) { throw new NullPointerException(zzVu.zzIR().zzZ42(new byte[]{105, 108, 101, 99, 115, 110, 78, 101, 109, 97, 101})); } else { (new zzXDb()).zzY0J(licenseName, zzWJD.zzWIQ()); } }
public void setLicense(InputStream stream) throws Exception { if (stream == null) { throw new NullPointerException(zzVu.zzIR().zzZ42(new byte[]{116, 115, 101, 114, 109, 97})); } else { (new zzXDb()).zzY0J(stream); } } ```
setLicense的两个重载方法最终都调用了(new zzXDb()).zzY0J(stream);中的zzY0J方法,进入zzY0J方法观察代码发现重点在于void zzY0J方法下面的 (InputStream var1) throws Exception这个重载方法里面,但是里面代码很多不太好找到关键代码,所以转头去寻找关于验证对外调用的静态方法,最终找到了在zzY0J方法下面的
```java static byte[] zzX8p() { boolean var0 = zzWiV == null || zzWiV.zzWSL == zzYeQ.zzX0q || (new Date()).after(zzWiV.zzZ3l) || zzYKk.zzWy3() == 4096; if (zzW5s == 0L) { zzW5s ^= zzVWj; }
boolean var1 = false; if (zzZB8.zzxn() != null) { var1 = zzZB8.zzZ7p() == zzu3.zzX0q; byte[] var2 = var0 && var1 ? zzYeQ.zzX0q : zzYeQ.zzXgr; return var2; } else { return null; } }
static byte[] zzWQR() { boolean var0 = zzWiV == null || zzWiV.zzWSL == zzYeQ.zzX0q || (new Date()).after(zzWiV.zzZ3l) || zzYKk.zzWy3() == 4096; boolean var1 = zzZB8.zzZ7p() == zzu3.zzX0q; byte[] var2 = var0 && var1 ? zzYeQ.zzX0q : zzYeQ.zzXgr; return var2; } ```
这两个方法主要在于对外返回了一个byte数组,返回值是zzYeQ中的静态常量,所以重点就在于上面的判断语句boolean var0 = zzWiV == null || zzWiV.zzWSL == zzYeQ.zzX0q || (new Date()).after(zzWiV.zzZ3l) || zzYKk.zzWy3() == 4096;让它返回什么数据。 这里需要分析zzWiV.zzWSL zzWiV.zzZ3l zzYKk.zzWy3()这三个数据,在当前zzXDbclass文件中搜索找到在void zzY0J(InputStream var1) throws Exception方法中关键的关键位置赋值了
this.zzWSL = zzYeQ.zzXgr;
zzWiV = this;
观察它上下位置代码发现看起来都是做验证错误的处理,所以可以尝试去掉上下的验证。 再来看zzWiV.zzZ3l变量属性为Date应该是时间什么的可以直接给个最大值。 然后是zzYKk.zzWy3()进入看到
static int zzWy3() {
return zzYU8 == 128 && !zzyS ? 256 : 4096;
}
那么返回值就是256和4096二选一,尝试后选择返回256。
void zzY0J(InputStream var1)方法体为this.zzZ3l = new java.util.Date(Long.MAX_VALUE);//Date赋值最大值
this.zzWSL = zzYeQ.zzXgr;//直接返回验证成功的执行
zzWiV = this;//直接返回验证成功的执行
zzYKk类下的static int zzWy3()方法体为return 256;
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.28.0-GA</version>
</dependency>
package top.fanliu;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
public class Pojie {
public static void main(String[] args) {
modifyPptJar();
}
/**
* 修改slides.jar包里面的校验
*/
public static void modifyPptJar() {
try {
//这一步是完整的jar包路径,选择自己解压的jar目录
ClassPool.getDefault().insertClassPath("C:\\Users\\53094\\Downloads\\aspose-slides-22.11-jdk16.jar");
CtClass zzZJJClass = ClassPool.getDefault().getCtClass("com.aspose.slides.internal.of.public");
CtMethod[] methodA = zzZJJClass.getDeclaredMethods();
for (CtMethod ctMethod : methodA) {
CtClass[] ps = ctMethod.getParameterTypes();
if (ps.length == 3 && ctMethod.getName().equals("do")) {
System.out.println("ps[0].getName==" + ps[0].getName());
ctMethod.setBody("{}");
}
}
//这一步就是将破译完的代码放在桌面上
zzZJJClass.writeFile("C:\\Users\\53094\\Desktop\\");
} catch (Exception e) {
System.out.println("错误==" + e);
}
}
}
运行修改方法后会在桌面生成 com 修改后的文件夹

String sourceFile = "D:\\b.doc";//输入的文件
String targetFile = "D:\\转换后.pdf";//输出的文件
/**
* Word转PDF操作
*
* @param sourceFile 源文件
* @param targetFile 目标文件
*/
public static void doc2pdf(String sourceFile, String targetFile) {
try {
long old = System.currentTimeMillis();
FileOutputStream os = new FileOutputStream(targetFile);
com.aspose.words.Document doc = new com.aspose.words.Document(sourceFile);
doc.save(os, com.aspose.words.SaveFormat.PDF);
os.close();
long now = System.currentTimeMillis();
System.out.println("共耗时:" + ((now - old) / 1000.0) + "秒"); //转化用时
} catch (Exception e) {
e.printStackTrace();
}
}
博主将破解 Aspose 系列的代码放在 github 上面,你也可以克隆到本地进行破解
源码地址:pojieAspose