iOS私有API檢測掃描思路以及工具開發(Python3 + Django)

風險點

1、class-dump有些文件會報錯,需要查看下

2、mach-o文件中的依賴除了系統,是不是還需要dump第三方其他的庫進行掃描@xpath

3、私有api在公開的Framework及私有的PrivateFramework都有。

4、9.2.5的iOS系統對應的Xcode 8是有docset的,後面的Xcode都有新的文件格式了,下面有介紹,需要自己分析,但是數據結構有點亂,可能我我還沒悟出來。。。。。。

5.編譯好的db和docset文件晚點上傳到雲盤

前言

最近SDK的開發,經常會給到安全組掃描安全漏洞,會有一項報告是私有API警告,就想着自己實現一個工具來提前掃描。看了網上很多文章,基本上都是簡單的介紹,大多數資料都是網易遊戲開源的一個iOS private_api_ckecker項目,項目現在已經不維護了,而且是用Python2Flask寫的,而且Bug好多,但是思路是可以研究一下的。

下面就用Python3Django重寫該項目,把Bug都給修復了,而且會記錄一下該掃描思路的不足以及如何構建私有API庫

檢測方法

符號表

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

動態分析

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

靜態分析

在對二進制文件反彙編結果的基礎上,進行靜態分析:

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

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

檢測思路

1、通過class-dump導出Frameworks以及PrivateFrameworks中可執行文件的頭文件,通過腳本提取方法分別爲SET_A集合和SET_E集合
2、通過Framework中的Header文件夾下暴露的頭文件進行提取,通過腳本提取方法設置爲SET_B集合
3、找到Xcode內置的com.apple.adc.documentation.iOS.docset數據庫(iOS 9.3之後修改了內置數據結構,後面介紹再介紹),多表查詢出對應的API,設置SET_C集合
4、那麼SET_F =(SET_A - SET_B - SET_C)就是公有Framework下對應的私有API,設置爲集合SET_F
5、原本B集合中的API就是私有庫裏面的,因此都不能被使用,則最終的私有API集合爲
SET_D = SET_F + SET_E
6、使用class-dump反編譯ipa包中的app文件,然後和SET_D做交集即可獲取到。

以下是構建所用到的表名
集合A — framework_dump_apis framework可執行文件dump後的api集合
集合B — framework_header_apis framework暴露的頭文件api集合
集合C — document_apis 內置文檔docset數據集合
集合D — all_private_apis 最終私有apis集合
集合E — private_framework_dump_apis 私有framework可執行文件dump後的集合
集合F — framework_private_apis 集合A - 集合B - 集合 C剩下的apis
集合G — white_list_apis 白名單

當項目啓動的時候會根據數據庫不存在就會創建這7張表,其中db_names是對應的配置文件中的數組

def create_relate_tables():
    sql = ("create table %s("
           "id integer primary key AUTOINCREMENT not null, "
           "api_name varchar, "
           "class_name varchar, "
           "type varchar, "
           "header_file varchar, "
           "source_sdk varchar, "
           "source_framework varchar )")
    for db_name in db_names.keys():
        SqliteHandler().execute_sql(sql % (db_names[db_name]), ())

構建集合A(framework_dump_apis)

首先我們要知道如何拿到系統Framework的對應路徑,在Xcode中配置啓動參數DYLD_PRINT_INITIALIZERS = 1,啓動之後就能在控制檯拿到對應的全路徑。FrameworkPrivateFramework都是在該路徑下

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

網上的Demo都是很早之前的Demo,路徑已經變化了,所以最新的路徑獲取方式按上面的方式拿即可。

api_utils.py中我們會針對集合A調用如下

# SET_A  dump framework所有的API,Mach-o文件導出對應頭文件,給framework路徑作爲參數
def frame_work_dump_apis(version, framework_folder):
    """
    class-dump framework下庫生成的所有頭文件api
    """
    # dump 目標文件的framework到指定目錄 /tmp/public_headers/xxx.framework/Headers/xxx.h  返回值 /tmp/public-headers/ 打成.h
    framework_header_path = __class_dump_frameworks(framework_folder, 'public_headers/')

    # 獲取.h文件集合
    all_headers = __get_headers_from_path(framework_header_path)

    # 解析文件內容,獲得api
    framework_apis = __get_apis_from_headers(version, all_headers)

    return framework_apis

