1#!/bin/bash
2
3###############################################################################
4# kcinstall
5#
6# A script for personalizing and installing kernelcaches and related files to
7# iOS devices.
8#
9# Andrew Myrick, Mike Smith
10# Copyright © 2012,2103 Apple Inc. All rights reserved.
11###############################################################################
12
13EDM="xcrun -sdk iphoneos.internal embedded_device_map"
14MOBDEV=`xcrun -sdk iphoneos.internal -f mobdev`
15PERSONALIZE_IMG4="xcrun -sdk iphoneos.internal personalize_img4"
16TCPRELAY="xcrun -sdk iphoneos.internal tcprelay"
17FSREMOUNT="`dirname $0`/fsremount"
18
19CLEAN_TMP=
20TCPRELAY_OUTPUT=
21TCPRELAY_PID=
22
23kBoardIDKey="BoardId"
24kChipIDKey="ChipID"
25kECIDKey="UniqueChipID"
26
27VERBOSELEVEL=0
28ROOTPASS="alpine"
29KCACHEDST=/System/Library/Caches/com.apple.kernelcaches/kernelcache
30SEPOSDST=/usr/standalone/firmware/sep-firmware.img4
31PTYPE=" -X"
32BOARDID=
33CHIPID=
34ECID=
35LOCATIONID=
36UDID=
37
38function usage() {
39	echo "Usage: `basename $0` [-l location|-d udid] [options] [-S path|-C path] [-k kernelcache|-s sepos|-c file]"
40	echo
41	echo "Device selection:"
42	echo "	-l Location ID"
43	echo "	-u UDID"
44	echo
45	echo "Options:"
46	echo "	-F Force local signing"
47	echo "	-h Print this help"
48	echo "	-k Kernelcache to install"
49	echo "	-s SEP/OS to install"
50	echo "  -c arbitrary file to install"
51	echo "	-S override SEP/OS install location"
52	echo "	-C override SEP/OS install location"
53	echo "	-p Root password (default: alpine)"
54	echo "	-X Enable user authlisting (default)"
55	echo "	-v Enable verbosity (may specify more than once)"
56}
57
58function cleanup() {
59	if [[ $TCPRELAY_PID ]]; then
60		kill $TCPRELAY_PID
61		wait $TCPRELAY_PID 2>/dev/null
62	fi
63	if [ $CLEAN_TMP ]; then
64		rm -rf $TMPDIR
65	fi
66}
67
68trap cleanup EXIT
69exec 9>/dev/null
70
71args=$(getopt Fhk:l:p:s:S:c:C:u:vX $*)
72if [ $? != 0 ]; then
73	usage; exit 1
74fi
75set -- $args
76while [ $# -gt 0 ]; do
77	case $1 in
78	-F)
79		PTYPE=" -F";;
80	-h)
81		usage; exit 0;;
82	-k)
83		KCACHE=$2; shift;;
84	-s)
85		SEPOS=$2; shift;;
86	-S)
87		SEPOSDST=$2; shift;;
88	-c)
89		COPYFILE=$2; shift;;
90	-C)
91		COPYDST=$2; shift;;
92	-l)
93		if [[ $UDID ]]; then
94			echo "Error: -l may not be specified with -u"
95			echo
96			usage; exit 1;
97		fi;
98		LOCATIONID=$2;
99		shift;;
100	-p)
101		ROOTPASS=$2;
102		shift;;
103	-u)
104		if [[ $LOCATIONID ]]; then
105			echo "Error: -u may not be specified with -l"
106			echo
107			usage; exit 1;
108		fi;
109		UDID=$2;
110		shift;;
111	-v)
112		case $VERBOSELEVEL in
113		0)
114			exec 9>&1
115			VERBOSELEVEL=1;;
116		1)
117			set -x
118			VERBOSELEVEL=2;;
119		*)
120			;;
121		esac;;
122	-X)
123		PTYPE=" -X";;
124	--)
125		if [ $# != 1 ]; then
126			echo "Unrecognized options: $*"
127			usage; exit 1
128		fi
129		;;
130	*)
131		echo "Unrecognized option: $1"
132		usage; exit 1;;
133	esac
134	shift
135done
136
137# Validate kernelcache and device location
138
139if [[ ( -z $KCACHE && -z $SEPOS && -z $COPYFILE ) ]]; then
140	echo "Error: at least one of -k, -s or -c must be specified."
141	echo
142	usage; exit 1;
143fi
144
145if [[ $LOCATIONID ]]; then
146	mobdev list 2>&1 | grep $LOCATIONID > /dev/null
147	if [ $? -ne 0 ]; then
148		echo "Cannot find device at location $LOCATIONID"
149		exit $?
150	fi
151	MBDEVICE=" -l $LOCATIONID"
152	TRDEVICE=" --locationid $LOCATIONID"
153elif [[ $UDID ]]; then
154	mobdev list 2>&1 | grep $UDID > /dev/null
155	if [ $? -ne 0 ]; then
156		echo "Cannot find device with UDID $UDID"
157		exit $?
158	fi
159	MBDEVICE=" -u $UDID"
160	TRDEVICE=" --serialnumber $UDID"
161fi
162
163TMPDIR=`mktemp -d -t \`basename $0\``
164if [[ -z $TMPDIR ]]; then
165    echo "Could not create temporary directory"
166    exit 1;
167fi
168CLEAN_TMP=1
169
170# Personalize the kernelcache / SEP/OS if needed
171
172CHIPID=`$MOBDEV $MBDEVICE get NULL $kChipIDKey 2>&1 | awk '/CFNumber/ {print $5}' | sed 's/[+|,]//g'`
173if [[ -z $CHIPID ]]; then
174	echo "Failed to read ChipID from device"
175	exit 1;
176fi
177
178IMAGEFORMAT=`$EDM -query SELECT DISTINCT ImageFormat FROM Targets WHERE ChipID==$CHIPID`
179if [[ $IMAGEFORMAT == 'im4p' ]]; then
180	BOARDID=`$MOBDEV $MBDEVICE get NULL $kBoardIDKey 2>&1 | awk '/CFNumber/ {print $5}' | sed 's/[+|,]//g'`
181	if [[ -z $BOARDID ]]; then
182		echo "Failed to read BoardID from device"
183		exit 1;
184	fi
185
186	ECID=`$MOBDEV $MBDEVICE get NULL $kECIDKey 2>&1 | awk '/ECID in hex/ { print $4}'`
187	if [[ -z $ECID ]]; then
188		echo "Failed to read ECID from device"
189		exit 1;
190	fi
191
192	if [[ -n $KCACHE ]]; then
193		echo "Personalizing kernelcache for device with BoardID=`printf %d $BOARDID` ChipID=`printf 0x%x $CHIPID` ECID=`printf 0x%x $ECID`"
194		$PERSONALIZE_IMG4 -a -b $BOARDID -c $CHIPID -d 1 -e $ECID -n 0 -s 0 -i KernelCache=$KCACHE -i RestoreKernelCache=$KCACHE -o $TMPDIR $PTYPE >&9
195		if [ $? -ne 0 ]; then
196			echo "Personalization failed."
197			exit 1
198		fi
199		KCACHE=$TMPDIR/"`basename ${KCACHE}`.img4"
200	fi
201
202	if [[ -n $SEPOS ]]; then
203		echo "Personalizing SEP/OS for device with BoardID=`printf %d $BOARDID` ChipID=`printf 0x%x $CHIPID` ECID=`printf 0x%x $ECID`"
204		$PERSONALIZE_IMG4 -a -b $BOARDID -c $CHIPID -d 1 -e $ECID -n 0 -s 0 -i SEP=$SEPOS -i RestoreSEP=$SEPOS -l -o $TMPDIR $PTYPE >&9
205		if [ $? -ne 0 ]; then
206			echo "Personalization failed."
207			exit 1
208		fi
209		SEPOS=$TMPDIR/"`basename ${SEPOS%.*}`.img4"
210	fi
211fi
212
213# Fire up tcprelay and wait for it start
214
215TCPRELAY_OUTPUT=`mktemp -t tcprelay`
216if [[ -z $TCPRELAY_OUTPUT ]]; then
217    echo "Could not create tcprelay output"
218    exit 1;
219fi
220$TCPRELAY $TRDEVICE --dynamicports --autoexit telnet rsync > $TCPRELAY_OUTPUT 2>&1 &
221TCPRELAY_PID=$!
222
223while [[ `stat -f "%z" $TCPRELAY_OUTPUT` -eq 0 ]]; do
224	sleep 1
225done
226
227TELNET_PORT=`awk '/127\.0\.0\.1.*telnet/ {print $9}' $TCPRELAY_OUTPUT | awk -F : '{print $2}'`
228RSYNC_PORT=`awk '/127\.0\.0\.1.*rsync/ {print $9}' $TCPRELAY_OUTPUT | awk -F : '{print $2}'`
229
230# Mount the device's root filesystem read-write
231
232$FSREMOUNT localhost $TELNET_PORT $ROOTPASS rw >&9
233case $? in
234	128)
235		SKIP_MOUNT_RO=1;;
236	0)
237		;;
238	*)
239		echo "Failed to mount root filesystem rw"
240		exit 1;;
241esac
242
243# Send over the kernelcache
244if [[ -n $KCACHE ]]; then
245	RSYNC_PASSWORD=$ROOTPASS rsync -v $KCACHE rsync://root@localhost:$RSYNC_PORT/root/$KCACHEDST >&9
246	if [ $? -ne 0 ]; then
247		echo "Failed to install kernelcache"
248		exit 1
249	else
250		echo "Kernelcache installed successfully."
251	fi
252fi
253
254# Send over the SEP/OS
255if [[ -n $SEPOS ]]; then
256	RSYNC_PASSWORD=$ROOTPASS rsync -v $SEPOS rsync://root@localhost:$RSYNC_PORT/root/$SEPOSDST >&9
257	if [ $? -ne 0 ]; then
258		echo "Failed to install SEP/OS"
259		exit 1
260	else
261		echo "SEP/OS installed successfully."
262	fi
263fi
264
265# Copy the file
266if [[ -n $COPYFILE ]]; then
267	RSYNC_PASSWORD=$ROOTPASS rsync -v $COPYFILE rsync://root@localhost:$RSYNC_PORT/root/$COPYDST >&9
268	if [ $? -ne 0 ]; then
269		echo "Failed to install $COPYDST"
270		exit 1
271	else
272		echo "$COPYDST installed successfully."
273	fi
274fi
275
276
277# Remount the root filesystem read-only if needed
278
279if [ ! $SKIP_MOUNT_RO ]; then
280	$FSREMOUNT localhost $TELNET_PORT $ROOTPASS ro >&9
281	if [ $? -ne 0 ]; then
282		echo "Failed to mount root filesystem ro"
283		exit 1
284	fi
285fi
286
287