安卓逆向之破解某成人APP播放次數限制

悖論發表於2023-01-29

前言

某成人水果APP非VIP使用者存在播放次數限制,每天只能播放3次。超過3次需要購買VIP。 由於過於貧窮,於是抽空,對其安卓APP進行了逆向分析,最終成功破解了其播放次數限制。

破解思路

思路一

透過使用發現,當我點選列表,跳轉至影片詳情頁時,播放次數就會減一。如果APP開發人員,更改播放次數和獲取影片詳情時使用了兩個單獨的介面,我們就可以透過遮蔽更改播放次數的介面,讓可播放次數永遠不減少,從而實現無限播放

思路二

APP存在匿名註冊,安裝並開啟APP後,無需手機號和郵箱,會自動生成一個匿名賬號。這種匿名註冊一般都是會根據手機硬體相關的資訊生成uid。這樣的話,可以在每次播放次數用完後,hook手機硬體相關的API, 返回新的硬體資訊,註冊新的匿名賬號,從而實現無限播放

抓包

有了大致思路,老規矩,先抓包看看APP的介面資訊。開啟BurpSuite, 配置代理。發現請求和響應都存在加密。

1.png

2.png

所以需要先解密。

查詢解密函式

因為App最終還是需要使用明文資料的,所以APP中肯定有解密函式。

開啟Jadx-gui, 將apk開啟,發現沒有加殼。搜尋請求引數關鍵字_ver, timestamp,sign等,定位到加密邏輯

3.png

4.png

5.png

6.png

EncrytUitl.encrypt這個方法進行了加密,EncryUtil.decrypt 這個方法進行解密。

解密

由於加解密都是透過so庫完成,逆向出完整解密邏輯比較困難並且也沒有必要。所以直接透過frida + brida + burpsuite,直接呼叫解密函式進行解密

  1. 編寫frida hook程式碼

    function calldecrypt(str) {    
     if (!str) {
         return;
     }
     return new Promise((resolve) => {
         Java.perform(function x() {
             const encryptUtil = Java.use('com.qq.lib.EncryptUtil')
             const ret = encryptUtil.decrypt(str, 'PT0dPVxYXglbDARZXwlfXwwMDApYDgReWV4JXl4ICAtfWAxcJD4WCD8SFRYCFQ==')
             resolve(ret)
         });
     });
    }
    
    function callencrypt(str) {
     if (!str) {
         return;    
     }
     return new Promise(resolve => {
         const encryptUtil = Java.use('com.qq.lib.EncryptUtil')
         const ret = encryptUtil.encrypt(str, 'PT0dPVxYXglbDARZXwlfXwwMDApYDgReWV4JXl4ICAtfWAxcJD4WCD8SFRYCFQ==')
         resolve(ret)
     });
    }
    
    rpc.exports = {
     calldecrypt,
     callencrypt,
    }
  2. 設定brida

    7.png

    8.png

  3. 點選decrypt標籤即可解密

    9.png

watching介面

解密後,發現/api/mv/watching 介面非常可疑,很像是用來標記觀看次數。報文如下

// request
{
    "bundle_id":"gov.bkzuj.pikpnf",
    "id_log":"83952",
    "log":"{\"83952\":1}",
    "new_player":"fx",
    "oauth_id":"98fd1cc24524a16ee0f8ef0e76040411",
    "oauth_type":"android",
    "timestamp":"1674835200",
    "token":"867cee9b486e89748097588652a94b0a",
    "version":"3.3.0"
}

// response
{
    "crypt":true,
    "data":{
        "canWatchCount":3, // 可以觀看的次數
        "left":1, // 剩餘次數
        "todayTimestamp":1674835200,
        "watched":2 // 已觀看次數
    },
    "isLogin":false,
    "isVV":false,
    "msg":"",
    "needLogin":false,
    "status":1
}

使用burpsuite intercept並且drop相關請求,可觀看次數的確沒有減少。

然而經過多次嘗試,發現次數雖然沒有減少,但是當連續觀看3次之後,需要重啟APP或者去個人中心頁面,重新整理個人資訊,才能繼續觀看。

猜測可能是因為APP會在記憶體中維持一個狀態,進行計數。重啟或者去個人中心頁面重新整理,會重置這個狀態。

繼續抓包,發現重啟或者重新整理個人中心頁面,都會呼叫/api/users/getBaseInfo ,其響應報文中有一個can_watch欄位

{
    "crypt":true,
    "data":{
        // ...
        "can_watch": 1,
        // ...
    },
    "isLogin":false,
    "isVV":false,
    "msg":"",
    "needLogin":false,
    "status":1
}

搜尋can_watch相關程式碼

10.png

11.png

12.png

程式碼邏輯為,介面請求成功後,如果can_watch >= 0, 則將其存放到sp中,key為key_left_watch_count

搜尋key_left_watch_count 相關程式碼

13.png

14.png

15.png

16.png

相關程式碼邏輯大致為,進入詳情頁播放時,判斷是否為vip,如果是,則跳過can_watch邏輯,直接可以播放。如果非vip,則從sp中讀取can_watch的值,如果大於1,則可以播放,並且將can_watch - 1 後重新寫入sp中

綜合以上分析,破解的步驟如下

  1. 遮蔽掉/api/mv/watching 介面呼叫
  2. user.getIs_vip 永遠返回true

破解

  1. 使用frida進行hook, 驗證想法是否正確。一切正常,成功執行
  2. 開啟AndroidKiller, 反編譯出smail程式碼
  3. 修改smail程式碼,修改如下。這裡為了UI介面正常顯示,順便修改了getVip_level , 讓它永遠返回4

    17.png
    18.png

    直接goto掉介面呼叫相關的邏輯
    19.png

  4. 回編譯,重簽名。最終效果如下

    20.png

結語

破解APP可以先揣測開發的開發思路,然後透過抓包,反編譯,靜態分析,hook,逐步理解APP執行邏輯。最終制定破解方案,修改程式碼,完成破解。這次又白嫖了一個vip,真香?

相關文章