> For the complete documentation index, see [llms.txt](https://sansong.gitbook.io/cyber/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sansong.gitbook.io/cyber/rev/windows/win32-gestion-des-exceptions.md).

# Win32 - Gestion des exceptions

Cette page parle de la gestion des exceptions sur Win32.

{% hint style="success" %}
**Interruption:** demande au CPU d'interrompre l'exécution du code actuel pour exécuter du code hors du flux actuel.

* **interruption** <mark style="color:green;">logicielle</mark>**:** demandée par le CPU après une certaine instruction ou suite d'instruction (division par zéro ou erreur de segmentation par exemple)
* **interruption** <mark style="color:green;">matérielle</mark>**:** provoquée par un périphérique extérieur (appuyer sur une touche du clavier ou déplacer la souris par exemple)

Avant d'exécuter le code correspondant à l'exécution, le CPU sauvegarde l'état du processus actuel pour pouvoir y retourner (registres généraux, EIP, registres de segment, CR3, etc.) -> <mark style="color:green;">context switch</mark>.
{% endhint %}

2 types de mécanismes pour gérer les exceptions:

* **SEH (Structured Exception Handling):** valable pour un seul thread
* **VEH (Vectored Exception Handling):** extension des SEH, valable pour tout l'exécutable.&#x20;

## **SEH**

N'est valable que pour un thread, pas pour tout le programme.

Exemple de code qui utilise SEH:

```cpp
__try {
// code protégé
}
__except ( UNE_EXCEPTION ) {
// code du handler de l'exception
}
```

## **VEH**

Prioritaire sur les SEH. Peut gérer toutes les exceptions de l'application/exécutable.

* possible de créer des handlers appelées n'importe où dans le programme. Les handlers sont appelés dans leur ordre d'ajout sauf mention contraire
* ajouter un handler: [<mark style="color:green;">`AddVectoredExceptionHandler`</mark>](https://learn.microsoft.com/fr-fr/windows/win32/api/errhandlingapi/nf-errhandlingapi-addvectoredexceptionhandler)

```cpp
PVOID AddVectoredExceptionHandler(
  ULONG                       First,   // ordre dans lequel le handler est appelé
                                       // !0: premier & 0: dernier
  PVECTORED_EXCEPTION_HANDLER Handler  // adresse du handler 
                                       // il est exécuté quand l'interruption a lieu
);
```

* supprimer un handler: [<mark style="color:green;">`RemoveVectoredExceptionHandler`</mark>](https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-removevectoredexceptionhandler)

```cpp
ULONG RemoveVectoredExceptionHandler(
  PVOID Handle
);
```

## Détails sur les handlers

La signature du handler est:

```cpp
 EXCEPTION_DISPOSITION
 __cdecl _except_handler(
     struct _EXCEPTION_RECORD *ExceptionRecord,
     void * EstablisherFrame,
     struct _CONTEXT *ContextRecord,
     void * DispatcherContext
     );
```

<table><thead><tr><th width="211" align="center">Type</th><th>                                                Contenu de la structure/rôle</th></tr></thead><tbody><tr><td align="center"><mark style="color:green;"><code>EXCEPTION_RECORD</code></mark></td><td><ul><li>infos sur l'exception</li></ul><pre class="language-cpp"><code class="lang-cpp">typedef struct _EXCEPTION_RECORD {
 DWORD ExceptionCode; // numéro de l'exception
 DWORD ExceptionFlags;
 struct _EXCEPTION_RECORD *ExceptionRecord;
 PVOID ExceptionAddress; // adresse où elle a eu lieu
 DWORD NumberParameters;
 DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
 }  EXCEPTION_RECORD;
</code></pre></td></tr><tr><td align="center"><mark style="color:green;"><code>_CONTEXT</code></mark></td><td><ul><li>valeurs du contexte sauvegardé (les registres)</li></ul><pre class="language-cpp"><code class="lang-cpp">typedef struct _CONTEXT
{                                    // Offset
    DWORD ContextFlags;              // 0x0
    DWORD   Dr0;                     // 0x4
    DWORD   Dr1;                     // 0x8
    DWORD   Dr2;                     // 0xC
    DWORD   Dr3;                     // 0x10
    DWORD   Dr6;                     // 0x14
    DWORD   Dr7;                     // 0x18
    FLOATING_SAVE_AREA FloatSave;    // 0x1C
    DWORD   SegGs;                   // 0x8C
    DWORD   SegFs;                   // 0x90
    DWORD   SegEs;                   // 0x94
    DWORD   SegDs;                   // 0x98
    DWORD   Edi;                     // 0x9C
    DWORD   Esi;                     // 0xA0
    DWORD   Ebx;                     // 0xA4
    DWORD   Edx;                     // 0xA8
    DWORD   Ecx;                     // 0xAC
    DWORD   Eax;                     // 0xB0
    DWORD   Ebp;                     // 0xB4
    DWORD   Eip;                     // 0xB8
    DWORD   SegCs;                   // 0xBC
    DWORD   EFlags;                  // 0xC0
    DWORD   Esp;                     // 0xC4
    DWORD   SegSs;                   // 0xC8
} CONTEXT;
</code></pre></td></tr></tbody></table>

Le <mark style="color:green;">TIB (Thread Information Block)</mark> ou <mark style="color:green;">TEB (Thread Environment Block)</mark> est une structure qui contient des infos sur le thread dont un pointeur vers une structure <mark style="color:green;">`EXCEPTION_REGISTRATION_RECORD`</mark> qui contient un pointeur vers le handler d'exception du thread. Le TIB contient une chaine de SEH qui est parcourue à chaque exception jusqu'à ce que l'OS en trouve une pour l'exception courante. La chaine est sur la pile.

Les structures <mark style="color:green;">`EXCEPTION_REGISTRATION_RECORD`</mark> ont la signature suivante:

```c
typedef struct _EXCEPTION_REGISTRATION_RECORD {
   PEXCEPTION_REGISTRATION_RECORD *Next; // pointeur vers le prochain SEH
   PEXCEPTION_ROUTINE Handler; // pointeur vers _except_handler (le vrai handler)
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
```

Le registre <mark style="color:green;">`FS`</mark> pointe vers le premier SEH de la chaine:

* <mark style="color:green;">`FS:[0]`</mark> contient l'adresse du premier SEH

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FbD5ogY8OI8PLeeLWgztT%2Fimage.png?alt=media&amp;token=1d7a6e4c-2ddc-41b3-bd8f-d93c01fe10f3" alt=""><figcaption><p>Source: <a href="https://www.securitysift.com/windows-exploit-development-part-6-seh-exploits/">https://www.securitysift.com/windows-exploit-development-part-6-seh-exploits/</a></p></figcaption></figure>

{% hint style="info" %}
Trouver des instructions assembleurs comme celles-ci signifient souvent la mise en place d'un bloc <mark style="color:green;">`_try/_except`</mark> manuellement et donc d'un SEH.

```c
 push handler // adresse du nouvel handler (_except_handler) empilé
 push dword ptr fs:[0] // pointeur vers le premier SEH de la chaine est empilé
 mov dword fs:[0], esp // le nouveau handler est placé au début de la chaine
```

**Mais pourquoi l'adresse du nouvel handler est empilé en premier ?**

Si on empile l'adresse du nouvel handler d'abord puis celle de l'ancien, l'instruction&#x20;

`mov dword fs:[0], esp` place l'adresse de l'ancien handler à <mark style="color:green;">`fs:[0]`</mark> et on n'a pas placé le nouvel handler au début de la chaine ?

Il faut se rappeler que la liste chainée contient des <mark style="color:green;">`EXCEPTION_REGISTRATION_RECORD`</mark> qui ont la structure suivante:

```c
typedef struct _EXCEPTION_REGISTRATION_RECORD {
   PEXCEPTION_REGISTRATION_RECORD *Next; // pointeur vers le prochain SEH
   PEXCEPTION_ROUTINE Handler; // pointeur vers _except_handler (le vrai handler)
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
```

Enfait, one ne place pas l'adresse du nouvel handler au début de la chaine mais **on ajoute une structure&#x20;**<mark style="color:green;">**`EXCEPTION_REGISTRATION_RECORD`**</mark>**&#x20;au début de la chaine qui contient un pointeur vers l'ancien premier&#x20;**<mark style="color:green;">**`EXCEPTION_REGISTRATION_RECORD`**</mark>**&#x20;de la chaine:**&#x20;

* `push dword ptr fs:[0]`

**Et un pointeur vers le nouvel handler (**<mark style="color:green;">**`_except_handler`**</mark>**):**

* `push handler`

La pile ressemble à ça:

```
              |                 |
              +-----------------+
adr_x:        |     handler     |  // adresse du nouvel handler (_except_handler)
              +-----------------+
              | ancien handler  | // adresse de la première structure 
adr_y:  ESP-> |     fs:[0]      | // EXCEPTION_REGISTRATION_RECORD de la chaine
              +-----------------+
```

Après l'instruction `mov dword fs:[0], esp` <mark style="color:green;">`fs:[0]`</mark> pointe vers `adr_y` sur la pile qui correspond au <mark style="color:green;">`PEXCEPTION_REGISTRATION_RECORD *Next`</mark> de la nouvelle première structure <mark style="color:green;">`EXCEPTION_REGISTRATION_RECORD`</mark> de la chaine. Juste après se trouve l'adresse du nouveau handler `adr_x` qui correspond au <mark style="color:green;">`PEXCEPTION_ROUTINE Handler`</mark> de la structure.
{% endhint %}

**Et si on ajoute des VEH ?**

Tous les handlers sont exécutés, en commençant par le premier VEH jusqu'au dernier, puis en passant par tous les SEH sur la pile. Le premier handler à renvoyer <mark style="color:green;">`EXCEPTION_CONTINUE_EXECUTION`</mark> (qu'il soit de type VEH ou SEH) interrompt l'ensemble du processus d'exécution des handlers et l'exécution du programme peut reprendre normalement.

