Charles 是一款Windows、Mac和Linux下的HTTP代理/ HTTP监视器/反向代理的强大工具,免费试用版有30天的期限,购买正版需要至少30美刀。
由于Charles使用java开发,对此有多种破解方式,原理上大同小异:
查找授权验证代码,将其修改为通过验证,更新jar包。
本文通过ASM字节码操作库来修改jar包中的class文件,需要准备以下工具:
- JD-GUI :java反编译工具
- asm-all:asm jar包最好下载4.0以后的all版本,简单方便, 我这里使用asm-all-6.0_BETA.jar 。关于使用方法官网有一份非常好的文档User guide这里不做赘述
- java IDE: Eclipse,IntelliJ IDE,Android Studio等等都可以,纯文本大神绕道
分析反编译代码
官网下载Charles解压到本地,将lib/charles.jar
拷出来(备份的需要,防止破解意外), jd-gui打开
$ jd-gui charles.jar
查找字符串常量This is a 30 day trial version
(试用期限提示语, 前几个单词即可),
在SplashWindow.class
这个类中
public void showSharewareStatus() {
showStatus("This is a 30 day trial version. If you continue using Charles you must\npurchase a license. Please see the Help menu for details.");
}
public void showRegistrationStatus() {
if (kKPk.lcJx()) {
showStatus("Registered to: " + kKPk.JZlU());
return;
}
showSharewareStatus();
}
lcJx()
,JZlU()
都是kKPk
类的静态方法:
public final class kKPk{
public static boolean lcJx() {
kKPk localkKPk;
return (localkKPk = KcPF).JZlU;
}
public static String JZlU() {
//省略代码
return localkKPk.yNVB;
}
}
从反编译代码中可以看出lcJx()
用于判断是否注册(true
注册), JZlU()
返回注册给的用户。因此首先修改这两个方法,我们的目标是形如:
public static boolean lcJx(){
return true;
}
public static String JZlU(){
return "tlb@jmu";
}
制定破解方案
从上面的分析,我们指定以下破解方案的执行步骤
- 将kKPk.class从charles.jar中抽取出来
- 通过asm修改
lcJx()
,JZlU()
方法 - 将修改后的kKPk.class重新打包进charles.jar
开撸
# 提取kKPk.class
$ jar xvf charles.jar com/xk72/charles/kKPk.class
# 更新jar包中的指定类(替换或添加)
$ jar uvf charles.jar com/xk72/charles/kKPk.class
字节码修改代码
package com.example.asm.charles;
import com.example.Util;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;
public class Charles {
public static void main(String[] args) throws Exception {
ClassReader cr = new ClassReader("com.xk72.charles.kKPk");
ClassWriter cw = new ClassWriter(0);
CharlesAdapter adapter = new CharlesAdapter(ASM5, cw);
cr.accept(adapter, ASM5);
// 将byte[]写入到新的文件中
Util.saveClass(cw.toByteArray(), "com/xk72/charles/kKPk2.class");
}
static class CharlesAdapter extends ClassVisitor{
public CharlesAdapter(int i, ClassVisitor classVisitor) {
super(i, classVisitor);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
// boolean lcJx()方法
if (name.equals("lcJx" ) && desc.equals("()Z")) {
mv.visitCode();
mv.visitInsn(ICONST_1);
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 0);
mv.visitEnd();
return null;
}
// String JZlU()方法
if (name.equals("JZlU") && desc.equals("()Ljava/lang/String;")) {
mv.visitCode();
mv.visitLdcInsn("tlb@jmu");
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 0);
mv.visitEnd();
return null;
}
return mv;
}
}
}
这里需要注意的是new ClassReader("com.xk72.charles.kKPk")
其中com.xk72.charles.kKPk
是从charles.jar中提取的原始class文件, 应该将路径添加到classpath中,或者连通目录结构拷贝到工程的class文件输出目录中, 以确保可以加载到, 修改完成保存到kKPk2.class
(这里只是文件名不一样, 其中的类还是kKPk). 最后将kKPk2重命名为kKPk打包进charles.jar即可.
破解方式有很多种, 笔者前段时间研究了下android组件化的实现,接触到强大ASM库, 于是产生通过这种方式来破解基于java开发的跨平台软件的想法, 初窥门径耳.
附Linux破解版地址:
- 4.2.7(2018.12.21) 完整代码
链接: https://pan.baidu.com/s/1VjWh_Jy8SXspX08ZH_2wCg 提取码: 3rnu
sha256: 4c8d3152ff25d8e752f73abe6dc6dd0c0603e99adca501ba155fe16cc7a8739d - 4.5.4 (2019.12.02)完整代码
修改com.xk72.charles.DIWy
类的下面两个方法,然后打包替换lib/charles.jar
原始的package com.xk72.charles; public class DIWy { public static boolean OZtq() { return true; } public static String wkKg() { return "tlb@jmu"; } }
charles.jar
重命名为charles0.jar
一并打包进去了.
链接: https://pan.baidu.com/s/1gB6j_JGOIE2daFwqGY6U9w 提取码: s78x
sha256: e2cae913dba9d0cead61b916bdb79d54c9eb459c9249b1a7dd63e144605e85b1