周六闲来无事,想到之前很容易对aspose.cell v21.4完成破解研究(见博客),但是对aspose.words没有完成破解分析。
这次换个思路,基于License进行class文件分析。
一:从官网下载
下载地址:Java Word Processor API | Aspose.Words for Java;
或者使用maven方式进行jar包下载:
<!-- maven配置 -->
<repositories>
<repository>
<id>AsposeJavaAPI</id>
<name>Aspose Java API</name>
<url>https://repository.aspose.com/repo/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>21.12</version>
<type>pom</type>
</dependency>
</dependencies>
需注意:从官网下载的jar包为实时混淆,混淆的class名各不相同,因此本博客列出关键方法,但是方法名不保证一致。
二:License安装
见Licensing and Subscription | Documentation
关键代码:
com.aspose.words.License license = new com.aspose.words.License();
license.setLicense("path:\\Aspose.Words.Java.lic");
给出License示例:
<License>
<Data>
<LicensedTo>Aspose Scotland Team</LicensedTo>
<EmailTo>billy.lundie@aspose.com</EmailTo>
<LicenseType>Developer OEM</LicenseType>
<LicenseNote>Limited to 1 developer, unlimited physical locations</LicenseNote>
<OrderID>140408052324</OrderID>
<UserID>94236</UserID>
<OEM>This is a redistributable license</OEM>
<Products>
<Product>Aspose.Total for Java</Product>
</Products>
<EditionType>Enterprise</EditionType>
<SerialNumber>9a59547c-41f0-428b-ba72-7c4368f151d7</SerialNumber>
<SubscriptionExpiry>20221231</SubscriptionExpiry>
<LicenseVersion>3.0</LicenseVersion>
<LicenseInstructions>http://www.aspose.com/corporate/purchase/license-instructions.aspx</LicenseInstructions>
</Data>
<Signature>FO3PHsblgDt8F59sMT1l1amyi9qk2V6E8dQkIP7LdTJSxDibNEFu1zOinQbqFfKv/ruttvcxoROkc1tUe0DtO6cP1Zf6J0VemgSY8i/LZECTGszRqJVQRZ0MoVnBhuPAJk5eli7fhVcF8hWd3E4XQ3LzfmJCuaj2NEteRi5Hrfg=</Signature>
</License>
进入License.class文件,基于IDEA自带反编译功能,我们可以看到源码:下图zzWf9为混淆类,可能每人下载类均不相同,记住该类名。
public class License {
public License() {
zzZ3T.zzZAj();
}
public void setLicense(String licenseName) throws Exception {
if (licenseName == null) {
throw new NullPointerException(zzZ3T.zzZAj().zzMk(new byte[]{105, 108, 101, 99, 115, 110, 78, 101, 109, 97, 101}));
} else {
(new zzWf9()).zzY98(licenseName, zzZdu.zzXGb());
}
}
public void setLicense(InputStream stream) throws Exception {
if (stream == null) {
throw new NullPointerException(zzZ3T.zzZAj().zzMk(new byte[]{116, 115, 101, 114, 109, 97}));
} else {
(new zzWf9()).zzY98(stream);
}
}
}
可以看到License类中有zzWf9的2个重载方法,关键在于zzY98方法(最后提示混淆方法不保证一致!)。
进入核心验证方法:
void zzY98(InputStream var1) throws Exception {
if (var1 == null) {
throw new NullPointerException(zzZ3T.zzZAj().zzMk(new byte[]{116, 115, 101, 114, 109, 97}));
} else if (!this.zzXvE(var1)) {
throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{110, 73, 97, 118, 105, 108, 32, 100, 105, 108, 101, 99, 115, 110, 32, 101, 105, 115, 110, 103, 116, 97, 114, 117, 46, 101, 80, 32, 101, 108, 115, 97, 32, 101, 97, 109, 101, 107, 115, 32, 114, 117, 32, 101, 104, 116, 32, 101, 105, 108, 101, 99, 115, 110, 32, 101, 105, 102, 101, 108, 119, 32, 115, 97, 110, 32, 116, 111, 109, 32, 100, 111, 102, 105, 101, 105, 46, 100}));
} else {
zzWhX var2 = zzWcN;
if (var2 == null) {
var2 = zzYf8(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 76, 46, 99, 105, 110, 101, 101, 115, 66, 46, 97, 108, 107, 99, 105, 76, 116, 115}), (String)null);
zzWcN = var2;
}
zzWhX var3 = zzX3l;
if (var3 == null) {
var3 = zzYf8(zzZ3T.zzZAj().zzMk(new byte[]{111, 67, 104, 110, 108, 111, 97, 100, 101, 116, 76, 46, 99, 105, 110, 101, 101, 115, 66, 46, 97, 108, 107, 99, 105, 76, 116, 115}), zzZDr);
zzX3l = var3;
}
if (zznY.zzZHa() > 0) {
throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{110, 73, 97, 118, 105, 108, 32, 100, 105, 108, 101, 99, 115, 110, 32, 101, 105, 115, 110, 103, 116, 97, 114, 117, 46, 101, 80, 32, 101, 108, 115, 97, 32, 101, 97, 109, 101, 107, 115, 32, 114, 117, 32, 101, 104, 116, 32, 101, 105, 108, 101, 99, 115, 110, 32, 101, 105, 102, 101, 108, 119, 32, 115, 97, 110, 32, 116, 111, 109, 32, 100, 111, 102, 105, 101, 105, 46, 100}));
} else if (!var2.contains(this.zzWO4) && !var3.contains(this.zzWO4)) {
boolean var4 = false;
String[] var5 = this.zzWJJ;
int var6 = var5.length;
int var7 = 0;
while(var7 < var6) {
String var8 = var5[var7];
if (!var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 32}) + "Java") && !var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{111, 67, 104, 110, 108, 111, 97, 100, 101, 116, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 32}) + "Java")) {
if ((var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97})) || var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{111, 67, 104, 110, 108, 111, 97, 100, 101, 116, 84, 46, 116, 111, 108, 97}))) && "Java".equals(zzZ3T.zzZAj().zzMk(new byte[]{78, 46, 84, 69}))) {
var4 = true;
break;
}
if (!var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 80, 32, 111, 114, 117, 100, 116, 99, 70, 32, 109, 97, 108, 105, 121})) && !var8.equals(zzZ3T.zzZAj().zzMk(new byte[]{111, 67, 104, 110, 108, 111, 97, 100, 101, 116, 84, 46, 116, 111, 108, 97, 80, 32, 111, 114, 117, 100, 116, 99, 70, 32, 109, 97, 108, 105, 121}))) {
if (var8.equals("Aspose.Words for Java")) {
var4 = true;
break;
}
String var9;
String var10;
if ("Java".equals(zzZ3T.zzZAj().zzMk(new byte[]{78, 46, 84, 69}))) {
var9 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{97, 88, 97, 109, 105, 114, 46, 110, 110, 65, 114, 100, 105, 111, 100});
var10 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 88, 32, 109, 97, 114, 97, 110, 105, 65, 46, 100, 110, 111, 114, 100, 105});
String var11 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{110, 65, 114, 100, 105, 111, 32, 100, 105, 118, 32, 97, 97, 88, 97, 109, 105, 114, 110});
String var12 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 65, 32, 100, 110, 111, 114, 100, 105, 118, 32, 97, 105, 88, 32, 109, 97, 114, 97, 110, 105});
String var13 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{79, 105, 32, 83, 105, 118, 32, 97, 97, 88, 97, 109, 105, 114, 110});
String var14 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 105, 32, 83, 79, 118, 32, 97, 105, 88, 32, 109, 97, 114, 97, 110, 105});
String var15 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{97, 77, 32, 99, 105, 118, 32, 97, 97, 88, 97, 109, 105, 114, 110});
String var16 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 77, 32, 99, 97, 118, 32, 97, 105, 88, 32, 109, 97, 114, 97, 110, 105});
if (var8.equals(var9) || var8.equals(var10) || var8.equals(var11) || var8.equals(var12) || var8.equals(var13) || var8.equals(var14) || var8.equals(var15) || var8.equals(var16)) {
var4 = true;
break;
}
}
if ("Java".equals(zzZ3T.zzZAj().zzMk(new byte[]{97, 74, 97, 118, 65, 46, 100, 110, 111, 114, 100, 105}))) {
var9 = "Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{102, 32, 114, 111, 32}) + zzZ3T.zzZAj().zzMk(new byte[]{110, 65, 114, 100, 105, 111, 100});
var10 = zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 84, 46, 116, 111, 108, 97, 102, 32, 114, 111, 65, 32, 100, 110, 111, 114, 100, 105});
if (var8.equals(var9) || var8.equals(var10)) {
var4 = true;
break;
}
}
if (var8.equals("Aspose.Words") && "Java".equals(zzZ3T.zzZAj().zzMk(new byte[]{78, 46, 84, 69}))) {
var4 = true;
break;
}
if (var8.equals("Aspose.Words" + zzZ3T.zzZAj().zzMk(new byte[]{80, 32, 111, 114, 117, 100, 116, 99, 70, 32, 109, 97, 108, 105, 121}))) {
var4 = true;
break;
}
++var7;
continue;
}
var4 = true;
break;
}
var4 = true;
break;
}
if (!var4) {
throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{104, 84, 32, 101, 105, 108, 101, 99, 115, 110, 32, 101, 115, 105, 110, 32, 116, 111, 118, 32, 108, 97, 100, 105, 102, 32, 114, 111, 116, 32, 105, 104, 32, 115, 114, 112, 100, 111, 99, 117, 46, 116}));
} else {
Date var17 = (new SimpleDateFormat("yyyy.MM.dd")).parse("2021.12.01");
if (var17.after(this.zzWec)) {
throw new IllegalStateException(this.zzY4i.zzXJX(new byte[]{84, 104, 101, 32, 115, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 32, 105, 110, 99, 108, 117, 100, 101, 100, 32, 105, 110, 32, 116, 104, 105, 115, 32, 108, 105, 99, 101, 110, 115, 101, 32, 97, 108, 108, 111, 119, 115, 32, 102, 114, 101, 101, 32, 117, 112, 103, 114, 97, 100, 101, 115, 32, 117, 110, 116, 105, 108, 32}) + (new SimpleDateFormat(this.zzY4i.zzXJX(new byte[]{100, 100, 32, 77, 77, 77, 32, 121, 121, 121, 121}), Locale.ENGLISH)).format(this.zzWec) + this.zzY4i.zzXJX(new byte[]{44, 32}) + this.zzY4i.zzXJX(new byte[]{98, 117, 116, 32, 116, 104, 105, 115, 32, 118, 101, 114, 115, 105, 111, 110, 32, 111, 102, 32, 116, 104, 101, 32, 112, 114, 111, 100, 117, 99, 116, 32, 119, 97, 115, 32, 114, 101, 108, 101, 97, 115, 101, 100, 32, 111, 110, 32}) + (new SimpleDateFormat(this.zzY4i.zzXJX(new byte[]{100, 100, 32, 77, 77, 77, 32, 121, 121, 121, 121}), Locale.ENGLISH)).format(var17) + this.zzY4i.zzXJX(new byte[]{46, 32}) + this.zzY4i.zzXJX(new byte[]{80, 108, 101, 97, 115, 101, 32, 114, 101, 110, 101, 119, 32, 116, 104, 101, 32, 115, 117, 98, 115, 99, 114, 105, 112, 116, 105, 111, 110, 32, 111, 114, 32, 117, 115, 101, 32, 97, 32, 112, 114, 101, 118, 105, 111, 117, 115, 32, 118, 101, 114, 115, 105, 111, 110, 32, 111, 102, 32, 116, 104, 101, 32, 112, 114, 111, 100, 117, 99, 116, 46}));
} else if ((new Date()).after(this.zzYS9)) {
throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{104, 84, 32, 101, 105, 108, 101, 99, 115, 110, 32, 101, 97, 104, 32, 115, 120, 101, 105, 112, 101, 114, 46, 100}));
} else if (this.zzWec.getYear() < 2099) {
this.zzWnu = zzYjw.zzVS4;
zzWhV = this;
}
}
} else {
throw new IllegalStateException(zzZ3T.zzZAj().zzMk(new byte[]{104, 84, 115, 105, 108, 32, 99, 105, 110, 101, 101, 115, 105, 32, 32, 115, 105, 100, 97, 115, 108, 98, 100, 101, 32, 44, 108, 112, 97, 101, 101, 115, 99, 32, 110, 111, 97, 116, 116, 99, 65, 32, 112, 115, 115, 111, 32, 101, 111, 116, 111, 32, 116, 98, 105, 97, 32, 110, 32, 97, 101, 110, 32, 119, 105, 108, 101, 99, 115, 110, 46, 101}));
}
}
}
这个方法有2个注意点:
1、日期判断硬编码:21.12版本注册日期应为2021.12.01日之后,2099年之前;
2、第4行zzXvE方法进行License校验(因为验证不通过提示就是License Invalid)。
本License内容原为.NET,<Product>Aspose.Total for .NET</Product>,这时signature验证通过,但是本博客修改为<Product>Aspose.Total for Java</Product>,因此在校验时会提示License Invalid。
综上,哎~对了,又到了字节码编辑的时候了。但是接下来有个地方是有难度的,
三、class文件编辑
主要在zzWf9.zzXvE校验方法,其中有2个地方修改:
源码:
private boolean zzXvE(InputStream var1) throws Exception {
DocumentBuilderFactory var2 = zzXcV.zzWQB();
DocumentBuilder var3 = var2.newDocumentBuilder();
Document var4 = var3.parse(var1);
Element var5 = var4.getDocumentElement();
Element var6 = zzWKk(var5, zzZ3T.zzZAj().zzMk(new byte[]{97, 68, 97, 116}));
Element var7 = zzWKk(var5, zzZ3T.zzZAj().zzMk(new byte[]{105, 83, 110, 103, 116, 97, 114, 117, 101}));
boolean var8 = zzWxF((Node)var6, (Node)var7);
Element var9 = zzWKk(var6, zzZ3T.zzZAj().zzMk(new byte[]{114, 80, 100, 111, 99, 117, 115, 116}));
NodeList var10 = var9.getElementsByTagName(zzZ3T.zzZAj().zzMk(new byte[]{114, 80, 100, 111, 99, 117, 116}));
this.zzWJJ = new String[var10.getLength()];
for(int var11 = 0; var11 < this.zzWJJ.length; ++var11) {
this.zzWJJ[var11] = var10.item(var11).getFirstChild().getNodeValue();
}
this.zzWO4 = zzZqK(var6, zzZ3T.zzZAj().zzMk(new byte[]{101, 83, 105, 114, 108, 97, 117, 78, 98, 109, 114, 101}));
this.zzWec = zzWxF(var6, zzZ3T.zzZAj().zzMk(new byte[]{117, 83, 115, 98, 114, 99, 112, 105, 105, 116, 110, 111, 120, 69, 105, 112, 121, 114}));
this.zzYS9 = zzWxF(var6, zzZ3T.zzZAj().zzMk(new byte[]{105, 76, 101, 99, 115, 110, 69, 101, 112, 120, 114, 105, 121}));
return var8;
}
1、zzXvE,方法体第7行,
boolean var8 = zzWxF((Node)var6, (Node)var7);
这一行需要跟踪调试,经多次调试发现最后是由ThreadLocal对象进行控制,列出zzWf9关键调用链:
// 进入方法一:
private static boolean zzWxF(Node var0, Node var1) throws Exception {
return zzZp8((Node)var0, (Node)var1, (String)null);
}
// 进入方法二:
private static boolean zzZp8(Node var0, Node var1, String var2) throws Exception {
byte[] var3;
if (var0 != null) {
StringBuilder var4 = new StringBuilder();
zzZp8(var4, var0);
var3 = var4.toString().getBytes("UTF-16LE");
} else {
var3 = new byte[0];
}
byte[] var6;
if (var1 != null) {
String var5 = var1.getFirstChild().getNodeValue();
var6 = zzZbv.zz3m(var5);
} else {
var6 = new byte[0];
}
if (var2 == null) {
if (zzWjD(var0)) {
var2 = var6.length == 128 ? zzZDr : zzWxc;
} else {
var2 = var6.length == 128 ? zzYAU : zzX1P;
}
}
return zzZp8(var3, var6, var2);
}
// 进入方法三:
private static boolean zzZp8(byte[] var0, byte[] var1, String var2) throws Exception {
String var3 = zzZ3T.zzZAj().zzMk(new byte[]{81, 65, 66, 65});
byte[] var4 = zzZbv.zz3m(var3);
byte[] var5 = zzZbv.zz3m(var2);
return zzxN.zzZp8(var5, var4, var0, var1);
}
发现进入zzxN.zzZp8方法,列出zzxN调用链:
// 进入方法一:
static boolean zzZp8(byte[] var0, byte[] var1, byte[] var2, byte[] var3) throws Exception {
return zzZqK(var0, var1, var2, var3);
}
// 进入方法二:
private static boolean zzZqK(byte[] var0, byte[] var1, byte[] var2, byte[] var3) throws Exception {
boolean var4 = false;
if (var3.length != var0.length) {
zznY.zzWUu(1);
var4 = true;
}
byte[] var5 = zzZbv.zzXvz(var3);
zzZto var6;
byte[] var7;
byte[] var8 = zzZbv.zzZo9(var7 = zzZbv.zzZp8(var6 = new zzZto(var0, var1), var5), var6.getModulus().bitLength() >> 3);
byte[] var9 = zzWxF(var0, var2, var8.length);
if (var8.length != var9.length) {
var4 = true;
} else {
for(int var10 = 0; var10 < var8.length; ++var10) {
var4 = var4 || var8[var10] != var9[var10];
}
}
zzTq var14;
(var14 = new zzTq(var7)).zzYBj(var5.length, var9.length, false);
zzXUN var11;
(var11 = new zzXUN()).write(var7, 0, var7.length);
var14.zzZp8(var11, var9, var5.length);
if (var14.zzWw9()) {
var5[0] = 0;
var5[1] = 17;
}
zzZ3V var12 = new zzZ3V(var14, var8, true, var14.zzWw9());
int[] var13 = new int[var7.length];
zzXcS.zzZp8(var7, 0, var13, 0, var7.length);
var12.zzZp8(var14);
var12.zzZ8j();
var12.zzbx(true);
var14.zzZ7L(true);
zzZ3T.zzZAj().zzMk(new byte[]{50, 49, 52, 51, 54, 53, 56, 55, 48, 57, 66, 65, 68, 67, 70, 69});
var12.zzZ8j();
var12.zzWs3(zzZ3T.zzZAj().zzMk(new byte[]{115, 65, 111, 112, 101, 115, 87, 46, 114, 111, 115, 100}));
return !var4;
}
在该方法中发现返回校验结果了,再次寻找关键代码,经多次调试终于发现倒数第5行:
var12.zzbx(true);
该方法为关键方法,进入zzZ3V.zzbx方法,列出调用链:
final void zzbx(boolean var1) throws Exception {
this.zzJu = 0;
if (this.zzWza.zzGe() > 0L) {
int var2 = (int)this.zzWza.zzGe() / 2 + 1;
byte[] var3 = this.zzWza.zzWnM();
if (zznY.zzZxN() == 255) {
zznY.zzWWo(128);
}
int var4 = var3.length - 1;
for(int var5 = 0; var5 < var2; ++var5) {
if ((var3[var4] & 255) == 255) {
++this.zzhe;
}
if (var3[var4] != (byte)this.zzZ3G.zz8A()[var4]) {
zznY.zzWUu(128);//注意这行
}
--var4;
}
}
}
特别注意这行:zznY.zzWUu(128);
终于来到最后的zznY类:
static void zzWUu(int var0) {
zzXKA.set(Integer.valueOf(var0));
}
在这一行,需要进行修改使这个ThreadLocal设置值为0(具体请自己调试,后续有>0判断)。
2、zzXvE,最后返回修改为true;
4、总结:
1、zznY.zzWUu修改,每次设置值为0:
static void zzWUu(int var0) {
byte var1 = 0;//需自己修改字节码文件
zzXKA.set(Integer.valueOf(var1));
}
2、zzWf9.zzXvE修改,返回校验结果true:
private boolean zzXvE(InputStream var1) throws Exception {
DocumentBuilderFactory var2 = zzXcV.zzWQB();
DocumentBuilder var3 = var2.newDocumentBuilder();
Document var4 = var3.parse(var1);
Element var5 = var4.getDocumentElement();
Element var6 = zzWKk(var5, zzZ3T.zzZAj().zzMk(new byte[]{97, 68, 97, 116}));
Element var7 = zzWKk(var5, zzZ3T.zzZAj().zzMk(new byte[]{105, 83, 110, 103, 116, 97, 114, 117, 101}));
boolean var8 = zzWxF((Node)var6, (Node)var7);
Element var9 = zzWKk(var6, zzZ3T.zzZAj().zzMk(new byte[]{114, 80, 100, 111, 99, 117, 115, 116}));
NodeList var10 = var9.getElementsByTagName(zzZ3T.zzZAj().zzMk(new byte[]{114, 80, 100, 111, 99, 117, 116}));
this.zzWJJ = new String[var10.getLength()];
for(int var11 = 0; var11 < this.zzWJJ.length; ++var11) {
this.zzWJJ[var11] = var10.item(var11).getFirstChild().getNodeValue();
}
this.zzWO4 = zzZqK(var6, zzZ3T.zzZAj().zzMk(new byte[]{101, 83, 105, 114, 108, 97, 117, 78, 98, 109, 114, 101}));
this.zzWec = zzWxF(var6, zzZ3T.zzZAj().zzMk(new byte[]{117, 83, 115, 98, 114, 99, 112, 105, 105, 116, 110, 111, 120, 69, 105, 112, 121, 114}));
this.zzYS9 = zzWxF(var6, zzZ3T.zzZAj().zzMk(new byte[]{105, 76, 101, 99, 115, 110, 69, 101, 112, 120, 114, 105, 121}));
return true;//自己修改字节码文件
}
修改上述两处,即可完成对Aspose.Words jar破解,需配合License文件。
示例代码:
//读取word文件,保存为PDF文件。
License license = new License();
license.setLicense("E:\\Aspose.License.xml");
Document doc = new Document("E:\\Aspose.Crack.docx");
SaveOutputParameters parameters = doc.save("E:\\cracked.pdf", SaveOptions.createSaveOptions(SaveFormat.PDF));
System.out.println(parameters.getContentType());
转化后文件,无水印无页码限制:
搞定收工。