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.
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...
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:
- noyau générique: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/elf-generic.html
- core AMD64: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-AMD64/LSB-Core-AMD64/book1.html
la LSB est essentiellement liée à d'autres normes avec des extensions mineures, en particulier:
-
générique (à la fois par SCO):
- System V ABI 4.1 (1997) http://www.sco.com/developers/devspecs/gabi41.pdf , n ° 64 bits, bien qu'un nombre magique est réservé. De même pour les fichiers de base.
- System V ABI Update DRAFT 17 (2003) http://www.sco.com/developers/gabi/2003-12-17/contents.html , ajoute 64 bits. Seules les mises à jour des chapitres 4 et 5 du document précédent: les autres restent valables et sont toujours référencées.
-
architecture spécifique:
- IA-32: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-IA32/LSB-Core-IA32/elf-ia32.html , les points la plupart du temps à http://www.sco.com/developers/devspecs/abi386-4.pdf
- AMD64: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-AMD64/LSB-Core-AMD64/elf-amd64.html , pointe principalement sur http://www.x86-64.org/documentation/abi.pdf
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
= 8x00
: octets réservés. Doit être réglé à 0. -
1 0:
e_type
=01 00
= 1 (big endian) =ET_REl
: format déplaçableSur l'exécutable, il est
02 00
pourET_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
= 8x00
: 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
= 8x00
: 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
7x00
=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
ete_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
.
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
.
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 tableici,
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.htmlces 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
7x00
:SHF_ALLOC
etSHF_EXECINSTR
: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#sh_flags , conformément à la section.data
-
90 0:
sh_addr
= 8x00
: 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.2notez 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
etsh_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 poursh_addr
, ou aussi pour les symboles à l'intérieur desh_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 sortiereadelf
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 "
faithello_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'utiliserst_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 pourSTT_FILE
. -
10 13:
st_shndx
= table des symboles index des en-têtes de Section =f1ff
=SHN_ABS
. Obligatoire pourSTT_FILE
. -
20 0:
st_value
= 8x00
: requis pour la valeur deSTT_FILE
-
20 8:
st_size
= 8x00
: non attribué taille
a partir du readelf
, nous interprétons les autres rapidement.
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_NOTYPEPuis 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.
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érationS + A
, où:-
S
: la valeur du symbole sur le fichier objet, ici0
parce que nous pointons vers le00 00 00 00 00 00 00 00
demovabs "15192620920"x0,%rsi
-
A
: l'addend, présent dans le champr_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é avecld -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
= 8x00
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 copierp_vaddrr
- 60 0:
p_filesz
=d7 00 00 00 00 00 00 00
: TODO vsp_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
.