MachO 代碼簽名剖析

驗證代碼的正確性是計算機科學中最難的問題之一,因為不存在普遍意義的正確的算法,所以這一驗證通常使用數(shù)字簽名處理。數(shù)字簽名主要做兩部分工作:
驗證代碼的來源是否合法。 代碼是否被修改過。
代碼簽名并非蘋果獨有技術,Java 和 Android 的 Dalvik 都在使用,但蘋果公司是最早開始使用的。大家可以通過閱讀下文思考代碼來源是否合法和代碼是否被修改過的驗證是如何實現(xiàn)的。
本篇文章主要參考自 Jonathan Levin 的《最強 iOS 和 macOS 安全寶典》代碼簽名一章。
測試環(huán)境:macOS 11.2.3。 測試項目: /bin/ls?在 x86_64 架構下的 MachO 文件。iOS 下的文件與之相差不大。
代碼簽名格式
在了解代碼簽名機制前,非常有必要了解代碼簽名的包含的內容。代碼簽名附著在 MachO 的尾部。加載命令為LC_CODE_SIGNATURE,它指向一個超級二進制塊Code Signature,該二進制塊又包含了多個其他的子二進制塊。之前寫過一篇文章,有講解如何手動解析這個簽名二進制塊:深入理解MachO數(shù)據(jù)解析規(guī)則。
下面是該二進制塊的層級結構:

超級二進制塊是一個目錄性質的結構,用于指定子二進制塊的位置,各個子二進制塊才是代碼簽名的主要角色。
子二進制塊類型
子二進制塊類型通過不同值進行表示:
| 值 | 二進制塊類型 |
|---|---|
| 0x0000 | 代碼目錄 |
| 0x0002 | 需求 |
| 0x0005 | 授權 |
| 0x10000 | CMS 二進制塊 |
| 0x10001 | 身份證明(未使用) |
本篇主要就是對這幾個子二進制塊的功能和部分實現(xiàn)進行分析。
二進制塊的提取
jtool 是 Jonathan Levin 開發(fā)的一款主要用于 MachO 分析的高效工具,可以使用 homebrew 進行安裝。
$?brew?install?jtool
我們可以使用 jtool 單獨提取代碼簽名部分:
$?jtool?-arch?x86_64?-e?signature?/bin/ls
Extracting?Code?Signature?(5728?bytes)?into?ls.signature
$?od?-t?x1?-A?x?ls.signature?#原始字節(jié)內容
0000000????fa??de??0c??c0??00??00??14??86??00??00??00??03??00??00??00??00
0000010????00??00??00??24??00??00??00??02??00??00??02??61??00??01??00??00
0000020????00??00??02??9d??fa??de??0c??02??00??00??02??3d??00??02??01??00
0000030????00??00??00??00??00??00??00??7d??00??00??00??30??00??00??00??02
0000040????00??00??00??0e??00??00??d2??30??20??02??0b??0c??00??00??00??00
0000050????00??00??00??00??63??6f??6d??2e??61??70??70??6c??65??2e??6c??73
#?...
也可以使用 MachO 找到 Code Signature 塊進行查看。
代碼簽名的子二進制塊
我們可以使用 jtool 查看代碼簽名內容的分析:
$?jtool?-arch?x86_64?--sig?-v?/bin/ls
Blob?at?offset:?53808?(5728?bytes)?is?an?embedded?signature?of?5254?bytes,?and?3?blobs
?Blob?0:?Type:?0?@36:?Code?Directory?(573?bytes)
??Version:?????20100
??Flags:???????none?(0x0)
??Platform?Binary
??CodeLimit:???0xd230
??Identifier:??com.apple.ls?(0x30)
??CDHash:??????46cc1da7c874a5853984a286ffecb48daf2f65f023d10258a31118acfc8a3697?(computed)
??#?of?Hashes:?14?code?+?2?special
??Hashes?@125?size:?32?Type:?SHA-256
???Requirements?blob:?a8ccc60c2a5bff15805beb8687c6a899db386d964a5eb3cf3c895753f6879cea?(OK)
???Bound?Info.plist:?Not?Bound
???Slot???0?(File?page?@0x0000):?e4a537939e00f4974e02b03d36e4dab75f7dc095d2214ba66bc53c73c145ceff?(OK)
???Slot???1?(File?page?@0x1000):?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7?(OK)
???Slot???2?(File?page?@0x2000):?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7?(OK)
???Slot???3?(File?page?@0x3000):?4a7cb3e6c1b3a6ac82e3575239ee53d4f0d3bed260fed63438fd21ce0d00392e?(OK)
???Slot???4?(File?page?@0x4000):?9ec9e4e02292dfda34ef3caa8317e8bfbcc41a46b18d994dba45febe31b8c660?(OK)
???Slot???5?(File?page?@0x5000):?037285f744f366210cde48821261d4a5f5b739dcf0b82f94144613e92c4b7c07?(OK)
???Slot???6?(File?page?@0x6000):?be89c764e52382702918f2db62ff24d9df40410fe894b11d505a4abc1f854340?(OK)
???Slot???7?(File?page?@0x7000):?a6b322014743965656e796155c1e0bf22e19a3e8770a43f1111cfbc961037d26?(OK)
???Slot???8?(File?page?@0x8000):?a643fc9485d941019cbdeead1d5c47add9382417ebe4d15768221f3763553b84?(OK)
???Slot???9?(File?page?@0x9000):?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7?(OK)
???Slot??10?(File?page?@0xa000):?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7?(OK)
???Slot??11?(File?page?@0xb000):?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7?(OK)
???Slot??12?(File?page?@0xc000):?23304ae11c1ade4411cb63a0955eb644574b8af416e4e3818e382421272ae1b4?(OK)
???Slot??13?(File?page?@0xd000):?e0ca7b7000d04057e71c49365b1937711b3557f6b91e0fa144791c66de2a7a4d?(OK)
?Blob?1:?Type:?2?@609:?Requirement?Set?(60?bytes)?with?1?requirement:
?0:?Designated?Requirement?(@20,?28?bytes):?SIZE:?28
??Ident:?(com.apple.ls)?AND?Apple?Anchor
?Blob?2:?Type:?10000?@669:?Blob?Wrapper?(4585?bytes)?(0x10000?is?CMS?(RFC3852)?signature)
CA:?Apple?Certification?Authority?CN:?Apple?Root?CA
CA:?Apple?Certification?Authority?CN:?Apple?Code?Signing?Certification?Authority
CA:?Apple?Certification?Authority?CN:?Apple?Root?CA
CA:?Apple?Certification?Authority?CN:?Apple?Root?CA
CA:?Apple?Certification?Authority?CN:?Apple?Code?Signing?Certification?Authority
CA:?Apple?Software?CN:?Software?Signing
Time:?201222002625Zi
它有三個 Blob,即三個子二進制塊,Blob 0 是代碼簽名 Blob 1是需求,Blob 2 是 CMS,下面是對這幾個 Blob 的分析。
代碼目錄(Code Directory)
代碼目錄是簽名塊的主體,它提供了簽名資源的散列值(哈希值)。代碼簽名并非對整個文件進行簽名,因為有時二進制文件可能很大,計算全部內容占用資源較多;而且二進制的加載是按需加載,不會一開始就都全部映射到內存中。簽名時會將整個 MachO 文件劃分成多個頁,每個頁單獨簽名。
代碼目錄部分就是對簽名信息的描述,其中包含了各個分頁的簽名值,簽名算法和分頁大小等內容。代碼簽名的數(shù)據(jù)結構如下:
/*
?*?C?form?of?a?CodeDirectory.
?*/
typedef?struct?__CodeDirectory?{
?uint32_t?magic;?????/*?magic?number?(CSMAGIC_CODEDIRECTORY)?*/
?uint32_t?length;????/*?total?length?of?CodeDirectory?blob?*/
?uint32_t?version;????/*?compatibility?version?*/
?uint32_t?flags;?????/*?setup?and?mode?flags?*/
?uint32_t?hashOffset;???/*?offset?of?hash?slot?element?at?index?zero?*/
?uint32_t?identOffset;???/*?offset?of?identifier?string?*/
?uint32_t?nSpecialSlots;???/*?number?of?special?hash?slots?*/
?uint32_t?nCodeSlots;???/*?number?of?ordinary?(code)?hash?slots?*/
?uint32_t?codeLimit;????/*?limit?to?main?image?signature?range?*/
?uint8_t?hashSize;????/*?size?of?each?hash?in?bytes?*/
?uint8_t?hashType;????/*?type?of?hash?(cdHashType*?constants)?*/
?uint8_t?platform;????/*?platform?identifier;?zero?if?not?platform?binary?*/
?uint8_t?pageSize;????/*?log2(page?size?in?bytes);?0?=>?infinite?*/
?uint32_t?spare2;????/*?unused?(must?be?zero)?*/
?/*?Version?0x20100?*/
?uint32_t?scatterOffset;????/*?offset?of?optional?scatter?vector?*/
?/*?Version?0x20200?*/
?uint32_t?teamOffset;????/*?offset?of?optional?team?identifier?*/
?/*?followed?by?dynamic?content?as?located?by?offset?fields?above?*/
}?CS_CodeDirectory;
結合 CodeDirectory 的偏移量,可以從 MachOView 里查看到這部分數(shù)據(jù)的內容:

找到對應數(shù)據(jù)結構中的含義,我們關注其中三個 uint8_t 類型的值:
| 參數(shù) | 值 | 含義 |
|---|---|---|
| hashSize | 0x20 | hash 值大小,為 0x20 字節(jié)。 |
| hashType | 0x02 | 表示簽名算法,0x01 表示 SHA-1,0x02表示SHA-256。從 macOS10.12 和 iOS11開始,蘋果轉向使用 SHA-256。 |
| pageSize | 0x0C | 這里是一個計算公式:log2(PageSize) = 0x0C |
根據(jù)公式算出分頁大小:PageSize = 2 ^ 0x0C = 4096 = 0x1000 = 4K。這跟系統(tǒng)的內存分頁大小是一致的。
由此可知整個 MachO 文件會按照 0x1000 字節(jié)的大小進行分頁,分頁使用 SHA-256 算出散列值。這些計算出的散列值會記錄在代碼插槽(Code Slots)里。
代碼插槽驗證
上面Slot 從 0 到 13 的標記對應的都是代碼插槽。
有了計算規(guī)則我們還可以手動驗證代碼簽名的正確性,我們以前三個代碼插槽為例,也即前 0x1000 字節(jié)的內容,嘗試手動計算其散列值。
$?lipo?/bin/ls?-thin?x86_64?-output?/tmp/ls_x86_64
$?dd?bs=0x1000?skip=0?count=1?if=/tmp/ls_x86_64?2>/dev/null?|?openssl?sha256
SHA256(stdin)=?e4a537939e00f4974e02b03d36e4dab75f7dc095d2214ba66bc53c73c145ceff
$?dd?bs=0x1000?skip=1?count=1?if=/Users/zhangferry/ls_x86_64?2>/dev/null?|?openssl?sha256
SHA256(stdin)=?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7
$?dd?bs=0x1000?skip=2?count=1?if=/Users/zhangferry/ls_x86_64?2>/dev/null?|?openssl?sha256
SHA256(stdin)=?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7
注意到后面兩個插槽計算結果一樣,這是因為這兩部分數(shù)據(jù)為補齊位,它們全部為0。
跟前三個代碼插槽的值進行對比:
Slot???0?(File?page?@0x0000):?e4a537939e00f4974e02b03d36e4dab75f7dc095d2214ba66bc53c73c145ceff?(OK)
Slot???1?(File?page?@0x1000):?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7?(OK)
Slot???2?(File?page?@0x2000):?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7?(OK)
發(fā)現(xiàn)兩邊散列值一樣,輸出內容后面的 OK 是 jtool 驗證的結果。
這里可以看到代碼插槽有14個,F(xiàn)ile page 里的內容表示相對起始地址。另外注意到輸出部分有一句注釋:
#?of?Hashes:?14?code?+?2?special
其表示有 14 個代碼插槽和 2 個特殊插槽。
特殊插槽
特殊插槽的出現(xiàn)是因為App由多個內容組成,并非只有二進制文件,為了保證這些非二進制文件的完整性,對它們也會進行簽名,它們的簽名值就是特殊插槽。因為代碼插槽的索引是從0開始的,而且其大小不固定,為了把特殊插槽也能排列進去,就選用負數(shù)來表示特殊插槽的含義。以下是特殊插槽的定義:
| # | 插槽目的 |
|---|---|
| -1 | 綁定的info.plist |
| -2 | 需求(requirement):二進制塊嵌入代碼簽名 |
| -3 | 資源目錄:CodeSignature/CodeResources文件的散列值 |
| -4 | 具體應用:實際上未被使用 |
| -5 | 授權(entitlement):嵌入在代碼簽名中的授權 |
我們可以在上方 jtool 的輸出內容里找到特殊插槽的內容:
Requirements?blob:?a8ccc60c2a5bff15805beb8687c6a899db386d964a5eb3cf3c895753f6879cea?(OK)
Bound?Info.plist:?Not?Bound
因為特殊插槽作用是固定的,也就沒用序號表示。
代碼簽名需求(Requirements)
目前代碼簽名只是分塊取散列值,保存起來,但好像還不夠強大。蘋果公司已經(jīng)為代碼簽名增加了另外一個機制:需求(requirements)。它可以自定義規(guī)則以施加特定限制,比如允許哪些動態(tài)庫加載。
需求有一套特殊的語法規(guī)則,其表達由操作數(shù)和操作碼組成,豐富的操作碼集使得構建任何數(shù)量的邏輯條件成為可能。可以在requirements.h[1] 文件里查看都有哪些操作碼。
enum?ExprOp?{
?opFalse,??????//?unconditionally?false
?opTrue,???????//?unconditionally?true
?opIdent,??????//?match?canonical?code?[string]
?opAppleAnchor,?????//?signed?by?Apple?as?Apple's?product
?opAnchorHash,?????//?match?anchor?[cert?hash]
?opInfoKeyValue,?????//?*legacy*?match?Info.plist?field?[key;?value]
?opAnd,???????//?binary?prefix?expr?AND?expr
?opOr,???????//?binary?prefix?expr?OR?expr
?opCDHash,??????//?match?hash?of?CodeDirectory?directly
?opNot,???????//?logical?inverse
?opInfoKeyField,?????//?Info.plist?key?field?[string;?match?suffix]
?opCertField,?????//?Certificate?field?[cert?index;?field?name;?match?suffix]
?opTrustedCert,?????//?require?trust?settings?to?approve?one?particular?cert?[cert?index]
?opTrustedCerts,?????//?require?trust?settings?to?approve?the?cert?chain
?opCertGeneric,?????//?Certificate?component?by?OID?[cert?index;?oid;?match?suffix]
?opAppleGenericAnchor,???//?signed?by?Apple?in?any?capacity
?opEntitlementField,????//?entitlement?dictionary?field?[string;?match?suffix]
?exprOpCount??????//?(total?opcode?count?in?use)
};
對需求的編譯是由 csreq 進行的,對需求的驗證可以使用 codesign -v。
我們這里來嘗試解讀下已有的需求內容。
大部分二進制文件的需求只是驗證簽名身份,即使用證書是否為蘋果所頒發(fā)。在 App Store 里的應用則使用更嚴格的規(guī)則集。我們可以查看 Xcode 的代碼簽名需求:
$?codesign?-d?-r-?/Applications/Xcode.app/Contents/MacOS/Xcode
Executable=/Applications/Xcode.app/Contents/MacOS/Xcode
designated?=>?(anchor?apple?generic?and?certificate?leaf[field.1.2.840.113635.100.6.1.9]?/*?exists?*/?or?anchor?apple?generic?and?certificate?1[field.1.2.840.113635.100.6.2.6]?/*?exists?*/?and?certificate?leaf[field.1.2.840.113635.100.6.1.13]?/*?exists?*/?and?certificate?leaf[subject.OU]?=?APPLECOMPUTER)?and?identifier?"com.apple.dt.Xcode"
注意這里有不少 1.2.840.113635 開頭的標識,它代表的是國際通用標準證書中蘋果公司的分支(iso.member-body.us.appleOID)。其中100對應了安全相關的一些定義 appleDataSecurity,詳細內容可以看這里oidref.com[2]。

