總所周知Apple會對其上架的App進行審核,其中包含敏感詞與私有API的檢查。常規(guī)做法就是獲取到敏感詞并將其混淆。
比如MiniProgram、小程序等字樣會被列入App審核的黑名單當中,極有可能會被拒審。日常開發(fā)中需要盡可能規(guī)避類似字樣,但并非所有類似字樣會被檢查到。提交Apple審核的是ipa包而非源碼,其中有很多信息都被編譯器優(yōu)化,如臨時變量名、注釋等信息。故避開會打入ipa包內部的消息就可以成功規(guī)避審核的風險。
ipa包中具體文件描述可自行搜索學習,大致可分為資源文件與可執(zhí)行二進制文件。資源文件中圖片、視頻等非可文本化文件不進行檢查,可文本化文件通過grep可以進行掃描與定位。本文主要講解怎么在可執(zhí)行二進制文件(Mach-O)中檢查敏感詞,有人可能會問直接通過string將Mach-O文本化再grep一下就行了,何必大動干戈。string與grep的組合確實能一定程度上檢查當前Mach-O中是否包含敏感詞,但無法檢查中文、代碼段中的消息。同時對于一個龐大的Mach-O文件,只檢查出是否含有敏感詞,而不告知敏感詞是類名、方法名還是常量,也不告知屬于那個類或哪個文件。這種行為與只告知你app crash了而沒有提供crash日志一樣都屬于耍流氓。
出于對中文、代碼段檢查的支持與敏感詞的描述、定位這兩個目的,本文主要講解如何解析Mach-O中的信息,對Mach-O想要詳細了解的朋友可自行搜索學習。
Mach-O主要結構:
- Header部分:描述文件基本信息(如CPU、架構、文件類型、加載命令個數(shù))
- loadCommands部分:描述各個Data部分的內存分布,對系統(tǒng)內核加載器和動態(tài)連接器起指導作用
- Data部分:存放代碼與數(shù)據(jù)
- __TEXT 段: 包含了執(zhí)行代碼以及其他只讀數(shù)據(jù)
- __DATA段: 包含了程序數(shù)據(jù),該段可寫;
- __LINKEDIT段: 含有為動態(tài)鏈接庫使用的原始數(shù)據(jù),比如符號,字符串,重定位表條目等等。
前兩部分都是描述信息與加載信息,核心內容都在Data中,按難易程度分別進行以下檢查。
- 常量檢查(__TEXT與__LINKEDIT段)
- 聲明檢查(__DATA段)
- 實現(xiàn)檢查(__TEXT段)
常量檢查
檢查代碼中定義的常量,其中包含英文常量與中文常量
- 英文常量存在于
__TEXT -> __cstring或__DATA -> __cfstringsection中,該信息通過otool <binary path> -v -s <seg> <sec>即可獲取 - 中文常量存在于
__TEXT -> __ustringsection中,但由于中文unicode編碼形式存儲,故otool只能以二進制的形式展示。需自行解析:
unicode:國家標準化組織定義的,包含了世界上所有的的字符,采用兩個字節(jié)表示一個字符(即16位)
ARM處理器用到的指令集分為 ARM 和 THUMB 兩種。ARM指令長度固定為32bit,THUMB指令長度固定為16bit,所以 ARM64指令集的指令長度為32bit。
考慮otool會將信息簡化輸出,即去除多余的0,需將otool讀取到的unicode數(shù)據(jù)按32bit進行補齊,再根據(jù)架構進行高低位顛倒(armv7與arm64需前16位與后16位顛倒,x86_64需前8位與后8位顛倒)才能讀取出原本的中文常量。
聲明檢查
檢查類定義、方法定義中是否包含敏感詞,解析__TEXT -> _objc_classname與__TEXT -> _objc_methnamesection中的信息即可獲。這為了不重復造輪子,使用前人提供的class-dump將所有的聲明信息導出,并通過grep進行查找即可。
實現(xiàn)檢查
檢查__TEXT -> __text代碼實現(xiàn)是否包含敏感詞,這并非是檢查全部的源碼實現(xiàn),畢竟通過ipa包無法還原全源碼,這里檢查的是通過Mach-O文件中現(xiàn)有的信息反編譯出的偽代碼。
-
otool -tV <binary path>獲取__text中的偽代碼與對應的偏移地址 -
atos -o <binary path> < address>獲取對應偏移地址的所屬的類與方法