[TOC]
先來學習下三個重要概念:
public/private keys, entitlements, and provisioning pro?les.
entitlements是嵌入在App里的XML字符串,表示App可以做什么不可以做什么
App里的provisioning pro?le文件包含entitlements、可以安裝到的真機列表、用來驗證簽名的public key
Public/private keys
Requesting a Certi?cate From A Certi?cate Authority
You created a public/private key, sent up the public key to Apple servers (by the .csr ?le).
You can view the names, or identities, of your public/private key pairs used for signing your applications with the following Terminal command:
security find-identity -p codesigning -v
This command queries the macOS system keychain, looking for valid identities that contain a private key (-v) and whose type can codesign (-p codesigning).
This ouput will display identities that are valid, which can produce a code signed application. If you look for identities that contain the phrase “iPhone Developer], it’s likely that this identity can be used to sign an iOS application on your device.
? bin security find-identity -p codesigning -v
1) D1F90756076CA6CD2495ED5798E146E2426300C0 "iPhone Distribution: Kun Li (2QNTW3GT23)"
2) 6B9065E80E180DD48807C09F92E4D505812E029C "iPhone Developer: zhoujie_903@163.com (TCDLVFEQHJ)"
2 valid identities found
Never, ever, delete a private key!
you can export the public certi?cates using the following command:
security find-certificate -c "iPhone Developer: Derek Selander (8AW8QLCX5U)" -p
This will output the public, x509 certi?cate of iPhone Developer: Derek Selander (8AW8QLCX5U) to stdout and format it in PEM format.
Use the Terminal command to cat this newly created ?le:
cat /tmp/public_cert.cer
-----BEGIN CERTIFICATE----MIIFnDCCBISgAwIBAgIIFMKm2AG4HekwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
...
This is how you can tell this certi?cate is in PEM.
From here, you can use the openssl Terminal command to query the public, x509 certi?cate:
openssl x509 -in /tmp/public_cert.cer -inform PEM -text -noout
- The x509 option says that the openssl command should be able to work with a x509 certi?cate.
- You provide the -in to the path of public_cert.cer with the decoding format of PEM (-inform PEM).
- You specify you don’t want to output a certi?cate with the -noout param.
- But instead, you do want the certi?cate in a (somewhat) readable “text” format with the -text option.
There’s two ways to display a certi?cate: DER and PEM.
PEM can be read by the Terminal (since it’s in base64 encoding)
while DER, in highly professional coding terms, will produce gobbledygook and make the Terminal beep a lot.
Entitlements
Embedded in (almost) every compiled application is a set of entitlements: again, this is an XML string embedded in the application saying what an app can and can’t do.
For example, App Groups, iCloud Services, Push Noti?cations, Associated Domains all will modify the entitlments to your app. These capabilities shown in Xcode are but a small piece of the entitlements on Apple platforms as the majority of them are private to Apple and enforced through code signing.
重點:
Probably the most important entitlement, at least in this book, is the get-task-allow entitlement, found on all your software compiled with a developer certi?cate. This allows the program in question to be attached to a debugger.
You can view the entitlements of an application through the codesign Terminal command.
Find the entitlements of the macOS Finder application:
codesign -d --entitlements :- /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
- The -d option says to display the option immediately following in the command, which is the --entitlements.
- The - says to print to stdout
- The : says to omit the blob header and length.
Provisioning pro?les
A provisioning pro?le includes the public x509 certi?cate, the list of approved devices, as well as the entitlements all embedded into one ?le.
provisioning pro?les默認位置:
~/Library/MobileDevice/Provisioning Profiles/
你和Xcode創建的provisioning pro?les以UUID作為文件名,比如fe0c62fd-0f18-4974-821b-84b3337b0c12.mobileprovision
你可以通過security命令查看.mobileprovision文件的內容:
PP_FILE=$(ls ~/Library/MobileDevice/Provisioning\ Profiles/ *mobileprovision | head -1)
security cms -D -i "$PP_FILE"
the security command which decodes (-D) the cryptographic message syntax (cms) format of the provisioning pro?le, specifying the input path via the -i option.
- AppIDName This is the name of the Application ID that is tied to this provisioning pro?le
- TeamIdenti?er the unique team ID Apple has given me for my team identity
- Entitlements contains the Entitlements of what the app can and can’t do with this signature.
- ProvisionedDevices contains an array of approved devices this provisioning pro?le can install on, given by a device’s UDID.
- DeveloperCerti?cates is an array that contains base64-encoded x509 certi?cates.
- IsXcodeManaged is a Boolean value that indicates if Xcode manages this provisioning pro?le.
- Name which is the name of the provisioning pro?le that Apple displays to identify the provisioning pro?les on https:// developer.apple.com/account/ios/pro?le/limited.
探索WordPress app
apple商店上下載的ipa是經過加殼的,不能用于重簽名。
這里以開源App:Wordpress v10.9 來重簽名:
下載地址:https://github.com/wordpress-mobile/WordPress-iOS/releases/tag/10.9
也可以從像PP助手上下載越獄過的App來重簽名
The provisioning pro?le
APP_FILE="/full/path/to/WordPress.app"
security cms -D -i "$APP_FILE/embedded.mobileprovision"
在這個provisioning pro?le,包含了如下信息:
- Apple給Automattic, Inc.公司的team identi?er為 3TMU3BH3NK.
- Wordpress app使用了iCloud服務, 因為在entitlements dictionary可以看到com.apple.developer.icloud*等keys. It also looks to make use of certain extension like "App Groups".
- get-task-allow 是 false, 調試器不能attach, 不能調試
DeveloperCerti?cates鍵的值為x509 證書base64編碼后的值
CERT_DATA=MIIFozCCBIu...
解碼后保存到/tmp/wordpress_cert.cer:
echo "$CERT_DATA" | base64 -D > /tmp/wordpress_cert.cer
通過openssl命令查看證書內容:
openssl x509 -in /tmp/wordpress_cert.cer -inform DER -text -noout
輸出如下:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 786948871528664923 (0xaebcdd447dc4f5b)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority
Validity
Not Before: Jan 17 13:26:41 2018 GMT
Not After : Jan 17 13:26:41 2019 GMT
Subject: UID=PZYM8XX95Q, CN=iPhone Distribution: Automattic, Inc. (PZYM8XX95Q), OU=PZYM8XX95Q, O=Automattic, Inc., C=US
可以看出在“Automattic, Inc”工作的某人用“iPhone Distribution: Automattic, Inc. (PZYM8XX95Q)”簽名了這個App
Embedded executables
除主應用外,還包含extensions(share extension, today widgets, 和其它等),./Plugins文件夾里的每個extension都有自己的application identi?er和embedded.mobileprovision、簽名
_CodeSignature文件夾
在真機的app中有一個_CodeSignature文件夾,里面只包含一個叫CodeResources文件。
它是一個XML plist文件,內容為***.app內的每個非可執行文件的checksum值
可以通過openssl命令自己來計算文件的checksum值:
openssl sha1 -binary "$APP_FILE/AboutViewController.nib" | base64
Apple已開始為Xcode 10創建的ios app從SHA-1 checksums過渡到SHA-256 checksums
重點:
CodeResources文件自身的checksum被嵌入到可執行文件中。這意味著在xxx.app中修改任一文件,甚至增加一個文件夾,不重新簽名的話,app都會安裝失敗。
重簽名app
需要做如下的步驟:
- 復制有效的provisioning pro?le文件到xxx.app為embedded.mobileprovision文件
- 修改Info.plist的CFBundleIdenti?er鍵的值為provisioning pro?le文件中包含的application identi?er的值.
- 以合適的entitlements和provisioning pro?le中包含的開發者身份來重簽名App(合適的entitlements從provisioning pro?le文件里提取)
你能從
~/Library/MobileDevice/Provisioning Profiles/
或
https://developer.apple.com/
上得到provisioning pro?le文件
(Optional)生成有效的provisioning profile文件
替換provisioning pro?le文件
復制mobileprovision文件為app內的embedded.mobileprovision
PP_PATH=~/Downloads/ProvisProfile_92618.mobileprovision
cp "$PP_PATH" "$APP_FILE/embedded.mobileprovision"
刪除Plugins、Watch等文件夾
app里如果有Plugins文件夾時,里面的每個擴展都有自己唯一的application identi?er、唯一的provisioning pro?le。你能用其他唯一的provisioning pro?le重簽名每個擴展,但作為demo,這樣做太復雜了,我們選擇刪除整個Plugins文件夾。
刪除這些文件夾后,重簽名后的app就沒有相應的功能了,但作為演示可以接受。
修改Info.plist文件
可以從provisioning pro?le提取到application identi?er:
security cms -D -i "$PP_PATH" | grep application-identifier -A1
上面的命令得到如下輸出:
<key>application-identifier</key>
<string>H4U46V6494.com.selander.code-signing</string>
通過替換Info.plist中CFBundleIdenti?er鍵的值來修改app的application identi?er:
plutil -replace CFBundleIdentifier -string H4U46V6494.com.selander.codesigning "$APP_FILE/Info.plist"
修改WordPress app顯示的名字:
plutil -replace CFBundleDisplayName -string "Woot" "$APP_FILE/Info.plist"
提取entitlements
因為entitlements在provisioning pro?le不是XML,而是字典,為了簡單起見:從可執行文件中提取原來entitlements并保存到entitlements.xml文件,再從provisioning pro?le中提取新的entitlements并替換掉entitlements.xml文件中原來的entitlements
- 從可執行文件中提取entitlements并保存到文件:
codesign -d --entitlements :/tmp/entitlements.xml "$APP_FILE/WordPress"
cat /tmp/entitlements.xml
- 從provisioning pro?le生成新的entitlements.xml的模板:
security cms -D -i "$PP_PATH" > /tmp/scratch
使用xpath命令只提取entitlement信息到剪切板:
xpath /tmp/scratch '//*[text() = "Entitlements"]/following-sibling::dict' | pbcopy
現在剪切板包含了有效的entitlements,打開/tmp/entitlements.xml文件,刪除<dict>包含的內容,粘帖剪切板的內容
最終的/tmp/entitlements.xml文件內容包含像如下的entitlements信息:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>keychain-access-groups</key>
<array>
<string>P9DNHPPVMB.*</string>
</array>
<key>get-task-allow</key>
<true/>
<key>application-identifier</key>
<string>P9DNHPPVMB.com.jason.DeleteMe</string>
<key>com.apple.developer.team-identifier</key>
<string>P9DNHPPVMB</string>
</dict>
</plist>
重點:/tmp/entitlements.xml這個文件的格式一定要正確,我在實踐時因為xpath .... | pbcopy
命令生成了錯誤的<true />
多了一個空格,而生成了不正確的entitlements.xml文件,導致安裝成功后但啟動app就崩潰
最后簽名WordPress app
You now have performed all the setup.
You have a valid signing identity;
you have a valid provisioning pro?le embedded in the WordPress application at embedded.mobileprovision;
you have removed the Plugins directory;
and you have the entitlements of the new provisioning pro?le found at /tmp/entitlements.xml.
使用codesign命令用你的身份來重簽名App:
codesign -f -s "iPhone Developer: Derek Selander (8AW8QLCX5U)" "$APP_FILE"/Frameworks/*
codesign --entitlements /tmp/entitlements.xml -f -s "iPhone Developer: Derek Selander (8AW8QLCX5U)" "$APP_FILE"
主要腳本代碼
命令 | 文件 | 功能 | 命令行 |
---|---|---|---|
plutil | Info.plist | 替換CFBundleIdentifier | plutil -replace CFBundleIdentifier -string "xxx" Info.plist |
security | embedded.mobileprovision | 二進制解碼為文本格式 | security cms -D -i xxx.mobileprovision |
codesign | 可執行文件 | 提取entitlements | codesign -d --entitlements :xxx.xml WeChat |
codesign | xxx.app | 重簽名 | codesign --deep -f -s "iPhone Developer: xxx@yyy.com (TCDLVFEQHJ)" --entitlements zzz.xml xxx.app |
# 定義要重簽名的app文件路徑、和有效的mobileprovision文件路徑
APP_FILE=~/Desktop/WeChat/Payload/WeChat.app
PP_PATH=~/Desktop/ProvisProfile_92618.mobileprovision
# 從mobileprovision文件中提取得到application-identifier(app id或CFBundleIdentifier)
EXTRACTED_ENT="/tmp/extracted_ent"
security cms -D -i "$PP_PATH" > "$EXTRACTED_ENT"
APP_IDENTIFIER=$(/usr/bin/xpath "$EXTRACTED_ENT" '//*[text() = "application-identifier"]/following-sibling::string[1]/text()' 2>/dev/null | cut -d. -f 2-80 )
echo "app id is : $APP_IDENTIFIER"
# 復制mobileprovision文件為app內的embedded.mobileprovision
cp "$PP_PATH" "$APP_FILE/embedded.mobileprovision"
# 刪除PlugIns、Watch等文件夾
rm -rf "$APP_FILE/PlugIns"
rm -rf "$APP_FILE/Watch"
# 替換Info.plist內的CFBundleIdentifier、CFBundleDisplayName
plutil -replace CFBundleDisplayName -string "Woot" "$APP_FILE/Info.plist"
plutil -replace CFBundleIdentifier -string ${APP_IDENTIFIER} "$APP_FILE/Info.plist"
# 生成簽名app用的entitlements.xml
codesign -d --entitlements :/tmp/entitlements.xml "$APP_FILE/WeChat"
security cms -D -i "$PP_PATH" > /tmp/scratch
# 這名命令有點缺陷:生成的<true />多了一個空格,正確的是<true/>
xpath /tmp/scratch '//*[text() = "Entitlements"]/following-sibling::dict' | pbcopy
# [不要用剪切板,否則會把上面命令得到剪切板中的內容給覆蓋了。把剪切板上的內容粘貼到/tmp/entitlements.xml中]
open /tmp/entitlements.xml
# 重簽名Frameworks、 和app
codesign --deep -f -s "iPhone Developer: zhoujie_903@163.com (TCDLVFEQHJ)" "$APP_FILE/Frameworks/"*
codesign --deep -f -s "iPhone Developer: zhoujie_903@163.com (TCDLVFEQHJ)" --entitlements /tmp/entitlements.xml "$APP_FILE"
# 安裝到真機
mobdevim -i "$APP_FILE"
完整詳細代碼可以參考dsresign
參考
- dsresign App重簽名的bash腳本
- iOS App Signer App重簽名的應用程序
- Inside Code Signing
- 代碼簽名探析
- 《Advanced Apple Debugging & Reverse Engineering》Chapter 20: Code Signing