標籤:

PKG安裝包的管理與文件格式分析

不同的操作系統都有專屬於自己的軟體安裝包格式。如Ubuntu系統上的deb安裝包,Windows系統上的msi安裝包等。macOS系統使用pkg作為軟體安裝包格式。

大多數macOS上開發的程序都不需要安裝程序,它們只是一個以app結尾的Bundle包,使用zip壓縮一下,或者dmg製作一份鏡像,是這類程序的主要發布方式。然而,一些App有一些特定的需求,比如:向系統配置面板寫配置程序、安裝屏幕保護程序、讀寫特定的目錄與文件等。此時就可以製作pkg安裝包程序來安裝這類特殊的程序了。當然,由於這些特殊性,pkg安裝程序無法通過蘋果官方商店來發布。

0x1 構建pkg

pkg安裝程序能夠擴展程序安裝內容以及讀寫特定目錄的特性,來源於pkg支持的腳本特性,pkg安裝程序允許開發人員在程序的安裝過程中,運行自己編寫的Bash腳本程序。

蘋果官方在低版本的Xcode工具中提供了PackageMaker用來製作pkg,該工具沒有直接包含在XCode開發套件中,如果要使用它,需要到蘋果的開發者官網上去下載它。下載安裝好該工具後,運行PackageMaker.app就可以製作pkg了。

本小節製作一個pkg安裝包,完成以下目標:將上一小節的myframeworktest程序安裝到Applications目錄下,隨便將myframework.framework框架安裝到~/Library/frameworks目錄中。啟動PackageMaker,點擊菜單File->New,在彈出的對話框中,在Organization一欄輸入機構信息,如「com.macbook」,點擊OK返回程序主界面,點擊File->Save,將工程保存為pkg_install.pmdoc。

將上一小節的myframeworktest程序放到app目錄下,將app目錄直接拖入PackageMaker的主界面,會自己加程序添加配置信息,點擊Configuration可以設置一些安裝時的配置信息,如圖所示:

install指定要安裝的程序路徑,這裡已經指定好了;Destination指定程序要安裝的位置,默認為「/Applications」目錄;取消勾選「Allow custom location」選項,讓程序只能安裝到/Applications目錄下,Package Identifier指定安裝包的標識符,macOS記錄安裝過的pkg就是通過它來識別的,手動卸載pkg時需要用到它;Package Version指定安裝包的版本,版本號是識別pkg版本升級的關鍵,為pkg指定升級腳本時需要用到;Restart Action指定pkg安裝完成後,是否需要執行註銷、關機或重啟等操作;Require admin authentication複選框指定安裝器需要管理員許可權,為pkg指定的安裝腳本如果需要管理員許可權的話,就需要在此勾選上。

配置好後,點擊Contents標籤,配置需要安裝的內容,PackageMaker已經默認選擇好了要安裝的內容為myframeworktest.app,並且文件的讀寫與執行許可權也自動設置好了,如圖所示:

關於文件的讀寫許可權設罷,一個建議的設置如下表所示:

表4.1 Contents許可權設置

nnOwnernGroupnPermissionsnApplicationsnrootnadminnrwxrwxr-xnSystemnrootnadminnrwxrwxr-xnLibrarynrootnadminnrwxrwxr-xnExtensionsnrootnadminnrwxrwxr-xn

關於拖入Contents中的程序,這裡有一個技巧!macOS系統會為操作過的文件夾中生成一個隱藏的.DS_Store文件,如果系統開啟了顯示隱藏文件的選項,直接打包程序會在安裝包中包含隱藏的.DS_Store文件,需要在拖入Contents前將它們全部刪除,可以使用如下的命令:

find ./ -name ".DS_Store" -exec rm -f {} ;n

點擊Components標籤,配置組件信息。取消掉「Allow Relocation」複選框,否則即使提示安裝完成,在/Applications目錄下也看不到安裝後的程序。如圖所示:

點擊Scripts標籤,配置運行安裝器時需要執行的腳本。將編寫好的腳本分別保存為preflight與postflight,然後將它們放到script目錄下,執行以下命令為它們賦上可執行許可權:

