成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

關于iOS私有API掃描

移動開發(fā)
近期研究了關于私有 API 掃描這個主題。研讀了業(yè)界現(xiàn)有的相關文章后發(fā)現(xiàn),很多都是簡單的摘錄,也不對存在的謬誤做任何點評。本人在閱讀了網易游戲開源的 iOS private api checker 項目后,對如何構建私有 API 庫、該項目又是如何識別 APP 中的私有 API、該方案存在哪些問題,一一做了闡述。

導讀:近期研究了關于私有 API 掃描這個主題。研讀了業(yè)界現(xiàn)有的相關文章后發(fā)現(xiàn),很多都是簡單的摘錄,也不對存在的謬誤做任何點評。本人在閱讀了網易游戲開源的 iOS private api checker 項目后,對如何構建私有 API 庫、該項目又是如何識別 APP 中的私有 API、該方案存在哪些問題,一一做了闡述。

關于iOS私有API掃描

審核案例

自定義方法和私有 API 重名

APP 沒有被拒絕,但是 Apple 提醒下次更新時修改相關 API 名稱。

然而多年前,廣為使用的 Three20 里包含和私有 API 重名的方法,導致很多使用該框架的 APP 審核不通過。

使用了非公開方法

Apple 發(fā)現(xiàn) APP 使用了非公開的方法 allowsAnyHTTPSCertificateForHost:,拒絕的同時還提供開發(fā)者自查的方法。

未執(zhí)行到的私有 API 調用

Qzone 中曾自定義接口 _define: 但是并沒有調用過,結果也被 Apple 發(fā)現(xiàn)并拒絕上架。UITextView 導出的頭文件中有該方法。

Tim Cook 威脅下架 Uber 應用

Uber 使用私有 API獲取設備的序列號,蘋果 CEO 嚴厲斥責該行為并威脅要下架。

調用方式

直接調用

  1. [self.view recursiveDescription]; 

因為私有 API 沒有暴露出來,編譯會報錯。可以添加匿名 Category 聲明下私有方法。 

  1. @interface UIView()  
  2. -(id)recursiveDescription;  
  3. @end 

字符拼接 

  1. NSArray *parts = @[@"_priva", @"teMethod"];  
  2. NSString *selectorString = [parts componentsJoinedByString:@""];  
  3. [self performSelector:NSSelectorFromString(selectorString) withObject:nil]; 

代碼混淆 

  1. // statusBar  
  2. NSData *data = [NSData dataWithBytes:(unsigned char[]){0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x61, 0x72} length:9];  
  3. NSString *key = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; 

檢測方法

符號表

用 nm, otool 等工具導出二進制包的函數(shù)符號表,以檢查私有 API 的調用。缺點是無法檢測字符串拼接方法的私有 API 調用。

動態(tài)掃描

動態(tài)掃描需要應用運行起來,每當調用方法時就判斷是否是私有 API,但是效率會很低,而且不能保證代碼完全覆蓋。

靜態(tài)分析

在對二進制文件反匯編結果的基礎上,進行靜態(tài)分析:

  • 找出動態(tài)調用 API 方法如 performSelector: ,以及調用對象的類
  • 檢查參數(shù),如果參數(shù)是拼接方法生成,推導求得拼接的結果

如何推導,請閱讀加拿大 Laval University 發(fā)表的題為 Static Analysis of Binary Code to Isolate Malicious Behaviors 的論文。如果拼接字符串由服務端下發(fā),依舊可以避開檢查。

網易方案

構建私有 API 庫

從 Github 下載 iOS-private-api-checker 后,可使用 WEB 的方式上傳一個 IPA 進行掃描。我們可以使用 virtualenv 創(chuàng)建一個虛擬環(huán)境,來安裝所需的依賴庫,以免影響系統(tǒng)級的 Python 環(huán)境。 

  1. # 創(chuàng)建虛擬環(huán)境 
  2. virtualenv venv 
  3.  
  4. # 啟用虛擬環(huán)境 
  5. . venv/bin/activate 
  6.  
  7. # 安裝依賴的庫 
  8. pip install -r requirements.txt 
  9.  
  10. # 啟動監(jiān)測服務 
  11. python run_web.py 

