mac Charles v4.0.2詳細破解教程

wooyoo發表於2016-12-18

背景

Charles是一款十分優秀的抓包軟體,尤其是在mac作業系統下。Charles是一款商用軟體,其體驗版雖然能夠使用全部功能,但是有以下幾個使用上不方便的地方:

  1. 啟動時有10s的提示購買視窗,並且每過4分鐘,當你再次點選Charles進行操作時,還會彈出一個持續5s的提示購買視窗。提示購買視窗截圖如下:

    mac Charles v4.0.2詳細破解教程

  2. 一旦執行超過30分鐘,就會彈出下面的error提示視窗,並且終止程式:

    mac Charles v4.0.2詳細破解教程

出於學習和研究目的,在成功破解了Charles之後,發現Charles破解可以作為一個十分典型的Jar包破解教學,因為其破解流程具有以下幾個特點:

  1. Jar經過了程式碼混淆。
  2. 破解思路多。
  3. 可以利用的破解工具多。

在詳細地講解Charles破解之前,我先介紹一下環境:

  • macOS 10.11.6
  • charles v4.0.2
  • jdk 1.8

好了,接下來,讓我們來詳細地講解一下如何對Charles進行破解。

破解流程

思路一

程式碼分析和定位

在背景中我們介紹過了,Charles體驗版未對功能做過閹割,所以第一種破解思路是取消掉Charles對體驗版做的限制。不管怎麼樣,所有Jar包破解的第一步,都是通過反編譯軟體來閱讀程式碼。這裡我們使用的是JD-GUI,這是一款十分優秀的java反編譯軟體,除了能夠閱讀反編譯之後的程式碼之外,還可以進行搜尋。

我們首先進入Charles,在mac下可以通過右鍵Charles的圖示,然後選擇顯示包內容。進入到Java目錄,然後用JD-GUI開啟charles.jar:

mac Charles v4.0.2詳細破解教程

經過觀察之後,我們發現charles.jar經過了程式碼混淆,這樣程式內部的很多邏輯只能靠經驗去猜測了。由於提示購買視窗那裡有個"Delay",所以我們先搜尋所有出現過"Delay"字串的地方。通過分析搜尋結果,我們大膽猜測SplashWindow是用來處理視窗的相關邏輯,並且setDelay方法是用來設定視窗的顯示時間:

mac Charles v4.0.2詳細破解教程

然後我們搜尋所有呼叫setDelay方法的程式碼,十分幸運的是,我們一下子就發現了設定啟動介面10s delay時間和設定每過4分鐘後的5s delay時間的程式碼位置。

刪除啟動介面的10s delay

第一處是位於com.xk72.charles.gui的V.class中:

mac Charles v4.0.2詳細破解教程

為了給我們接下來的操作打好基礎,我們首先需要對jar包進行解壓縮。但是由於Charles做過了基於類名的程式碼混淆,這會導致jar包無法在檔名不區分大小寫的作業系統上解壓出來(比如a.class會在解壓的過程中被A.class給覆蓋掉)。這是一種十分常見的混淆技術,由於我們最常用的windows和mac作業系統都是不區分檔名大小寫的作業系統,所以這種混淆辦法可以將大部分的bad boy給攔截在外。下圖為ProGuard混淆工具開啟類名混淆的截圖:

mac Charles v4.0.2詳細破解教程

為了解決這種情況,一般我都會使用Linux系統來解壓經過類名混淆過的Jar包。解壓之後,我們通過JBE位元組碼修改工具來對V.class的位元組碼進行修改。對於JBE的使用方法,大家可以參考我寫的另一篇文章:www.jianshu.com/p/a61f0f447…。修改V.class的方法很簡單,只需要將run方法對應的位元組碼刪除,只留下return即可。

刪除每過4分鐘的5s delay

第二處位於SplashWindow的a方法中:

mac Charles v4.0.2詳細破解教程

修改方法很簡單,只需要將對應的位元組碼除了return之外全部刪除,使其成為一個空方法即可。

刪除半個小時退出的程式碼

最後,我們需要刪去半個小時退出的程式碼。定位程式碼的思路很簡單,搜尋error對話方塊中出現過的字串即可,這裡我們通過JD-GUI搜尋"unlicensed copy",從搜尋結果中,我們可以找到com.xk72.charles包下的e.class中有對應的邏輯:

mac Charles v4.0.2詳細破解教程

修改方法同上,利用JBE將run方法對應的位元組碼除了return之外全部刪除,使其成為一個空方法即可。

最後

將上述所有hack過的class檔案打包進原來的jar包中(一定要在linux系統下),替換原來charles app下的charles.jar,啟動後我們發現已被成功破解。

思路二

程式碼分析和定位

其實思路一併不是一種常規思路,大部分情況下,我們都會對處理"License"的方法進行破解。如果大家完整地走過一遍思路一的方法,那麼肯定會發現在com.xk72.charles包下有一個叫做License.class的類,並且該類的a方法經常被外部呼叫,用來判斷是否已經註冊成功,比如每過4分鐘顯示提示購買視窗的邏輯:

mac Charles v4.0.2詳細破解教程

修改判斷是否已經註冊方法