$ chmod a+x ./preinstalln$ chmod a+x ./postflightn

將script目錄直接拖入Scripts Directory旁的文本框中,此時,Preflight與Postflight腳本會自動設置完成。如圖所示:

可以設置的腳本有六個,它們按照執行順序分別是:

  • preflight:點擊安裝界面上的Install按鈕時運行此腳本。該腳本在程序每次安裝時都會運行。
  • preinstall/preupgrade:針對單程序安裝包(pkg),該腳本會在preflight腳本運行之後運行,針對多程序安裝包(mpkg),該腳本會在用戶按下Install銨鈕後執行。preinstall與preupgrade的區別在於:preinstall只會在用戶第一次安裝該程序時執行,而preupgrade相反,如果之前安裝過該程序,那麼該腳本才會執行,preupgrade用於軟體升級時使用。區分程序是否為第一次安裝是通過pkg安裝器Installer.app來完成的,Installer.app通過查看/private/var/db/receipts目錄,查看目錄中是否有以程序包名命名的pkg文件,如果存在,說明已經安裝過,反之,為第一次安裝。
  • postinstall/postupgrade:該腳本在程序安裝完之後才運行。它們的區別與preinstall/preupgrade一樣。
  • postflight:該腳本在postinstall/postupgrade腳本之後運行。

    PackageMaker支持Shell腳本與Perl腳本,此處編寫的是Shell腳本,preinstall腳本的內容如下:

#!/usr/bin/env bashnnecho "Running myframeworktest.app preinstall script."necho "Killing myframeworktest.app."nkillall "myframeworktest"nnecho "Finding old versions of myframeworktest."nmdfind -onlyin /Applications "kMDItemCFBundleIdentifier==fc.myframeworktest" | xargs -I % rm -rf %nnecho "Removed old versions of myframeworktest.app, if any."necho "Ran myframeworktest.app preinstall script."nnexit 0n

這段腳本首先使用killall殺掉正在運行的myframeworktest.app進程;接著使用mdfind在/Applications目錄下查找程序標識符為」fc.myframeworktest「的程序包路徑,找到後使用rm -rf將其刪除掉。

再來看看postflight腳本的內容:

#!/usr/bin/env bashnnecho "Running myframeworktest.app postinstall script."necho "Installing myframework.framework."nnrm -rf ~/Library/Frameworks/myframework.frameworknmkdir ~/Library/Frameworks/myframework.frameworkncp -r /Applications/myframeworktest.app/Contents/Frameworks/myframework.framework/* ~/Library/Frameworks/myframework.frameworknnchmod -R 6777 ~/Library/Frameworks/myframework.frameworknecho "Ran myframeworktest.app postinstall script."nnexit 0n

該腳本運行時,myframeworktest.app程序包已經安裝到了/Applications目錄下,將myframework.framework拷貝到~/Library/Frameworks目錄下,然後修改它的許可權為任何人都可讀可寫可執行,最後執行完後調用」exit 0」退出腳本。

配置好要安裝的內容與執行腳本後,點擊Contents上面的圖標,對pkg進行配置,點擊Configuration,在Title旁的文本框中輸入安裝包的標題,例如」mframeworktest installer「;User Sees處選擇Easy Install Only(簡單安裝)即可;Install Destination處勾選Volume selected by user。

點擊Requirements標籤,設置pkg運行的系統要求。點擊界面左下角的加號」+「按鈕,添加兩條規則:一條是System OS Version(e.g. 10.x.x),另一條是Target OS Version(e.g. 10.x.x),都設置成」>=「10.6,如圖所示:

最後的Actions標籤頁不用去管它。配置完了後,還可以點擊界面右上角的Edit interface按鈕來編輯安裝程序的界面。包括:Background、Introduction、Read me、License與Finish up。它們每一項都是一個頁面,內容可以選擇系統默認的Default,也可以直接寫一段文本甙入(Embedded)進去,或者選擇一個外部的rtf文檔或html網頁。如圖所示,為一段手寫的Read Me:

以上所有操作完成後,點擊PackageMaker左上角的Build銨鈕進行構建,或者Build and Run按鈕構建成功後直接運行。構建完成後會針對單程序安裝包或多程序安裝包生成一個pkg或mpkg文件,該文件是最終可以發布的產品,接下來只需要對其進行安裝測試,沒問題就可以發布了。

在新版本的XCode中,提供了命令行工具productbuild來打包製作pkg。本小節PackageMaker操作的步驟可以執行以下命令完成:

$ productbuild --component app/myframeworktest.app /Applications --scripts script ~/Desktop/out.pkgn

命令執行完後,就會在當前用戶桌面上生成pkg文件,當然編譯時可以指定--sign參數來為pkg簽名,pkg的簽名不是使用codesign,如果創建pkg時沒有對其進行簽名,或者手動修改過pkg的內容,可以使用工具productsign來對pkg進行簽名。

介紹了官方的pkg創建工具後,再來看看目前市面上常用的pkg製作工具。

喜歡命令行編譯的開發人員一定會喜歡工具Luggage(下載地址:「unixorn/luggage」),它提供了一種自定義腳本的方式來編譯構建pkg文件。該工具的使用方法很簡單,只需要將整個github上的文件複製到/usr/local/share/luggage就完成了安裝。至於腳本如何編寫,可以參考Luggage提供的樣例,

地址為「unixorn/luggage-examples」,以編譯樣例中的fex程序為例,在命令行下執行以下命令:

$ makennUsagennmake clean - clean up work files.nmake dmg - roll a pkg, then stuff it into a dmg file.nmake zip - roll a pkg, then stuff it into a zip file.nmake pkg - roll a pkg.nmake pkgls - list the bill of materials that will be generated by the pkg.nn$ make pkgnPassword:nmake -f Makefile -e pack-fexnnDisabling bundle relocation.nIf you need to override permissions or ownerships, override modify_packageroot in your MakefilenCreating /tmp/the_luggage/Fex-20160902/payload/Fex-20160902.pkg with /usr/bin/pkgbuild.nsudo /usr/bin/pkgbuild --root /tmp/the_luggage/Fex-20160902/root n --component-plist /tmp/the_luggage/Fex-20160902/luggage.pkg.component.plist n --identifier com.huronhs.Fex n --filter "/CVS$" --filter "/.svn$" --filter "/.cvsignore$" --filter "/.cvspass$" --filter "/(._)?.DS_Store$" --filter "/.git$" --filter "/.gitignore$" n --scripts /tmp/the_luggage/Fex-20160902/scripts n --version 20160902 n --ownership preserve --quiet n /tmp/the_luggage/Fex-20160902/payload/Fex-20160902.pkgnn$ lsnFex-20160902.pkg Makefile fexn

從輸出中可以看出,除了構建pkg,Luggage還支持生成dmg與zip打包的程序,非常方便。

與Luggage類似的還有createOSXinstallPkg(下載地址:「munki/createOSXinstallPkg」),使用方法也很簡單,有興趣的讀者可以到github頁面上查看如何使用。

最後,還有一款免費強大的pkg安裝包製作工具Iceberg(下載地址:s.sudre.free.fr/Softwar ),該工具可以修改安裝程序界面的背景圖片,此處就不去討論它的用法了,有興趣的讀者可以去它的官網下載了試試。

0x2 pkg的安裝與卸載

安裝pkg很簡單,只要雙擊pkg,或者雙擊mpkg,就會彈出安裝嚮導,按照步驟不停點擊Next,直到安裝完成,安裝過程中,可能執行一些操作可能會需要管理器許可權,系統會彈出提示要求用戶輸入管理員密碼,按照操作輸入密碼即可。除了雙擊安裝外,還可以使用命令行工具installer進行靜默安裝,執行以下命令可以安裝上一小節的pkg:

$ sudo installer -pkg ./myframework_installer.pkg -target LocalSystemnPassword:ninstaller: Package name is myframework installerninstaller: Upgrading at base path /ninstaller: The upgrade was successful.n

