Jump to content

線程檢查之前的Frida stalker 檢查的替代方法是通過以下調用訪問當前線程狀態:

43.png

然後,由於以下比較,它檢查state-ts_64.__pc 是否在libsystem_kernel.dylib 中:

44.png

換句話說,如果state-ts_64.__pc 與mach_msg 的距離小於0x4000,則認為它在libsystem_kernel.dylib 中。

乍一看,對這個RASP 檢查可能不是很熟悉,但由於之前與EVT_CODE_TRACING 相關的檢查旨在檢測Frida Stalker,因此該檢查也可能旨在檢測Frida Stalker。

為了證實這個假設,我開發了一個小測試用例,在一個獨立的二進製文件中重現了這個檢查,我們可以根據它是否通過Frida stalker 來觀察差異:

45.png

Stalker 測試用例的輸出

46.png

沒有Stalker 的測試用例的輸出

通過使用函數gum_stalker_exclude 從跟踪者中排除庫libsystem_kernel.dylib ,從而輕鬆繞過此檢查:

47.png

可以看到,state-ts_64.__pc 位於libsystem_kernel.dylib 中:

48.png

排除內存範圍的測試用例的輸出

應用加載的庫RASP 事件EVT_APP_LOADED_LIBRARIES 旨在檢查Mach-O 依賴項的完整性。換句話說,它檢查Mach-O 導入的庫是否被修改。

Assemblyranges:0x100E4CDF8–0x100e4d39c

由於dladdr 函數,與此檢查相關的代碼首先訪問Mach-O 標頭:

49.png

dl_info 包含庫的基地址,其中包含第一個參數中提供的地址,因此,一個Mach-O二進製文件會連同它的標頭文件Dl_info一起加載。 Dli_fbase實際上指向mach_header_64。

然後該函數遍歷類似LC_ID_DYLIB 的命令以訪問依賴項的名稱:

50.png

此名稱包含依賴項的路徑。例如,我們可以按如下方式訪問此列表:

51.png

依賴項的名稱用於填充哈希表,其中哈希值以32 位編碼:

52.png

在後面的代碼中,這個計算表將與另一個哈希表(代碼中硬編碼的)進行比較,如下所示:

53.png

哈希示例如果某些庫已被修改為註入,例如FridaGadget.dylib,那麼動態計算的哈希將與代碼中硬編碼的哈希不匹配。

雖然這種檢查的執行是相當“標準”的,但有幾點值得一提:

首先,哈希函數似乎是一個派生的MurmurHash。

其次,哈希是32位編碼的,但是圖4中的代碼引用了64位的X11/X12寄存器。這實際上是一個限制內存訪問次數的編譯器優化。

最後,在每個檢查實例中,硬編碼的哈希值在二進製文件中重複。在SingPass 中,此RASP 檢查出現兩次,因此我們在以下位置找到這些值:0x100E4CF38、0x100E55678。這種重複可能用於防止易於修復的單點位置(single spot location)。

代碼系統庫此檢查與事件EVT_CODE_SYSTEM_LIB 相關聯,該事件包括驗證內存系統庫及其在dyld 共享緩存(磁盤上)中的內容的完整性。

Assemblyranges:0x100ED5BF8–0x100ED5D6Cand0x100ED5E0C–0x100ED62D4

此檢查通常以以下模式開始:

54.png

如果帶有給定check_region_cbk 回調的iterate_system_region 的結果不為0,它會觸發EVT_CODE_SYSTEM_LIB 事件:

55.png

要理解這個檢查背後的邏輯,我們需要了解iterate_system_region 函數的用途以及它與回調check_region_cbk 的關係。

iterate_system_region該函數旨在調用系統函數vm_region_recurse_64,然後根據可能觸發第一個參數check_region_cbk中給出的回調的條件過濾它的輸出。

iterate_system_region首先通過SYS_shared_region_check_np系統調用訪問dyld共享緩存的基址。這個地址用於讀取和記憶dyld_cache_header結構中的一些屬性:

1.共享緩存標頭;

2.共享緩存結束地址;

3.與共享緩存相關的其他限制;

計算過程如下:

56.png

從逆向工程的角度來看,用於記憶這些信息的堆棧變量與稍後調用的vm_region_recurse_64 的參數信息別名。我不知道這種混疊是否是故意的,但它使結構的逆向工程變得更加複雜。

在vm_region_recurse_64上有一個循環,它查詢vm_region_submap_info_64信息,查找dyld共享緩存範圍內的這些地址。由於mach_msg_type_number_t *infoCnt參數被設為19,我們可以確定查詢的類型(vm_region_submap_info_64):

57.png

此循環在某些條件下中斷,並且在其他條件下觸發回調。正如稍後解釋的那樣,回調驗證dyld 共享緩存中存在的庫的內存完整性。

這個循環在某些條件下中斷,而在其他條件下觸發回調。回調會驗證dyld共享緩存中存在的庫在內存中的完整性。

基本上,如果發生以下情況,就會觸發對共享緩存進行深度檢測的回調:

58.png

check_region_cbk當條件滿足時,iterate_system_region調用check_region_cbk,第一個參數中帶有可疑地址:

59.png

在分析SingPass 時,只有一個回調函數與iterate_system_region一起使用,它的代碼並沒有特別混淆(字符串除外)。一旦我們知道這些檢查與dyld共享緩存有關,我們就可以很容易地弄清楚這個函數中涉及的結構。這個回調位於0x100ed5e0c地址,並重命名為check_region_cbk。

它首先訪問有關地址的信息:

60.png

此信息用於讀取與地址參數關聯的__TEXT 段的內容。

61.png

__TEXT 字符串以及共享緩存的不同路徑(如/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64e 和標頭的魔法值:0x01010b9126:dyld_v1 arm64e 或0x01010b9116:dyld_v1 arm64)都被編碼。

另一方面,該函數打開dyld_shared_cache 並查找包含與地址參數關聯的庫的共享緩存部分:

62.png

第二次調用mmap()的目的是加載包含庫代碼的共享緩存部分。然後,該函數檢查__TEXT段的內容是否與內存中的內容相匹配。執行此比較的循環位於0x100ED6C58 -0x100ED6C70。

我們可以從這個RASP檢查的描述中觀察到,開發者花了很多精力來避免性能問題和內存消耗。另一方面,在我的測試中從來沒有調用過回調check_region_cbk(即使我掛鉤了系統函數)。我不知道是不是因為我誤解了條件,但最後,我必須手動強制條件。

RASP 的設計弱點由於保存函數指針的不同#EVT_* 靜態變量,混淆器能夠為支持的RASP 事件提供專用回調。儘管如此,應用程序開發人員定義的函數init_and_check_rasp 將所有這些指針設置為同一個回調:hook_detect_cbk_user_def。在這樣的設計中,所有RASP 事件最終都在一個函數中,這削弱了不同RASP 檢查的強度。

這意味著我們只需要針對這個函數來禁用或繞過RASP 檢查。

63.png

由於這個缺點,我可以防止應用程序一啟動就顯示錯誤消息。

它存在另外兩個RASP 檢查:EVT_APP_MACHO 和EVT_APP_SIGNATURE,由於開發人員未啟用它們,因此在SingPass 中不存在。

總結一方面商業解決方案實現了強大而先進的RASP 功能,例如,內聯繫統調用分佈在應用程序的不同位置。另一方面,應用程序的開發人員通過為所有事件設置相同的回調來削弱RASP 功能。此外,該應用程序似乎沒有使用商業解決方案提供的本機代碼混淆,這使得RASP 檢查不受靜態代碼分析的保護。無論用戶提供什麼配置,對這些檢查強制執行代碼混淆都是值得的。

0 Comments

Recommended Comments

There are no comments to display.

Guest
Add a comment...