第一步是class-dump出頭文件組織結構和Xcode內置的Framework中的Headers結構一致,然後導入到工程下的/tmp/public_headers/xxxxx.framework/Headers/xxxxx.h

第二步把所有目錄下的頭文件集合成數組[(frameworkname, prefix, 具體路徑),()]

第三步提取頭文件中的方法,類以及類型等屬性[{'class':'','methods':'','type':''},{},{}],這裏Python的正則提取就不介紹了,太多了,可以看工程源碼,都是獨立可以使用的模塊

第四步把上述信息組裝成對應的keyvalue,對應framework_dump_apis表中的字段

第五步多插入庫 右側數據結構[{'class':'','methods':'','type':''},{},{}]

# (:api_name,:api_name,:api_name,:api_name,:api_name,:api_name)
# 多插
def insert_apis(table_name, datas):
    """
    Mysql
    https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-executemany.html
    如果是 [(),(),()] 則用%s
    如果是[{},{},{}] 就用 :name取值
    """
    sql = "insert into " + table_name + " (api_name,class_name,type,header_file,source_sdk,source_framework) values (:api_name,:class_name,:type,:header_file,:source_sdk,:source_framework)"
    return SqliteHandler().insert_many(sql, datas)

此時,公有庫Dump出來的所有API表就建立好了,可以查看framework_dump_apis表,裏面根據關鍵字能搜索到你平時用的API,一共有139610
注意:
當我們在Framework目錄下進行dump的時候有些結果是嵌套在裏面的,比如Framework內部還有Framework,比如AVFoundation.Frameworks,提取的時候千萬不能忘掉,而且每個版本有可能不同,需要注意

在這裏插入圖片描述
那麼最後提取出來是142724

構建集合B(framework_header_apis)

Framework的Header中頭文件的路徑獲取方式已經介紹過了

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks

由於我們構建集合A的時候Dump出來的頭文件結構規則和Framework結構一直,所以和構建集合A不同的是就是不需要dump,直接把頭文件導出,然後挨個分析導出對應的數據結構即可,然後統一多插入庫即可,代碼和第一步用到的一樣。
注意:
這裏的結構和Framework可執行文件那裏一樣會出現嵌套結構,雖然我們自己dump到tmp目錄下是不會有,但是公用代碼的話,這裏也需要處理一下上面嵌套的結構,可以在上面給的路徑下看到對應的AVFoundation.Framework也一樣嵌套

可以看下簡單的日誌路徑:

頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Headers/AVAudioUnitReverb.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Headers/AVPlayerMediaSelectionCriteria.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Headers/AVDepthData.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Headers/AVCaptureFileOutput.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Headers/AVAudioUnitTimeEffect.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Headers/AVUtilities.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Frameworks/AVFAudio.framework/Headers/AVAudioUnitSampler.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Frameworks/AVFAudio.framework/Headers/AVAudioEngine.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Frameworks/AVFAudio.framework/Headers/AVAudioUnitGenerator.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Frameworks/AVFAudio.framework/Headers/AVAudioTime.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Frameworks/AVFAudio.framework/Headers/AVAudioUnitMIDIInstrument.h
頭文件讀入,正在處理正則---> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/AVFoundation.framework/Frameworks/AVFAudio.framework/Headers/AVAudioUnitEffect.h

A集合和B集合主要的提取邏輯都是在api_utils.py文件中,已經加上註釋。
那麼最後提取出21551

構建集合C(document_apis)

Xcode 9以下,Apple的文檔是以docSet的格式存在的。這是官方提供的XML信息,裏面包含了所有版本的文檔信息。

# 各版本 iOS docSet 的元信息
https://developer.apple.com/library/downloads/docset-index.dvtdownloadableindex
# iOS 8.1 docSet
https://devimages-cdn.apple.com/docsets/20141020/031-07735-A.dmg
# iOS 9.3.5 docSet
https://devimages-cdn.apple.com/docsets/20160321/031-52212-A.dmg