工程的 app/templates/main/index_page.html 里介紹了檢查的原理:

  1. 通過 class-dump 導出 Frameworks 及 PrivateFrameworks 的頭文件,分別設置為集合 PU 和 PR
  2. 通過 Xcode 代碼提示的 SQLite 數(shù)據(jù)庫查詢出所有的 documented API,設置為集合 DA
  3. 那么 PU - DA 為公有 Framework 中的私有 API,設置為 A
  4. PR 為私有 Framework 中的 API,都不能使用。則私有 API 集合 PRAPI = A + PR
  5. 使用 class-dump 反編譯 ipa 中的 APP 文件,然后和 PRAPI 集合取交集即可獲得

但是,項目根目錄下的 README.md 寫道:

  • 私有的api = (class-dump Framework下的庫生成的頭文件中的api - (Framework下的頭文件里的api = 有文檔的api + 沒有文檔的api)) + PrivateFramework下的api

我***眼看到這個公式,對其中每一個運算項的含義不是非常肯定,對括號里寫上等于號也是有疑問的。另外,這個公式里還提到了 Framework 下的頭文件里的 API,而在 index_page.html 中完全沒有提到。所以,建議先無視這個公式,對 index_page.html 里的文字也不要糾結。

閱讀 build_api_db.py 時,看到方法 rebuild_private_api 中的注釋里寫道: 

  1. set_E private api  
  2. undocument_api = set_B - set_C  
  3. set_E = set_A - set_C - undocument_api = set_A - set_B  
  4. if include_private_framework: set_E = set_E + set_D 

單從集合運算的角度看 set_E = set_A - set_C - undocument_api 和 set_A - set_B 能不能劃等號?講道理,應該是 set_E = set_A - (set_B + set_C) 吧。這里的 + 是套用原作者的簡化寫法,指集合的 ∪ 運算。所以,建議無視這個注釋。

注釋表述的雖有問題,但通讀代碼發(fā)現(xiàn)實際實現(xiàn)的邏輯是沒有問題的。現(xiàn)根據(jù) build_api_db.py 及相關的代碼所對應的構建私有 API 庫的原理做一簡要闡述:

  1. set_A,表示從系統(tǒng) Frameworks 目錄下所有的 .framework 文件 dump 出的頭文件解析出的 API 集合。對應 ios_private.db 中的 framework_dump_apis 表記錄。
  2. set_B,表示從系統(tǒng) Frameworks 目錄下所有的 .framework 文件中的頭文件解析出的 API 集合。對應 ios_private.db 中的 framework_header_apis 表記錄。
  3. set_C,表示從 docSet 中索引文件解析出來的 API 集合。對應 ios_private.db 中的 document_apis 表記錄。
  4. set_D,表示從系統(tǒng) PrivateFrameworks 目錄下所有的 .framework 文件 dump 出的頭文件解析出的 API 集合。對應 ios_private.db 中的 private_framework_dump_apis 表記錄。
  5. set_E,表示私有 API,從 set_A 中識別出的私有 API 對應 framework_private_apis 中的記錄,表 private_apis 中的是加上 set_D 的記錄。
  6. 如果 rebuild_sdk_private_api 函數(shù)的第二個參數(shù)是 False 則 set_D 不會被加入到 private_apis 表中。

構建集合 A

api_utils.py 中已經封裝好了使用 class-dump 導出 .framework 的頭文件。所以不需要 DumpFrameworks.pl 這類的外部腳本,而且 DumpFrameworks.pl 生成的頭文件目錄結構和本項目不吻合。也不需要下載 Nicolas Seiot 基于 RuntimeBrowser 導出的頭文件。

