<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          MachO 代碼簽名剖析

          共 13936字,需瀏覽 28分鐘

           ·

          2021-11-29 14:37

          ????關(guān)注后回復(fù)?“進(jìn)群”?,拉你進(jìn)程序員交流群????


          作者丨zhangferry

          來源丨iOS成長之路

          驗(yàn)證代碼的正確性是計算機(jī)科學(xué)中最難的問題之一,因?yàn)椴淮嬖谄毡橐饬x的正確的算法,所以這一驗(yàn)證通常使用數(shù)字簽名處理。數(shù)字簽名主要做兩部分工作:

          • 驗(yàn)證代碼的來源是否合法。
          • 代碼是否被修改過。

          代碼簽名并非蘋果獨(dú)有技術(shù),Java 和 Android 的 Dalvik 都在使用,但蘋果公司是最早開始使用的。大家可以通過閱讀下文思考代碼來源是否合法和代碼是否被修改過的驗(yàn)證是如何實(shí)現(xiàn)的。

          本篇文章主要參考自 Jonathan Levin 的《最強(qiáng) iOS 和 macOS 安全寶典》代碼簽名一章。

          • 測試環(huán)境:macOS 11.2.3。
          • 測試項(xiàng)目:/bin/ls ?在 x86_64 架構(gòu)下的 MachO 文件。iOS 下的文件與之相差不大。

          代碼簽名格式

          在了解代碼簽名機(jī)制前,非常有必要了解代碼簽名的包含的內(nèi)容。代碼簽名附著在 MachO 的尾部。加載命令為LC_CODE_SIGNATURE,它指向一個超級二進(jìn)制塊Code Signature,該二進(jìn)制塊又包含了多個其他的子二進(jìn)制塊。之前寫過一篇文章,有講解如何手動解析這個簽名二進(jìn)制塊:深入理解MachO數(shù)據(jù)解析規(guī)則。

          下面是該二進(jìn)制塊的層級結(jié)構(gòu):

          超級二進(jìn)制塊是一個目錄性質(zhì)的結(jié)構(gòu),用于指定子二進(jìn)制塊的位置,各個子二進(jìn)制塊才是代碼簽名的主要角色。

          子二進(jìn)制塊類型

          子二進(jìn)制塊類型通過不同值進(jìn)行表示:

          二進(jìn)制塊類型
          0x0000代碼目錄
          0x0002需求
          0x0005授權(quán)
          0x10000CMS 二進(jìn)制塊
          0x10001身份證明(未使用)

          本篇主要就是對這幾個子二進(jìn)制塊的功能和部分實(shí)現(xiàn)進(jìn)行分析。

          二進(jìn)制塊的提取

          jtool 是 Jonathan Levin 開發(fā)的一款主要用于 MachO 分析的高效工具,可以使用 homebrew 進(jìn)行安裝。

          $?brew?install?jtool

          我們可以使用 jtool 單獨(dú)提取代碼簽名部分:

          $?jtool?-arch?x86_64?-e?signature?/bin/ls
          Extracting?Code?Signature?(5728?bytes)?into?ls.signature
          $?od?-t?x1?-A?x?ls.signature?#原始字節(jié)內(nèi)容
          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 塊進(jìn)行查看。

          代碼簽名的子二進(jìn)制塊

          我們可以使用 jtool 查看代碼簽名內(nèi)容的分析:

          $?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,即三個子二進(jìn)制塊,Blob 0 是代碼簽名 Blob 1是需求,Blob 2 是 CMS,下面是對這幾個 Blob 的分析。

          代碼目錄(Code Directory)

          代碼目錄是簽名塊的主體,它提供了簽名資源的散列值(哈希值)。代碼簽名并非對整個文件進(jìn)行簽名,因?yàn)橛袝r二進(jìn)制文件可能很大,計算全部內(nèi)容占用資源較多;而且二進(jìn)制的加載是按需加載,不會一開始就都全部映射到內(nèi)存中。簽名時會將整個 MachO 文件劃分成多個頁,每個頁單獨(dú)簽名。

          代碼目錄部分就是對簽名信息的描述,其中包含了各個分頁的簽名值,簽名算法和分頁大小等內(nèi)容。代碼簽名的數(shù)據(jù)結(jié)構(gòu)如下:

          /*
          ?*?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;

          結(jié)合 CodeDirectory 的偏移量,可以從 MachOView 里查看到這部分?jǐn)?shù)據(jù)的內(nèi)容:

          找到對應(yīng)數(shù)據(jù)結(jié)構(gòu)中的含義,我們關(guān)注其中三個 uint8_t 類型的值:

          參數(shù)含義
          hashSize0x20hash 值大小,為 0x20 字節(jié)。
          hashType0x02表示簽名算法,0x01 表示 SHA-1,0x02表示SHA-256。從 macOS10.12 和 iOS11開始,蘋果轉(zhuǎn)向使用 SHA-256。
          pageSize0x0C這里是一個計算公式:log2(PageSize) = 0x0C

          根據(jù)公式算出分頁大小:PageSize = 2 ^ 0x0C = 4096 = 0x1000 = 4K。這跟系統(tǒng)的內(nèi)存分頁大小是一致的。

          由此可知整個 MachO 文件會按照 0x1000 字節(jié)的大小進(jìn)行分頁,分頁使用 SHA-256 算出散列值。這些計算出的散列值會記錄在代碼插槽(Code Slots)里。

          代碼插槽驗(yàn)證

          上面Slot 從 0 到 13 的標(biāo)記對應(yīng)的都是代碼插槽。

          有了計算規(guī)則我們還可以手動驗(yàn)證代碼簽名的正確性,我們以前三個代碼插槽為例,也即前 0x1000 字節(jié)的內(nèi)容,嘗試手動計算其散列值。

          $?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

          注意到后面兩個插槽計算結(jié)果一樣,這是因?yàn)檫@兩部分?jǐn)?shù)據(jù)為補(bǔ)齊位,它們?nèi)繛?。

          跟前三個代碼插槽的值進(jìn)行對比:

          Slot???0?(File?page?@0x0000):?e4a537939e00f4974e02b03d36e4dab75f7dc095d2214ba66bc53c73c145ceff?(OK)
          Slot???1?(File?page?@0x1000):?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7?(OK)
          Slot???2?(File?page?@0x2000):?ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7?(OK)

          發(fā)現(xiàn)兩邊散列值一樣,輸出內(nèi)容后面的 OK 是 jtool 驗(yàn)證的結(jié)果。

          這里可以看到代碼插槽有14個,F(xiàn)ile page 里的內(nèi)容表示相對起始地址。另外注意到輸出部分有一句注釋:

          #?of?Hashes:?14?code?+?2?special

          其表示有 14 個代碼插槽和 2 個特殊插槽。

          特殊插槽

          特殊插槽的出現(xiàn)是因?yàn)锳pp由多個內(nèi)容組成,并非只有二進(jìn)制文件,為了保證這些非二進(jìn)制文件的完整性,對它們也會進(jìn)行簽名,它們的簽名值就是特殊插槽。因?yàn)榇a插槽的索引是從0開始的,而且其大小不固定,為了把特殊插槽也能排列進(jìn)去,就選用負(fù)數(shù)來表示特殊插槽的含義。以下是特殊插槽的定義:

          #插槽目的
          -1綁定的info.plist
          -2需求(requirement):二進(jìn)制塊嵌入代碼簽名
          -3資源目錄:CodeSignature/CodeResources文件的散列值
          -4具體應(yīng)用:實(shí)際上未被使用
          -5授權(quán)(entitlement):嵌入在代碼簽名中的授權(quán)

          我們可以在上方 jtool 的輸出內(nèi)容里找到特殊插槽的內(nèi)容:

          Requirements?blob:?a8ccc60c2a5bff15805beb8687c6a899db386d964a5eb3cf3c895753f6879cea?(OK)
          Bound?Info.plist:?Not?Bound

          因?yàn)樘厥獠宀圩饔檬枪潭ǖ模簿蜎]用序號表示。

          代碼簽名需求(Requirements)

          目前代碼簽名只是分塊取散列值,保存起來,但好像還不夠強(qiáng)大。蘋果公司已經(jīng)為代碼簽名增加了另外一個機(jī)制:需求(requirements)。它可以自定義規(guī)則以施加特定限制,比如允許哪些動態(tài)庫加載。

          需求有一套特殊的語法規(guī)則,其表達(dá)由操作數(shù)和操作碼組成,豐富的操作碼集使得構(gòu)建任何數(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 進(jìn)行的,對需求的驗(yàn)證可以使用 codesign -v。

          我們這里來嘗試解讀下已有的需求內(nèi)容。

          大部分二進(jìn)制文件的需求只是驗(yàn)證簽名身份,即使用證書是否為蘋果所頒發(fā)。在 App Store 里的應(yīng)用則使用更嚴(yán)格的規(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 開頭的標(biāo)識,它代表的是國際通用標(biāo)準(zhǔn)證書中蘋果公司的分支(iso.member-body.us.appleOID)。其中100對應(yīng)了安全相關(guān)的一些定義 appleDataSecurity,詳細(xì)內(nèi)容可以看這里oidref.com[2]。

          對照Xcode的簽名需求,我們可以大致推斷出這些規(guī)則的含義:

          • 由蘋果簽名且證書節(jié)點(diǎn)包含 6.1.9 即 Mac App Store App。
          • 或由蘋果簽名且證書節(jié)點(diǎn)包含 6.2.6 即 "dev_program"。(推測是開發(fā)版本的應(yīng)用)
          • 其證書節(jié)點(diǎn)包含 6.1.13 即 Developer ID Applications。
          • 證書的團(tuán)隊標(biāo)識符(OU)為 APPLECOMPUTER 且 BundleId 為 com.apple.dt.Xcode。

          注意其中最后一項(xiàng)的內(nèi)容,限定了團(tuán)隊標(biāo)識符和BundleId,這樣就能夠解決應(yīng)用被重簽名的問題了。

          CMS

          CMS 是Cryptographic Message Syntax的縮寫,是一種標(biāo)準(zhǔn)的簽名格式,由RFC3852[3]定義。書中并沒有提這部分內(nèi)容,但我認(rèn)為這部分恰恰是代碼簽名最關(guān)鍵的步驟。

          CMS 格式的簽名中,除了包含證書之外,還承載了一些其他的信息,比如簽名屬性 signedAttrs

          上面說了 CodeDirectory 里保存了 MachO 分頁的 Hash 值,只要保證這個 CodeDirectory 不被修改就可以了。所以對代碼目錄進(jìn)行 Hash 計算,獲得 CDHash,然后對這個 CDHash 進(jìn)行簽名就可以了。

          注意這一步才是真正的簽名,其開始涉及加密,前面的代碼插槽只是提供摘要信息。

          注意到 jtool 的簽名輸出里有這樣一句:

          CDHash:??????46cc1da7c874a5853984a286ffecb48daf2f65f023d10258a31118acfc8a3697?(computed)

          這就是外部計算的 CDHash 值,用于跟 signedAttrs 里的內(nèi)容進(jìn)行對比。而更關(guān)鍵的是對 signedAttrs 的加密驗(yàn)證,實(shí)際驗(yàn)證流程比較復(fù)雜,感興趣的小伙伴可以閱讀這篇細(xì)說iOS代碼簽名(三)[4]。

          我結(jié)合文中簽名校驗(yàn)內(nèi)容和上面的代碼插槽,畫出了表示簽名校驗(yàn)的整個流程:

          這里有兩處 Hash 對比,一個是對 signedAttrs 的解密,確保其是可信任的。另一處是 CDHash 的對比,確保代碼未被修改。

          signerInfo 里包含了 signedAttrs 、簽名使用的 Hash 算法、加密算法、簽名數(shù)據(jù)等信息。再結(jié)合證書里的公鑰,我們就可以驗(yàn)證,signedAttrs 的有效性。

          授權(quán)

          除了確保代碼的真實(shí)性和完整性,代碼簽名還為蘋果公司及其強(qiáng)大的安全機(jī)制提供了授權(quán)(entitlement)功能。授權(quán)文件也被包含在簽名里,其散列值放在索引為-5的插槽中。授權(quán)文件是一個 XML 格式的文件,我們可以使用 jtool --ent 查看其內(nèi)容,因?yàn)?ls 沒有授權(quán)文件,我們以 Mac 端微信為例進(jìn)行查看:

          $?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、聲音輸入、攝像頭等一系列權(quán)限。在應(yīng)用訪問特定 API 的時候蘋果可以根據(jù)這些授權(quán)判定該行為是否合法。因?yàn)樘O果公司是應(yīng)用的終極簽名者,所以簽名過程中也可以很容易的修改授權(quán),比如 com.apple.security.sandbox.container-required 這一表示沙盒權(quán)限的值就會被強(qiáng)制安置到授權(quán)文件中。

          強(qiáng)制驗(yàn)證代碼簽名

          為了使代碼簽名真正有效,非常重要的一步就是要保證驗(yàn)證過程順利執(zhí)行,沒有遺漏。當(dāng)前簽名驗(yàn)證是發(fā)生在內(nèi)核模式下,而非用戶模式下。簽名的驗(yàn)證發(fā)生在兩個階段:加載可執(zhí)行文件時、實(shí)際訪問二進(jìn)制代碼時(Page Fault)。分成兩個階段也是出于性能方面的考慮,因?yàn)槎M(jìn)制文件是動態(tài)加載的,對于還沒加載的部分僅當(dāng)其加載如內(nèi)存時,也即發(fā)生 Page Fault 時再進(jìn)行簽名驗(yàn)證。

          可執(zhí)行文件的加載

          可執(zhí)行文件的加載出現(xiàn)在execve()/mac_execve()posix_spawn()系統(tǒng)調(diào)用被觸發(fā)的時候。對于MachO,exec_mach_imgact()會被調(diào)用,在解析文件時它會找到LC_CODE_SIGNATURE的位置。代碼簽名二進(jìn)制塊會被加載到內(nèi)核的統(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*/))

          當(dāng) Page Fault 滿足以上條件時將觸發(fā)簽名驗(yàn)證過程:

          1、該頁面正在用戶空間中映射

          2、這個頁面還沒有被發(fā)現(xiàn)為 tainted

          3、該頁屬于一個代碼簽名對象

          4、頁面還沒有被驗(yàn)證,或者還沒有被映射為可寫狀態(tài)

          代碼簽名的漏洞

          代碼簽名機(jī)制雖然強(qiáng)大,保護(hù)著應(yīng)用的安全,但依然被攻破過,以下講解幾例曾經(jīng)出現(xiàn)的漏洞。

          JIT(即時生成代碼)

          該情況發(fā)生在 Page Fault 過程,如果該頁內(nèi)容是用于 JIT,將會被特殊標(biāo)記,可以創(chuàng)建和執(zhí)行任意代碼,而無需代碼簽名。

          從 iOS 10 開始,蘋果公司開始在64位的設(shè)備上加固 JIT。采用專門的 memcpy() 將JIT映射到可執(zhí)行但不可讀的內(nèi)存上,然后可執(zhí)行的 JIT 映射為不可寫,可寫的 JIT 映射為不可執(zhí)行狀態(tài)。

          Jekyll 應(yīng)用

          Jekyll 應(yīng)用的含義是應(yīng)用在提交至 App Store 時表現(xiàn)為無害,但其實(shí)它包含惡意功能,只不過這些功能處于休眠狀態(tài)。過審之后和本地服務(wù)器進(jìn)行合作,自愿公開其地址空間和符號,通過代碼注入或者返回導(dǎo)向編程(Return Oriented Programming,ROP),觸發(fā)預(yù)置的惡意程序。

          目前還沒有可靠的打擊 ROP 的方法,但因?yàn)樯澈袡C(jī)制的緣故,惡意代碼的影響范圍是可控的。

          蘋果公司使用 LLVM BitCode 向 App Store 提交應(yīng)用的方案,也會使惡意應(yīng)用難以事先知曉其地址空間。

          內(nèi)存鎖定

          從上面我們知道發(fā)生Page Fault會觸發(fā)簽名驗(yàn)證的流程,那如果沒有Page Fault就不會存在簽名驗(yàn)證了。按照mmap -> mlock -> memcpy -> mprotect 的調(diào)用順序,應(yīng)用可以修改可執(zhí)行內(nèi)存,以任何看起來合適的方式修補(bǔ)內(nèi)存。雖然XNU通常會阻止將曾經(jīng)可寫的內(nèi)存設(shè)置為r-x,但當(dāng)內(nèi)存鎖定時,會繞過該檢測。

          蘋果在iOS 9.3中修復(fù)了這個漏洞。

          總結(jié)

          我們再來嘗試回答開頭上面遺留的問題:

          1、如何驗(yàn)證代碼的來源是否合法?

          主要通過證書來驗(yàn)證來源是否合法,所有的開發(fā)者證書都由蘋果頒發(fā),且被 Root CA 認(rèn)證。另外依托于需求(requirements),還可以再擴(kuò)展一些其他驗(yàn)證方式。

          2、如何確認(rèn)代碼是否被修改過。

          主要通過代碼插槽和 CDHash,再對 CDHash 進(jìn)行簽名,就可確認(rèn)其是否被修改過。注意實(shí)際驗(yàn)證流程有兩處關(guān)鍵的 Hash 比對,可以再結(jié)合上面的流程圖加深理解。


          引用資料

          [1]

          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]

          細(xì)說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


          -End-


          最近有一些小伙伴,讓我?guī)兔φ乙恍?面試題?資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

          點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點(diǎn)這里好文分享給更多人↓↓

          瀏覽 65
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜视频色| 啪啪啪www. | 欧美国产日韩激情 | 亚洲成人无码专区在线 | 欧美黑人一级A片免费看 |