点击数:55
反编译
下载Charles应用并安装,获取源文件:Mac OS下打开finder>应用程序,找到Charles,右键=>显示包内容=>Contents=>Java=>charles.jar。
将找到的charles.jar包拷贝一份到桌面或者其他目录,使用JD_GUI打开。如果没有JD_GUI,请自行度娘安装该软件。
在jd_gui界面中,可以看到charles.jar包的全部内容,和预想的一样,代码使用了混淆,看到的基本都是abcdef等等的类名。如果够幸运的话,能在com.xk72.charles包下面找到License类。请找这个类:com.xk72.charles.gui.frames.RegisterFrame.java
。一定能找到此类,因为这个类就是改程序的入口,为什么这么肯定呢?是有原因,我们都知道Java应用的入口函数是main方法。不管代码怎么混淆,你总得告诉JVM,你的入口是什么吧。
public static void main(String[] paramArrayOfString)
{
new RegisterFrame(null).setVisible(true);
}
在RegisterFrame的构造器中,我们寻找this.bRegister.addActionListener(new f(this));
这一句话,但是记住,addActionListener
方法中的参数名可能并不是new f(this)
,因为不同的版本中,混淆的类名、属性、方法名各不同。
public RegisterFrame(Frame paramFrame)
{
super(paramFrame, true);
setTitle("Register Charles");
this.tName = new JTextField(20);
this.tSerial = new JTextField(20);
this.bRegister = new JButton("Register");
this.bCancel = new JButton("Cancel");
Container localContainer;
(localContainer = getContentPane()).setLayout(new MigLayout("wrap,fill", "[label][fill,grow]"));
localContainer.add(new JLabel("Registered Name:"));
localContainer.add(this.tName);
localContainer.add(new JLabel("License Key:"));
localContainer.add(this.tSerial);
localContainer.add(this.bCancel, "tag cancel,split 2,span,center");
localContainer.add(this.bRegister, "tag ok");
this.bCancel.addActionListener(new e(this));
this.bRegister.addActionListener(new f(this));
pack();
if (paramFrame != null)
{
(paramFrame = new Point(paramFrame.getLocation())).translate(20, 20);
setLocation(paramFrame);
}
getRootPane().setDefaultButton(this.bRegister);
getRootPane().getInputMap(1).put(KeyStroke.getKeyStroke("ESCAPE"), "escape");
getRootPane().getActionMap().put("escape", new RegisterFrame.3(this));
}
继续跟进f
这个类,基本逻辑没有改变,判断条件应该是:(localObject = License.a(paramActionEvent, str)) != null
这一句。可能你已经注意到这句话了,Thank you for registering. Charles will now close. Please start Charles again to continue.”, “Charles Registration,我们其实可以搜索这一句话来快速定位代码位置。
final class f
implements ActionListener
{
f(RegisterFrame paramRegisterFrame) {}
public final void actionPerformed(ActionEvent paramActionEvent)
{
paramActionEvent = RegisterFrame.a(this.a).getText().trim();
String str = RegisterFrame.b(this.a).getText().trim();
if ((paramActionEvent.length() > 0) && (str.length() > 0)) {
Object localObject;
if ((localObject = License.a(paramActionEvent, str)) != null)
{
ExtendedJOptionPane.a(this.a, localObject, "Charles Registration", 2);
return;
}
ExtendedJOptionPane.a(this.a, "Thank you for registering. Charles will now close. Please start Charles again to continue.", "Charles Registration", 1);
(localObject = CharlesContext.getInstance()).getConfiguration().getRegistrationConfiguration().setName(paramActionEvent);
((CharlesContext)localObject).getConfiguration().getRegistrationConfiguration().setKey(str);
((CharlesContext)localObject).exit(0, true);
}
}
}
继续跟踪,终于到了License.a方法,这个就是我们用到的最终的方法。但是,请注意,如果你不够幸运,这个类的名字可能就不是License,而是其他字符串,甚至是一个简单的abc。
package com.xk72.charles;
import java.io.UnsupportedEncodingException;
public final class License
{
private static String a = "Thanks for looking at the source. Please register Charles if you use it.";
private static License b;
private boolean c;
private String d;
private int e;
private License.LicenseType f;
private static final int g = 1;
private static final int h = 2;
private static final int i = 3;
private static final long j = 8800536498351690864L;
private static final long k = -5408575981733630035L;
private static final long l = -6517524745266237632L;
private static final long m = 5911726755176091652L;
private static final String[] n = { "b241993e8a12c782348e4652cc22c2501d9d6f248e91a3d849275666a0ff7d954fdf638f0d03098c52c4710a5e619b9b09cd6cd027ea3bdb937172b3fdf0bded3d684333798880bb78780f6f6644580409ac882bc021732a", "b241993e8a12c782348e4652cc22c250c30afb881b44ba4dd936c44a573755b5276046dc3ae32e58d10f467421f51ca607b0e29f53cd8f38dd9eee548398b195348e4652cc22c2502feb5f8fd884cb3c9a330ee10954d071", "8a24264c4ae5e5371d663158ccbd75e5a5d70bef5d61291ba3af58b92fe98f7a5c8f83abe09b0c1b3f469d5ad85a3a01e81a2248a290b22d05f52db22eb8b10af10437ddcf2f437b1a519b09a9c4f2c374a882757515e2e2fde238a4eccc62d3fc36d9a77dcbd7cc05236b02716005836b21e58a07330bb18136139263e71a0f79382179978b680a", "96122782ec21d0584881fa8dc6b2ff60585bcafbaeec4bd03874fc7ce730dcb3515b9fb963790219047bf20363167e9967cc1b0851ae39b63d831e55c196a04d7ae5be1b671bedc43b7e8ca175e6d3af2610c3f6b5863d0000ccc9ff3b971946974d3cb7be340cb08475020696df69ac20764f7709cb63e3aac239578db58e85" };
private static final int o = 32;
private static int p;
private static int q;
private static int r;
private static int s;
private static int t;
private static int u;
private static int v;
private static int w;
private static int x;
private static int y;
private static int z;
private static int A;
private static int B;
private static int C;
private static int D;
private static int E;
private static int F;
private static int G;
private static int H;
private static int I;
private static int J;
private static int K;
private static int L;
private static int M;
private static int N;
private static int O = (N = (M = (L = (K = (J = (I = (H = (G = (F = (E = (D = (C = (B = (A = (z = (y = (x = (w = (v = (u = (t = (s = (r = (q = (p = -1209970333) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527) + -1640531527;
private int P;
private int Q;
private int R;
private int S;
private int T;
private int U;
private int V;
private int W;
private int X;
private int Y;
private int Z;
private int aa;
private int ab;
private int ac;
private int ad;
private int ae;
private int af;
private int ag;
private int ah;
private int ai;
private int aj;
private int ak;
private int al;
private int am;
private int an;
private int ao;
private static final int ap = -1209970333;
private static final int aq = -1640531527;
public License()
{
this.c = false;
this.d = "Unregistered";
}
private License(String paramString1, String paramString2)
{
this.f = License.LicenseType.c;
this.c = true;
this.d = paramString1;
}
private static void a(License paramLicense)
{
b = paramLicense;
}
public static boolean a()
{
License localLicense;
return (localLicense = b).c;
}
public static String b()
{
License localLicense = b;
switch (p.a[localLicense.f.ordinal()])
{
case 1:
return localLicense.d;
case 2:
return localLicense.d + " - Site License";
case 3:
return localLicense.d + " - Multi-Site License";
}
return localLicense.d;
}
public static String a(String paramString1, String paramString2)
{
try
{
paramString1 = new License(paramString1, paramString2);
}
catch (LicenseException localLicenseException)
{
return (paramString1 = localLicenseException).getMessage();
}
paramString1 = paramString1;
b = paramString1;
return null;
}
private boolean c()
{
return this.c;
}
private String d()
{
switch (p.a[this.f.ordinal()])
{
case 1:
return this.d;
case 2:
return this.d + " - Site License";
case 3:
return this.d + " - Multi-Site License";
}
return this.d;
}
private int e()
{
return this.e;
}
private License.LicenseType f()
{
return this.f;
}
private String a(int paramInt)
{
d(8800536498351690864L);
try
{
byte[] arrayOfByte1 = new byte[(paramInt = n[paramInt]).length() / 2];
for (int i1 = 0; i1 < arrayOfByte1.length; i1++) {
arrayOfByte1[i1] = ((byte)Integer.parseInt(paramInt.substring(i1 << 1, (i1 << 1) + 2), 16));
}
byte[] arrayOfByte2;
for (paramInt = (arrayOfByte2 = c(arrayOfByte1)).length; arrayOfByte2[(paramInt - 1)] == 0; paramInt--) {}
return new String(arrayOfByte2, 0, paramInt, "UTF-8");
}
catch (UnsupportedEncodingException localUnsupportedEncodingException) {}
return "";
}
private boolean b(String paramString1, String paramString2)
{
String str = (str = str = paramString1).replaceAll("[ ]", " ");
long l1 = c(str, paramString2);
boolean bool;
if ((bool = a(l1))) {
return true;
}
if (str.equals(paramString1)) {
return false;
}
long l2 = c(paramString1, paramString2);
return a(l2);
}
private static String a(String paramString)
{
paramString = paramString;
return paramString.replaceAll("[ ]", " ");
}
private boolean a(long paramLong)
{
int i1 = b(paramLong);
d(paramLong);
long l1 = paramLong;
for (paramLong = 0; paramLong < i1 + 35; paramLong++) {
l1 = c(l1);
}
return l1 == 5911726755176091652L;
}
private long c(String paramString1, String paramString2)
{
if (paramString2.length() != 18) {
throw new LicenseException(a(0));
}
if ((paramString2.equalsIgnoreCase("7055ce2f8cb4f9405f")) || (paramString2.equalsIgnoreCase("5bae9d8cdea32760ae")) || (paramString2.equalsIgnoreCase("f3264994d9ea6bc595")) || (paramString2.equalsIgnoreCase("b9930cef009d3a7865")) || (paramString2.equalsIgnoreCase("62bd6a5f95aa67998e")) || (paramString2.equalsIgnoreCase("a1c536c35904e64584")) || (paramString2.equalsIgnoreCase("d6e5590ecc05edd9b3")) || (paramString2.equalsIgnoreCase("8fbe36ce2726458b18")) || (paramString2.equalsIgnoreCase("042a8352caf1188945")) || (paramString2.equalsIgnoreCase("9d26d5088770221c3c")) || (paramString2.equalsIgnoreCase("e19b2a01905e4129bf")) || (paramString2.equalsIgnoreCase("68ebe4c9d792f31057")) || (paramString2.equalsIgnoreCase("4e4beb8a43e9feb9c7")) || (paramString2.equalsIgnoreCase("d04d85b44b306fc9ec")) || (paramString2.equalsIgnoreCase("2b5d21a38c9452e342")) || (paramString2.equalsIgnoreCase("88cb89c26a813bce44")) || (paramString2.equalsIgnoreCase("76c9ee78c8ab124054")) || (paramString2.equalsIgnoreCase("729db7c98163ac7d3d")) || (paramString2.equalsIgnoreCase("7c1d4761993c412472")) || (paramString2.equalsIgnoreCase("08bc0b7ec91cd0f4aa")) || (paramString2.equalsIgnoreCase("25bafae175decaedcc")) || (paramString2.equalsIgnoreCase("3181aae6822ef90ccd")) || (paramString2.equalsIgnoreCase("d7a8fe9dc9dc919f87")) || (paramString2.equalsIgnoreCase("728dae81d9d22aca03")) || (paramString2.equalsIgnoreCase("119a9b593348fa3e74")) || (paramString2.equalsIgnoreCase("04ab87c8d69667878e")) || (paramString2.equalsIgnoreCase("4b282d851ebd87a7bb")) || (paramString2.equalsIgnoreCase("ed526255313b756e42")) || (paramString2.equalsIgnoreCase("ed5ab211362ab25ca7")) || (paramString2.equalsIgnoreCase("18f4789a3df48f3b15")) || (paramString2.equalsIgnoreCase("67549e44b1c8d8d857")) || (paramString2.equalsIgnoreCase("4593c6c54227c4f17d")) || (paramString2.equalsIgnoreCase("1c59db29042e7df8ef")) || (paramString2.equalsIgnoreCase("a647e3dd42ce9b409b")) || (paramString2.equalsIgnoreCase("7e06d6a70b82858113")) || (paramString2.equalsIgnoreCase("ef4b5a48595197a373")) || (paramString2.equalsIgnoreCase("0ac55f6bebd0330640")) || (paramString2.equalsIgnoreCase("1beda9831c78994f43")) || (paramString2.equalsIgnoreCase("8a2b9debb15766bff9")) || (paramString2.equalsIgnoreCase("da0e7561b10d974216")) || (paramString2.equalsIgnoreCase("86257b04b8c303fd9a")) || (paramString2.equalsIgnoreCase("a4036b2761c9583fda")) || (paramString2.equalsIgnoreCase("18e69f6d5bc820d4d3")) || (paramString2.equalsIgnoreCase("a13746cb3d1c83bca6"))) {
throw new LicenseException(a(1));
}
Object localObject;
long l1 = Long.parseLong((localObject = paramString2).substring(2, 10), 16) << 32 | Long.parseLong(((String)localObject).substring(10, 18), 16);
paramString2 = Integer.parseInt((localObject = paramString2).substring(0, 2), 16);
d(-5408575981733630035L);
long l2;
if (b(l2 = c(l1)) != paramString2) {
throw new LicenseException(a(1));
}
this.e = ((int)(l2 << 32 >>> 32 >>> 24));
if (this.e == 1)
{
this.f = License.LicenseType.a;
}
else if (this.e == 4)
{
switch ((int)(l2 << 32 >>> 32 >>> 16 & 0xFF))
{
case 1:
this.f = License.LicenseType.a;
break;
case 2:
this.f = License.LicenseType.b;
break;
case 3:
this.f = License.LicenseType.c;
break;
default:
throw new LicenseException(a(1));
}
}
else
{
if (this.e < 4) {
throw new LicenseException(a(3));
}
throw new LicenseException(a(1));
}
d(8800536498351690864L);
try
{
paramString1 = paramString1.getBytes("UTF-8");
localObject = this;
if ((i1 = (paramString2 = paramString1.length) + 4) % 8 != 0) {
i1 += 8 - i1 % 8;
}
byte[] arrayOfByte = new byte[i1];
System.arraycopy(paramString1, 0, arrayOfByte, 4, paramString2);
arrayOfByte[0] = (paramString2 >> 24);
arrayOfByte[1] = ((byte)(paramString2 >> 16));
arrayOfByte[2] = ((byte)(paramString2 >> 8));
arrayOfByte[3] = ((byte)paramString2);
localObject = localObject = ((License)localObject).c(arrayOfByte);
paramString1 = 0;
int i1 = (paramString2 = localObject).length;
for (int i2 = 0; i2 < i1; i2++)
{
String str = paramString2[i2];
paramString1 = (paramString1 ^= str) << 3 | paramString1 >>> 29;
}
paramString1 = (paramString1 = paramString1) ^ (int)(l2 >> 32);
return 0xA58D19C600000000 | paramString1 << 32 >>> 32;
}
catch (UnsupportedEncodingException localUnsupportedEncodingException) {}
return -1L;
}
private static final long b(String paramString)
{
return Long.parseLong(paramString.substring(2, 10), 16) << 32 | Long.parseLong(paramString.substring(10, 18), 16);
}
private static final int c(String paramString)
{
return Integer.parseInt(paramString.substring(0, 2), 16);
}
private static final int a(byte[] paramArrayOfByte)
{
int i1 = 0;
for (int i4 : paramArrayOfByte) {
i1 = (i1 ^= i4) << 3 | i1 >>> 29;
}
return i1;
}
private static final int b(long paramLong)
{
long l1 = 0L;
for (int i1 = 56; i1 >= 0; i1 -= 8) {
l1 ^= paramLong >>> i1 & 0xFF;
}
return Math.abs((int)(l1 & 0xFF));
}
private byte[] b(byte[] paramArrayOfByte)
{
int i1;
int i2;
if ((i2 = (i1 = paramArrayOfByte.length) + 4) % 8 != 0) {
i2 += 8 - i2 % 8;
}
byte[] arrayOfByte = new byte[i2];
System.arraycopy(paramArrayOfByte, 0, arrayOfByte, 4, i1);
arrayOfByte[0] = (i1 >> 24);
arrayOfByte[1] = ((byte)(i1 >> 16));
arrayOfByte[2] = ((byte)(i1 >> 8));
arrayOfByte[3] = ((byte)i1);
return paramArrayOfByte = c(arrayOfByte);
}
private byte[] c(byte[] paramArrayOfByte)
{
byte[] arrayOfByte = new byte[paramArrayOfByte.length];
int i1 = paramArrayOfByte.length;
int i2 = 0;
long l1 = 0L;
for (int i3 = 0; i3 < i1; i3++)
{
l1 = l1 <<= 8 | paramArrayOfByte[i3] & 0xFF;
i2++;
if (i2 == 8)
{
l1 = c(l1);
arrayOfByte[(i3 - 7)] = ((byte)(int)(l1 >>> 56));
arrayOfByte[(i3 - 6)] = ((byte)(int)(l1 >>> 48));
arrayOfByte[(i3 - 5)] = ((byte)(int)(l1 >>> 40));
arrayOfByte[(i3 - 4)] = ((byte)(int)(l1 >>> 32));
arrayOfByte[(i3 - 3)] = ((byte)(int)(l1 >>> 24));
arrayOfByte[(i3 - 2)] = ((byte)(int)(l1 >>> 16));
arrayOfByte[(i3 - 1)] = ((byte)(int)(l1 >>> 8));
arrayOfByte[i3] = ((byte)(int)l1);
i2 = 0;
l1 = 0L;
}
}
return arrayOfByte;
}
private long c(long paramLong)
{
// ...此处省略代码
return ((paramLong = (i24 << i48 | i24 >>> 32 - i48) + this.ao) << 32) + (l13 & 0xFFFFFFFF);
}
private void d(long paramLong)
{
// ...此处省略一大段算法生成代码
}
static
{
License localLicense;
b = localLicense = new License();
}
}
我们修改这个三个方法:public static boolean a()
、public static String b()
、public static String a(String paramString1, String paramString2)
,根据猜测前面两个中间,第一个是检测是否注册,第二个是返回注册的用户名。第三个看代码,虽然返回null,其中用于赋值校验。
我们尝试修改三个静态方法,分别返回true
、你用的名字或其他字符串
、null
。
public static boolean a()
{
return true;
}
public static String b()
{
License localLicense = b;
return "xxxooo";
}
public static String a(String paramString1, String paramString2)
{
return null;
}
逆编译
我们将修改后的代码再次逆向思维来编译成class文件。
javac charles/com/xk72/charles/License.java -d .
出现如下错误,错误多大几十处,仔细看了一遍代码,发现都是一些private的方法,说明只有当前类调用,因此全部删除无用的私有代码,这个问题算是解决:
位置: 类 License
charles.jar.src/com/xk72/charles/License.java:262: 错误: 找不到符号
byte[] arrayOfByte = new byte[i1];
^
符号: 变量 i1
继续编译,出现如下错误:
charles.jar.src/com/xk72/charles/License.java:12: 错误: 找不到符号
private License.LicenseType f;
^
符号: 类 LicenseType
位置: 类 License
charles.jar.src/com/xk72/charles/License.java:155: 错误: 找不到符号
private License.LicenseType f()
^
符号: 类 LicenseType
位置: 类 License
charles.jar.src/com/xk72/charles/License.java:85: 错误: 找不到符号
this.f = License.LicenseType.c;
^
符号: 变量 LicenseType
错误信息很明显,找不到License.LicenseType,从类名字上可以看出,应该是内部类或者内部枚举,License.LicenseType.c
从这一句可以看出应该是枚举,尝试再说。从剩余的代码中可以看出,该枚举中有三个枚举类型应该可能用到:
public enum LicenseType {
a,b,c;
private LicenseType() {}
}
继续反编译,依然除了问题,但是这次的问题是这样的:
charles.jar.src/com/xk72/charles/License.java:122: 错误: 找不到符号
catch (LicenseException localLicenseException)
^
符号: 类 LicenseException
位置: 类 License
charles.jar.src/com/xk72/charles/License.java:124: 错误: 找不到符号
return (paramString1 = localLicenseException).getMessage();
^
符号: 方法 getMessage()
很明显,找不到已有的class,原来包中的类文件依赖没找到。因此,我们编译的命令必须要指定classpath。我们将原来的charles.jar新拷贝一份,重命名为charles-1.zip,解压该zip文件,得到charles-1这样的文件,这个作为我们依赖的classpath。
javac -cp /Users/xxx/Desktop/charles-1 /Users/xxx/Desktop/charles/com/xk72/charles/License.java -d ./
继续编译,发现编译成功了。在当前目录下,可以看到生成的包com,里面有类:License.class
和License$LicenseType.class
。
下面是修改后编译成功的源码:
package com.xk72.charles;
import java.io.UnsupportedEncodingException;
public final class License
{
private static String a = "Thanks for looking at the source. Please register Charles if you use it.";
private static License b;
private boolean c;
private String d;
private int e;
private License.LicenseType f;
public License()
{
this.c = false;
this.d = "Unregistered";
}
private License(String paramString1, String paramString2)
{
this.f = License.LicenseType.c;
this.c = true;
this.d = paramString1;
}
public static boolean a()
{
return true;
}
public static String b()
{
License localLicense = b;
return "zhoujunwen";
}
public static String a(String paramString1, String paramString2)
{
return null;
}
static
{
License localLicense;
b = localLicense = new License();
}
public enum LicenseType {
a,b,c;
private LicenseType() {}
}
}
打包
得到了编译好的class文件,我们再把该文件打包到charles.jar包中。
jar uf /Users/xxx/Desktop/charles.jar com/xk72/charles/License\$LicenseType.class
jar uf /Users/xxx/Desktop/charles.jar com/xk72/charles/License.class
再用这个新的charles.jar包替换原来的jar包即可破解成功。
更新
- version:4,2,1,时间:2017年12月22日11:38:37
最近反编译4.2.1的时候,发现License类的名字已经为oFTR,并且多了一个构造函数,且私有属性的定义也发生了一些细微的变化。
此次破解,直接干掉了内部的枚举类:
package com.xk72.charles;
public final class oFTR
{
private static String Yuaz = "我不知道提示的是什么,反正我知道破解成功了";
private boolean lktV = false;
private String ecCn = "Unregistered";
private static oFTR knIQ;
public oFTR() {}
private oFTR(String paramString1, String paramString2)
{
this(paramString1, paramString2, 4);
}
private oFTR(String paramString1, String paramString2, int paramInt)
{
this.ecCn = paramString1;
this.lktV = true;
}
public static boolean Yuaz()
{
return true;
}
public static void knIQ()
{
oFTR localoFTR;
knIQ = localoFTR = new oFTR();
}
public static String lktV()
{
return "xxxooo";
}
public static String Yuaz(String paramString1, String paramString2)
{
return null;
}
}
直接编译并打包:
javac charles/com/xk72/charles/oFTR.java -d .
jar uf ~/Desktop/charles.jar com/xk72/charles/oFTR.class
替换之后charles.jar之后,打开应用,运行OK!
- version:4.2.8,时间:2019年07月02日15:39:41
package com.xk72.charles;
import java.io.UnsupportedEncodingException;
public final class qHTb
{
private static String DdNM = "我不知道提示的是什么,反正我知道破解成功了";
private static qHTb twLa;
private boolean gbef = false;
private String lPpR = "registered";
public qHTb() {}
private qHTb(String paramString1, String paramString2)
{
this(paramString1, paramString2, 4);
}
private qHTb(String paramString1, String paramString2, int paramInt)
{
this.lPpR = paramString1;
this.gbef = true;
}
private static void DdNM(qHTb paramqHTb)
{
twLa = paramqHTb;
}
public static boolean DdNM()
{
return true;
}
public static void twLa()
{
qHTb localqHTb;
twLa = localqHTb = new qHTb();
}
public static String gbef()
{
return "zhoujunwen";
}
public static String DdNM(String paramString1, String paramString2)
{
return null;
}
static {}
}
直接编译并打包:
javac charles/com/xk72/charles/oFTR.java -d .
jar uf ~/Desktop/charles.jar com/xk72/charles/oFTR.class
替换之后charles.jar之后,打开应用,运行OK!