iOS 9.3.5是最後一個能獲取到的docset文件了。下載後的com.apple.adc.documentation.iOS.docset文件,顯示包內容打開docSet 內部的 Contents/Resources/docSet.dsidx就是我們要獲取到的集合C,把這個文件拖進Navicat,看下錶結構
在這裏插入圖片描述
打開我們的ZTOKEN表,表字段ZTYPENAME就是我們要關注獲取到的方法類型,主要是以下幾個

  • func(pk=1) 全局C函數
  • instm(pk=4) instance method 對象方法
  • clm (pk=2) class method 類方法
  • intfm (pk=6) interface method (- 協議)
  • intfcm (pk=22)interface class method (+ 協議)
def get_dsidx_apis(db_path):
    
    sql = "SELECT T.Z_PK," \
          " T.ZTOKENNAME," \
          " T.ZTOKENTYPE," \
          " T.ZCONTAINER, " \
          "F.ZDECLAREDIN FROM ZTOKEN as T" \
          " INNER JOIN ZTOKENMETAINFORMATION as F ON T.Z_PK=F.ZTOKEN" \
          " WHERE ZTOKENTYPE IN (1,2,4,6,22)"
    return SqliteHandler(db_path=db_path).execute_select(sql,())

db_path是我們下載的docset文件的路徑,首先通過查詢ZTOKENZTOKENMETAINFORMATION進行連表查詢,然後再根據對應的字段查對應的表把我們建的數據庫表字段對應好,然後組裝成能進行多插的數據結構,插入對應的表即可,一共32150

api_name     ZTOKEN--- ZTOKENNAME字段
class_name   ZTOKEN--- ZCONTAINER-- > ZCONTAINERNAME字段
type         ZTOKEN--- ZTOKENTYPE字段
header_file  ZTOKEN--- ZTOKENMETAINFORMATION--- ZDECLAREDIN字段---ZHEADER--> ZHEADERPATH字段
source_sdk   12.1
source_framework  ZTOKEN--- ZTOKENMETAINFORMATION--- ZDECLAREDIN字段---ZHEADER--> FRAMEWORKNAME字段

但是在iOS 9.3.5之後,Xcode不在內置docset數據庫,而是換了一種數據結構,反正看起來雖然有點邏輯,但是很難提取完整。


雖然說iOS 9之後咱們能用到的API基本沒太大的變化,也能用上面的方式進行提取,但是如果要精益求精,就必須按新的API數據結構來提取了,具體如下

Xcode 9之後的API 內置在一個Framework裏面,主要是兩個文件:map.db和cache.db

/Applications/Xcode.app/Contents/SharedFrameworks/DNTDocumentationSupport.framework/Versions/A/Resources/external

1、以UIButton爲例,在map.db裏面查詢對應的uuid

select uuid from map where source_language = 1 and reference_path = 'uikit/uibutton'

2、然後到cache.db的refs表中查詢到對應的data_id

select data_id from refs where uuid = 'hcOyO61dSB'

3、上面根據uuid拿到的data_id是2187,然後在同級目錄下找到fs文件夾,找到對應的資源文件


上面拿到的文件是經過蘋果最新的無損壓縮算法LZFSE進行壓縮的,Github上已經有人實現了LZFSE算法實現,下載後編譯得到lzfse,然後放進/usr/local/bin

lzfse -decode -i /Applications/Xcode.app/Contents/SharedFrameworks/DNTDocumentationSupport.framework/Versions/A/Resources/external/fs/2187 -o /Users/mikejing191/Desktop/2187.json

解壓後的文件是一個字符串,也不是Json字符串,感覺他是由許多個Json字符串組合而成,你可以通過以下簡單的算法拿到一段段的Json,但是有時候解析出來也不是正確的Json格式,就非常噁心了

def get_decode_json(filepath):
    with open(filepath, 'rb') as f:
        text = f.read()
        filter_text = text.decode('utf-8', 'ignore')
        # print(filter_text)
        return filter_text
    return []
