Comment faire un fichier ELF exécutable sous Linux avec un éditeur hex?

par curiosité. Ce n'est évidemment pas une très bonne solution pour la programmation réelle, mais disons que je voulais faire un exécutable dans Bless (un éditeur hex).

mon architecture est x86. Qu'est-ce qu'un programme très simple que je peux faire? Un bonjour à tout le monde? Une boucle infinie? Similaire à cette" question , mais sous Linux.

8
demandé sur Community 2014-10-10 11:25:37

2 réponses

comme mentionné dans mon commentaire, vous écrirez essentiellement votre propre en-tête elf pour l'exécutable en éliminant les sections inutiles. Il y a encore plusieurs sections requises. La documentation au Muppetlabs-TinyPrograms fait un travail juste expliquant ce processus. Pour le plaisir, voici quelques exemples:

l'équivalent de/bin / true (45 octets):

00000000  7F 45 4C 46 01 00 00 00  00 00 00 00 00 00 49 25  |.ELF..........I%|
00000010  02 00 03 00 1A 00 49 25  1A 00 49 25 04 00 00 00  |......I%..I%....|
00000020  5B 5F F2 AE 40 22 5F FB  CD 80 20 00 01           |[_..@"_... ..|
0000002d

votre classique ' Hello World!"(160 octets):

00000000  7f 45 4c 46 01 01 01 03  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  74 80 04 08 34 00 00 00  |........t...4...|
00000020  00 00 00 00 00 00 00 00  34 00 20 00 02 00 28 00  |........4. ...(.|
00000030  00 00 00 00 01 00 00 00  74 00 00 00 74 80 04 08  |........t...t...|
00000040  74 80 04 08 1f 00 00 00  1f 00 00 00 05 00 00 00  |t...............|
00000050  00 10 00 00 01 00 00 00  93 00 00 00 93 90 04 08  |................|
00000060  93 90 04 08 0d 00 00 00  0d 00 00 00 06 00 00 00  |................|
00000070  00 10 00 00 b8 04 00 00  00 bb 01 00 00 00 b9 93  |................|
00000080  90 04 08 ba 0d 00 00 00  cd 80 b8 01 00 00 00 31  |...............1|
00000090  db cd 80 48 65 6c 6c 6f  20 77 6f 72 6c 64 21 0a  |...Hello world!.|
000000a0

n'oubliez pas de les rendre exécutables...

9
répondu David C. Rankin 2014-10-10 09:55:51

Décompiler une MSNA bonjour tout le monde, et de comprendre chaque octet

Version de cette réponse avec un TOC agréable et plus de contenu: http://www.cirosantilli.com/elf-hello-world (atteignant la limite de 30k ici)

normes

ELF est spécifié par le LSB:

la LSB est essentiellement liée à d'autres normes avec des extensions mineures, en particulier:

un résumé pratique peut être trouvé à:

man elf

sa structure peut être examinée d'une manière lisible par l'homme via des utilitaires comme readelf et objdump .

Générer l'exemple

nous allons décomposer un minimum exécutable Linux x86-64 exemple:

section .data
    hello_world db "Hello world!", 10
    hello_world_len  equ $ - hello_world
section .text
    global _start
    _start:
        mov rax, 1
        mov rdi, 1
        mov rsi, hello_world
        mov rdx, hello_world_len
        syscall
        mov rax, 60
        mov rdi, 0
        syscall

compilé avec:

nasm -w+all -f elf64 -o 'hello_world.o' 'hello_world.asm'
ld -o 'hello_world.out' 'hello_world.o'

Versions:

  • MSNA 2.10.09
  • Binutils version 2.24 (contient ld )
  • Ubuntu 14.04

nous n'utilisons pas de programme C car cela compliquer l'analyse, qui sera Niveau 2: -)

Hexdumps

hd hello_world.o
hd hello_world.out

sortie: https://gist.github.com/cirosantilli/7b03f6df2d404c0862c6

Global de la structure du fichier

un fichier ELF contient les parties suivantes:

  • en-tête ELF. Points à la position de la table d'en-tête de section et le en-tête du programme de la table.

  • table d'en-tête de Section (optionnel sur l'exécutable). Chacun a des en-têtes de section e_shnum , chaque pointant vers la position d'une section.

  • n sections, avec N <= e_shnum (optionnel sur l'exécutable)

  • table d'en-tête de programme (seulement sur l'exécutable). Chacun a des en-têtes de programme e_phnum , chaque pointant vers le position d'un segment.

  • n segments, avec N <= e_phnum (optionnel sur l'exécutable)

L'ordre de ces pièces est pas fixe: la seul chose, c'est l'en-tête ELF qui doit être la première chose sur le fichier: Générique docs disent:

en-tête ELF

la façon la plus facile d'observer l'en-tête est:

readelf -h hello_world.o
readelf -h hello_world.out

sortie: https://gist.github.com/cirosantilli/7b03f6df2d404c0862c6

Octets dans le fichier d'objet:

00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  01 00 3e 00 01 00 00 00  00 00 00 00 00 00 00 00  |..>.............|
00000020  00 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000030  00 00 00 00 40 00 00 00  00 00 40 00 07 00 03 00  |....@.....@.....|

exécutable:

00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  b0 00 40 00 00 00 00 00  |..>.......@.....|
00000020  40 00 00 00 00 00 00 00  10 01 00 00 00 00 00 00  |@...............|
00000030  00 00 00 00 40 00 38 00  02 00 40 00 06 00 03 00  |....@.8...@.....|

Structure représentée:

typedef struct {
    unsigned char   e_ident[EI_NIDENT];
    Elf64_Half      e_type;
    Elf64_Half      e_machine;
    Elf64_Word      e_version;
    Elf64_Addr      e_entry;
    Elf64_Off       e_phoff;
    Elf64_Off       e_shoff;
    Elf64_Word      e_flags;
    Elf64_Half      e_ehsize;
    Elf64_Half      e_phentsize;
    Elf64_Half      e_phnum;
    Elf64_Half      e_shentsize;
    Elf64_Half      e_shnum;
    Elf64_Half      e_shstrndx;
} Elf64_Ehdr;

ventilation manuelle:

  • 0 0: EI_MAG = 7f 45 4c 46 = 0x7f 'E', 'L', 'F' : elfe numéro magique

  • 0 4: EI_CLASS = 02 = ELFCLASS64 : 64 bit elf

  • 0 5: EI_DATA = 01 = ELFDATA2LSB : big endian data

  • 0 6: EI_VERSION = 01 : format version

  • 0 7: EI_OSABI (seulement dans la mise à jour de 2003) = 00 = ELFOSABI_NONE : aucune extension.

  • 0 8: EI_PAD = 8x 00 : octets réservés. Doit être réglé à 0.

  • 1 0: e_type = 01 00 = 1 (big endian) = ET_REl : format déplaçable

    Sur l'exécutable, il est 02 00 pour ET_EXEC .

  • 1 2: e_machine = 3e 00 = 62 = EM_X86_64 : pour l'architecture AMD64

  • 1 4: e_version = 01 00 00 00 : doit être 1

  • 1 8: e_entry = 8x 00 : point d'entrée d'adresse d'exécution, ou 0 si non applicable comme pour le fichier objet car il n'y a pas de point d'entrée.

    sur l'exécutable, c'est b0 00 40 00 00 00 00 00 . TODO: que pouvons-nous cela définir? Le noyau semble mettre l'IP directement sur cette valeur, elle n'est pas codée en dur.

  • 2 0: e_phoff = 8x 00 : offset de table d'en-tête de programme, 0 si non présent.

    40 00 00 00 sur l'exécutable, c'est à dire qu'il commence immédiatement après l'en-tête ELF.

  • 2 8: e_shoff = 40 7x 00 = 0x40 : en-tête de section offset de fichier de tableau, 0 s'il n'est pas présent.

  • 3 0: e_flags = 00 00 00 00 TODO. Arch spécifiques.

  • 3 4: e_ehsize = 40 00 : la taille de cette tête d'elfe. TODO pourquoi ce champ? Comment peut-il varier?

  • 3 6: e_phentsize = 00 00 : taille de chaque en-tête de programme, 0 s'il n'est pas présent.

    38 00 sur l'exécutable: il est de 56 octets de long

  • 3 8: e_phnum = 00 00 : nombre d'entrées d'en-tête de programme, 0 s'il n'y en a pas.

    02 00 sur le fichier exécutable: il y a 2 entrées.

  • 3 A: e_shentsize et e_shnum = 40 00 07 00 : taille de l'en-tête de section et nombre d'entrées

  • 3 E: e_shstrndx ( Section Header STRing iNDeX ) = 03 00 : index de la section .shstrtab .

"15192990920 de la Section" en-tête de la table

Tableau de Elf64_Shdr des structures.

Chaque entrée contient des métadonnées sur une section donnée.

e_shoff de l'en-tête ELF donne la position de départ, 0x40 ici.

e_shentsize et e_shnum de l'en-tête ELF disent que nous avons 7 entrées, chaque 0x40 octets de long.

ainsi la table prend des octets de 0x40 à 0x40 + 7 + 0x40 - 1 = 0x1FF.

certains noms de sections sont réservés pour certains types de sections: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections p.ex. .text nécessite un SHT_PROGBITS type et SHF_ALLOC + SHF_EXECINSTR

readelf -S hello_world.o :

There are 7 section headers, starting at offset 0x40:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .data             PROGBITS         0000000000000000  00000200
       000000000000000d  0000000000000000  WA       0     0     4
  [ 2] .text             PROGBITS         0000000000000000  00000210
       0000000000000027  0000000000000000  AX       0     0     16
  [ 3] .shstrtab         STRTAB           0000000000000000  00000240
       0000000000000032  0000000000000000           0     0     1
  [ 4] .symtab           SYMTAB           0000000000000000  00000280
       00000000000000a8  0000000000000018           5     6     4
  [ 5] .strtab           STRTAB           0000000000000000  00000330
       0000000000000034  0000000000000000           0     0     1
  [ 6] .rela.text        RELA             0000000000000000  00000370
       0000000000000018  0000000000000018           4     2     4
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

struct représenté par chaque rubrique:

typedef struct {
    Elf64_Word  sh_name;
    Elf64_Word  sh_type;
    Elf64_Xword sh_flags;
    Elf64_Addr  sh_addr;
    Elf64_Off   sh_offset;
    Elf64_Xword sh_size;
    Elf64_Word  sh_link;
    Elf64_Word  sh_info;
    Elf64_Xword sh_addralign;
    Elf64_Xword sh_entsize;
} Elf64_Shdr;

Sections

l'Indice 0 de la section

contenu dans les octets 0x40 à 0x7F.

la première section est toujours magique: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html dit:

si le nombre de sections est supérieur ou égal à SHN_LORESERVE (0xff00), e_shnum a la valeur SHN_UNDEF (0) et le nombre réel d'entrées de table d'en-tête de section est contenu dans le champ sh_size de l'en-tête de section à l'index 0 (sinon, le membre sh_size de l'entrée initiale contient 0).

il y a aussi d'autres sections magiques détaillées dans Figure 4-7: Special Section Indexes .

SHT_NULL

Dans l'index 0, SHT_NULL est obligatoire. Y a-t-il d'autres utilisations pour cela: Quelle est l'utilisation de la section SHT_NULL dans ELF? ?

.section des données

.data est la section 1:

00000080  01 00 00 00 01 00 00 00  03 00 00 00 00 00 00 00  |................|
00000090  00 00 00 00 00 00 00 00  00 02 00 00 00 00 00 00  |................|
000000a0  0d 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000b0  04 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
  • 80 0: sh_name = 01 00 00 00 : index 1 dans la .shstrtab string table

    ici, 1 dit que le nom de cette section commence à la première caractère de cette section, et se termine au premier caractère NUL, formant la chaîne .data .

    .data est l'un des noms de section qui a un sens prédéfini http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html

    ces sections contiennent des données initialisées qui contribuent à l'image mémoire du programme.

  • 80 4: sh_type = 01 00 00 00 : SHT_PROGBITS : le contenu de la section N'est pas spécifié par ELF, seulement par la façon dont le programme l'interprète. Normal depuis une section .data .

  • 80 8: sh_flags = 03 7x 00 : SHF_ALLOC et SHF_EXECINSTR : http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#sh_flags , conformément à la section .data

  • 90 0: sh_addr = 8x 00 : dans quelle adresse virtuelle la section sera placée pendant l'exécution, 0 si non placé

  • 90 8: sh_offset = 00 02 00 00 00 00 00 00 = 0x200 : nombre d'octets entre le début du programme et le premier octet dans cette section

  • a0 0: sh_size = 0d 00 00 00 00 00 00 00

    si nous prenons 0XD octets à partir de sh_offset 200, nous voyons:

    00000200  48 65 6c 6c 6f 20 77 6f  72 6c 64 21 0a 00        |Hello world!..  |
    

    AHA! Donc notre chaîne "Hello world!" est dans la section des données comme nous l'avions dit sur le NASM.

    une fois que nous serons diplômés de hd , nous regarderons cela comme:

    readelf -x .data hello_world.o
    

    dont sortie:

    Hex dump of section '.data':
      0x00000000 48656c6c 6f20776f 726c6421 0a       Hello world!.
    

    NASM définit des propriétés décentes pour cela section parce qu'elle traite .data par magie: http://www.nasm.us/doc/nasmdoc7.html#section-7.9.2

    notez aussi que c'était un mauvais choix de section: un bon compilateur C mettrait la chaîne dans .rodata à la place, parce qu'elle est en lecture seule et qu'elle permettrait d'autres optimisations OS.

  • a0 8: sh_link et sh_info = 8x 0: ne s'appliquent pas à ce type de section. http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections

  • b0 0: sh_addralign = 04 = TODO: pourquoi cet alignement est-il nécessaire? Est-ce seulement pour sh_addr , ou aussi pour les symboles à l'intérieur de sh_addr ?

  • b0 8: sh_entsize = 00 = la section ne contient pas de tableau. Si != 0, cela signifie que l' l'article contient un tableau de taille fixe entrées. Dans ce fichier, nous voyons à partir de la sortie readelf que c'est le cas pour les sections .symtab et .rela.text .

.section de texte

maintenant que nous avons fait une section manuellement, graduons et utilisons le readelf -S des autres sections.

  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 2] .text             PROGBITS         0000000000000000  00000210
       0000000000000027  0000000000000000  AX       0     0     16

.text est exécutable mais pas inscriptible: si nous essayons de écrivez à elle segfaults Linux. Voyons si nous avons vraiment quelque code là - bas:

objdump -d hello_world.o

donne:

hello_world.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_start>:
   0:       b8 01 00 00 00          mov    "1519160920"x1,%eax
   5:       bf 01 00 00 00          mov    "1519160920"x1,%edi
   a:       48 be 00 00 00 00 00    movabs "1519160920"x0,%rsi
  11:       00 00 00
  14:       ba 0d 00 00 00          mov    "1519160920"xd,%edx
  19:       0f 05                   syscall
  1b:       b8 3c 00 00 00          mov    "1519160920"x3c,%eax
  20:       bf 00 00 00 00          mov    "1519160920"x0,%edi
  25:       0f 05                   syscall

si nous enregistrons b8 01 00 00 sur le hd , nous voyons que cela se produit seulement à 00000210 , ce qui est ce que la section dit. Et la taille est 27, ce qui correspond aussi. Nous devons donc parler de la bonne section.

cela ressemble au code de droite: un write suivi d'un exit .

la partie la plus intéressante est la ligne a qui fait:

movabs "1519170920"x0,%rsi

pour transmettre l'adresse de la chaîne à l'appel système. Actuellement, le 0x0 est juste un emplacement. Après le lien se produit, il sera modifié pour contenir:

4000ba: 48 be d8 00 60 00 00    movabs "1519180920"x6000d8,%rsi

cette modification est possible en raison des données de la section .rela.text .

SHT_STRTAB

Sections avec sh_type == SHT_STRTAB sont appelés string tables .

ils tiennent un tableau null séparé de chaînes.

ces sections sont utilisées par d'autres sections lorsque des noms de chaîne doivent être utilisés. La section d'utilisation dit:

  • quelle table de chaîne ils utilisent
  • qu'est-ce que l'index sur la table de la chaîne cible où la chaîne commence

ainsi, par exemple, nous pourrions avoir une table de chaîne de caractères contenant: TODO: doit-il commencer par "15191710920" ?

Data: "1519190920" a b c "1519190920" d e f "1519190920"
Index: 0 1 2 3  4 5 6 7  8

et si une autre section veut utiliser la chaîne d e f , ils doivent pointer vers l'index 5 de cette section (lettre d ).

sections de table à cordes notables:

  • .shstrtab
  • .strtab

.shstrtab

"15193010920 de la Section" type: sh_type == SHT_STRTAB .

nom Commun: "15194100920 de la section" en-tête de la table des chaînes .

le nom de la section .shstrtab est réservé. La norme dit:

cette section contient les noms des sections.

cette section reçoit indiqué par le champ e_shstrnd de l'en-tête ELF lui-même.

les index de chaîne de cette section sont pointés par le champ sh_name des entêtes de section, qui dénotent les chaînes.

cette section n'a pas de SHF_ALLOC marqué, de sorte qu'il n'apparaîtra pas sur le programme d'exécution.

readelf -x .shstrtab hello_world.o

Donne:

Hex dump of section '.shstrtab':
  0x00000000 002e6461 7461002e 74657874 002e7368 ..data..text..sh
  0x00000010 73747274 6162002e 73796d74 6162002e strtab..symtab..
  0x00000020 73747274 6162002e 72656c61 2e746578 strtab..rela.tex
  0x00000030 7400                                t.

les données dans cette section ont un format fixe: http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html

si nous regardons les noms des autres sections, nous voyons qu'elles contiennent toutes des numéros, par exemple la section .text est le numéro 7 .

Ensuite chaque chaîne se termine lorsque le premier caractère NUL est trouvé, p.ex. le caractère 12 est " 15191710920 " juste après .text" 15191860920" .

.symtab

"15193010920 de la Section" type: sh_type == SHT_SYMTAB .

nom commun: table de symboles .

tout d'abord nous notons que:

  • sh_link = 5
  • sh_info = 6

pour les sections SHT_SYMTAB , ces nombres signifient que:

  • cordes qui donnent les noms des symboles figurent à la section 5, .strtab
  • les données de réinstallation se trouvent à la section 6, .rela.text

un bon outil de haut niveau pour démonter cette section est:

nm hello_world.o

qui donne:

0000000000000000 T _start
0000000000000000 d hello_world
000000000000000d a hello_world_len

C'est cependant une vue de haut niveau qui omet certains types de symboles et dans lequel les types de symboles . Un démontage plus détaillé peut être obtenu avec:

readelf -s hello_world.o

qui donne:

Symbol table '.symtab' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello_world.asm
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2
     4: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    1 hello_world
     5: 000000000000000d     0 NOTYPE  LOCAL  DEFAULT  ABS hello_world_len
     6: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    2 _start

le format binaire de la table est documenté à http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html

les données sont:

readelf -x .symtab hello_world.o

qui donne:

Hex dump of section '.symtab':
  0x00000000 00000000 00000000 00000000 00000000 ................
  0x00000010 00000000 00000000 01000000 0400f1ff ................
  0x00000020 00000000 00000000 00000000 00000000 ................
  0x00000030 00000000 03000100 00000000 00000000 ................
  0x00000040 00000000 00000000 00000000 03000200 ................
  0x00000050 00000000 00000000 00000000 00000000 ................
  0x00000060 11000000 00000100 00000000 00000000 ................
  0x00000070 00000000 00000000 1d000000 0000f1ff ................
  0x00000080 0d000000 00000000 00000000 00000000 ................
  0x00000090 2d000000 10000200 00000000 00000000 -...............
  0x000000a0 00000000 00000000                   ........

les rubriques sont de type:

typedef struct {
    Elf64_Word  st_name;
    unsigned char   st_info;
    unsigned char   st_other;
    Elf64_Half  st_shndx;
    Elf64_Addr  st_value;
    Elf64_Xword st_size;
} Elf64_Sym;

comme dans le tableau de section, la première entrée est magique et fixe aucun sens des valeurs.

STT_FILE

la rubrique 1 A ELF64_R_TYPE == STT_FILE . ELF64_R_TYPE est continué à l'intérieur de st_info .

Octet analyse:

  • 10 8: st_name = 01000000 = caractère 1 dans le .strtab , qui jusqu'au suivant " 15191710920 " fait hello_world.asm

    cette information le fichier peut être utilisé par le linker pour décider quelles sections de segment vont.

  • 10 12: st_info = 04

    Bits 0-3 = ELF64_R_TYPE = Type = 4 = STT_FILE : le but principal de cette entrée est d'utiliser st_name pour indiquer le nom du fichier qui a généré ce fichier objet.

    Bits 4 À 7 = ELF64_ST_BIND = Liaison = 0 = STB_LOCAL . Valeur requise pour STT_FILE .

  • 10 13: st_shndx = table des symboles index des en-têtes de Section = f1ff = SHN_ABS . Obligatoire pour STT_FILE .

  • 20 0: st_value = 8x 00 : requis pour la valeur de STT_FILE

  • 20 8: st_size = 8x 00 : non attribué taille

a partir du readelf , nous interprétons les autres rapidement.

STT_SECTION

il y a deux entrées de ce type, l'une pointant vers .data et l'autre vers .text (index de section 1 et 2 ).

Num:    Value          Size Type    Bind   Vis      Ndx Name
  2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
  3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2

pour faire Quel est leur but?

STT_NOTYPE

Puis viennent les symboles les plus importants:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  4: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    1 hello_world
  5: 000000000000000d     0 NOTYPE  LOCAL  DEFAULT  ABS hello_world_len
  6: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    2 _start

hello_world la chaîne est dans la section .data (index 1). Sa valeur est 0: Il pointe vers le premier octet de cette section.

_start est marqué avec GLOBAL visibilité depuis que nous avons écrit:

global _start

in NASM. Cela est nécessaire car il doit être considéré comme le point d'entrée. Contrairement à C, les étiquettes NASM sont locales.

SHN_ABS

hello_world_len les points à l' spécial st_shndx == SHN_ABS == 0xF1FF .

0xF1FF est choisi de manière à ne pas entrer en conflit avec d'autres sections.

st_value == 0xD == 13 qui est la valeur que nous y avons stockée sur l'ensemble: la longueur de la chaîne Hello World! .

cela signifie que la délocalisation n'affectera pas cette valeur: c'est une constante.

c'est une petite optimisation que notre assembleur fait pour nous et qui a le support ELF.

Si nous avions utilisé l'adresse de hello_world_len n'importe où, l'assembleur n'aurait pas été en mesure de le marquer comme SHN_ABS , et l'éditeur de liens aurait supplémentaire de réinstallation travailler plus tard.

SHT_SYMTAB sur l'exécutable

par défaut, NASM place également un .symtab sur l'exécutable.

utilisé uniquement pour le débogage. Sans les symboles, nous sommes complètement aveugle, et doit désosser tout.

vous pouvez le supprimer avec objcopy , et l'exécutable continuera de tourner. Ces exécutables sont appelés dépouillé exécutables .

.strtab

contient des cordes pour la table de symboles.

cette section A sh_type == SHT_STRTAB .

est indiqué par sh_link == 5 de la section .symtab .

readelf -x .strtab hello_world.o

Donne:

Hex dump of section '.strtab':
  0x00000000 0068656c 6c6f5f77 6f726c64 2e61736d .hello_world.asm
  0x00000010 0068656c 6c6f5f77 6f726c64 0068656c .hello_world.hel
  0x00000020 6c6f5f77 6f726c64 5f6c656e 005f7374 lo_world_len._st
  0x00000030 61727400                            art.

cela implique que c'est une limitation de niveau ELF que les variables globales ne peuvent pas contenir de caractères NUL.

.rela.texte

"15193010920 de la Section" type: sh_type == SHT_RELA .

nom commun: section de réinstallation .

.rela.text contient les données de déplacement qui indique comment l'adresse doit être modifiée lorsque le dernier exécutable est lié. Cela pointe vers les octets de la zone de texte qui doivent être modifiés lorsque la liaison se produit pour pointer vers les emplacements de mémoire corrects.

essentiellement, il traduit le texte de l'objet contenant le paramètre 0x0 adresse:

   a:       48 be 00 00 00 00 00    movabs "1519340920"x0,%rsi
  11:       00 00 00

au code exécutable contenant le 0x6000d8 final:

4000ba: 48 be d8 00 60 00 00    movabs "1519350920"x6000d8,%rsi
4000c1: 00 00 00

pointé par sh_info = 6 de la section .symtab .

readelf -r hello_world.o donne:

Relocation section '.rela.text' at offset 0x3b0 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
00000000000c  000200000001 R_X86_64_64       0000000000000000 .data + 0

cette section n'existe pas dans l'exécutable.

Les octets sont:

00000370  0c 00 00 00 00 00 00 00  01 00 00 00 02 00 00 00  |................|
00000380  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

le struct représenté est:

typedef struct {
    Elf64_Addr  r_offset;
    Elf64_Xword r_info;
    Elf64_Sxword    r_addend;
} Elf64_Rela;

:

  • 370 0: r_offset = 0xC: adresse dans le .text dont l'adresse sera modifier

  • 370 8: r_info = 0x200000001. Contient 2 champs:

    • ELF64_R_TYPE = 0x1: la signification dépend de l'architecture exacte.
    • ELF64_R_SYM = 0x2: index de la section à laquelle les points d'adresse, so .data qui est à l'index 2.

    L'amd64 ABI dit que le type 1 est appelé R_X86_64_64 et qu'il représente l'opération S + A , où:

    • S : la valeur du symbole sur le fichier objet, ici 0 parce que nous pointons vers le 00 00 00 00 00 00 00 00 de movabs "15192620920"x0,%rsi
    • A : l'addend, présent dans le champ r_added

    cette adresse est ajoutée à la section sur laquelle la réinstallation s'effectue.

    Ce déménagement l'opération agit sur un total de 8 octets.

  • 380 0: r_addend = 0

ainsi dans notre exemple nous concluons que la nouvelle adresse sera: S + A = .data + 0 , et donc la première chose dans la section de données.

table d'en-tête de programme

n'apparaît que dans l'exécutable.

contient des informations sur la façon dont l'exécutable doit être placé dans la mémoire virtuelle du processus.

l'exécutable est généré à partir de fichiers objets par le linker. Les principaux emplois de l'éditeur de liens ne sont:

  • " déterminez quelles sections des fichiers objet vont entrer dans quels segments de l'exécutable.

    dans Binutils, cela revient à analyser un script de linker, et de traiter avec un tas de défauts.

    vous pouvez obtenir le script de linker utilisé avec ld --verbose , et mettre un personnalisé avec ld -T .

  • faire la réinstallation sur les sections de texte. Cela dépend de la façon dont plusieurs articles sont mis en mémoire.

readelf -l hello_world.out donne:

Elf file type is EXEC (Executable file)
Entry point 0x4000b0
There are 2 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000000d7 0x00000000000000d7  R E    200000
  LOAD           0x00000000000000d8 0x00000000006000d8 0x00000000006000d8
                 0x000000000000000d 0x000000000000000d  RW     200000

 Section to Segment mapping:
  Segment Sections...
   00     .text
   01     .data

sur l'en-tête ELF, e_phoff , e_phnum et e_phentsize nous a dit qu'il y a 2 en-têtes de programme, qui commencent à 0x40 et sont 0x38 octets de long chacun, ils sont donc:

00000040  01 00 00 00 05 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 40 00 00 00 00 00  00 00 40 00 00 00 00 00  |..@.......@.....|
00000060  d7 00 00 00 00 00 00 00  d7 00 00 00 00 00 00 00  |................|
00000070  00 00 20 00 00 00 00 00                           |.. .....        |

et:

00000070                           01 00 00 00 06 00 00 00  |        ........|
00000080  d8 00 00 00 00 00 00 00  d8 00 60 00 00 00 00 00  |..........`.....|
00000090  d8 00 60 00 00 00 00 00  0d 00 00 00 00 00 00 00  |..`.............|
000000a0  0d 00 00 00 00 00 00 00  00 00 20 00 00 00 00 00  |.......... .....|

Structure représentée http://www.sco.com/developers/gabi/2003-12-17/ch5.pheader.html :

typedef struct {
    Elf64_Word  p_type;
    Elf64_Word  p_flags;
    Elf64_Off   p_offset;
    Elf64_Addr  p_vaddr;
    Elf64_Addr  p_paddr;
    Elf64_Xword p_filesz;
    Elf64_Xword p_memsz;
    Elf64_Xword p_align;
} Elf64_Phdr;

répartition du premier:

  • 40 0: p_type = 01 00 00 00 = PT_LOAD : TODO. Je pense qu'il va être chargé en mémoire. D'autres types ne le sont pas nécessairement.
  • 40 4: p_flags = 05 00 00 00 = exécuter et lire les permissions, no write TODO
  • 40 8: p_offset = 8x 00 TODO: qu'est-ce que c'est? On dirait des décalages du début des segments. Mais cela signifierait que certains segments sont entrelacés? Il est possible de jouer avec un peu avec: gcc -Wl,-Ttext-segment=0x400030 hello_world.c
  • 50 0: p_vaddr = 00 00 40 00 00 00 00 00 : adresse de mémoire virtuelle initiale pour charger ce segment en
  • 50 8: p_paddr = 00 00 40 00 00 00 00 00 : adresse physique initiale à charger en mémoire. Ne concerne que les systèmes dans lesquels le programme peut définir son adresse physique. Autrement, comme dans le système V comme les systèmes, peut être n'importe quoi. NASM semble copier p_vaddrr
  • 60 0: p_filesz = d7 00 00 00 00 00 00 00 : TODO vs p_memsz
  • 60 8: p_memsz = d7 00 00 00 00 00 00 00 : total 15193130920"
  • 70 0: p_align = 00 00 20 00 00 00 00 00 : 0 ou 1 signifie pas d'alignement requis pour faire qu'est-ce que cela signifie? sinon redondant avec d'autres champs

la seconde est analogue.

puis le:

 Section to Segment mapping:

de la section readelf nous dit que:

  • 0 est le segment .text . Aha, c'est pourquoi il est exécutable, et non inscriptible
  • 1 est le segment .data .
33