對照Xcode的簽名需求,我們可以大致推斷出這些規(guī)則的含義:
由蘋果簽名且證書節(jié)點包含 6.1.9 即 Mac App Store App。 或由蘋果簽名且證書節(jié)點包含 6.2.6 即 "dev_program"。(推測是開發(fā)版本的應用) 其證書節(jié)點包含 6.1.13 即 Developer ID Applications。 證書的團隊標識符(OU)為 APPLECOMPUTER 且 BundleId 為 com.apple.dt.Xcode。
注意其中最后一項的內容,限定了團隊標識符和BundleId,這樣就能夠解決應用被重簽名的問題了。
CMS
CMS 是Cryptographic Message Syntax的縮寫,是一種標準的簽名格式,由RFC3852[3]定義。書中并沒有提這部分內容,但我認為這部分恰恰是代碼簽名最關鍵的步驟。
CMS 格式的簽名中,除了包含證書之外,還承載了一些其他的信息,比如簽名屬性 signedAttrs。
上面說了 CodeDirectory 里保存了 MachO 分頁的 Hash 值,只要保證這個 CodeDirectory 不被修改就可以了。所以對代碼目錄進行 Hash 計算,獲得 CDHash,然后對這個 CDHash 進行簽名就可以了。
注意這一步才是真正的簽名,其開始涉及加密,前面的代碼插槽只是提供摘要信息。
注意到 jtool 的簽名輸出里有這樣一句:
CDHash:??????46cc1da7c874a5853984a286ffecb48daf2f65f023d10258a31118acfc8a3697?(computed)
這就是外部計算的 CDHash 值,用于跟 signedAttrs 里的內容進行對比。而更關鍵的是對 signedAttrs 的加密驗證,實際驗證流程比較復雜,感興趣的小伙伴可以閱讀這篇細說iOS代碼簽名(三)[4]。
我結合文中簽名校驗內容和上面的代碼插槽,畫出了表示簽名校驗的整個流程:

這里有兩處 Hash 對比,一個是對 signedAttrs 的解密,確保其是可信任的。另一處是 CDHash 的對比,確保代碼未被修改。
signerInfo 里包含了 signedAttrs 、簽名使用的 Hash 算法、加密算法、簽名數(shù)據(jù)等信息。再結合證書里的公鑰,我們就可以驗證,signedAttrs 的有效性。
授權
除了確保代碼的真實性和完整性,代碼簽名還為蘋果公司及其強大的安全機制提供了授權(entitlement)功能。授權文件也被包含在簽名里,其散列值放在索引為-5的插槽中。授權文件是一個 XML 格式的文件,我們可以使用 jtool --ent 查看其內容,因為 ls 沒有授權文件,我們以 Mac 端微信為例進行查看:
$?jtool?-arch?x86_64?--ent?/Applications/WeChat.app/Contents/MacOS/WeChat
"1.0"?encoding="UTF-8"?>
"-//Apple//DTD?PLIST?1.0//EN"?"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
"1.0">
?com.apple.security.app-sandbox
?<true/>
?com.apple.security.application-groups
?
??5A4RE8SF68.com.tencent.xinWeChat
?
?com.apple.security.device.audio-input
?<true/>
?com.apple.security.device.camera
?<true/>
?com.apple.security.device.microphone
?<true/>
?com.apple.security.files.downloads.read-write
?<true/>
?com.apple.security.files.user-selected.read-write
?<true/>
?com.apple.security.network.client
?<true/>
?com.apple.security.network.server
?<true/>
?com.apple.security.personal-information.location
?<true/>
這里我們可以看到其中包含了沙盒、application-groups、聲音輸入、攝像頭等一系列權限。在應用訪問特定 API 的時候蘋果可以根據(jù)這些授權判定該行為是否合法。因為蘋果公司是應用的終極簽名者,所以簽名過程中也可以很容易的修改授權,比如 com.apple.security.sandbox.container-required 這一表示沙盒權限的值就會被強制安置到授權文件中。
強制驗證代碼簽名
為了使代碼簽名真正有效,非常重要的一步就是要保證驗證過程順利執(zhí)行,沒有遺漏。當前簽名驗證是發(fā)生在內核模式下,而非用戶模式下。簽名的驗證發(fā)生在兩個階段:加載可執(zhí)行文件時、實際訪問二進制代碼時(Page Fault)。分成兩個階段也是出于性能方面的考慮,因為二進制文件是動態(tài)加載的,對于還沒加載的部分僅當其加載如內存時,也即發(fā)生 Page Fault 時再進行簽名驗證。
可執(zhí)行文件的加載
可執(zhí)行文件的加載出現(xiàn)在execve()/mac_execve()或posix_spawn()系統(tǒng)調用被觸發(fā)的時候。對于MachO,exec_mach_imgact()會被調用,在解析文件時它會找到LC_CODE_SIGNATURE的位置。代碼簽名二進制塊會被加載到內核的統(tǒng)一高速緩存緩沖區(qū)中。
Page Fault時的處理
可以查看 osfmk/vm/vm_fault.c[5] 的代碼:
/*
?*?CODE?SIGNING:
?*?When?soft?faulting?a?page,?we?have?to?validate?the?page?if:
?*?1.?the?page?is?being?mapped?in?user?space
?*?2.?the?page?hasn't?already?been?found?to?be?"tainted"
?*?3.?the?page?belongs?to?a?code-signed?object
?*?4.?the?page?has?not?been?validated?yet?or?has?been?mapped?for?write.
?*/
#define?VM_FAULT_NEED_CS_VALIDATION(pmap,?page,?page_obj)??\
?((pmap)?!=?kernel_pmap?/*1*/?&&?????\
??!(page)->cs_tainted?/*2*/?&&?????\
??(page_obj)->code_signed?/*3*/?&&?????\
??(!(page)->cs_validated?||?(page)->wpmapped?/*4*/))
當 Page Fault 滿足以上條件時將觸發(fā)簽名驗證過程:
1、該頁面正在用戶空間中映射
2、這個頁面還沒有被發(fā)現(xiàn)為 tainted
3、該頁屬于一個代碼簽名對象
4、頁面還沒有被驗證,或者還沒有被映射為可寫狀態(tài)
代碼簽名的漏洞
代碼簽名機制雖然強大,保護著應用的安全,但依然被攻破過,以下講解幾例曾經(jīng)出現(xiàn)的漏洞。
JIT(即時生成代碼)
該情況發(fā)生在 Page Fault 過程,如果該頁內容是用于 JIT,將會被特殊標記,可以創(chuàng)建和執(zhí)行任意代碼,而無需代碼簽名。
從 iOS 10 開始,蘋果公司開始在64位的設備上加固 JIT。采用專門的 memcpy() 將JIT映射到可執(zhí)行但不可讀的內存上,然后可執(zhí)行的 JIT 映射為不可寫,可寫的 JIT 映射為不可執(zhí)行狀態(tài)。
Jekyll 應用
Jekyll 應用的含義是應用在提交至 App Store 時表現(xiàn)為無害,但其實它包含惡意功能,只不過這些功能處于休眠狀態(tài)。過審之后和本地服務器進行合作,自愿公開其地址空間和符號,通過代碼注入或者返回導向編程(Return Oriented Programming,ROP),觸發(fā)預置的惡意程序。
目前還沒有可靠的打擊 ROP 的方法,但因為沙盒機制的緣故,惡意代碼的影響范圍是可控的。
蘋果公司使用 LLVM BitCode 向 App Store 提交應用的方案,也會使惡意應用難以事先知曉其地址空間。
內存鎖定
從上面我們知道發(fā)生Page Fault會觸發(fā)簽名驗證的流程,那如果沒有Page Fault就不會存在簽名驗證了。按照mmap -> mlock -> memcpy -> mprotect 的調用順序,應用可以修改可執(zhí)行內存,以任何看起來合適的方式修補內存。雖然XNU通常會阻止將曾經(jīng)可寫的內存設置為r-x,但當內存鎖定時,會繞過該檢測。
蘋果在iOS 9.3中修復了這個漏洞。
總結
我們再來嘗試回答開頭上面遺留的問題:
1、如何驗證代碼的來源是否合法?
主要通過證書來驗證來源是否合法,所有的開發(fā)者證書都由蘋果頒發(fā),且被 Root CA 認證。另外依托于需求(requirements),還可以再擴展一些其他驗證方式。
2、如何確認代碼是否被修改過。
主要通過代碼插槽和 CDHash,再對 CDHash 進行簽名,就可確認其是否被修改過。注意實際驗證流程有兩處關鍵的 Hash 比對,可以再結合上面的流程圖加深理解。
引用資料
requirements.h: https://opensource.apple.com/source/libsecurity_codesigning/libsecurity_codesigning-36591/lib/requirement.h.auto.html
[2]oidref.com: http://oidref.com/1.2.840.113635.100
[3]RFC3852: https://www.ietf.org/rfc/rfc3852.txt
[4]細說iOS代碼簽名(三): http://xelz.info/blog/2019/01/11/ios-code-signature-3/
[5]osfmk/vm/vm_fault.c: https://opensource.apple.com/source/xnu/xnu-4570.71.2/osfmk/vm/vm_fault.c.auto.html