我們需要做的是,保證目標系統(tǒng) (比如 8.1) 的模擬器在本機已經安裝,并且知道 Frameworks 及 PrivateFrameworks 的路徑。

前者只需要在 Xcode / Preferences / Components / Simulator 中將 iOS 8.1 Simulator 下載下來即可。后者可以通過創(chuàng)建一個 Xcode 工程,設置啟動參數(shù) DYLD_PRINT_INITIALIZERS = 1 就可以在控制臺找到 .framework 的全路徑,比如:

  1. /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 8.1.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks 

需要注意的是,上述路徑 iOS 和 8.1 之間存在一個空格。這個空格會引起執(zhí)行 class-dump 的腳本出問題,具體如何修復后面會給出建議。

根據(jù)我的實驗結果,將上述路徑的 8.1 改成 9.3 或者 10.3 即為不同系統(tǒng)下的路徑。iOS 11.4 的路徑是:

  1. /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks 

我們不需要記住這些路徑,需要的是掌握獲取路徑的方法,用 find 命令也是 OK 的。

構建集合 B

Frameworks 路徑已經在構建集合 A 的部分介紹過,api_utils.py 中 framework_header_apis 方法就是用于構建 Frameworks 目錄下所有的 .framework 文件中的頭文件解析出的 API 集合。看出和集合 A 的區(qū)別了吧?一個是直接處理 .framework 中包含的頭文件,一個是從 .framework 中的 Mach-O 文件導出對應的頭文件。

構建集合 A/D 其實就比構建集合 B 多一步,即 dump 的過程。這也是為何在 dump 時,導出頭文件的目錄和系統(tǒng) framework 文件內部結構一致,這樣使得接下來的構建集合過程的代碼可以通用。

構建集合 C

生成 documented API 集合的主要障礙在于,本機缺乏 docSet。本文寫于 2018 年 9 月初,我的工作機上只有 Xcode 9,而新版本的 Xcode 已經使用新的文檔格式并直接集成在 Xcode 中。其實蘋果官方提供了一個包含各版本文檔鏈接等信息的 XML,將該 XML 下載到本地即可從中找到 iOS 8.1 等的文檔下載鏈接。 

  1. # 各版本 iOS docSet 的元信息  
  2. https://developer.apple.com/library/downloads/docset-index.dvtdownloadableindex 
  3.  
  4. # iOS 8.1 docSet  
  5. https://devimages-cdn.apple.com/docsets/20141020/031-0773***.dmg 
  6.  
  7. # iOS 9.3.5 docSet  
  8. https://devimages-cdn.apple.com/docsets/20160321/031-52212-A.dmg 

安裝下載下來的 dmg 后,在 Mac OS 根目錄下便出現(xiàn) docSet 文件了,你可以隨意挪位置。docSet 內部的 Contents/Resources/docSet.dsidx 是我們獲得集合 C 的數(shù)據(jù)源。

本人習慣使用 SQLPro for SQLite 工具查看 sqlite 數(shù)據(jù)庫文件,將 docSet.dsidx 重命名為 docSet.sqlite 即可雙擊打開。其中 ZTOKENTYPE 表中的 func,instm,clm,intfm,intfcm 五種類型是我們要關注的:

  • func 表示全局 C 函數(shù)
  • instm 表示實例方法 instance method
  • clm 表示類方法 class method
  • intfm 表示協(xié)議方法,- 開頭
  • intfcm 表示協(xié)議方法,+ 開頭

憑感覺猜測 intf 是 interface 的縮寫,interface 即 OOP 的接口而不是 Obj-C 定義類的那個 interface

