Dans un précédent article de blog, j’ai expliqué comment déboguer le bootloader des appareils Apple. Le problème de cet article c’est qu’il nécessitait un câble qui n’est plus disponible à la vente depuis plus de deux ans.
Cette fois-ci je vais présenter une solution équivalente permettant d’accéder à la sortie série d’un iPhone mais aussi de faire du debugging avancé via SWD.
Cette solution a été développée par Thomas Roth (stacksmashing) et présentée à la DEFCON.
Solution de debugging interne d’Apple
Avant de parler de la solution de Thomas Roth. Il est important de comprendre pourquoi elle a été développée.
A la sortie de l’usine les appareils de Apple sont verrouillés de façon matérielle pour des raisons de sécurité. Il n’est pas possible d’y attacher un debugger à part sur sa propre application en cours de développement.
D’après Vice, certains chercheurs en sécurité iOS auraient mis la main sur des appareils non-verrouillés (dev-fused) utilisés lors des premières phases de développement des appareils. Mais sans le matériel adéquat, ces appareils n’ont aucune utilité.
C’est là qu’interviennent les câbles internes d’Apple avec des noms de primate tels que Kong, Kanzi ou Koba.
Câbles Gorilla (30 pins) Kong et Kanzi - photo de 1nsane_dev sur Twitter.
Utilisés avec un logiciel nommé astris, il est possible de faire du debugging de bas niveau (SWD) et d’exploiter pleinement le potentiel des appareils de développement.
Le logiciel astris - photo de 1nsane_dev sur Twitter.
Cet outil permet de stopper l’exécution des cœurs du CPU ainsi que de lire et d’écrire dans leur mémoire et bien d’autres possibilités à condition d’avoir un appareil de développement ainsi qu’un de ces câbles peu ordinaires.
Avec ces trois composants on peut avoir un contrôle de total du processeur principal d’un appareil Apple. Malheureusement ce n’est pas très pratique ni étique comme solution.
Tamarin
A la DEFCON 30, Thomas Roth a présenté The hitchhacker’s guide to iPhone Lightning & JTAG hacking. Dans ses slides il explique les particularités du connecteur lightning et les protocoles associés.
De plus il présente aussi une carte permettant d’obtenir une sortie UART et la possibilité de faire du Serial Wire Debugging (SWD).
Tamarin cable.
Cette carte n’étant pas encore disponible, Thomas Roth propose d’utiliser une carte Raspberry Pi Pico et un câble lightning pour faire notre propre sonde UART et SWD.
Assemblage du câble
Pour fabriquer votre propre câble il faudra un Raspberry Pi Pico, un câble lightning et des câbles GPIO.
Ensuite, il suffit de suivre la documentation sur Github pour l’assemblage.
Dans un premier temps je n’ai pas pu faire fonctionner la connexion car il semble que certains câbles comme celui que j’ai acheté ont un brochage différent que celui décrit. Merci à tihmstar pour le tip.
J’ai fini par souder les fils du câble lightning à des câbles GPIO que j’ai branché sur les pins de la carte de cette façon :
- GPIO1
L1n
(vert) - GPIO2
L1p
(blanc) - GND
GND
(noir) - GPIO3
ID1
(orange) - GPIO4
ID0
(rouge) - 5V
5V
(yellow)
Après une session de soudage, voici le résultat, pas si catastrophique.
Résultat catastrophique mais fonctionnel de mes soudures.
Mais n’étant pas satisfait du résultat, j’ai préféré commander des breakout boards de eLabGuy pour avoir un setup plus propre, comme ci-dessous.
Setup sans soudure - thanks tjkr0wn.
Compilation du firmware
Pour pouvoir interagir avec Tristar, la puce du connecteur lightning femelle embarquée dans l’iPhone, et lui dire qu’on va faire du SWD, il nous faut un logiciel pour envoyer les signaux via le cable lightning mâle.
On va donc compiler le firmware pour le Raspberry Pico nommé : tamarin-firmware.
Pour ça, j’ai utilisé une machine sous Ubuntu 22.04.
Avant tout, il faut installer les paquets nécessaires à la compilation du firmware: sudo apt install cmake gcc-arm-none-eabi g++ git
.
On peut ensuite procéder au téléchargement du SDK et du firmware pour le Pico, puis à sa compilation.
1
2
3
4
5
6
7
8
9
mkdir -p tamarin; cd $_
git clone https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk; git submodule update --init
export PICO_SDK_PATH=$(pwd)
cd ../
git clone https://github.com/stacksmashing/tamarin-firmware.git; cd $(basename $_ .git)
mkdir build; cd $_
cmake ..
make -j
Si tout s’est bien passé vous devriez avoir les fichiers suivants dans le répertoire build
:
1
2
3
4
5
6
7
λ ~/tamarin/tamarin-firmware/build(main*) » ls
CMakeCache.txt lightning_tx.pio.h tamarin_firmware.dis
CMakeFiles Makefile tamarin_firmware.elf
cmake_install.cmake pico-sdk tamarin_firmware.elf.map
elf2uf2 pioasm tamarin_firmware.hex
generated probe.pio.h tamarin_firmware.uf2
lightning_rx.pio.h tamarin_firmware.bin
Le fichier qui nous intéresse est tamarin_firmware.uf2
que l’on va copier sur la carte Raspberry Pi Pico.
Installation du firmware
Branchez la carte sur un des ports USB de votre ordinateur et en même temps appuyez sur le bouton BOOTSEL
(vous ne pouvez pas le rater c’est le seul bouton présent sur le Pico). Un nouveau block device devrait être détecté.
1
2
3
4
5
λ ~/tamarin/tamarin-firmware/build(main*) » lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
[..]
sdb 8:16 1 128M 0 disk
└─sdb1 8:17 1 128M 0 part
Montez-le dans un répertoire, copiez tamarin_firmware.uf2
dans ce même répertoire et démontez-le
1
2
3
4
λ ~/tamarin/tamarin-firmware/build(main*) » sudo mkdir -p /mnt/pico
λ ~/tamarin/tamarin-firmware/build(main*) » sudo mount /dev/sdb1 $_
λ ~/tamarin/tamarin-firmware/build(main*) » sudo cp tamarin_firmware.uf2 $_
λ ~/tamarin/tamarin-firmware/build(main*) » sudo umount $_
Vérifiez avec dmesg
que le nouveau périphérique USB est détécté
1
2
3
4
5
6
7
usb 1-4: New USB device found, idVendor=2b3e, idProduct=0004, bcdDevice= 1.00
usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-4: Product: Tamarin Cable
usb 1-4: Manufacturer: stacksmashing
usb 1-4: SerialNumber: 31337
cdc_acm 1-4:1.0: ttyACM0: USB ACM device
cdc_acm 1-4:1.3: ttyACM1: USB ACM device
Le premier device ttyACM0
correspond à l’interface du firmware permettant d’interagir avec lui. Le second est utilisé pour le mode DCSD, qui permet d’avoir une sortie série vers ce périphérique.
Connexion
Maintenant que le firmware est installé on peut se connecter au Raspberry Pi Pico avec minicom
: sudo minicom -D /dev/ttyACM0 -b 115200
. Appuyez sur entrée et le menu du firmware s’affiche.
Menu du firmware tamarin.
On se retrouve avec 5 options :
- JTAG mode : active le mode JTAG via la puce dans le connecteur lightning femelle (Tristar/Hydra).
- DCSD mode : active le mode DCSD qui permet d’avoir une sortie série sur le second périphérique (ttyACM1).
- Reset device : Redémarrage l’appareil.
- Reset and DFU : Redémarre en mode DFU.
- Reset Tamarin Cable : Réinitialise la connexion USB du firmware.
Le mode USB n’est pas supporté, donc il n’est pas possible de faire du transfert de données ou de charger un appareil avec ce cable.
DCSD
Pour utiliser le mode DCSD, c’est assez comparé au SWD que l’on va voir juste après. Il suffit d’activer le mode DCSD (2).
Dans une deuxième fenêtre de terminal on se connecte au device /dev/ttyACM1
via minicom
. Puis on peut brancher un appareil avec un port lightning femelle. Dans mon cas un iPhone 14 Pro.
Tamarin en mode DCSD et la sortie série.
Le firmware a bien envoyé la requête au Tristar et confirme que le mode DCSD est activé. On peut donc acceder à la sortie série de l’iPhone lorsqu’il démarre.
Serial Wire Debug (SWD)
Le SWD est à peu près similaire à JTAG, mais n’existe que sur les architectures ARM et utilise moins de pins que JTAG.
Pour la suite de cet article on va utiliser le logiciel OpenOCD pour pouvoir faire du debugging de bas niveau avec un iPhone 7 demote l’aide de checkm8.
OpenOCD
Téléchargeons et compilons le fork d’OpenOCD pour le Pico et le firmware du Tamarin.
1
2
3
4
5
6
7
apt update; apt install -y libtool pkg-config libusb-1.0-0-dev
git clone https://github.com/stacksmashing/openocd.git
cd openocd
git submodule update --init
./bootstrap
./configure --enable-tamarin
make -j
Le binaire compilé est ensuite disponible dans ./src/openocd
.
Demotion
Comme dit plus haut, les appareils de production sont verrouillés et la fonctionnalité de SWD n’est pas activée. C’est là qu’entre en jeu checkm8 une faille de sécurité dans la stack USB de certains appareils qui permet justement de demote un appareil.
Le terme demotion désigne la modification de l’état du fusible effectif d’un appareil pour permettre le débogage de l’Application Processor dans notre cas.
Checkm8 est limité à certains appareils (A5 - A11). Pour la suite de l’article, j’utilise un iPhone 7 avec une puce A10 Fusion.
A l’aide de checkra1n on peut exploiter checkm8 pour demote l’appareil.
Demotion d’un iPhone 7 avec checkra1n.
JTAG mode
Sur le Pico, activez le mode JTAG, puis branchez votre appareil au connecteur lightning (si vous faites l’inverse ça ne fonctionnera pas).
Dès que l’iPhone est branché, le firmware envoie la requête au Tristar qui active le mode JTAG.
Activation du mode JTAG.
Connexion avec OpenOCD
Le mode JTAG est activé et l’iPhone est demoted. Il nous reste qu’à récupérer le fichier configuration disponible ici.
Lancez OpenOCD : sudo ./src/openocd -f tcl/interface/tamarin.cfg -f t8010.cfg
.
Si vous souhaitez acceder à OpenOCD depuis une machine différente vous pouvez ajouter l’option -c bindto 0.0.0.0
pour que le logiciel écoute sur toutes les interfaces.
Si vous avez une erreur du type Erro: Can't find a tamarin device! Please check device connections and permissions
et que la carte est branchée, le VID n’est pas le bon.
Vous devrez changer la valeur de la macro VID
dans le fichier src/jtag/drivers/tamarin.c:80
à la valeur 0x2B3E
.
Si tout se passe bien OpenOCD se lance et écoute sur différents ports en local.
Exécution d’OpenOCD pour le tamarin
Le port qui nous intéresse est le 4444 avec lequel on peut se connecter via telnet pour accéder à l’interface de contrôle.
Liste des coeurs du CPU
Depuis cette interface on peut sélectionner un des cœurs du SOC et stopper son exécution. Cela permet par la suite d’attacher un débogger comme GDB ou même IDA.
IDA
IDA embarque un debugger qui permet de se connecter au serveur GDB de OpenOCD. Il suffit de spécifier les informations de connexion de la machine sur laquelle tourne OpenOCD comme ci-dessous.
Cette option se trouve dans Debugger -> Process options
.
Option de connexion au serveur de debug.
Il ne reste plus qu’à démarrer le debugging en appuyant sur “play” (le bouton vert).
On arrive dans la fonction arch_halt
(sub_1000004FC
). Tout comme GDB il y a la possibilité de placer des breakpoints, step in d’instruction en instructions et visualiser la stack ainsi que les registres.
IDA Pro en mode debug.
Dans cet article on a vu comment faire son propre câble JTAG pour les appareils Apple avec port lightning et comment déboguer la SecureROM de l’iPhone dans IDA.
Ce câble a quelques inconvénients : il faudra quand même utiliser un câble normal pour jailbreak avec checkra1n. Si le JTAG est activé sur le câble il faudra le débrancher puis le brancher à nouveau qu’il soit réinitialisé.
Ce blog post est assez similaire à celui sur le debugging de l’iBoot. Mais cette fois-ci j’avais envie de mettre en avant ce cable DIY et ses possibilités tout en utilisant du matériel accessible et open source.
J’espère que ce post vous a plu, n’hésitez pas à me contacter sur Twitter ou même par mail si vous avez des questions.
Liens et sources :