**Récap de ce qu'il se passe quand une exception a lieu:**

* Une instruction entraîne une interruption
* L'OS parcours la chaine de VEH
* Si aucun ne correspond à l'exception il passe aux SEH
* L'OS regarde dans le TIB à <mark style="color:green;">`FS:[0]`</mark> pour savoir où commence la chaine de structures <mark style="color:green;">`EXCEPTION_REGISTRATION_RECORD`</mark>
* Il parcours la chaine jusqu'à trouver un <mark style="color:green;">`_except_handler`</mark> qui corresponde à l'exception

{% hint style="info" %}
En pratique tous les handlers sont exécutés jusqu'à ce qu'un d'entre eux renvoie la valeur <mark style="color:green;">`EXCEPTION_CONTINUE_EXECUTION`</mark> signifiant qu'il s'est occupé de l'exception. Si une autre valeur est renvoyée par <mark style="color:green;">`_except_handler`</mark> le handler suivant de la chaine est appelé
{% endhint %}

* L'exécution du programme reprend normalement
* Si aucun handler ne correspond, le dernier de la chaine est exécuté (handler par défaut 0xFFFFFF)

## **Exemples d'implémentation**

### **SEH**

Code tiré de [l'article génial de Matt Pietrek](https://www-user.tu-chemnitz.de/~heha/hsn/chm/Win32SEH.chm/)

```c
//==================================================
// MYSEH - Matt Pietrek 1997
// Microsoft Systems Journal, January 1997
// FILE: MYSEH.CPP
// To compile: CL MYSEH.CPP
//==================================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>

DWORD  scratch;

EXCEPTION_DISPOSITION
__cdecl
_except_handler(
    struct _EXCEPTION_RECORD *ExceptionRecord,
    void * EstablisherFrame,
    struct _CONTEXT *ContextRecord,
    void * DispatcherContext )
{
    unsigned i;

    // Indicate that we made it to our exception handler
    printf( "Hello from an exception handler\n" );

    // Change EAX in the context record so that it points to someplace
    // where we can successfully write
    ContextRecord->Eax = (DWORD)&scratch;

    // Tell the OS to restart the faulting instruction
    return ExceptionContinueExecution;
}

int main()
{
    DWORD handler = (DWORD)_except_handler;

    __asm
    {				// Build EXCEPTION_REGISTRATION record:
	push    handler		// Address of handler function
	push    FS:[0]		// Address of previous handler
	mov     FS:[0],ESP	// Install new EXECEPTION_REGISTRATION
    }

    __asm
    {
	mov     eax,0		// Zero out EAX
	mov     [eax], 1	// Write to EAX to deliberately cause a fault
    }

    printf( "After writing!\n" );

    __asm
    {				// Remove our EXECEPTION_REGISTRATION record
	mov     eax,[ESP]	// Get pointer to previous record
	mov     FS:[0], EAX	// Install previous record
	add     esp, 8		// Clean our EXECEPTION_REGISTRATION off stack
    }

    return 0;
}
```

Le code assembleur ressemble à ça:

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FMxFnzDS3iIPwUQJGbfLP%2Fseh0.png?alt=media&amp;token=3101981f-5ee7-449f-a1f3-30ba64071378" alt="" width="333"><figcaption><p>Code assembleur de la fonction main</p></figcaption></figure>

On voit bien la suite d'instruction dont on a parlé plus tôt.

```cpp
mov    [ebp+var_4], offset loc_40024C
push   [ebp+var_4]
push   large dword ptr fs:0
mov    large fs:0, esp
```

L'adresse du nouveau handler `offset loc_40024C` est empilée puis celle de l'ancien premier SEH avant de placer l'adresse de début de la nouvelle structure <mark style="color:green;">`EXCEPTION_REGISTRATION_RECORD`</mark> à <mark style="color:green;">`fs:0`</mark>.

On retrouve bien la suite d'instructions du code qui cause l'exception:

```c
mov    eax, 0
mov    byte ptr [eax], 1
```

Si on exécute le programme, IDA renvoie un message d'alerte comme prévu indiquant qu'une tentative d'accès à la mémoire 0x0 a été faite.

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FDo2SdnT7h7fOOgch9ieg%2FCapture%20d'%C3%A9cran%202024-03-13%20223458.png?alt=media&amp;token=a773ac03-ef3a-43bf-9e71-5bc21d90fa47" alt=""><figcaption><p>Message d'alerte</p></figcaption></figure>

Après avoir mis un breakpoint à l'adresse du handler on voit son code.

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FMFAlHBDFlBtC0pzOEpnZ%2Fseh1.png?alt=media&amp;token=00be7450-76e4-4c6b-9fe6-327a3ddd3c66" alt=""><figcaption><p>Code de _except_handler</p></figcaption></figure>

Dans le code de Matt Pietrek le eax du contexte est modifié.

```cpp
ContextRecord->Eax = (DWORD)&scratch;
```

Regardons comment les registres du contexte sont accédés en pratique.

Les arguments de <mark style="color:green;">`_except_handler`</mark> ont été empilés avant l'appel de la fonction. Pour rappel ses arguments sont:

```cpp
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
```

Un pointeur vers <mark style="color:green;">`ContextRecord`</mark> est d'abord mis dans eax:

```c
mov    eax, [esp+10h]
```

Le registre du contexte eax (`ContextRecord->Eax`) est atteint avec l'offset 0xB0 (voir tableau complet en haut de page). Et l'adresse de `scratch` y est mise.

```c
mov dword ptr [eax+0B0h], offset unk_400300
```

{% hint style="success" %}
Les registre du contexte sont les registres sauvegardés de l'exécution du programme avant l'exception. Modifier le eax du contexte règle le problème d'accès mémoire invalide car après l'exception eax contiendra l'adresse de la variable `scratch` qui est valide.
{% endhint %}

### VEH

```c
#include <windows.h>
#include <stdio.h>

LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
    printf("Exception caught!\n");
    printf("Exception code: 0x%08X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);
    return EXCEPTION_CONTINUE_SEARCH;
}

int main() {
    // Ajouter le handler VEH
    if (!AddVectoredExceptionHandler(1, VectoredExceptionHandler)) {
        printf("Failed to set up vectored exception handler!\n");
        return 1;
    }

    printf("Throwing an exception...\n");

    // Simuler une exception de division par zéro
    int a = 10, b = 0;
    int result = a / b;

    return 0;
}
```

Le handler <mark style="color:green;">`VectoredExceptionHandler`</mark> a la structure suivante:

```cpp
LONG PvectoredExceptionHandler(
  [in] _EXCEPTION_POINTERS *ExceptionInfo
)
```

Il prend un argument de type <mark style="color:green;">`PEXCEPTION_POINTERS`</mark> qui a pour signature:

```cpp
typedef struct _EXCEPTION_POINTERS {
  PEXCEPTION_RECORD ExceptionRecord;
  PCONTEXT          ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
```

Il contient un pointeur vers une structure qui décrit l'exception et un autre vers une structure du contexte. La structure décrivant l'exception est de la forme suivante.

```cpp
typedef struct _EXCEPTION_RECORD {
  DWORD                    ExceptionCode; // code qui va gérer l'exception
  DWORD                    ExceptionFlags;
  struct _EXCEPTION_RECORD *ExceptionRecord;
  PVOID                    ExceptionAddress;
  DWORD                    NumberParameters;
  ULONG_PTR                ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
```

La structure <mark style="color:green;">`PCONTEXT`</mark> est décrite en [#annexes](#annexes "mention").

Le code du handler <mark style="color:green;">`VectoredExceptionHandler`</mark> est le suivant.

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2F6THrGKavBSMzON24stg1%2FCapture%20d'%C3%A9cran%202024-03-17%20203406.png?alt=media&amp;token=0f52fe3f-0705-4a20-a759-45c32f74035f" alt="" width="563"><figcaption></figcaption></figure>

Un pointeur vers <mark style="color:green;">`ExceptionCode`</mark> est placé au sommet de la pile (`mov  [esp+4], eax`). A la fin de la fonction, l'exécution saute au code de l'exception. Quand on exécute le programme IDA renvoie comme prévu un avertissement concernant la division par zéro.

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FILxQE3VW9wyQ31xdjkST%2FCapture%20d'%C3%A9cran%202024-03-18%20232405.png?alt=media&amp;token=84d27622-9fdf-4d5f-8697-d891688c30f1" alt="" width="420"><figcaption></figcaption></figure>

Si on continue l'exécution on arrive dans le code de <mark style="color:green;">`VectoredExceptionHandler`</mark> qu'on a vu juste au-dessus. Après l'instruction `retn  4` le CPU saute au code qui va gérer l'exception (ici la dll ntdll32.dll)

<figure><img src="https://1813806532-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FZRRTPIEA4wb6exZozwS0%2Fuploads%2FgQHhLWWsUZnVQyEmHvZN%2FCapture%20d'%C3%A9cran%202024-03-18%20232447.png?alt=media&amp;token=ad75163e-8b1b-491c-b894-fe22a5fa6971" alt=""><figcaption></figcaption></figure>

Le code ci-dessus met fin au programme.

## Vulnérabilité

Les programmes 32-bit peuvent être vulnérables à des [SEH buffer overflow](/cyber/pwn/stack/seh-buffer-overflow.md).

## Annexes

{% hint style="info" %}

```cpp
// Structure CONTEXT (winnt.h)

typedef struct _CONTEXT {
  DWORD64 P1Home;
  DWORD64 P2Home;
  DWORD64 P3Home;
  DWORD64 P4Home;
  DWORD64 P5Home;
  DWORD64 P6Home;
  DWORD   ContextFlags;
  DWORD   MxCsr;
  WORD    SegCs;
  WORD    SegDs;
  WORD    SegEs;
  WORD    SegFs;
  WORD    SegGs;
  WORD    SegSs;
  DWORD   EFlags;
  DWORD64 Dr0;
  DWORD64 Dr1;
  DWORD64 Dr2;
  DWORD64 Dr3;
  DWORD64 Dr6;
  DWORD64 Dr7;
  DWORD64 Rax;
  DWORD64 Rcx;
  DWORD64 Rdx;
  DWORD64 Rbx;
  DWORD64 Rsp;
  DWORD64 Rbp;
  DWORD64 Rsi;
  DWORD64 Rdi;
  DWORD64 R8;
  DWORD64 R9;
  DWORD64 R10;
  DWORD64 R11;
  DWORD64 R12;
  DWORD64 R13;
  DWORD64 R14;
  DWORD64 R15;
  DWORD64 Rip;
  union {
    XMM_SAVE_AREA32 FltSave;
    NEON128         Q[16];
    ULONGLONG       D[32];
    struct {
      M128A Header[2];
      M128A Legacy[8];
      M128A Xmm0;
      M128A Xmm1;
      M128A Xmm2;
      M128A Xmm3;
      M128A Xmm4;
      M128A Xmm5;
      M128A Xmm6;
      M128A Xmm7;
      M128A Xmm8;
      M128A Xmm9;
      M128A Xmm10;
      M128A Xmm11;
      M128A Xmm12;
      M128A Xmm13;
      M128A Xmm14;
      M128A Xmm15;
    } DUMMYSTRUCTNAME;
    DWORD           S[32];
  } DUMMYUNIONNAME;
  M128A   VectorRegister[26];
  DWORD64 VectorControl;
  DWORD64 DebugControl;
  DWORD64 LastBranchToRip;
  DWORD64 LastBranchFromRip;
  DWORD64 LastExceptionToRip;
  DWORD64 LastExceptionFromRip;
} CONTEXT, *PCONTEXT;
```

{% endhint %}

## Références

{% embed url="<https://learn.microsoft.com/fr-fr/windows/win32/debug/structured-exception-handling>" %}

{% embed url="<https://learn.microsoft.com/fr-fr/windows/win32/debug/vectored-exception-handling>" %}

{% embed url="<https://www-user.tu-chemnitz.de/~heha/hsn/chm/Win32SEH.chm/>" %}

{% embed url="<https://www.securitysift.com/windows-exploit-development-part-6-seh-exploits/>" %}