pkg的卸載就沒這麼簡單了!蘋果公司沒有提供直接卸載pkg的方法,上一小節沒有製作pkg格式的卸載程序,而是編寫了一個簡單的腳本,只需要雙擊運行它就可以卸載上一節製作的pkg。腳本的代碼如下:

#!/usr/bin/env bashnnif [ -d ~/Library/Frameworks/myframework.framework ]; thenn /bin/rm -rf ~/Library/Frameworks/myframework.frameworknfinnif [ -d /Application/myframework.app ]; thenn /bin/rm -rf /Applications/myframeworktest.appnfinnecho done.n

對於沒有提供卸載程序的pkg,卸載它們就只能手動或者依賴第三方的工具。例如UninstallPKG(下載地址:corecode.at/uninstallpk ),這是一個收費軟體,安裝並運行該軟體後,它會收集系統中安裝的所有pkg軟體,然後使用列表形式展示出來,如圖所示:

點擊View Package…按鈕,可以查看pkg在系統中寫入了哪些文件內容,點擊Uninstall Package…可以直接卸載pkg。

UninstallPKG是如何做到收集與卸載系統中安裝的pkg呢?其實它的原理並不難。它通過讀取/private/var/db/receipts下的pkg列表,然後使用lsbom查看這些pkg文件的bom信息,找到bom文件中保存的文件列表,將它們列舉出來,卸載的時候將它們全部刪除即可。執行如下的命令列表可以查看上一小節pkg的信息:

$ cd /private/var/db/receiptsn$ ls | grep macbookncom.macbook.myframeworkInstaller.pkg.bomncom.macbook.myframeworkInstaller.pkg.plistn$ lsbom -pf ./com.macbook.myframeworkInstaller.pkg.bomn.n./myframeworktest.appn./myframeworktest.app/Contentsn./myframeworktest.app/Contents/Frameworksn./myframeworktest.app/Contents/Frameworks/myframework.frameworkn./myframeworktest.app/Contents/Frameworks/myframework.framework/Resourcesn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versionsn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/An./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/Resourcesn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/Resources/Info.plistn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/_CodeSignaturen./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/_CodeSignature/CodeResourcesn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/myframeworkn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/Currentn./myframeworktest.app/Contents/Frameworks/myframework.framework/myframeworkn./myframeworktest.app/Contents/Info.plistn./myframeworktest.app/Contents/MacOSn./myframeworktest.app/Contents/MacOS/myframeworktestn./myframeworktest.app/Contents/PkgInfon./myframeworktest.app/Contents/Resourcesn./myframeworktest.app/Contents/Resources/Base.lprojn./myframeworktest.app/Contents/Resources/Base.lproj/MainMenu.nibn./myframeworktest.app/Contents/_CodeSignaturen./myframeworktest.app/Contents/_CodeSignature/CodeResourcesn

可以看出,腳本中執行的命令,在~/Library/Frameworks目錄中安裝的myframework.framework並沒有列出來,而只有在Contents中指定的內容。上面查看bom信息使用的lsbom命令,其實,查看pkg中的內容還有一種更簡單的方法,在雙擊運行pkg後,不要點擊Continue按鈕,而是點擊菜單File->Show Files,pkg中包含的文件內容就一目了然了,如圖所示:

除了手動的去/private/var/db/receipts目錄下讀取pkg列表,還可以使用pkg管理工具pkgutil來查看系統中安裝的pkg信息,不過只有查看功能,不能卸載。執行如下命令可以查看上一節安裝的pkg信息,效果與上面一樣:

$ pkgutil --pkgs | grep -i com.macbookncom.macbook.myframeworkInstaller.pkgn$ pkgutil --files com.macbook.myframeworkInstaller.pkgnmyframeworktest.appnmyframeworktest.app/Contentsnmyframeworktest.app/Contents/Frameworksnmyframeworktest.app/Contents/Frameworks/myframework.frameworknmyframeworktest.app/Contents/Frameworks/myframework.framework/Resourcesnmyframeworktest.app/Contents/Frameworks/myframework.framework/Versionsnmyframeworktest.app/Contents/Frameworks/myframework.framework/Versions/Anmyframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/Resourcesnmyframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/Resources/Info.plistnmyframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/_CodeSignaturenmyframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/_CodeSignature/CodeResourcesnmyframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/myframeworknmyframeworktest.app/Contents/Frameworks/myframework.framework/Versions/Currentnmyframeworktest.app/Contents/Frameworks/myframework.framework/myframeworknmyframeworktest.app/Contents/Info.plistnmyframeworktest.app/Contents/MacOSnmyframeworktest.app/Contents/MacOS/myframeworktestnmyframeworktest.app/Contents/PkgInfonmyframeworktest.app/Contents/Resourcesnmyframeworktest.app/Contents/Resources/Base.lprojnmyframeworktest.app/Contents/Resources/Base.lproj/MainMenu.nibnmyframeworktest.app/Contents/_CodeSignaturenmyframeworktest.app/Contents/_CodeSignature/CodeResourcesn

0x3 pkg文件格式

pkg分為pkg與mpkg,前者是針對單程序安裝;後者是針對多程序安裝,它包含一個或多個的子包(Sub Package)。pkg本身又有兩種格式,一種是與Bundle一樣,有著特定組織結構的目錄,上一小節生成的pkg的安裝包就是這種格式的,還有一種是xar格式的文件,下面分別對這兩種格式的安裝包進行分析。

首先看myframework_installer.mpkg,使用tree命令(系統默認沒有此命令,可以使用「brew install tree」進行安裝)查看它的目錄結構如下:

$ tree ./myframework_installer.mpkg/n./myframework_installer.mpkg/n└── Contentsn ├── Packagesn │ └── app.pkgn │ └── Contentsn │ ├── Archive.bomn │ ├── Archive.pax.gzn │ ├── Info.plistn │ ├── PkgInfon │ └── Resourcesn │ ├── en.lprojn │ │ └── Description.plistn │ ├── package_versionn │ ├── postflightn │ └── preflightn ├── Resourcesn │ └── en.lprojn └── distribution.distnn8 directories, 9 filesn

對於外層的mpkg,它的Packages目錄下存放的是pkg文件列表,也就是子包列表;Resources目錄存放了pkg用到的資源、如本地化資源、圖像、rtf文檔、pdf文檔等;還有一個distribution.dist文件,這是一個xml文檔,包含了要安裝的子包、運行時腳本等信息。對於當前的mpkg,它的內容如下:

<?xml version="1.0" encoding="utf-8" standalone="no"?>n<installer-script minSpecVersion="1.000000" authoringTool="com.apple.PackageMaker" authoringToolVersion="3.0.6" authoringToolBuild="201">n <title>myframework installer</title>n <options customize="never" allow-external-scripts="no" rootVolumeOnly="false"/>n <installation-check script="pm_install_check();"/>n <volume-check script="pm_volume_check();"/>n <script>function pm_volume_check() {n if(!(my.target.systemVersion && /* >= */ system.compareVersions(my.target.systemVersion.ProductVersion, 10.6) >= 0)) {n my.result.title = Failure;n my.result.message = Installation cannot proceed, as not all requirements were met.;n my.result.type = Fatal;n return false;n }n return true;n}nnfunction pm_install_check() {n if(!(/* >= */ system.compareVersions(system.version.ProductVersion, 10.6) >= 0)) {n my.result.title = Failure;n my.result.message = Installation cannot proceed, as not all requirements were met.;n my.result.type = Fatal;n return false;n }n return true;n}n</script>n <choices-outline>n <line choice="choice0"/>n </choices-outline>n <choice id="choice0" title="app">n <pkg-ref id="com.macbook.myframeworkInstaller.pkg"/>n </choice>n <pkg-ref id="com.macbook.myframeworkInstaller.pkg" installKBytes="108" version="1.0" auth="Root">file:./Contents/Packages/app.pkg</pkg-ref>n</installer-script>n

pm_install_check()與pm_volume_check()分別做安裝時檢查與卷標檢查,下面的choices-outline部分指定了安裝時使用的choice,也就是選擇執行哪個子包,對於當前mpkg包,它只有一個pkg,choice的id為「choice0」,指向的路徑是「file:./Contents/Packages/app.pkg」。