我們可以大膽地認定該方法是用來判斷是否已經註冊的。順著該思路,我們先來嘗試將該方法修改為永遠返回true。通過JBE將a方法對應的位元組碼修改為:

iconst_1
ireturn複製程式碼

然後啟動Chalres發現一直卡在Loading Preferences介面。為了找出原因,再介紹一個小技巧,我們可以通過命令列的方式來啟動程式:java -jar ../Charles.app/Contents/Java/charles.jar,然後我們可以看到控制檯會輸出對應的錯誤堆疊。從錯誤堆疊中我們可以看到有空指標的異常:

mac Charles v4.0.2詳細破解教程

可以看到是License的b方法:

mac Charles v4.0.2詳細破解教程

修改顯示License名稱方法

我們大膽猜測該方法是用來顯示License名稱的,我們將其修改為返回一個固定的字串(自己想叫什麼就叫什麼)。讀者如果對java位元組碼不是很熟悉的話,有一種最簡單的做法是自己先寫一個demo java程式,然後用JBE檢視對應的位元組碼:

ldc "wooyoo"
areturn複製程式碼

繼續執行時又丟擲了VerifyError:

mac Charles v4.0.2詳細破解教程

這個錯誤是JVM位元組碼校驗失敗產生的。該問題很大概率是因為JBE的bug,為了解決這個問題,我們有以下3種解決方法:

  1. 關閉JVM的位元組碼校驗。
  2. 使用javassist來修改位元組碼。
  3. 自己編譯License
關閉JVM的位元組碼校驗

為了關閉JVM的位元組碼校驗,我們需要新增額外的JVM啟動引數。由於我的電腦的java環境是1.8,所以只能通過新增-noverify引數,如果是1.7的話,還可以使用-XX:-UseSplitVerifier。我們可以在Charles程式目錄下的info.plist檔案裡面新增關閉引數:

......
<key>JVMOptions</key>
<array>
<string>-noverify</string>
......複製程式碼
使用javassist來修改位元組碼

javassist常用來動態地修改位元組碼,廣泛地用於各個java框架裡面(這裡用它來破解軟體真是有點不好意思)。修改程式碼如下:

public class Test {
    public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath("/path/to/charles.jar");
        CtClass license = classPool.get("com.xk72.charles.License");
        // 修改a方法
        CtMethod aOldMethod = license.getDeclaredMethod("a", null);
        CtMethod aNewMethod = CtNewMethod.copy(aOldMethod, license, null);
        aOldMethod.setName("a_old");
        aNewMethod.setBody("{return true;}");
        aNewMethod.setName("a");
        license.addMethod(aNewMethod);
        // 修改b方法
        CtMethod bOldMethod = license.getDeclaredMethod("b", null);
        CtMethod bNewMethod = CtNewMethod.copy(bOldMethod, license, null);
        bOldMethod.setName("b_old");
        bNewMethod.setBody("{return \"wooyoo\";}");
        bNewMethod.setName("b");
        license.addMethod(bNewMethod);
        // 輸出新的License.class
        license.writeFile("/path/to/License.class");
    }
}複製程式碼

需要注意的是,在使用javassist的時候,我們無法移除原來方法裡面的邏輯。基於此,我們有以下兩種做法:

  • 先copy原來方法,然後呼叫setBody方法往新方法中塞入自己的邏輯。上述程式碼採用的就是這種方法。

  • 可以在原來的方法中呼叫insertBefore方法,比如:

    CtMethod bMethod = license.getDeclaredMethod("b", null);
    bMethod.insertBefore("{if(1<2) return \"wooyoo\"}")複製程式碼

    我們通過一個肯定會執行的if語句,來達到僅執行自己邏輯的效果。

自己編譯License

該方法也是參考了網路上另一個人的破解文章:blog.csdn.net/endlu/artic…,我這裡就不再贅述了,有興趣的讀者可以看這篇文章。

最後

將上述hack過的License.class檔案打包進原來的jar包中。這裡我們不一定要在Linux系統下完成打包,可以借用jar命令直接完成class檔案的替換:jar -uvf charles.jar com/xk72/charles/License.class。然後將新的jar包替換原來charles app下的charles.jar,啟動後我們發現已被成功破解。

總結

再次宣告以上教程都是基於學習和研究的目的,我們強烈建議大家有能力的話還是要購買正版軟體。好了我們來總結一下通過這次破解我們需要掌握的知識點:

  • 經過類名混淆過的Jar無法在不區分檔名大小寫的作業系統上進行壓縮或者解壓縮。我們可以借用Linux作業系統來完成壓縮或者解壓縮操作。
  • 破解的思路一般都是修改處理"License"的邏輯。但是我們不能侷限於此,比如在本例中,我們可以去除掉體驗版的限制,同樣達到破解的目的。
  • 某些情況下如果破解不成功,我們可以通過命令列啟動程式,然後通過觀察控制檯輸出的錯誤堆疊來看到底是哪裡出了問題。另外如果程式有日誌檔案的話,我們還可以去日誌檔案中分析錯誤原因。
  • 我們可以通過JBE、javassist甚至是自己重新編譯的方式來修改原來的位元組碼。

參考資料

相關文章