A few weeks back a friend ran into a strange little bug that ended up corrupting some relatively unimportant system files on their phone. Those files were important enough that iOS wouldn't boot as long as the files remained corrupted, but unimportant enough that simply deleting them would cause iOS to generate them anew. The problem was, the phone wouldn't boot, so we couldn't access those files to delete them. So begins our journey.
One of my first thoughts when I realized what had happened was of this little project called ssh-rd by msftguy. For those who might not know, it was a really useful tool back in the day for essentially live-booting a small OS that you could SSH into (using limera1n). Offline filesystem access would be a perfect fix for this scenario.
I googled and found two similar projects that appear to work with checkm8 devices. We have SSH-Ramdisk-Maker-and-Loader by Ralph0045, and telnetd_ramdisk by danieltroger. Neither project worked for me out of the box (I'll spare the details, see issues page on both projects). I also found this somewhat related guide. While neither project worked for me, they proved the idea was possible and the source code pointed me in the right direction.
Rather than develop and release another tool, I'll try to walk through the steps for doing this manually and provide a couple of scripts that will help you along the way. This isn't really for the faint of heart. You will need to compile a lot of tools, and what worked for me may not work for you. This guide assumes a 64-bit device, though it could be modified to work for 32-bit.
At the end of this guide, I'll also provide a few scripts that can be used as a quick-start point that will hopefully save someone a bit of time.
My setup:
- Mac running 10.15 (Catalina)
- iPhone 8 running 13.5
Dependencies
- A device vulnerable to checkm8 (A11 processor or earlier)
- Your IPSW
- While it will need to be for the correct device, this doesn't need to be the version you have installed. This is the IPSW we will use as a base image to boot. I used 13.5, I've heard it works well for other people too. iOS 14 may not work for you here. You might need to try multiple versions and see what works.
- An APTicket for your device.
- This does not need to be for any specific version. I used a 13.7 ticket.
- You can extract one from a saved SHSH2 blob.
- Some keys from theiphonewiki.
- These should match the firmware of your IPSW.
- XCode command line tools
- xcode-select --install
- The iPhone SDK for your firmware
- We need this to compile restored_external
- You don't need to be a developer to download the SDKs anymore
- Drop it in /Library/Developer/CommandLineTools/SDKs/
- If you can't compile it you can try my binary, but it probably won't work
- img4 via img4lib
- kairos
- Kernel64Patcher
- irecovery via libirecovery
- compareFiles.py
curl 'https://raw.githubusercontent.com/dualbootfun/dualbootfun.github.io/d947e2c9b6090a1e65a46ea6a58cd840986ff9d9/source/compareFiles.py' | sed -n '3,$p' > ./bin/compareFiles.py
- ldid2 via xerub's ldid fork
- iproxy via libusbmuxd
- PyBoot - An easy way to pwn DFU, though not the only way
- Something that can extract a deb file (I use 7zip)
- Some dropbear resources
- A launch daemon
- A host key
- Note: normally it's a bad idea to reuse host keys, but in this case, it's not going to be a problem since the interface will only ever be accessible over USB. If you want to generate your own dropbear host key, though, that's not a problem.
- restored_external.c
- We'll use a modified version of the one helpfully created by danieltroger
- A couple more things we'll wget along the way
Setting up
- Extract your IPSW and put the relevant contents into a folder called "resources". You will know which resources are relevant because they will be listed for your device / firmware in theiphonewiki. Not all the files in your ipsw will be relevant.
7z x ./iPhone_4.7_P3_13.5_17F75_Restore.ipsw - Restore ramdisk (you don't need the update ramdisk or root filesystem)
- Restore ramdisk trustcache
- DeviceTree
- applelogo
- iBEC
- iBSS
- kernelcache
- Add your SHSH2 blob to the "resources" folder
Building the bootchain
- Decrypt - We'll store the decrypted files in the "decrypted" folder. Be sure to use IVs and keys from theiphonewiki as necessary.
mkdir ./decrypted/
img4 -i ./resources/iBSS* -o ./decrypted/iBSS.dec -k IV+KEY
img4 -i ./resources/iBEC* -o ./decrypted/iBEC.dec -k IV+KEY
img4 -i ./resources/applelogo* -o ./decrypted/applelogo.dec
img4 -i ./resources/DeviceTree* -o ./decrypted/devicetree.dec
img4 -i ./resources/kernelcache* -o ./decrypted/kernelcache.dec
img4 -i ./resources/*dmg -o ./decrypted/ramdisk.dec.dmg - Patch - Patch a few things and put them in the "patched" folder. We generate a binary patch for the kernelcache to use later.
mkdir ./patched/
kairos ./decrypted/iBSS.dec ./patched/iBSS.patched
kairos ./decrypted/iBEC.dec ./patched/iBEC.patched -b "rd=md0 -v"
Kernel64Patcher ./decrypted/kernelcache.dec ./patched/kernelcache.patched -a
./bin/compareFiles.py ./decrypted/kernelcache.dec ./patched/kernelcache.patched # > ./kc.bpatch - Sign - Extract the apticket from the shsh2 file, and then use it to sign everything.
mkdir ./rd_image/
plutil -extract ApImg4Ticket xml1 -o - ./resources/*.shsh2 | xmllint -xpath '/plist/data/text()' - | base64 -D > ./apticket.der
img4 -i ./patched/iBSS.patched -o ./rd_image/iBSS.img4 -T ibss -A -M ./apticket.der
img4 -i ./patched/iBEC.patched -o ./rd_image/iBEC.img4 -T ibec -A -M ./apticket.der
img4 -i ./decrypted/applelogo.dec -o ./rd_image/applelogo.img4 -T logo -A -M ./apticket.der
img4 -i ./decrypted/devicetree.dec -o ./rd_image/devicetree.img4 -T rdtr -A -M ./apticket.der
img4 -i ./resources/kernelcache* -o ./rd_image/kernelcache.img4 -T rkrn -P ./kc.bpatch -J -M ./apticket.der
img4 -i ./resources/*trustcache -o ./rd_image/trustcache -M ./apticket.der
Build the ramdisk
- Create ramdisk skel, populate it with some binaries and paths
mkdir ./rd_skel/
cd ./rd_skel/
wget -q 'http://newosxbook.com/tools/binpack64-256.tar.gz'
tar xzf ./binpack64-256.tar.gz
rm -f ./binpack64-256.tar.gz
mkdir -p ./var/root/
chmod +x ./usr/bin/* - Install ncurses libs
wget -q 'https://apt.bingner.com/debs/1443.00/ncurses_6.1+20181013-1_iphoneos-arm.deb'
7z x ./ncurses*.deb > /dev/null
rm ./ncurses*.deb
tar xf ./data.tar 'usr/lib/'
rm ./data.tar
cd ./usr/lib/
ln -s ./libncurses.6.dylib libncurses.5.4.dylib
cd ../../../ - Add dropbear resources
mkdir -p ./rd_skel/System/Library/LaunchDaemons/
mkdir -p ./rd_skel/etc/dropbear/
wget -q 'https://gist.githubusercontent.com/compilingEntropy/60e84d15bc274f88b6f53e6c3788e8e9/raw/597355e51831cafef8751b598c15832bac5fdd9d/dropbear.plist' -O ./rd_skel/System/Library/LaunchDaemons/dropbear.plist
wget -q 'https://gist.githubusercontent.com/compilingEntropy/f7042cfb1f402c6eff0afb14014cefe1/raw/c7d73ad466a3e8af7828a58a5be0c6f490b68f3e/id_rsa' -O - | base64 -d > ./rd_skel/etc/dropbear/id_rsa
wget -q 'https://gist.githubusercontent.com/compilingEntropy/ff0a80f156a135f7c386598a44ba8bb3/raw/bd594fcb4d252de3c41a25a267c4350cb70078b9/motd' -O ./rd_skel/etc/motd - Resize and create ramdisk
cp -a ./decrypted/*.dec.dmg ./ramdisk.dmg
hdiutil resize -size 150MB ./ramdisk.dmg
mkdir ./mnt/
hdiutil attach -mountpoint ./mnt/ ./ramdisk.dmg - Build / sign restored_external, add it to ramdisk
mv ./mnt/usr/local/bin/restored_external{,_original}
wget -q 'https://gist.githubusercontent.com/compilingEntropy/3c6f19f85cdce53fdf44b7b84005023d/raw/c0057abfe00eba3971a9c0a1d296e07dde23467c/restored_external.c' -O ./resources/restored_external.c
xcrun -sdk iphoneos clang -arch arm64 ./resources/restored_external.c -o ./mnt/usr/local/bin/restored_external
ldid2 -S ./mnt/usr/local/bin/restored_external - Apply our modifications
rsync --ignore-existing -ahuK ./rd_skel/ ./mnt/
- Sign and pack the ramdisk
hdiutil unmount ./mnt/
rmdir ./mnt/
img4 -i ./ramdisk.dmg -o ./rd_image/ramdisk -T rdsk -A -M ./apticket.der - Cleanup
rm ./ramdisk.dmg
rm -rf ./rd_skel/
Booting the ramdisk
- Enter DFU mode
- pwn DFU
cd ./bin/PyBoot/
./pyboot.py -p - Send the files / commands with irecovery. Sometimes it helps to wait a second or two between sending images to give the device a chance to finish loading them.
echo 'sending ibss (to jump to recovery mode)'
irecovery -f ./iBSS.img4
echo 'sending ibss again'
irecovery -f ./iBSS.img4
echo 'sending ibec'
irecovery -f ./iBEC.img4
echo 'loading ibec'
irecovery -c go
echo 'sending ramdisk'
irecovery -f ./ramdisk
echo 'loading ramdisk'
irecovery -c ramdisk
echo 'sending logo'
irecovery -f ./applelogo.img4
echo 'loading logo'
irecovery -c 'setpicture 5'
echo 'sending devicetree'
irecovery -f ./devicetree.img4
echo 'loading devicetree'
irecovery -c devicetree
echo 'sending trustcache'
irecovery -f ./trustcache
echo 'validating firmware against trustcache'
irecovery -c firmware
echo 'sending kernel'
irecovery -f ./kernelcache.img4
echo 'booting now'
irecovery -c bootx
Connecting
- iproxy
iproxy 2222:22 &
- SSH - password is alpine
ssh root@127.0.0.1 -p 2222
- When you're done, simply:
reboot
Wrapping up
While there are some limitations to what we can do while connected to a device with an SSH ramdisk, this can be extremely useful for resolving certain issues. In the case of my friend, deleting a few corrupt files was all that was needed for the device to boot right up again!
I do want to mention that this process is untested on iOS 14. iOS 14 introduced a "feature" where the phone is able to detect whether a device was booted from DFU or not, and if it finds that it has been, the device will reboot. If you test this with iOS 14, please report your findings in the comments or let me know on twitter and I'll update this post.
Lastly, I'll include a few scripts you can use as a template to speed this process along. If they don't say "done" after running them, something has failed along the way and you'll have to debug that. These scripts aren't necessarily "out of the box"—you will have to modify them to fit your setup. With those caveats in mind, this should be a good starting point if you want to automate this process.
- build_bootchain.sh - don't forget to add the IV+KEY!
- make_rd.sh
- boot.sh