至于***版 iOS 的 documented API 怎么獲得,本人沒有研究。既然 Dash 的作者能生成 Apple API Reference 那理論上講應該是可以生成 dsidx 文件的。記錄有些許價值的 Dash Release Notes 作為日后研究的線索:

  • "Xcode 8 doesn’t come with docsets anymore and that means Dash won’t automatically support the iOS 10, macOS 10.12, watchOS 3 and tvOS 10 docs. I’m working on a version of Dash that supports the new docs and will release an update as soon as possible." -- Jun 14th, 2016
  • "Apple API Reference Support. Apple has new API docs. You can use them in Dash by installing the Apple API Reference docset." -- Jul 2nd, 2016
  • "The Apple API Reference docset now reads the docs from within Xcode 8. This reduces disk space usage while also allowing me to modify & improve the docs at display-time. Thanks a lot to the Xcode team at Apple for helping me understand the new documentation format!" -- Oct 25th, 2016

構建集合 D

同構建集合 A,路徑的 Frameworks 改成 PrivateFrameworks 即可:

  1. /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 8.1.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/ 

構建集合 E

以 set_A 為處理對象:

  1. 所有以 _ 開頭的方法,全部加到 set_E 中;
  2. 其他 API,如果不在 set_B 也不在 set_C 中,則加到 set_E 中
  3. 在不在 set_B / set_C 中的比較基準是 api_name,class_name,sdk 三個值

步驟 3 是基于 db 查詢來實現(xiàn)的

代碼缺陷

1、build_api_db.py 中 rebuild_sdk_private_api(sdk_version, False),需改成 True

2、build_api_db.py 中 if include_private_framework 之后應該是把 private_framework_apis 插入到數(shù)據(jù)表中,而不是 framework_dump_private_apis

3、api/api_utils.py 中 all_headers_path += iterate_dir(framework, "", os.path.join(framework_folder, header_path)) 應該改成 all_headers_path += iterate_dir(framework, "", header_path)

4、db/dsidx_dbs.py 中 sql = balabala 需要確認 dsidx 文件中五種 TOKEN 類型對應的 ID

比如我從 Apple 下載下來的 8.1 docSet 對應相同 ZTOKENTYPE 的 ID 不是 (3,9,12,13,16) 而是 (11,13,1,8,19)。

如果你是從百度網盤等地方直接下載別人的 ios_private.db,請打開這個 db 檢查下 document_apis 表中的數(shù)據(jù)真的都是 API 么。

另,原作者寫 (3,9,12,13,16) 是因為當時 iOS 7.0 docSet 里確實是這幾個 ID,這一點我通過往前翻 commit 記錄得到了確認。所以靈活一點的寫法是根據(jù) ZTYPENAME 篩選數(shù)據(jù)。

5、dump/class_dump_utils.py 中 ret = subprocess.call(cmd.split()) 健壯性不夠

我在 Xcode 9 安裝 iOS 8.1 模擬器后看到 Frameworks 路徑是帶空格的,經過 split 就會導致路徑被拆分成兩段。改成 ret = subprocess.call([class_dump_path, '-H', frame_path, '-o', out_path]) 應該就可以規(guī)避該問題。

掃描私有 API

主要邏輯

閱讀 iOS_private.py 梳理出識別 APP 中私有 API 的主要邏輯如下:

  1. 基于 strings 工具從 Mach-O 文件導出字符串,按空格拆解得到集合1
  2. 使用 otool -L 從 Mach-O 文件獲得用到的 Frameworks 及 PrivateFrameworks 列表
  3. 基于 class-dump 從 Mach-O 文件導出的頭文件信息,解析出類名變量名集合2、方法集合3
  4. 集合4 = 集合1 - 集合2(比較基準是 api_name)
  5. 表 framework_private_apis 中按 api_name, class_name 分組得到類名方法名組合的集合5
  6. 對集合5 和集合4 按 api_name 匹配,得到集中6
  7. 集合6 和集合 3 按 api_name, class_name 匹配,得到最終的私有 API 集合

步驟2 的結果可以作為步驟5 的部分條件。白名單表 whitelist 里的數(shù)據(jù),會從結果集中排除,對應到代碼邏輯上也是在步驟5 被過濾掉。

代碼缺陷

因上述步驟6 中是按 api_name, class_name 的組合做匹配條件的,故原始代碼的 SQL 語句中 group by 不僅要有 api_name 還應該加上 class_name 這個字端。