# 2439 是UIView
if __name__ == '__main__':
    result = get_decode_json('/Users/mikejing191/Desktop/2035.decode.json')

    num = 0
    result_array = result.split('}{')

    result = ''

    for str in result_array:
        print('')
        if num == 0:
            js = str + '}'
        elif num == len(result_array)-1:
            js = '{' + str
        else:
            js = '{' + str + '}'
        num += 1
        print(js)

但問題是,即使這樣拆開了拿,也不一定拿到的每個字符串就是Json字符串了,而且他的key都是基本上一個字母,不知道具體的含義,很難精準的提取出需要的API,如果有朋友能提取到數據庫,可以把提取到的數據庫分享下,非常感謝

構建集合E(private_framework_dump_apis)

由於集合A已經把結構調整好了,代碼都一樣,因此只要把集合A裏面的路徑改成如下即可

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

一共547128

構建集合F (framework_private_apis)

提取集合A中所有api數據集合SET_A,並且創建一個空的私有API集合PR_API
1、遍歷SET_A,判斷api是否以_開頭,如果是加入到PR_API集合中
2、其他API既不在集合B(頭文件API集合),也不在集合C(docset API集合),那麼也加入到PR_API集合中
3、不在集合B/集合C的判斷條件是 Sql語句中的Where語句,條件是api_name,class_name,source_sdk


def api_is_exist_in_table(table_name, api_obj):
    sql = "SELECT * FROM %s WHERE api_name = ? and class_name = ? and source_sdk = ?;" % table_name
    parameters = (api_obj['api_name'], api_obj['class_name'], api_obj['source_sdk'])
    return SqliteHandler().execute_select_one(sql, params=parameters)
# 所有公有framework下的API計算如下:
    # 屬於公有11707
    # 屬於私有102266
    # 屬於私有下劃線28751
    # 去重前 - --公有庫內的私有API length:131017

這裏從公有庫中提取去來的API需要根據class_nameapi_name進行去重,Python庫itertools提供了groupby方法,專門給數組根據key進行分組,以下是groupby的例子:

案例如下,根據date排序,分組之後取出該組下第一個即可,那麼上面是根據類名和方法名分組,去重取出第一個即可
rows = [
    {'address': '5412 N CLARK', 'date': '07/01/2012'},
    {'address': '5148 N CLARK', 'date': '07/04/2012'},
    {'address': '5800 E 58TH', 'date': '07/02/2012'},
    {'address': '2122 N CLARK', 'date': '07/03/2012'},
    {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
    {'address': '1060 W ADDISON', 'date': '07/02/2012'},
    {'address': '4801 N BROADWAY', 'date': '07/01/2012'},
    {'address': '1039 W GRANVILLE', 'date': '07/04/2012'},
]
def group_by_date(obj):
    return obj['date']
x = sorted(rows, key=group_by_date)

# 打印如下
[{'address': '5412 N CLARK', 'date': '07/01/2012'},
 {'address': '4801 N BROADWAY', 'date': '07/01/2012'},
 {'address': '5800 E 58TH', 'date': '07/02/2012'},
 {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
 {'address': '1060 W ADDISON', 'date': '07/02/2012'},
 {'address': '2122 N CLARK', 'date': '07/03/2012'},
 {'address': '5148 N CLARK', 'date': '07/04/2012'},
 {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}]

y = groupby(x, group_by_date)

for g, l in y:
    print(g)
    print(list(l)

# 打印如下
07 / 01 / 2012
    [{'address': '5412 N CLARK', 'date': '07/01/2012'}
    {'address': '4801 N BROADWAY', 'date': '07/01/2012'}]
07 / 02 / 2012
    [{'address': '5800 E 58TH', 'date': '07/02/2012'},
     {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
     {'address': '1060 W ADDISON', 'date': '07/02/2012'}]
07 / 03 / 2012
    [{'address': '2122 N CLARK', 'date': '07/03/2012'}]
07 / 04 / 2012
    [{'address': '5148 N CLARK', 'date': '07/04/2012'},
     {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}]

以下根據API的class_name和api_name進行去重

# api去重 根據api_name和class_name
def deduplication_api_list(apis):
    """
    相同類名和相同方法名去重
    :param apis:
    :return:
    """

    def group_by_api(api):
        return api['api_name'] + '/' + api['class_name']

    new_apis = []

    # 先排序
    apis = sorted(apis, key=group_by_api)

    # 再根據類名和方法名成組
    for group, itr in groupby(apis, key=group_by_api):
        l = list(itr)
        if l and len(l) > 0:
            new_apis.append(l[0])

    return new_apis

去重後公有庫內的私有API還剩 128854

構建最終集合D (all_private_apis)

最終的私有API集合只要把集合F和集合E合併入庫即可,一共675982

構建完Log

******************
SET_A
142724
********************
********************
SET_B
21551
*******************
********************
SET_C
32150
********************
********************
SET_E
547128
********************
********************
所有公有framework下的API計算如下:
屬於公有11707
屬於私有102266
屬於私有下劃線28751
********************
去重前---公有庫內的私有API length:131017
start group by....
去重後----公有庫內的私有API length:128854
公有庫下的私有API插入最終集合---all_private_apis---128854
私有庫API集合取出插入最終集合---all_private_apis---547128
SET_D  
675982
********************
SET_F
公有庫下的私有API插入獨立集合F集合---framework_private_apis---128854
********************

構建完的數據庫mkj_private_apis.db放在了雲盤,不想自己構建的可以下載下來放進根目錄

掃描私有API

1.解壓ipa,提取Mach-O

1、用zipfile解壓項目到tmp目錄下
2、首先得安裝macholib庫,通過python -mmacholib find xxxxxx掃描路徑下的可執行文件,默認掃描到的是數組,提取出第一個就是項目可執行文件。
3、strings去獲取可執行文件下可打印字符。strings主要是獲取非文本文件中包含的文本內容,用\n去分割成集合用set類型去表示集合1
4、otool -L 提取項目中用到的依賴庫PublicFramework和PrivateFramework
5、class_dump從Mach-O文件中導出頭文件信息,解析出類名,協議和變量名一樣用set類型集合2
6、集合1 - 集合2,由於是set類型,可以通過減法進行過濾得到集合3
7、class-dump的分析結果通過正則匹配到方法Method集合4
8、步驟4拿到的PublicFramework作爲sql語句的條件查詢SET_D(all_private_apis表),如果有白名單的話,再把白名單的私有API過濾,得到最終該掃描項目用到的框架裏面的私有API集合5

# 從SET_D 私有API庫裏面查找api_name 而且framework不屬於參數,而且不在白名單裏面
def get_private_api_list(framework=None):
    framework_str = _get_sql_in_strings(framework) # in frameworks
    private_db_name = db_names["SET_D"]
    white_list_containers = _get_white_lists_results()
    # 有frame過濾條件s
    if framework_str:
        sql = "select * from %s group by api_name, class_name having source_framework in "%(private_db_name) + framework_str + " and api_name not in " + white_list_containers + ";"
        params = ()
    else:
        sql = "select * from %s group by api_name, class_name having api_name not in "%(private_db_name) + white_list_containers + ";"
        params = ()
    private_apis = SqliteHandler().execute_select(sql, params)
    print(sql)
    return private_apis

9、集合3 和 集合5取交集,判斷集合5中的api_name是否在集合3裏面,把在的重新生成一個集合6,這裏集合3可以理解爲剩餘字符串的API關鍵字,如果和集合5私有API集合有交集,那麼就暫且認爲是有可能出現的私有API,統一爲集合6
10、遍歷集合6,和集合4產生交集,由於4和6都是有api_name,class_name等詳細信息的集合,因此最終根據這兩個值爲Key產生的交集,纔算的上真正的私有API調用,存在就是私有API,不存在就不是私有API。

掃描結果舉例

**************************************************
App可見Strings : 14634
App協議,變量屬性 : 3954
剩下的字符串--->  String -  App協議,變量屬性  : 11640
App方法名app_methods: 4088
App用的Public對應的private apis length :15125
**************************************************
strings剩餘可見字符串關鍵字和Publick對應的私有API集合交集後的私有API--->347
**************************************************
最終API掃描結果
method_in_app:0
method_not_in_app:347
private framework:0
**************************************************

## 使用方式和流程 上面介紹瞭如何構建私有API庫,還有很多不足,蒐集可能不全面。還介紹了用入庫的私有API如何進行ipa掃描,下面介紹下如何用Python3 + Django環境去使用。

1.構建私有API數據庫

如果用現成的跑完的數據庫,可以從這裏下載,放在雲盤了(晚上放),這裏面的表名對應的用途上面有介紹。

如果你會自己編譯入庫,就在config.py文件中找到sdks_configs,配置對應的路徑地址,framework_pathprivate_framework_path對應的是Framework可執行文件的路徑,framework_header_path頭文件路徑,前者需要自己dump,後者可以直接用,具體怎麼找路徑可以在上面構建集合A找到,docset_path路徑可以自己下載setdoc文件,上面構建集合C也是下載下來找到db文件的路徑,setdoc文件也是放在雲盤了(晚上放),可以自己下下來,試着跑腳本入庫。

最終會在項目根目錄多一個數據庫文件,用於掃描
在這裏插入圖片描述

2.虛擬環境配置

virtualenv方式

1.進入項目文件夾,用virtualenv創建虛擬環境,沒有該工具用pip install virtualenv / pip3 install virtualenv 安裝

2.virtualenv venv

3.virtualenv -p /usr/local/bin/python3 venv # 創建3的環境

4.pip install -r requirements.txt # 虛擬環境導入依賴

5.. venv/bin/activate # 啓動虛擬環境

Pycharm方式

1.下載項目下來,用Pycharm打開,然後點擊Pycharm — Preference — Project — Project Interpreter配置虛擬環境

2.點擊右邊的齒輪,選擇add,Virtualenv Environment — New Environment 默認確定即可

3.打開Pycharm下面的Terminal,進入虛擬環境,安裝依賴包

4.安裝 pip install -r requirements.txt

5.然後build_apis_db.py文件可以單獨跑,就會在項目主目錄下生成一個tmp文件夾生成對應frameworkdump之後的頭文件

6.最後自動會正則這些頭文件,然後寫入mkj_private_apis.db對應的表中進行後續匹配

3.直接腳本運行

把下載好的db文件或者自己編譯好的db文件如上面所示出現在根目錄,然後打開check_private_apis.py文件,修改main函數裏面的chech_multi(path)的參數,對應的path就是ipa文件所在目錄,腳本會批量掃描目錄下所有ipa並輸出Excel,可以在項目tmp目錄中找到生成的Excel文件
在這裏插入圖片描述
當然,這裏的私有API都是舉例測試用的,這裏的各種信息是掃描ipa包裏面的plist文件和mobileprovision文件出來的檢查ipa信息工具,下面的私有API就是根據我們拋出來的數據庫比對出來的,正常情況下是無信息的,需要再完善下,如果掃到了也需要人工干預確認下。

4.Django本地環境運行

Django不熟悉的可以看看另一個文章虛擬環境啓動Django的Hello World
上面已經安裝配置好了Django的運行環境,安裝好了所有依賴,依然cd到項目根目錄,然後執行啓動虛擬環境

. venv/bin/activate
python private_apis_app/manage.py runserver

啓動信息如下
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

July 10, 2019 - 11:14:01
Django version 2.2.3, using settings 'private_apis_app.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

這裏Django會有紅色的警告,告訴你數據庫沒遷移,不過我們先用不到,可以無視他,然後打開http://127.0.0.1:8000/check/,直接把ipa包拖入頁面區域,然後等跑數據,最終也會出現在頁面上
在這裏插入圖片描述

項目地址:
Python3私有API掃描工具
ipa信息掃描工具

參考文章:
iOS 私有API掃描總結
iOS 私有 API 調用檢測機制探討
iOS 私有API獲取
Docsets問題
應用安全審計
How do I check where my app is using IDFA
私有API-iOS10 openURL方法跳轉到設置界面失效的解決方法
私有API平安好房的大佬總結
Django和Flask入門
Django備忘錄
靜態掃描Git庫
RuntimeBrowser庫,所有API集合
xlswriter
python -m mod
utf-8 can’t decode byte…的解決方法
mysql excutemany
otool 用途

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章