Le PE Header - Pour commencer
|
05-05-2012, 19h48
(Modification du message : 08-12-2012, 17h36 par ark.)
Message : #1
|
|
supersnail
Éleveur d'ornithorynques Messages : 1,609 Sujets : 71 Points: 465 Inscription : Jan 2012 |
Le PE Header - Pour commencer
Introduction
Le format PE, est un format de fichier propre aux binaires Windows (c'est-à-dire exécutables, DLL, drivers) permettant à notre cher Windows de charger et lancer ces derniers. Ces structures contiennent des informations plus ou moins utiles, comme la table des sections, l'adresse du point d'entrée de l'exécutable ou encore l'adresse du dealer de chocapicz le plus proche de chez vous. Pour bien profiter de cette visite guidée, il est recommandé d'avoir des notions en C (utilisation massive de pointeurs/structures au programme), et être un minimum à l'aise avec la notation hexadécimale. Il est aussi recommandé d'avoir un éditeur hexadécimal et une calculatrice (celle de Windows fera l'affaire) pour réaliser quelques bidouillages. Préparatifs: les offsets, RVA et autres (D)WORDs Il va nous falloir ici différentier deux mondes totalement différents: le monde des fichiers, et celui de la mémoire vive.Un exécutable est un fichier de ce qu'il y a de plus banal (au même titre qu'un fichier texte ou une image jpg), mais qui a pour but ultime d'être chargé en mémoire par le système d'exploitation (en l'occurence Windows, ou ReactOS si vous êtes un libriste convaincu aimant le goût du risque ). Cet exécutable chargé en mémoire subira différentes transformations, le rendant ainsi complètement différent du fichier dont il est issu (modifiant les adresses mémoires par le fait). C'est pour ça que les ingénieurs de Microsoft ont introduit un système intelligent de découpage en sections et de RVA (Relative Virtual Address) qui sert de base à la plupart des adresses qu'on rencontrera, plûtot que de baser les adresses sur les offsets du fichier. Un offset est en quelque sorte la position d'un élement dans le fichier. Par exemple, si je veux lire le caractère à l'offset 4, je lirai le 5 caractère du fichier (étant donné que la numérotation des offset commence à partir de 0 au lieu de 1). A noter ici qu'on préfèrera utiliser une notation hexadécimale pour les offsets (ainsi que pour les adresses mémoire et les RVAs) par convention, même s'il est possible d'utiliser une notation décimale (ainsi le caractère à l'offset 10 ou 0x0A est le 11ème caractère du fichier, car 0x0A en hexadécimal correspond à 10 en décimal). Les RVA, elles sont plus subtiles, puisqu'elles dépendent d'une entité appelée "table de sections" pour exister. En effet, un exécutable range son code dans des "sections" qui seront chargées en mémoire selon les informations contenues dans la table des sections (sur laquelle je reviendrai plus en détail sur la 2ème partie du tuto). La RVA représente l'adresse en mémoire d'une entité, relativement à l'adresse de base, c'est-à-dire par rapport à l'adresse où est chargée l'exécutable par le système d'exploitation (souvent 0x00400000). Enfin, on trouvera souvent des noms du type "WORD", "DWORD" ou "BYTE" utilisés dans nos structures. Il s'agit juste de variables ayant une certaine longueur d'octets. Un DWORD pour Double Word est une variable qui prend 4 octets consécutifs en mémoire (en little-endian, c'est-à-dire que les octets successifs dans la RAM 0x01 0x02 0x03 0x04 donneront 0x04030201 représentés par le DWORD). Le WORD lui, est une variable qui prend 2 octets consécutifs en mémoire (toujours en little-endian), et le BYTE, un seul octet. Début de la randonnée: le MZ Header Ouvrons notre exécutable avec notre éditeur hexadécimal, et admirons la jolie bouille d'octets servie pour notre déjeuner ! Vous reconnaissez sûrement le célèbre "This program can't run in DOS mode" si vous vous êtes déjà amusés à ouvrir un exécutable dans le bloc-notes lorsque vous étiez petits. Les premiers octets correspondent ici au MZ Header, ou encore l'en-tête MS-DOS (et oui, un exécutable Windows n'est rien d'autre qu'un exécutable MS-DOS avec des fonctionnalités supplémentaires). Voici donc la structure représentant le MZ header: Code C :
typedef struct _IMAGE_DOS_HEADER { La plupart de ces champs étaient utiles lors d'une époque aujourd'hui révolue, mais subsiste encore un champ que Windows va lire: le champ e_lfanew (à l'offset 0x3c), qui contient l'offset du PE header à proprement parler. Là, certaines tapz qui n'ont pas suivi se sentiront perdues, ne sachant pas ce qu'est l'offset d'un fichier. L'offset X (où X est un nombre), correspond tout simplement au Xème octet du fichier. Autrement dit, le DWORD commençant au 0x3c-ème octet (c'est-à-dire au 60ème octet du fichier) nous donne où on peut trouver le PE header dans le fichier (rappelez-vous, on est en little-endian, il faut donc lire les octets 60 à 64 à l'envers). Après ce champ e_lfanew vient du code prévu pour MS-DOS (généralement un message affichant "This program can't run in DOS mode" dont les plus vieux se souviennent sûrement encore). Bref, passons ce moment d'extrême émotion, et continuons notre visite. Le PE Header Allons donc à l'offset indiqué par le vieux sage des montagnes pour y trouver notre PE Header. Les 4 premiers octets du PE header forment les lettres "PE", le tout suivi de deux octets nuls. Cette suite de caractères ne sert à rien, à part permettre à Windows de vérifier qu'il est bien face à un binaire bien formé (et non face un chocapicz mutant qui se serait perdu à cet endroit). Le PE Header est séparé (dixit winnt.h) en 2 structures de taille inégale: le IMAGE_FILE_HEADER qui est très court, et le IMAGE_OPTIONAL_HEADER, beaucoup plus grand. Voilà donc les structures représentant le PE Header: Code C :
typedef struct _IMAGE_NT_HEADERS { et le file header: Code C :
typedef struct _IMAGE_FILE_HEADER { Le File Header contient des informations intéressantes comme le nombre de sections de l'exécutable (on y reviendra plus tard), ou encore le champ "Characteristics", qui indique le type du binaire que l'on regarde (si c'est un driver, dll, exécutable...). La description de ces champs est facilement trouvable, il suffit de RTFM un peu Après le file header, vient l'Optional Header, qui contient une quantité non négligeable d'informations, concernant où l'image de l'exécutable sera placée en mémoire, la taille du code, la taille des données, la RVA du point d'entrée, et plein d'autres choses intéressantes. L'optional header est représentée par la structure suivante: Code C :
On peut voir à la fin de la structure un tableau de DataDirectory, qui contiennent les RVA vers la table d'imports, d'exports, de ressources ou encore de plein d'autres données (comme par exemple les en-têtes COM/CLR). A noter aussi que la célèbre protection anti-Reflector ne fait que modifier la valeur du champ "NumberOfRvaAndSizes", qui sert normalement à préciser combien de DataDirectory sont présents. A chaque index du tableau de DataDirectory correspond un type de données, ainsi le DataDirectory à l'index 0 correspond à la table d'export, l'index 1 à la table d'import, l'index 2 à la table de ressources... Références
Mon blog
Code : push esp ; dec eax ; inc ebp ; and [edi+0x41],al ; dec ebp ; inc ebp "VIM est merveilleux" © supersnail |
|
04-09-2012, 11h18
Message : #2
|
|
Swissky
Bon membre Messages : 523 Sujets : 32 Points: 96 Inscription : Apr 2012 |
RE: Le PE Header - Pour commencer
Lien à remplacer dans les Références : http://repo.aassfxxx.infos.st/docs/windo...ons_PE.pdf
|
|
04-09-2012, 11h18
Message : #3
|
|
supersnail
Éleveur d'ornithorynques Messages : 1,609 Sujets : 71 Points: 465 Inscription : Jan 2012 |
RE: Le PE Header - Pour commencer
En effet, merci :p
Mon blog
Code : push esp ; dec eax ; inc ebp ; and [edi+0x41],al ; dec ebp ; inc ebp "VIM est merveilleux" © supersnail |
|
« Sujet précédent | Sujet suivant »
|
Sujets apparemment similaires… | |||||
Sujet | Auteur | Réponses | Affichages | Dernier message | |
[C] Multiples arguments pour une fonction appelée dans un Thread | Junky | 0 | 119 |
29-03-2013, 17h55 Dernier message: Junky |
Utilisateur(s) parcourant ce sujet : 2 visiteur(s)