手把手教你如何破解付費MacApp

WJ_PENG ?發表於2019-04-11

目錄截圖.png Typora的自帶內容目錄截圖


前提準備

  • Hopper Disassembler(X,G,Esc,Space...,度娘/谷歌,後面簡稱Hopper

  • Xcode(App Store)

  • frida(前置條件配置好 python環境,下載好pip,隨後用pip下載,具體度娘/谷歌找攻略即可)

  • 彙編指令基礎掌握(je,jne,jmp,jle,mov,call,ret,nop,xor,lea,rax,rdi,rsi,rcx....)

  • lldb指令基礎掌握(bt, image lookup --address ,p, c...)

  • 終端操作(codesign *,frida *)

  • 具備一定的程式設計思想

  • 基礎逆向工程思維基礎掌握(Executable,重籤,覆蓋entitlements)

小目標:學習下一些簡單彙編知識以及市面上的一些付費App的付費模組的程式碼思路然後順便逆向破解 PDF Expert


複雜方案解決結果--->破解版傳送門

第一步

  • 目的---體驗原裝PDF Expert 修改任意pdf檔案後儲存檢視效果

1.1體驗未破解版流程

image

第二步

在儲存操作前是沒有提示升級到完整版這個彈窗的,所以我們在Xcode attach成功後可以嘗試增加個符號斷點windowWillLoad檢視呼叫棧的關係

  • 目的---檢視呼叫圖層間的呼叫關係,從而嘗試去定位到關鍵程式碼。

2.1 Xcode新建個工程

image

2.2 attach檢視對應圖層關係

2.2.1 Attach App
image

2.2.2 Debug View Hierarchy

image

檢視圖層關係 在增加符號斷點windowWillLoad後在修改後的pdf介面觸發儲存操作出現彈窗發現沒有停下斷點,說明這條路行不通。

但是我們也獲取到了一個關鍵資訊:最上面的控制元件名稱為 DMTrialController

第三步

  • 目的---使用frida通過已知彈出升級框的類 DMTrialController ,查詢出具體呼叫的方法堆疊

3.1 frida-trace 檢視對應方法呼叫關係

在終端上輸入 frida-trace -m "-[DMActivationController *]" PDF\ Expert

​
 22103 ms  -[DMTrialController trialObject]
​
 23074 ms  -[DMTrialController trialObject]
​
 23074 ms  -[DMActivationController performActivationStepWithStep:0x66]
​
 23074 ms     | -[DMActivationController isRunning]
​
 23074 ms     | -[DMActivationController setNextPerformStep:0x66]
​
 23074 ms     | -[DMActivationController updateStepControllerForCurrentStep]
​
 23074 ms     |    | -[DMActivationController nextPerformStep]
​
 23074 ms     |    | -[DMActivationController confirmedNextPerformStep:0x66] </pre>
複製程式碼

分析log輸出在點選儲存時觸發的方法為-[DMActivationController performActivationStepWithStep:0x66]

記錄下來回到第二步操作的2.4檢視付費彈窗堆疊資訊。

3.2 增加符號斷點檢視

image

再次儲存操作後觸發斷點終端檢視到嫌疑關鍵程式碼如下

 frame #1: 0x000000010227846c PDF Expert`___lldb_unnamed_symbol15828$PDF Expert + 380
 frame #2: 0x00000001022c5263 PDF Expert`___lldb_unnamed_symbol16722$PDF Expert + 371
 frame #3: 0x00000001022a1e37 PDF Expert`___lldb_unnamed_symbol16128$PDF Expert + 39 
複製程式碼

隨後 image定址找到具體App呼叫時的地址資訊

 (lldb) image lookup --address 0x0000000103818a7c
 Address: DevMateKit[0x0000000000029a7c] (DevMateKit.__TEXT.__text + 165540)
 Summary: DevMateKit`-[DMActivationController performActivationStepWithStep:]
(lldb) image lookup --address 0x00000001022c5263
 Address: PDF Expert[0x00000001003ff263] (PDF Expert.__TEXT.__text + 4180867)
 Summary: PDF Expert`___lldb_unnamed_symbol16722$PDF Expert + 371
(lldb) image lookup --address 0x00000001022a1e37
 Address: PDF Expert[0x00000001003dbe37] (PDF Expert.__TEXT.__text + 4036439)
 Summary: PDF Expert`___lldb_unnamed_symbol16128$PDF Expert + 39</pre>
複製程式碼

得到可疑地址

A:0x0000000000029a7c

B:0x00000001003ff263

C:0x00000001003dbe37

第四步

  • 目的---使用Hooper檢視可疑地址A,B,C。

4.1 科普使用Hooper

4.1.1 直接拽

image

image

4.1.2 稍等一會Hopper載入完成

4.1.3 追查付費彈窗底細

載入完成後在介面左側輸入剛才Xcode Attach控制元件名 DMTrialController

image

分析搜尋結果發現並沒有直接匹配的 DMTrialController 這玩意,猜想這個空間不是直接在主工程實現的,隨後看跟 DMTrialController 命名類似的DMTrialWelcomeStepController 看上圖可以找到檔案路徑並且發現關鍵字DevMateKit,其實早在Xcode Attach時檢視圖層就可以發現 這個付費彈窗的命名字首是DMT 而主工程的命名字首是PDF_Expert,很大概率付費彈窗是用的第三方庫.

DevMateKit傳送門

4.1.4 簡單瞭解DevmateKit有哪些功能

這個就不在這介紹了,通過傳送門下載的demo大概瞭解到DevMateKit是做一些付費彈窗,舉報彈窗,kevlar程式碼混淆。

4.2 逐個排查可疑地址

4.2.1 地址A:0x0000000000029a7cHopper 資訊分析

image

image

根據Hopper分析結果看出可疑地址A主要是在做一些繪製UI操作,我們主要目的是要改掉App中一個憑證欄位資訊類似 isActiva , isRegis 之類的字眼。排除掉可疑地址A。

4.2.2 地址B:0x00000001003ff263Hopper 資訊分析
image

發現定位到的地址操作符是test,這個有著重大嫌疑,大概率是此地址去做是否註冊判斷。我們繼續去排查可疑地址C。

4.2.3 地址C:0x00000001003dbe37Hopper 資訊分析

image

由分析結果得知可疑地址C主要是做saveDocument:操作,並且看上圖左邊紅色框可以得知這一頓操作沒有做一些test或者是cmp或者是提前ret之類的操作,基本也可以排除掉可疑地址C。

4.3 主要調查重點嫌疑地址

4.3.1 重新檢視可疑地址B:0x00000001003ff263的hopper資料,切換至控制流圖(Control Flow Graph)

備註:快捷鍵 Space

image

檢視上圖分析一處邏輯判斷為 al0x1test運算,但是此處邏輯地址為loc_1003ff259是由loc_1003ff11e的尾部的je跳轉過來的。

je的來源是上一層的al0x1test運算,再往上看可以知道al是通過call sub_1003b21b0的返回值賦值的。

4.3.2 解析第一層夢境function sub_1003b21b0

雙擊進入 sub_1003b21b0的function實現的控制流圖。進入目標func的控制流圖後發現是個蠻龐大的函式,縮放一下頁面看到整個流程圖如下

image

分析上圖控制流圖結構,猜想如果是已購買使用者的判斷邏輯應該是一條清晰的流程也就是右側箭頭所指的通道。

接著著重看如何才可以走到陽光大道上

image

分析上圖控制流圖重點是通過cmp r13b, 0x3je loc_1003b22cd,翻譯過來也就是判斷r13b是不是0x3如果是0x3就去陽光大道,那麼我們現在就想辦法把r13b變成0x3

image

繼續往上看得知r13beax懟過來的,而eax是通過function sub_100382d70 返回的。

那麼現在關鍵就是這個sub_100382d70

4.3.3 解析第二層夢境function sub_100382d70

雙擊進入sub_100382d70的function。

image

分析下第二層夢境的大概實現,首先第一眼嫌疑最大的是_O7RH3WAr7wAQMdz5Xv這個是被call的function是被混淆過的,其次通過這個_O7RH3WAr7wAQMdz5Xv 返回的al最後是跟0x1test

那麼現在冷靜下思考有以下兩個解決思路

  • 簡單方案-直接改sub_100382d70提前返回0x3然後去走陽光大道(如果忘記了為何要返回0x3,可以複習下4.3.2章節)

  • 複雜方案-修改_O7RH3WAr7wAQMdz5Xv 內部實現,繼續深究,改最根部判斷。

第五步

  • 目的---使用Hooper修改關鍵彙編運算邏輯。

5.1 簡單方案解法嘗試

5.1.1 修改sub_100382d70實現

根據上述操作我們得知sub_100382d70做了一串操作,先push,後mov後push…,我們現在其實只要返回個0x3即可,那直接選中sub_100382d70第一行修改,輸入mov rax,0x3 ,點Assemble and Go Next,再繼續輸入ret,繼續Assemble and Go Next

image

修改後的結果如下,隨後儲存新的Executable檔案

image

5.1.2 儲存Executable檔案

image

儲存Executable檔案時點Cancel

image

接下來就是驗證簡單方案是否可行了,把剛生成的新Executable檔案替換我們目標App內的舊Executable檔案,具體操作如下

image

5.1.3 覆蓋entitlements檔案

原版entitlements檔案資料如下,隨後吧identifier對應的那幾行幹掉

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
 <dict>
 <key>com.apple.application-identifier</key>
 <string>3L68KQB4HG.com.readdle.PDFExpert-Mac</string>
 <key>com.apple.developer.team-identifier</key>
 <string>3L68KQB4HG</string>
 <key>com.apple.security.get-task-allow</key>
 <true/>
 </dict>
</plist> 
複製程式碼

修改後

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
 <dict>
 <key>com.apple.security.get-task-allow</key>
 <true/>
 </dict>
</plist> 
複製程式碼

儲存後覆蓋目標App的entitlements檔案

 codesign -f -s - --entitlements entitlementsFilePathPrefix/Entitlements.plist appPathPrefix/PDF\ Expert.app 
複製程式碼

執行完終端輸出提示/Applications/PDF Expert 3.app: replacing existing signature即表示完成替換

5.1.4 檢驗簡單方案處理結果

隨後重新開啟目標App,首先檢查當前App的是否註冊狀態如下圖

image

隨後嘗試修改任意pdf檔案儲存,儲存成功!

5.2 複雜方案解決嘗試

5.2.1 _O7RH3WAr7wAQMdz5Xv的前世今生

早在上文4.1.4 簡單瞭解目標App所用的到第三方庫kevlar能支援程式碼混淆,我們先簡單看看_O7RH3WAr7wAQMdz5Xv的函式結構大概如下

image

看不是很懂,我們切換至虛擬碼視角看看

image

這裡我們發現個關鍵字串 kevlar,那基本可以認定此處的混淆程式碼的出處是DevMateKit了。

DevMateKit提供的demo工程我們搜尋kevlar

image

繼續懟進DMKevlarApplication.h看看程式碼

//! Function help with running timer for advanced check
#define DMKRunNewIntegrityCheckTimer DzVpwUg0VXKMIfCPA
FOUNDATION_EXTERN void DMKRunNewIntegrityCheckTimer(NSUInteger num, NSTimeInterval checkFrequency);
​
//! Checks if applicaion activated
#define DMKIsApplicationActivated PfCuPgJSp5KVlvc8W1
FOUNDATION_EXTERN BOOL DMKIsApplicationActivated(DMKevlarError *outKevlarError);
​
//! Returns user license info
#define DMKCopyLicenseUserInfo dReea3NiUFGwgD52YPa
FOUNDATION_EXTERN CFDictionaryRef DMKCopyLicenseUserInfo(void) CF_RETURNS_RETAINED;
​
//! Forces license validation request on DevMate server
#define DMKValidateLicense i2rRAQi8BfdE2G9geRSu
FOUNDATION_EXTERN void DMKValidateLicense(void (^completionHandler)(NSError *errorOrNil));
​
//! Deactivates application and invalidates license info
#define DMKInvalidateLicense kLLTbFMUP234v8xDp6Uck
FOUNDATION_EXTERN BOOL DMKInvalidateLicense(void);
​
/**
 This category will extend functionality of NSApplication to be complies with Kevlar concept of protection.
 Rigth now, some helper inteface have been declare there, because it is kind of complicated to load category.
 */
#define com_devmate_Kevlar YC2eXYjMnR
@interface NSApplication (com_devmate_Kevlar) 
複製程式碼

發現好多混淆函式,簡單看註釋//! Checks if applicaion activated 理論上這個函式應該是我們要找的最關鍵函式。

  • 那麼如何核實FOUNDATION_EXTERN BOOL DMKIsApplicationActivated(DMKevlarError *outKevlarError);就是我們要找的function呢?

現在手頭上有下載好的DevMateKit的demo工程,那麼直接run一個.app檔案出來丟到Hopper分析對比就可以了

image

上圖是run的CustomTrialExample這個target後的截圖,此時記錄下可疑函式的混淆標記為PfCuPgJSp5KVlvc8W1

隨後把這個CustomTrialExample.app丟到Hopper一頓分析後直接搜尋混淆標記PfCuPgJSp5KVlvc8W1

image

這下就基本上真相大白了,根據下圖對比得知

image

也就是我們基本核實

FOUNDATION_EXTERN BOOL DMKIsApplicationActivated(DMKevlarError *outKevlarError)

就是函式_O7RH3WAr7wAQMdz5Xv上輩子的初始形態了。

5.2.2 _O7RH3WAr7wAQMdz5Xv重寫

  • DMKIsApplicationActivated主要做了兩個事

    • 完整函式返回了BOOL

    • 傳入指標DMKevlarError *outKevlarError 內部可能做修改

  • 我們要重寫成什麼樣子?

    • 鑑於此function是要返回個是否已啟用/付費,那嘗試下完整函式返回固定為0x1

    • 常規來說已啟用/付費的使用者調此function理論上不應該有error存在,也就是我們需要把傳入指標對應的地址的內容變成0x0

那麼改成下圖的樣子也就基本上沒啥毛病了

image

轉成虛擬碼一看就懂了

int _O7RH3WAr7wAQMdz5Xv(int arg0) {
    rdi = arg0;
    if (rdi != 0x0) {
            *rdi = 0x0;
    }
    return 0x1;
}
複製程式碼

那麼我們就把我們重寫的目的都實現了,接下來就是見證奇蹟的時刻了

5.2.3 檢驗複雜方案處理結果

重寫 _O7RH3WAr7wAQMdz5Xv後和上文5.1.25.1.35.1.4做法一致,最後驗證出來結果也是一樣的可以儲存成功!

總結

快捷鍵補充(windows/Mac)

hopper修改 alt+a / option+a

hopper儲存 win+shift+e / cmd+shift+e

hopper定址 g

hopper查引用 x


逆向技術積累相關連結

[破解《Cornerstone》 by Chen華鋒 ](www.jianshu.com/p/5e4e459ee… ) //本人逆向程式設計的引路人


YY Flutter技術積累相關連結

flutter多例項實戰 by共田君

一行程式碼教你解決FlutterPlatformViews記憶體洩露 by AShawn

手把手教你在Flutter專案優雅的使用ORM資料庫 by williamwen1986

flutter通用基礎庫flutter_luakit_plugin by williamwen1986

github - flutter_luakit_plugin使用例子 by williamwen1986

手把手教你編譯Flutter engine by 共田君

手把手教你解決 Flutter engine 記憶體洩漏 by 共田君

github - 編譯產物下載 修復記憶體洩漏後的flutter engine(可直接使用)by 共田君

github demo - 修復記憶體洩漏後的flutter engine by 共田君

持續更新中...

相關文章