> For the complete documentation index, see [llms.txt](https://blog.s1rn3tz.ovh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blog.s1rn3tz.ovh/pentest-mobile/ios/corruptions-de-memoire-ios/arm64.md).

# ARM64

## Fondamentaux

### Instructions de base

Ces instructions agissent sur les valeurs des registres. Elles constituent la base du calcul ARM64.

<table data-header-hidden><thead><tr><th width="166">Instruction</th><th>Opération</th><th>Exemple</th><th>Notes</th></tr></thead><tbody><tr><td><code>MOV</code></td><td>Déeplacer / charger immédiatement</td><td><code>mov x0, #42</code></td><td>16 bits immédiat, ou registre à registre</td></tr><tr><td><code>MOVK</code></td><td>Déplacer et garder</td><td><code>movk x0, #0xDEAD, lsl #16</code></td><td>Insérer 16 bits à une position décalée</td></tr><tr><td><code>MOVN</code></td><td>Ne pas déplacer</td><td><code>movn x0, #6</code></td><td>Chargement par NON bit à bit : ~6 = -7 (complément à deux)</td></tr><tr><td><code>ADD</code></td><td>Ajouter</td><td><code>add x2, x0, x1</code></td><td>x2 = x0 + x1</td></tr><tr><td><code>SUB</code></td><td>Soustraire</td><td><code>sub x4, x3, x2</code></td><td>x4 = x3 - x2</td></tr><tr><td><code>MUL</code></td><td>Multiplier</td><td><code>mul x7, x5, x6</code></td><td>64 bits inférieurs du produit</td></tr><tr><td><code>AND</code></td><td>ET bit à bit</td><td><code>and x10, x8, x9</code></td><td>Opérations de masquage, extraction de bits</td></tr><tr><td><code>ORR</code></td><td>OU bit à bit</td><td><code>orr x12, x11, x9</code></td><td>Définir des bits spécifiques</td></tr><tr><td><code>EOR</code></td><td>XOR bit à bit</td><td><code>eor x15, x13, x14</code></td><td>Basculer les bits, obfuscation simple</td></tr><tr><td><code>LSL</code></td><td>Décalage logique vers la gauche</td><td><code>lsl x17, x16, #8</code></td><td>Multiplier par une puissance de 2</td></tr><tr><td><code>LSR</code></td><td>Décalage logique vers la droite</td><td><code>lsr x19, x18, #4</code></td><td>Division non signée par une puissance de 2</td></tr><tr><td><code>ASR</code></td><td>Décalage arithmétique vers la droite</td><td><code>asr x1, x0, #3</code></td><td>Division signée (préserve le bit de signature)</td></tr><tr><td><code>NEG</code></td><td>Nier</td><td><code>neg x0, x0</code></td><td>négation du complément à deux</td></tr></tbody></table>

### Accès mémoire

L'architecture ARM64 utilise des instructions de chargement/stockage explicites. La compréhension des modes d'adressage est essentielle pour interpréter les crash et les heap operations.

| `LDR`  | Registre de chargement (64 bits)  | `ldr x0, [x1]`              | Registre de base                                   |
| ------ | --------------------------------- | --------------------------- | -------------------------------------------------- |
| `LDRB` | Octet de chargement (8 bits)      | `ldrb w0, [x1, #5]`         | Base + décalage immédiat                           |
| `LDRH` | Load halfword (16 bits)           | `ldrh w0, [x1, x2]`         | Base + décalage du registre                        |
| `LDP`  | Paire de chargement (2 registres) | `ldp x29, x30, [sp], #16`   | Post-index (charger, puis ajouter 16 à SP)         |
| `STR`  | Registre de stockage (64 bits)    | `str x0, [x1]`              | Registre de base                                   |
| `STP`  | Paire de stockage                 | `stp x29, x30, [sp, #-16]!` | Pré-indexation (soustraire 16 de SP, puis stocker) |
| `ADRP` | Adresse de la page de 4 KB        | `adrp x0, label@PAGE`       | Adresse de page relative au PC                     |

### Branchement et flux de contrôle

| `B`      | Branche inconditionnelle                   | `b label`       | Plage relative au PC, ±128 Mo                              |
| -------- | ------------------------------------------ | --------------- | ---------------------------------------------------------- |
| `BL`     | Branche avec lien                          | `bl function`   | Définit X30 = adresse de retour, puis branches             |
| `BR`     | Branche à enregistrer                      | `br x16`        | Branche indirecte (utilisée par les stubs PLT)             |
| `BLR`    | Lien vers la branche pour s'inscrire       | `blr x8`        | Appel indirect : X30 = adresse de retour, saut vers X8     |
| `RET`    | Retour                                     | `ret`           | Branches vers X30. **Cible ROP.**                          |
| `CBZ`    | Comparer et brancher si zéro               | `cbz x0, label` | Aucun CMP requis — les tests s'enregistrent directement    |
| `CBNZ`   | Comparer et brancher si non nul            | `cbnz x1, loop` | Motif de bord arrière en boucle commune                    |
| `B.cond` | Branche conditionnelle                     | `b.eq target`   | Teste les indicateurs NZCV des CMP/TST précédents          |
| `CMP`    | Comparer (définit les indicateurs)         | `cmp x0, x1`    | Alias ​​pour`subs xzr, x0, x1`                             |
| `TST`    | Bits de test (ET, définit les indicateurs) | `tst x0, #0xFF` | Alias ​​pour`ands xzr, x0, #0xFF`                          |
| `SVC`    | Appel du superviseur                       | `svc #0x80`     | Appel système sur macOS/iOS (X16 = numéro d'appel système) |
| `BRK`    | Point de rupture                           | `brk #0`        | Déclenche débogage                                         |

### Conditions

<table data-header-hidden><thead><tr><th width="169">Suffixe</th><th>Signification</th><th>Flags testés</th><th>Cas d'usage</th></tr></thead><tbody><tr><td><code>EQ</code></td><td>Égal</td><td>Z = 1</td><td>Après <code>CMP x0, x1</code>: branchement si x0 == x1</td></tr><tr><td><code>NE</code></td><td>Non égal</td><td>Z = 0</td><td>Après <code>CMP</code>: branchement si x0 != x1</td></tr><tr><td><code>GT</code></td><td>Supérieur à (signé)</td><td>Z=0, N=V</td><td><code>if (a > b)</code></td></tr><tr><td><code>LT</code></td><td>Inférieur à (signé)</td><td>N ≠ V</td><td><code>if (a &#x3C; b)</code></td></tr><tr><td><code>GE</code></td><td>Supérieur ou égal (signé)</td><td>N = V</td><td><code>if (a >= b)</code></td></tr><tr><td><code>LE</code></td><td>Inférieur ou égal (signé)</td><td>Z=1 ou N!=V</td><td><code>if (a &#x3C;= b)</code></td></tr><tr><td><code>HI</code></td><td>Supérieur (non signé)</td><td>C=1, Z=0</td><td>Comparaison non signée</td></tr><tr><td><code>LO</code>/<code>CC</code></td><td>Inférieur (non signé)</td><td>C = 0</td><td>Comparaison non signée</td></tr><tr><td><code>MI</code></td><td>Moins (négatif)</td><td>N = 1</td><td>Le résultat était négatif</td></tr><tr><td><code>PL</code></td><td>Plus (non négatif)</td><td>N = 0</td><td>Le résultat était positif ou nul.</td></tr><tr><td><code>VS</code></td><td>Débordement</td><td>V = 1</td><td>Un débordement signé s'est produit</td></tr></tbody></table>

## AAPCS64

#### À quoi sert l’AAPCS64 ?

C’est l’ensemble des règles qui définissent comment les fonctions ARM64 :

* reçoivent leurs arguments,
* renvoient leurs résultats,
* utilisent les registres,
* gèrent la pile (stack).

Toutes les fonctions en environnement iOS ARM64 suivent ces règles.

### Passage des arguments

Les 8 premiers arguments sont passés dans les registres :

| Argument | Registre |
| -------- | -------- |
| arg1     | X0       |
| arg2     | X1       |
| arg3     | X2       |
| arg4     | X3       |
| arg5     | X4       |
| arg6     | X5       |
| arg7     | X6       |
| arg8     | X7       |

* Les arguments à partir du 9e sont placés sur la pile.
* X8 est utilisé pour certaines structures volumineuses retournées par une fonction.

### Valeur de retour

* La valeur retournée est placée dans **X0**.
* Pour une valeur de 128 bits, **X0 + X1** sont utilisés.

### Registres sauvegardés

#### Caller-saved (X0–X18)

La fonction appelée peut les modifier librement.

* Si l’appelant veut conserver leur valeur, il doit les sauvegarder avant l’appel.

#### Callee-saved (X19–X28, X29, X30)

La fonction appelée doit les restaurer avant de retourner.

* C’est pourquoi on voit souvent X29 et X30 sauvegardés au début d’une fonction.

### Prologue et épilogue d’une fonction

Au début :

```
stp x29, x30, [sp, #-16]!mov x29, sp
```

* Sauvegarde X29 et X30 sur la pile.
* Crée une nouvelle frame.

À la fin :

```
ldp x29, x30, [sp], #16ret
```

* Restaure X29 et X30.
* Retourne à l’appelant via l’adresse contenue dans X30.

### Organisation d’une frame de pile

Une frame contient généralement :

```
Arguments supplémentaires
Adresse de retour (X30)
Frame pointer précédent (X29)
Registres sauvegardés (X19–X28)
Variables locales
```

La pile doit toujours être alignée sur **16 octets**.

### Importance pour l’exploitation (exploitation de vulnérabilités)

Lorsqu’un buffer local est situé sur la pile :

```
char buffer[64];
```

un dépassement de tampon (buffer overflow) peut écraser :

1. les variables voisines,
2. X29 sauvegardé,
3. X30 sauvegardé.

Si l’attaquant contrôle **X30**, alors lors du :

```
ret
```

le programme saute vers une adresse choisie par l’attaquant.

C’est l’équivalent ARM64 de l’écrasement de l’adresse de retour sur x86 et la base des attaques **ROP (Return-Oriented Programming)**.

#### Idée essentielle

Pour analyser un crash ou une vulnérabilité ARM64 :

* **X0–X7** → arguments de fonction.
* **X0** → valeur de retour.
* **X30** → adresse de retour.
* **X29** → frame pointer.
* Le motif `stp x29, x30` / `ldp x29, x30` est le signe classique du début et de la fin d’une fonction.
* Contrôler le **X30 sauvegardé sur la pile** permet potentiellement de contrôler le flot d’exécution lors du `RET`.

## Dispatch Objective-C (`objc_msgSend`)

Contrairement à C++, un appel de méthode Objective-C n'appelle pas directement une fonction.

Code source :

```
[obj method:arg];
```

devient approximativement :

```
X0 = obj      ; selfX1 = selector ; _cmdX2 = argBL _objc_msgSend
```

Le runtime Objective-C :

1. lit le pointeur `isa` de l'objet,
2. trouve sa classe,
3. cherche la méthode dans le cache ou la table des méthodes,
4. récupère l'IMP (adresse réelle de la fonction),
5. saute vers cette fonction.

### Registres importants

| Registre | Contenu                 |
| -------- | ----------------------- |
| X0       | `self` (objet)          |
| X1       | `_cmd` (sélecteur)      |
| X2+      | arguments de la méthode |

Exemple :

```
[weapon initWithName:@"Excalibur" damage:100];
```

devient :

```
X0 = weaponX1 = @selector(initWithName:damage:)X2 = @"Excalibur"X3 = 100BL _objc_msgSend
```

### Pourquoi c'est important en debugging ?

Dans LLDB :

```
breakpoint set -n objc_msgSend
```

Puis examiner :

```
register read x0register read x1
```

permet de savoir :

* quel objet reçoit le message ;
* quelle méthode est appelée.

C'est une technique fondamentale pour comprendre le comportement d'une application iOS.

### Intérêt pour l'exploitation

Le pointeur `isa` détermine quelle classe traite les messages.

Si une corruption mémoire permet de modifier `isa` :

```
Objet → faux isa → fausse classe → faux dispatch
```

alors les appels Objective-C peuvent être redirigés vers des structures contrôlées.

Cette famille d'attaques est souvent appelée :

* counterfeit object attacks ;
* JOP (Jump-Oriented Programming) basé sur le runtime Objective-C.

## Format binaire Mach-O

Les exécutables iOS/macOS utilisent le format **Mach-O**.

Structure simplifiée :

```
mach_header_64Load Commands__TEXT__DATA...
```

### Éléments importants

| `mach_header_64`    | Magic, type de processeur, flags (PIE)            | flag PIE → ASLR appliqué ; type de processeur confirmé ARM64                          |
| ------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `__TEXT`segment     | Code exécutable, chaînes de caractères littérales | Les gadgets ROP/JOP sont disponibles ici (lecture + exécution)                        |
| `__TEXT.__stubs`    | souches de saut équivalentes à PLT                | Appels indirects à des fonctions importées                                            |
| `__DATA`segment     | Variables globales, métadonnées ObjC              | `__objc_classlist`: toutes les classes ObjC dans le binaire                           |
| `__DATA.__got`      | Tableau de décalage global                        | Pointeur de fonction pour lazy binding ⇒ GOT overwrite attacks                        |
| `LC_LOAD_DYLIB`     | Bibliothèques dynamiques liées                    | Identifie les frameworks chargés (surface d'attaque).                                 |
| `LC_CODE_SIGNATURE` | données de signature de code                      | Doit être valide sur iOS, aucune injection de code sans contournement n'est autorisée |
| `LC_MAIN`           | Décalage du point d'entrée                        | L'exécution commence après la configuration de dyld.                                  |

***

## Principales protections de sécurité iOS ARM64

Apple combine plusieurs mécanismes.

<table data-header-hidden><thead><tr><th>Fonctionnalité</th><th>Effet</th><th width="180">Mécanisme ARM64</th><th>Version d'introduction</th></tr></thead><tbody><tr><td><strong>ASLR + PIE</strong></td><td>Randomise les adresses de chargement binaire/bibliothèque</td><td>Chaque <code>MH_PIE</code>exécutable reçoit une slide aléatoire</td><td>iOS 4.3</td></tr><tr><td><strong>W^X</strong></td><td>Les pages sont soit modifiables, soit exécutables, jamais les deux.</td><td>Les permissions des tables de pages sont appliquées par MMU.</td><td>Toujours</td></tr><tr><td><strong>Stack Canaries</strong></td><td>Détection des débordements de tampon de pile</td><td><code>-fstack-protector</code>: un cookie aléatoire sur la pile</td><td>N'est pas lié à une version mais au moment de la compilation</td></tr><tr><td><strong>PAC</strong></td><td>Codes d'authentification de pointeur</td><td>PACIA/PACIB pointeur de signes; AUTIA/AUTIB vérifient</td><td>A12+ (iPhone XS)</td></tr><tr><td><strong>MTE</strong></td><td>Extension d'étiquetage de mémoire</td><td>Tags de 4 bits sur les pointeurs et la mémoire ⇒ détection des use-after-free</td><td>ARM v8.5</td></tr><tr><td><strong>Signature de code</strong></td><td>Tout le code doit être signé par Apple ou le développeur.</td><td>Le noyau vérifie les hachages de page en cas de défaut</td><td>iOS 2.0</td></tr></tbody></table>

{% hint style="info" %}
**PAC sur ARM64:** Sur les appareils A12+, l'instruction `PACIASP`signe le registre de lien (X30) avec une clé dérivée du pointeur de pile. L'épilogue `AUTIASP`vérifie la signature avant le return `RET`. Si le registre de lien enregistré a été altéré, l'authentification échoue et le processus plante. Cela rend l'exécution traditionnelle de ROP nettement plus difficile sur les iPhones modernes, mais pas impossible.
{% endhint %}

## Tagged pointers vs Heap-allocated pointers

La distinction entre les **objets à pointeur taggé (tagged pointers)** et les **objets alloués sur le tas (heap-allocated)** est essentielle.

#### Fonctionnement interne des tagged pointers sur ARM64

* **Bit 63 (MSB)** : ce bit est positionné à **1** pour les tagged pointers sur ARM64. Le runtime vérifie en priorité ce bit afin de déterminer si le pointeur est taggé.
* **Charge utile obfusquée (payload)** : depuis **iOS 14** et **macOS Big Sur**, la valeur contenue dans un tagged pointer est masquée par une opération **XOR** avec une valeur aléatoire propre au processus. Le pointeur brut paraît donc aléatoire, mais le runtime est capable de reconstruire la valeur d'origine.
* **Indice de classe (tag class index)** : il est encodé dans les bits de poids fort et permet d'identifier le type de l'objet (par exemple `NSNumber`, `NSDate`, `NSString`, etc.).
* **Absence de pointeur `isa`** : comme l'objet est directement représenté par la valeur du pointeur, aucune allocation mémoire n'est effectuée et il n'existe donc pas de champ `isa`.

#### Pourquoi est-ce important pour l'exploitation de vulnérabilités ?

* Il est impossible de corrompre le champ **`isa`** d'un tagged pointer, car ce champ n'existe tout simplement pas : l'objet est entièrement représenté par la valeur du pointeur.
* Il est impossible de réaliser une attaque de type **use-after-free** sur un tagged pointer, puisqu'il n'a jamais été alloué sur le tas et qu'il n'y a donc rien à libérer.
* Les tagged pointers sont insensibles aux corruptions des métadonnées du tas, car ils contournent complètement l'allocateur mémoire.
* Lors du développement d'exploits ciblant des objets Objective-C, il est donc indispensable de vérifier que l'objet visé est **alloué sur le tas** et non représenté sous forme de tagged pointer.
* Les objets `NSNumber` contenant des entiers, les `NSString` courtes (environ jusqu'à 7 caractères) ainsi que les objets `NSDate` sont fréquemment implémentés sous forme de tagged pointers ; il est généralement préférable d'éviter de les cibler dans ce contexte.