改進建議

直接使用網易方案大概率是發(fā)現(xiàn)不了私有 API 的。檢測邏輯只考慮了 api_name, class_name 全匹配,局限性太大。

  1. 在私有 API 數(shù)據(jù)庫的建設上,TSRC 實驗室的做法是進一步增加條件,比如一些純小寫字母的 API,大多是一些 C 函數(shù),再過濾掉一批
  2. 在掃描算法的設計上,如果步驟5 只 group by api_name,步驟6 只匹配 api_name,同時在源代碼中存在 @selector(XXX) 這樣的字符串,基本可以認定該 api_name 為私有 API
  3. 對于靜態(tài)拼接或者加解密的 API,可以通過動態(tài) hook 的方式進行識別,但也存在一些局限性
  4. 加入 prefs: 及 App-Prefs: 協(xié)議的掃描

驗證特定 API

蘋果審核提出使用了不該用的某某 API,那么我們勢必要支持篩查該 API 用在何處,是我們的 APP 還是第三方 SDK 中。在代碼工程根目錄,執(zhí)行:

  1. find . -type f | grep ".a" | grep -v ".app" | xargs grep advertisingIdentifier 

遺留主題

在研讀網易游戲的開源方案時,對于 iOS 10+ 如何構建 documented API 數(shù)據(jù)集這個問題直接跳了過去,后續(xù)可進一步調研。

責任編輯:未麗燕 來源: 簡書
相關推薦

2015-10-21 10:21:13

隱私安全私有API個人信息安全

2018-12-18 11:36:30

私有云存儲云計算

2011-05-11 10:02:37

iOS

2013-06-03 16:27:49

iOS開發(fā)移動應用移動開發(fā)

2011-07-07 16:53:56

iOS 條形碼掃描

2011-08-11 14:46:25

2018-05-27 17:44:53

私有庫索引庫倉庫

2025-05-20 10:00:54

2013-12-17 11:18:53

iOS開發(fā)多媒體API

2010-10-20 09:10:29

私有云

2014-03-12 10:13:00

iOSSEL對象

2011-09-02 19:12:59

IOS應用Sqlite數(shù)據(jù)庫

2015-08-20 09:00:23

ios9api

2013-03-27 11:33:32

iOS開發(fā)iOSjson解析方式

2022-03-17 08:30:28

Python私有屬性私有方法

2011-08-23 16:48:41

Lua 5.1API 函數(shù)

2021-04-14 10:10:46

首席信息官APICIO

2015-05-28 13:42:08

2015-07-29 09:22:25

IOS多線程

2011-08-18 11:19:13

IOS開發(fā)Core Plot S
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一级黄色大片 | 日韩精品一区二区三区中文在线 | 97精品久久 | 久久国产精品视频 | 亚洲精品久久久久久久久久久久久 | 三级免费毛片 | 成人片在线看 | 日韩av一区二区在线观看 | 久久91精品久久久久久9鸭 | 国产亚洲精品久久yy50 | 粉色午夜视频 | 色女人天堂 | 99国内精品久久久久久久 | 婷婷在线网站 | 亚洲视频三区 | 午夜不卡福利视频 | 日韩国产中文字幕 | 先锋资源吧 | 国产国拍亚洲精品av | 久久成人精品视频 | 亚洲国产一区在线 | www.天天操 | 亚洲一区在线播放 | 亚洲一av | 中文在线一区 | 精品九九九 | 日本又色又爽又黄又高潮 | 亚洲免费在线观看av | 干一干操一操 | 亚洲欧洲精品一区 | 亚洲影音先锋 | 国产精品视频一区二区三区不卡 | 一级看片免费视频 | 亚洲精品乱码久久久久久按摩观 | 日本一区二区三区在线观看 | 亚洲电影第三页 | 欧美国产视频 | 在线观看国产 | 亚洲第一在线 | 女人夜夜春| 高清国产一区二区 |