标题: Charles 4.5.1逆向工程 创建: 2019-10-17 09:37 更新: 2019-10-24 17:14 链接: https://scz.617.cn/misc/201910170937.txt https://www.52pojie.cn/thread-1039171-1-1.html Charles https://www.charlesproxy.com/ https://www.charlesproxy.com/assets/release/4.5.1/charles-proxy-4.5.1-win64.msi 前两天剁了Burp Pro 2.1.04,今天写写如何破解Charles 4.5.1。 这事是这么来的。当时还没剁Burp,上网找它的最新版及其破解,只是想用用。一 般这种时候会先去问几个搞WEB安全的兄弟,假设他们手里有久经考验的版本。有天 问一个兄弟,你现在用什么版本的Burp?他很高冷地说他现在不怎么用Burp,用 Charles多些。懒得理他,他看我不理他,直接把他购买的License发给我,好吧,我 被动拥有了一个Key。当时一心想用Burp,Charles暂放一边。剁完Burp收拣临时文档 时又看到Charles的Key,Burp已经上刑场了,大家都是Java帮的,有难要同当,给 Burp找几个陪斩的吧,故剁之。 Charles如果未注册,启动时有个10s倒计时,30天试用期,每运行30分钟就自动退出。 Charles缺省是用其自带JDK 11启动的: C:\Program Files\Charles\jdk\ 正常注册流程是: 启动Charles->Help->Register Charles->输入Registered Name/License Key 这里会弹一个对话框,有Register、Cancel两个按钮。如何找到Register按钮对应的 代码?假设Register按钮用到了JButton,在按钮上显示"Register"这个字符串时会 用到javax.swing.AbstractButton.setText(),查看它的调用栈回溯,或许就能定位 Register按钮对应的代码。 对javax.swing.AbstractButton.setText()设断,命中时: [1] javax.swing.AbstractButton.setText (AbstractButton.java:297), pc = 0 [2] javax.swing.AbstractButton.init (AbstractButton.java:2,128), pc = 6 [3] javax.swing.JButton. (JButton.java:131), pc = 18 [4] javax.swing.JButton. (JButton.java:104), pc = 3 [5] com.xk72.charles.gui.frames.RegisterFrame. (null), pc = 45 [6] com.xk72.charles.gui.menus.HelpMenu$4.actionPerformed (null), pc = 8 [7] javax.swing.AbstractButton.fireActionPerformed (AbstractButton.java:1,967), pc = 83 爽到了,关注"com.xk72.charles.gui.frames.RegisterFrame.class"。 比起Burp Pro 2.1.04,Charles 4.5.1的混淆程度太弱,说是一马平川不为过。据说 Charles早期版本没有混淆过,比如4.0.2,我以前没接触过。 反编译 C:\Program Files\Charles\lib\charles.jar com.xk72.charles.gui.frames.RegisterFrame.class -------------------------------------------------------------------------- public class RegisterFrame extends JDialog { private final JTextField tName; private final JTextField tSerial; /* * Register按钮 */ private final JButton bRegister; /* * Cancel按钮 */ private final JButton bCancel; 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 gTDF( this ) ); /* * 在此添加Register按钮的处理代码,据此关注 * * com.xk72.charles.gui.frames.PeRA.class */ this.bRegister.addActionListener( new PeRA( 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 ) ); } -------------------------------------------------------------------------- com.xk72.charles.gui.frames.PeRA.class -------------------------------------------------------------------------- import com.xk72.charles.YQUd; final class PeRA implements ActionListener { PeRA( RegisterFrame paramRegisterFrame ) { } public final void actionPerformed ( ActionEvent paramActionEvent ) { paramActionEvent = RegisterFrame.tEdg( this.tEdg ).getText().trim(); String str = RegisterFrame.TryJ( this.tEdg ).getText().trim(); if ( ( paramActionEvent.length() > 0 ) && ( str.length() > 0 ) ) { Object localObject; /* * 为了注册成功,YQUd.tEdg()必须返回null。据此关注 * * com.xk72.charles.YQUd.class */ if ( ( localObject = YQUd.tEdg( paramActionEvent, str ) ) != null ) { /* * 注册失败 */ ExtendedJOptionPane.tEdg( this.tEdg, localObject, "Charles Registration", 2 ); return; } /* * 注册成功,显示注册信息 */ ExtendedJOptionPane.tEdg( this.tEdg, "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 ); } } } -------------------------------------------------------------------------- YQUd.class共有5个public method: public com.xk72.charles.YQUd(); public static boolean tEdg(); public static void TryJ(); public static java.lang.String NCuT(); public static java.lang.String tEdg(java.lang.String, java.lang.String); com.xk72.charles.YQUd.class -------------------------------------------------------------------------- public final class YQUd { private static YQUd TryJ; /* * 为true时表示已注册 */ private boolean NCuT; public YQUd () { this.NCuT = false; this.Rarr = "Unregistered"; } /* * 为true时表示已注册 */ public static boolean tEdg () { YQUd var0 = TryJ; return TryJ.NCuT; } public static void TryJ () { TryJ = new YQUd(); } /* * 这是要显示到"About Charles"的内容 */ public static String NCuT () { YQUd var0 = TryJ; switch( gTDF.tEdg[var0.aqrV.ordinal()] ) { case 1: return var0.Rarr; case 2: return var0.Rarr + " - Site License"; case 3: return var0.Rarr + " - Multi-Site License"; default: return var0.Rarr; } } /* * 务必让本函数返回null */ public static String tEdg ( String var0, String var1 ) { YQUd var3; try { var3 = new YQUd( var0, var1 ); } catch ( LicenseException var2 ) { return var2.getMessage(); } TryJ = var3; return null; } -------------------------------------------------------------------------- 编辑YQUd.java: -------------------------------------------------------------------------- package com.xk72.charles; public final class YQUd { /* * 只Patch这5个public method,其他private的扔掉即可 */ public YQUd () { } /* * 为true时表示已注册 */ public static boolean tEdg () { return( true ); } public static void TryJ () { } /* * 这是要显示到"About Charles"的内容,这个内容任意 */ public static String NCuT () { return "Anyting - Site License"; } /* * 务必让本函数返回null */ public static String tEdg ( String var0, String var1 ) { return null; } } -------------------------------------------------------------------------- 编译YQUd.java得到新的YQUd.class,替换charles.jar中的YQUd.class。 比起Burp,破解Charles实在是太简单了。要点是定位YQUd.class,每个版本混淆后 的名字都不一样,需要重新定位。由于已经讲清楚原理,以后破解都不需要动态调试 设断,直接在反编译器中静态定位。 最后说一下,前面那个购买License的兄弟,他是因为用了这么多年Charles破解版, 良心发现,决定支持一下正版,他做的对,我做的不对,不是反话。有些人可能认识 他,同盾马院长。 2019-10-18 17:17 scz ZTZ问我剁过CobaltStrike没。这是你们黑客用的工具,像我这样正直的程序员,绝 对不会接触它。以为是个很难搞的目标,不然特意问它干啥。一搜破解漫天飞,只不 过原包不是官方公开下载而已。KINGX写过一篇怎么剁: 手把手教你破解CobaltStrike 3.12 - [2018-11-09] https://kingx.me/CobaltStrike-Patch.html 这种剁起来实在太没有难度,也没必要啥都自己剁,搜着用吧。 2019-10-22 16:34 scz 怎么还有人要class?前面写得很详细了,文中的YQUd.java就是最终形态,不是缩略 版本。可能有些人只搞WWW安全,不太看得懂前文。 2019-10-24 17:14 scz 不要再用本文所说破解方案,看续篇: 《Charles 4.5.1逆向工程(2)》 https://scz.617.cn/misc/201910241714.txt https://www.52pojie.cn/thread-1042815-1-1.html TEAM MESMERiZE提供了通杀3.x、4.x的keygen。