Hey! I'm available for hire (and I appreciate tips)  

The first thing you may ask is "AJ, why are you wasting your time on this? There are already tutorials on how to do this!"

Well, yes, but there are new tools for Xcode 4.3 and up that do a better, simpler job than the cat <Payload|Script> | gzip -d - | cpio -id method.

Plus, here we get a chance to look at the Bom and plist as well!

Here's my use case. A friend of mine packaged a package for a company we used to work, but the documentation was never written or lost.

I want to use that work as the basis for a package that I'm creating, but I don't know where to start... so I'm going to rip apart the old package instead of starting from scratch.

Install Xcode

You can get Xcode from App Store, but you'll also need the Command line tools from https://developer.apple.com/downloads/index.action (Log in and then search for it in Developer Downloads)

Note: On Ubuntu (or other Linux) you will need to have lsbom and mkbom installed.

Unpack Example.pkg

See the File Commands section of http://developer.apple.com/.../pkgutil.1.html

So let's say I have ~/Downloads/ExampleApp.pkg (which I do!) and I want to extract / upack it.

pushd /tmp
pkgutil --expand ~/Downloads/ExampleApp.pkg /tmp/ExampleApp.unpkg

Then, for fun, I like to see what all was extracted

find /tmp/ExampleApp.unpkg

For reference, the long way of doing this (i.e. for Linux) is

mkdir -p /tmp/ExampleApp.unpkg
pushd /tmp/ExampleApp.unpkg
xar -xf ~/Downloads/ExampleApp.pkg
ls -d *.pkg | while read PKGDIR
  pushd $PKGDIR || continue
  mv Scripts Scripts.bin
  mkdir -p Scripts
    pushd Scripts
      cat ../Scripts.bin | gzip -d | cpio -id

Note that the file Scripts is unpackaged to a folder Scripts, with the scripts inside.

List Bom

I'm going to take a wild guess and say that Bom stands for Bill of materials, which is hardware engineers submit to off-site manufacturing facilities.

It's basically the tar --list (same as tar -t) of pkgs.

See the options section of http://developer.apple.com/.../lsbom.8.html

lsbom comexample.pkg/Bom

Note that you can extract just the Boms, with slightly less verbosity with pkgutil --bom ~/Downloads/ExampleApp.pkg

List PackageInfo

PackageInfo is already a plain text file. It contains the organization identifiers, install location, number of files and bytes to install, etc.

Extract Payload

So, you still end up having to use the old black magic for the payload

pushd ~/tmp/ExampleApp.unpkg/
pushd example.pkg/
cat Payload | gzip -d | cpio -id

The Distribution file and the Resources folder

The Resources folder contains resources that are needed for the installer, but not to be installed - such as the background image to use.

The Distribution file contains configuration information to be used by the installer as well as possibly some black magic JavaScript.


If you somehow accidentally deleted the package, but you didn't change anything you can repackage the pkg like-a-so:

pkgutil --flatten /tmp/ExampleApp.unpkg ~/Desktop/ExampleApp.pkg

But let's say you do change a file (or add one, etc). What then?

First we extract the Payload to a clean directory, like so:

mkdir /tmp/ExampleApp-example.pkg
pushd /tmp/ExampleApp-example.pkg
cat /tmp/ExampleApp.unpkg/example.pkg/Payload | gzip -d | cpio -id

Next you add / remove / edit your files

Then you repackage the Payload with a new Bom

rm /tmp/ExampleApp.unpkg/example.pkg/Payload
rm /tmp/ExampleApp.unpkg/example.pkg/Bom

# Still in /tmp/ExampleApp-example.pkg
find . | cpio -o --format odc | gzip -c \
  > /tmp/ExampleApp.unpkg/example.pkg/Payload
mkbom /tmp/ExampleApp-example.pkg/ /tmp/ExampleApp.unpkg/example.pkg/Bom

And finally update the PackgeInfo with the number of KB and files

installKBytes is the number of KiB assuming that the disk using 4KiB blocks, minus 4KiB:

du -sk /tmp/ExampleApp-example.pkg/ # subtract 4 from that number!!!

numberOfFiles is the number of nodes (files, directories, sockets, whatever), including .:

find /tmp/ExampleApp-example.pkg/ | wc

Those values can be edited at /tmp/ExampleApp.unpkg/example.pkg/PackageInfo

With all of that done you can now repackage the entire shebang:

pkgutil --flatten /tmp/ExampleApp.unpkg/ /tmp/ExampleApp.new.pkg

Note: If you used xar to extract (on Linux) instead of pkgutil, you'll need to use that again:

pushd /tmp/ExampleApp.unpkg
xar -cf ../ExampleApp.new.pkg .

Test and enjoy!

open /tmp/ExampleApp.new.pkg

Linux Only

If you need to do all of this from Linux - perhaps you need to edit a uuid for every download or something - then make sure to peek at the notes here as well:

Other References

By AJ ONeal

Was this useful to you? Share it!

Also, you can give me a tip or hire me.