app.pkg是要安裝的子包,是一個pkg格式的Bundle結構的目錄,它包含一個Contents子目錄,裡面有四個文件與一個目錄Resources,它們分別是:

  • Archive.bom:bom信息。存放的要安裝寫入的文件列表,可以使用「lsbom -pf」命令查看,效果與上一節講到的一樣。
  • Archive.pax.gz:使用pax格式打包後,再使用gzip壓縮的壓縮包,它的內容就是要安裝的內容,此處就是myframeworktest.app程序。可以執行以下命令進行解壓:

    $ cd ./myframework_installer.mpkg/Contents/Packages/app.pkg/Contents/n$ gunzip -d ./Archive.pax.gzn$ pax -rvf ./Archive.paxn.n./myframeworktest.appn./myframeworktest.app/Contentsn./myframeworktest.app/Contents/_CodeSignaturen./myframeworktest.app/Contents/_CodeSignature/CodeResourcesn./myframeworktest.app/Contents/Frameworksn./myframeworktest.app/Contents/Frameworks/myframework.frameworkn./myframeworktest.app/Contents/Frameworks/myframework.framework/myframeworkn./myframeworktest.app/Contents/Frameworks/myframework.framework/Resourcesn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versionsn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/An./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/_CodeSignaturen./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/_CodeSignature/CodeResourcesn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/myframeworkn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/Resourcesn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/A/Resources/Info.plistn./myframeworktest.app/Contents/Frameworks/myframework.framework/Versions/Currentn./myframeworktest.app/Contents/Info.plistn./myframeworktest.app/Contents/MacOSn./myframeworktest.app/Contents/MacOS/myframeworktestn./myframeworktest.app/Contents/PkgInfon./myframeworktest.app/Contents/Resourcesn./myframeworktest.app/Contents/Resources/Base.lprojn./myframeworktest.app/Contents/Resources/Base.lproj/MainMenu.nibn

  • Info.plist:pkg包的信息。如CFBundleIdentifier為pkg的標識;IFMajorVersion與IFMinorVersion分別為pkg的主版本與子版本號;IFPkgFlagInstalledSize為pkg安裝後所需要佔用的位元組大小。
  • PkgInfo:8位元組的標識。表明是一個pkg文件。

Resources目錄除了包含資源文件外,還包含了:

  • package_version:它是包版本文件。也就是使用PackageMaker製作pkg時設置的版本號;
  • postflight/preflight:pkg要執行的腳本文件。上一節中講過,此處是未經過加密明文存放的。

另外一種是xar格式的文件,可以使用如下命令查看myframework_installer.pkg文件的格式:

$ file ./myframework_installer.pkgn./myframework_installer.pkg: xar archive - version 1n

xar是壓縮的可擴展歸檔格式,可以使用xar命令對其進行解壓,執行如下命令解壓:

$ xar -xvf ./myframework_installer.pkgnDistributionnapp.pkg/PackageInfonapp.pkg/Bomnapp.pkg/Payloadnapp.pkg/Scriptsnapp.pkgnResources/en.lprojnResourcesn

Distribution與前面討論的distribution.dist文件基本一樣;Resources目錄與前面的也一樣;主要看app.pkg,它包含四個文件:

  • Bom:bom信息,存放的要安裝寫入的文件列表。可以使用「lsbom -pf」命令查看,效果與上一節講到的一樣。
  • PackageInfo:文本文件,包含了包的信息。可以使用cat命令查看它的內容:

$ cat app.pkg/PackageInfonn<pkg-info format-version="2" identifier="com.macbook.myframeworkInstaller.pkg" version="1.0" install-location="/Applications" auth="root">n <payload installKBytes="108" numberOfFiles="25"/>n <scripts>n <preinstall file="./preflight"/>n <postinstall file="./postflight"/>n </scripts>n <bundle id="fc.myframeworktest" CFBundleIdentifier="fc.myframeworktest" path="./myframeworktest.app" CFBundleVersion="1">n <bundle id="fc.myframework" CFBundleIdentifier="fc.myframework" path="./Contents/Frameworks/myframework.framework" CFBundleVersion="1"/>n </bundle>n <bundle-version>n <bundle id="fc.myframeworktest"/>n <bundle id="fc.myframework"/>n </bundle-version>n

  • Payload:經過gzip壓縮過的數據內容,本處為要安裝的myframework.app,可以使用如下命令進行解壓:

$ cat ./Payload | cpio -in3 blocksn

解壓成功後就會在當前目錄下生成myframework.app。

  • Scripts:經過gzip壓縮過的腳本。可以使用如下命令進行解壓:

$ cd app.pkgn$ cat ./Scripts | cpio -in181 blocksn

解壓成功後就會在當前目錄下生成未加密的preflight與postflight腳本。

0x4 破解pkg

對於pkg格式有一定了解後,修改或破解pkg就不會感到多難。破解pkg無非有以下三種:

  • 資源的替換或修改:針對文件夾類型的pkg,未加密,可直接進行修改替換;針對xar類型的pkg,需要先解壓xar,然後替換或修改完資源後,重新壓縮xar。
  • 安裝腳本的替換或修改:針對文件夾類型的pkg,未加密,可直接進行修改替換;針對xar類型的pkg,需要先解壓xar,然後解壓Scripts,然後替換或修改完腳本後,重新壓縮Scripts,最後重新壓縮xar。
  • 安裝內容的替換或修改:針對文件夾類型的pkg,未加密,但需要先對Archive.pax.gz進行解包,修改完後,需要重新打包回去;針對xar類型的pkg,需要先解壓xar,然後解壓Payload,替換或修改完數據後,重新壓縮Payload,最後重新壓縮xar。

以上步驟是操作思路,實際分析過程中,使用工具來做一些輔助工作是可以大大提高效率的,在拿到pkg後,首先快速瀏覽pkg文件,簡單分析出pkg的行為與可能要做的操作。推薦一款工具:Suspicious Package(下載地址:Mothers Ruin Software ),此工具提供了快速瀏覽插件,安裝完成後,在要操作的pkg上按下空格,就可以快速查看pkg,檢索要安裝的軟體內容,如圖所示:

還可以查看要執行的腳本的內容,如圖所示:

對pkg有了初步了解後,找到需要操作的地方後,下一步就是提取數據內容了,Suspicious Package支持數據的提取,使用Suspicious Package打開pkg文件後,在主界面的All Files列就可以查看所有文件,可以選中要導出的文件,直接拖出到Finder,或者點擊Action->Export,都可以將文件導出,操作效果如圖所示:

除了Suspicious Package外,介紹另外一款更強大的工具:Pacifist(下載地址:software you always wished someone would write ),這款工具支持多種文件數據的提取,其中就包括pkg,如圖所示:

選中要提取的文件,右鍵選擇「Extract to Custom Location…」,或者直接拖到要保存到的文件夾中,都可以將文件提取出來。

提取出來的文件,分析完成,修改好了後,就要打包回去了,Pacifist不支持將數據打包回去,如果修改的是Payload,可以使用如下命令將app目錄下的myframeworktest.app打包回去:

find app/* | cpio -o > ./Payloadn

如果是腳本文件,也可以如法炮製,最後就是將修改好的Payload或Scripts重新打包回去,可以使用執行「xar cvf」命令來操作,這裡推薦另一款圖形化工具:Flat Package Editor,該工具是蘋果官方提供的,與PackageMaker一起提供給開發人員,它可以對pkg直接進行增、刪、改操作,使用Flat Package Editor打開要操作的pkg,將修改好的Payload或Scripts拖回去,然後,點擊菜單File->Save就保存成功了!如圖所示:

操作完成後,pkg就算修改好了,接下來測試安裝沒問題就算破解完成了。

推薦閱讀:

Let's GoSSIP! 第二屆軟體與移動智能系統安全暑期學校小記
GoSSIP安全研究項目:孤挺花(Armariris) LLVM混淆框架

TAG:软件安全 |