前言
現在在OSX下辦公,開發中Charles作爲非常優秀的一款抓包工具必不可少。之前的3.x版本是公司的資管給輸入的註冊碼,用着非常爽。今天突然提示可以升級爲新版本,好奇和欣喜的心情就下載並安裝了。萬萬沒有想到,替換舊應用居然不會保留註冊信息。Orz。本着不麻煩別人的初衷,嘗試着自己動手破解。於是就這樣踏上了一條不歸路。
開門見山
先放出快速破解的方法給想快速解決的朋友。
- 下載破解好的charles.jar 文件。鏈接: https://pan.baidu.com/s/1qY9x70S 密碼: b46n
- (4.0.1版本已更新,鏈接: https://pan.baidu.com/s/1sl8cRox 密碼: ngzz)
- 4.1版本更新,感謝博友 黎稀 提供。鏈接:https://pan.baidu.com/s/1c14trDe 密碼: wc6d
- 在應用程序中右鍵Charles,選擇“顯示包內容”。
- 依次打開目錄:Contents -> Java
- 用下載的charles.jar替換目錄中的charles.jar。
- 重啓Charles即可。
自己動手
方法來源
想到破解時,首先是先google了一下,看是否有註冊機或分享的註冊碼等,畢竟是比較出名的軟件。結果是註冊機的信息幾乎沒有,註冊碼和自己破解的方法倒是得到了很多信息。但是都是針對3.x版本,因爲4.0纔剛剛發佈。
嘗試了幾個註冊碼,均無效,果斷放棄,開始瞭解自己動手破解。看了幾篇論壇的帖子,都是像本文上面一樣,提供了一個Charles.jar去替換。版本的原因,依然有效的希望不大,還是抱着希望試了試,fail。
既然方法是替換這個jar,那麼關於授權部分的代碼肯定是在這裏被前任修改了,只不過現在有變化而已。我工作使用的主語言就是Java,思考一下,覺得自己反編譯看看邏輯應該問題不大,既然有人成功過,現在也還是很有希望的。於是開始動手。
工具
- jdk
-
class反編譯軟件
jdk編譯代碼,打jar包。反編譯軟件隨意,看網友推薦jadx,不知是我用的不對還是怎麼,並未成功,我直接用的IDEA,class文件直接拖進去即可。
步驟
-
首先將charles.jar解壓,觀察內部類結構。雖然名字都擾碼變成了A.class之類的,但是com/xk72/charles中一眼就能看到有個License.class,如果我的英文沒爛到這個詞都認錯了,肯定和它有關係,打開瞧瞧。
-
變量和方法名都變了,看不出太多的信息,但是其中一個方法巨多的位運算,更加對我的猜想自信了。但是這個類有這麼多不知用途的方法,具體的邏輯有看不出來,怎麼辦呢?反正今天閒,一個一個的看其他的class,看看都哪裏調用了這個類的方法,對返回值是怎麼判斷的。如果能找到,應該有突破。
-
看了上百個class後,得到信息如下:
-
com/xk72/charles/gui/frames/下有個RegisterFrame,判斷爲填寫註冊嗎的界面,打開後發現同目錄的f.class爲那個註冊按鈕的事件監聽器。打開f.class.
123456789101112131415public final void actionPerformed(ActionEvent var1) {String var4 = RegisterFrame.a(this.a).getText().trim();String var2 = RegisterFrame.b(this.a).getText().trim();if(var4.length() > 0 && var2.length() > 0) {String var3;if((var3 = License.a(var4, var2)) != null) {ExtendedJOptionPane.a(this.a, var3, "Charles Registration", 2);return;}ExtendedJOptionPane.a(this.a, "Thank you for registering.......}}可以看出註冊碼的驗證邏輯都落在了License.a(var4, var2)這個方法上。只要修改了這個方法,使它永遠返回null,就可以繞過真正的驗證邏輯。
-
在第一步找到的Lisense.class的135行找到該方法如下:
1234567891011public static String a(String var0, String var1) {License var3;try {var3 = new License(var0, var1);} catch (LicenseException var2) {return var2.getMessage();}b = var3;return null;}將整個License.class內容複製到一個新文件License.java中。將上述方法改爲:
123456789101112public static String a(String var0, String var1) {if(0 < 1) return null;License var3;try {var3 = new License(var0, var1);} catch (LicenseException var2) {return var2.getMessage();}b = var3;return null;}於是就實現了永遠返回null。但是原本的邏輯中,如果驗證成功了會對b這個變量賦值。然過後,再調用b的時候可能會有問題報錯。於是繼續補洞,看看b都在哪裏被用到了。
-
-
就按照如上的方式,逐步找到了其他調用授權信息的地方。根據判斷邏輯改寫License.java中的幾方法。修改的內容如下:
123private License$LicenseType f;改爲private int f;12345678public static boolean a() {License var0 = b;return b.c;}改爲public static boolean a() {return true;}123456789101112131415161718192021222324252627public static String b() {License var0 = b;switch(p.a[var0.f.ordinal()]) {case 1:return var0.d;case 2:return var0.d + " - Site License";case 3:return var0.d + " - Multi-Site License";default:return var0.d;}}改爲public static String b() {License var0 = b;switch(var0.f) {case 0:return var0.d;case 1:return var0.d + " - Site License";case 2:return var0.d + " - Multi-Site License";default:return var0.d;}}12345678910111213141516171819202122232425private 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";default:return this.d;}}改爲private String d() {switch(this.f) {case 0:return this.d;case 1:return this.d + " - Site License";case 2:return this.d + " - Multi-Site License";default:return this.d;}}第246行:
123456789101112131415161718192021222324252627switch((int)(var6 << 32 >>> 32 >>> 16 & 255L)) {case 1:this.f = License$LicenseType.a;</div><div class="line" style="height:20px"> <span class="keyword" style="color:rgb(178,148,187)">break</span>;</div><div class="line" style="height:20px"> <span class="keyword" style="color:rgb(178,148,187)">case</span> <span class="number" style="color:rgb(181,189,104)">2</span>:</div><div class="line" style="height:20px"> <span class="keyword" style="color:rgb(178,148,187)">this</span>.f = License$LicenseType.b;break;case 3:this.f = License$LicenseType.c;break;default:throw new LicenseException(this.a(1));}改爲switch((int)(var6 << 32 >>> 32 >>> 16 & 255L)) {case 1:this.f = 0;break;case 2:this.f = 1;break;case 3:this.f = 2;break;default:throw new SerialException(this.a(1));}將所有的LicenseException替換爲SerialException。
刪除三個import:
123import com.xk72.charles.License$LicenseType;import com.xk72.charles.LicenseException;import com.xk72.charles.p; -
做完以上步驟,就可以使用javac單獨編譯License.java類了。
1javac -source 1.7 -target 1.7 -d . License.java會在目錄下產生com/xk72/charles/目錄,目錄中就是編譯好的License.class了。
-
利用jar命令將篡改過的class,打入charles.jar內。
1jar -uvf charles.jar com/xk72/charles/License.class -
大功告成。用新的charles.jar替換原有的,重啓charles。等待驚喜吧!
-
防止修改過程造成困擾,附上我的License.java,作爲參考。License.java