GNU/Linux >> Tutoriels Linux >  >> Linux

9 outils binutils GNU essentiels

Imaginez que vous n'ayez pas accès au code source d'un logiciel, mais que vous soyez toujours capable de comprendre comment le logiciel est implémenté, d'y trouver des vulnérabilités et, mieux encore, de corriger les bogues. Tout cela sous forme binaire. Cela ressemble à avoir des super pouvoirs, n'est-ce pas ?

Vous aussi, vous pouvez posséder de tels super pouvoirs, et les utilitaires binaires GNU (binutils) sont un bon point de départ. Les binutils GNU sont une collection d'outils binaires qui sont installés par défaut sur toutes les distributions Linux.

L'analyse binaire est la compétence la plus sous-estimée dans l'industrie informatique. Il est principalement utilisé par les analystes de logiciels malveillants, les rétro-ingénieurs et les personnes

travaillant sur des logiciels de bas niveau.

Cet article explore certains des outils disponibles via binutils. J'utilise RHEL mais ces exemples devraient fonctionner sur n'importe quelle distribution Linux.


[~]# cat /etc/redhat-release 
Red Hat Enterprise Linux Server release 7.6 (Maipo)
[~]# 
[~]# uname -r
3.10.0-957.el7.x86_64
[~]# 

Notez que certaines commandes d'empaquetage (comme rpm ) peut ne pas être disponible sur les distributions basées sur Debian, utilisez donc l'équivalent dpkg commande le cas échéant.

Développement logiciel 101

Dans le monde open source, beaucoup d'entre nous se concentrent sur les logiciels sous forme source; Lorsque le code source du logiciel est facilement disponible, il est facile d'obtenir simplement une copie du code source, d'ouvrir votre éditeur préféré, de prendre une tasse de café et de commencer à explorer.

Mais le code source n'est pas ce qui est exécuté sur le CPU; ce sont les instructions binaires ou en langage machine qui sont exécutées sur le CPU. Le fichier binaire ou exécutable est ce que vous obtenez lorsque vous compilez le code source. Les personnes compétentes en débogage obtiennent souvent leur avantage en comprenant cette différence.

Compilation 101

Avant de creuser dans le package binutils lui-même, il est bon de comprendre les bases de la compilation.

La compilation est le processus de conversion d'un programme à partir de sa source ou de sa forme textuelle dans un certain langage de programmation (C/C++) en code machine.

Le code machine est la séquence de 1 et de 0 qui sont compris par un processeur (ou un matériel en général) et peuvent donc être exécutés ou lancés par le processeur. Ce code machine est enregistré dans un fichier dans un format spécifique souvent appelé fichier exécutable ou fichier binaire. Sous Linux (et BSD, lors de l'utilisation de la compatibilité binaire Linux), cela s'appelle ELF (Executable and Linkable Format).

Le processus de compilation passe par une série d'étapes compliquées avant de présenter un fichier exécutable ou binaire pour un fichier source donné. Considérez ce programme source (code C) comme exemple. Ouvrez votre éditeur préféré et tapez ce programme :


#include <stdio.h>

int main(void)
{
printf("Hello World\n");
return 0;
}

Étape 1 :Prétraitement avec cpp

Le préprocesseur C (cpp ) est utilisé pour développer toutes les macros et inclure les fichiers d'en-tête. Dans cet exemple, le fichier d'en-tête stdio.h seront inclus dans le code source. stdio.h est un fichier d'en-tête qui contient des informations sur un printf fonction utilisée dans le programme. cpp s'exécute sur le code source et les instructions qui en résultent sont enregistrées dans un fichier appelé hello.i . Ouvrez le fichier avec un éditeur de texte pour voir son contenu. Le code source pour imprimer hello world se trouve en bas du fichier.


[testdir]# cat hello.c
#include <stdio.h>

int main(void)
{
printf("Hello World\n");
return 0;
}
[testdir]#
[testdir]# cpp hello.c > hello.i
[testdir]#
[testdir]# ls -lrt
total 24
-rw-r--r--. 1 root root 76 Sep 13 03:20 hello.c
-rw-r--r--. 1 root root 16877 Sep 13 03:22 hello.i
[testdir]#

Étape 2 :Compilation avec gcc

C'est l'étape où le code source prétraité de l'étape 1 est converti en instructions de langage d'assemblage sans créer de fichier objet. Il utilise la collection de compilateurs GNU (gcc ). Après avoir exécuté le gcc commande avec le -S option sur hello.i fichier, il crée un nouveau fichier appelé hello.s . Ce fichier contient les instructions en langage assembleur pour le programme C.

Vous pouvez afficher le contenu à l'aide de n'importe quel éditeur ou du chat commande.


[testdir]#
[testdir]# gcc -Wall -S hello.i
[testdir]#
[testdir]# ls -l
total 28
-rw-r--r--. 1 root root 76 Sep 13 03:20 hello.c
-rw-r--r--. 1 root root 16877 Sep 13 03:22 hello.i
-rw-r--r--. 1 root root 448 Sep 13 03:25 hello.s
[testdir]#
[testdir]# cat hello.s
.file "hello.c"
.section .rodata
.LC0:
.string "Hello World"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
call puts
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)"
.section .note.GNU-stack,"",@progbits
[testdir]#

Étape 3 :Assemblage avec as

Le but d'un assembleur est de convertir les instructions du langage d'assemblage en code de langage machine et de générer un fichier objet qui a un .o extension. Utilisez l'assembleur GNU comme qui est disponible par défaut sur toutes les plates-formes Linux.


[testdir]# as hello.s -o hello.o
[testdir]#
[testdir]# ls -l
total 32
-rw-r--r--. 1 root root 76 Sep 13 03:20 hello.c
-rw-r--r--. 1 root root 16877 Sep 13 03:22 hello.i
-rw-r--r--. 1 root root 1496 Sep 13 03:39 hello.o
-rw-r--r--. 1 root root 448 Sep 13 03:25 hello.s
[testdir]#

Vous avez maintenant votre premier fichier au format ELF; cependant, vous ne pouvez pas encore l'exécuter. Plus tard, vous verrez la différence entre un fichier objet et un fichier exécutable .


[testdir]# file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

Étape 4 : Lien avec ld

C'est la dernière étape de la compilation, lorsque les fichiers objets sont liés pour créer un exécutable. Un exécutable nécessite généralement des fonctions externes qui proviennent souvent de bibliothèques système (libc ).

Vous pouvez invoquer directement l'éditeur de liens avec le ld commande; cependant, cette commande est quelque peu compliquée. Au lieu de cela, vous pouvez utiliser le gcc compilateur avec le -v (verbeux) drapeau pour comprendre comment la liaison se produit. (En utilisant le ld commande de liaison est un exercice qu'il vous reste à explorer.)


[testdir]# gcc -v hello.o
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man [...] --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:[...]:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-mtune=generic' '-march=x86-64'
/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu [...]/../../../../lib64/crtn.o
[testdir]#

Après avoir exécuté cette commande, vous devriez voir un fichier exécutable nommé a.out :


[testdir]# ls -l
total 44
-rwxr-xr-x. 1 root root 8440 Sep 13 03:45 a.out
-rw-r--r--. 1 root root 76 Sep 13 03:20 hello.c
-rw-r--r--. 1 root root 16877 Sep 13 03:22 hello.i
-rw-r--r--. 1 root root 1496 Sep 13 03:39 hello.o
-rw-r--r--. 1 root root 448 Sep 13 03:25 hello.s

Exécution du fichier commande sur a.out montre qu'il s'agit bien d'un exécutable ELF :


[testdir]# file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=48e4c11901d54d4bf1b6e3826baf18215e4255e5, not stripped

Exécutez votre fichier exécutable pour voir s'il correspond aux instructions du code source :


[testdir]# ./a.out
Hello World

Cela fait! Il se passe tellement de choses dans les coulisses juste pour imprimer Hello World sur l'écran. Imaginez ce qui se passe dans des programmes plus complexes.

Explorer les outils binutils

Cet exercice a fourni une bonne base pour utiliser les outils qui sont dans le package binutils. Mon système a la version 2.27-34 de binutils; vous pouvez avoir une version différente selon votre distribution Linux.


[~]# rpm -qa | grep binutils
binutils-2.27-34.base.el7.x86_64

Les outils suivants sont disponibles dans les packages binutils :


[~]# rpm -ql binutils-2.27-34.base.el7.x86_64 | grep bin/
/usr/bin/addr2line
/usr/bin/ar
/usr/bin/as
/usr/bin/c++filt
/usr/bin/dwp
/usr/bin/elfedit
/usr/bin/gprof
/usr/bin/ld
/usr/bin/ld.bfd
/usr/bin/ld.gold
/usr/bin/nm
/usr/bin/objcopy
/usr/bin/objdump
/usr/bin/ranlib
/usr/bin/readelf
/usr/bin/size
/usr/bin/strings
/usr/bin/strip

L'exercice de compilation ci-dessus a déjà exploré deux de ces outils :le as La commande a été utilisée comme assembleur, et le ld La commande a été utilisée comme éditeur de liens. Lisez la suite pour en savoir plus sur les sept autres outils de package GNU binutils mis en évidence en gras ci-dessus.

readelf :affiche des informations sur les fichiers ELF

L'exercice ci-dessus mentionnait les termes fichier objet et fichier exécutable . En utilisant les fichiers de cet exercice, entrez readelf en utilisant le -h (en-tête) pour vider l'en-tête ELF des fichiers sur votre écran. Notez que le fichier objet se terminant par .o l'extension s'affiche sous la forme Type :REL (fichier réadressable) :


[testdir]# readelf -h hello.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 [...]
[...]
Type: REL (Relocatable file)
[...]

Si vous essayez d'exécuter ce fichier, vous obtiendrez une erreur indiquant qu'il ne peut pas être exécuté. Cela signifie simplement qu'il ne dispose pas encore des informations nécessaires à son exécution sur le CPU.

N'oubliez pas que vous devez ajouter le x ou bit exécutable sur le fichier objet en utilisant d'abord le chmod commande ou bien vous obtiendrez une autorisation refusée erreur.


[testdir]# ./hello.o
bash: ./hello.o: Permission denied
[testdir]# chmod +x ./hello.o
[testdir]#
[testdir]# ./hello.o
bash: ./hello.o: cannot execute binary file

Si vous essayez la même commande sur le a.out fichier, vous voyez que son type est un EXEC (fichier exécutable) .


[testdir]# readelf -h a.out
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
[...] Type: EXEC (Executable file)

Comme vu précédemment, ce fichier peut directement être exécuté par le CPU :


[testdir]# ./a.out
Hello World

Le lire La commande donne une mine d'informations sur un binaire. Ici, il vous indique qu'il est au format ELF64 bits, ce qui signifie qu'il ne peut être exécuté que sur un processeur 64 bits et ne fonctionnera pas sur un processeur 32 bits. Il vous indique également qu'il est destiné à être exécuté sur l'architecture X86-64 (Intel/AMD). Le point d'entrée dans le binaire est à l'adresse 0x400430, qui est juste l'adresse du main fonction dans le programme source C.

Essayez le readelf commande sur les autres binaires système que vous connaissez, comme ls . Notez que votre sortie (en particulier Type : ) peuvent différer sur les systèmes RHEL 8 ou Fedora 30 et versions ultérieures en raison des modifications apportées à l'exécutable indépendant de la position (PIE) pour des raisons de sécurité.


[testdir]# readelf -h /bin/ls
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)

Découvrez quelles bibliothèques système le ls la commande dépend de l'utilisation de ldd commande, comme suit :


[testdir]# ldd /bin/ls
linux-vdso.so.1 => (0x00007ffd7d746000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f060daca000)
libcap.so.2 => /lib64/libcap.so.2 (0x00007f060d8c5000)
libacl.so.1 => /lib64/libacl.so.1 (0x00007f060d6bc000)
libc.so.6 => /lib64/libc.so.6 (0x00007f060d2ef000)
libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f060d08d000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f060ce89000)
/lib64/ld-linux-x86-64.so.2 (0x00007f060dcf1000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007f060cc84000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f060ca68000)

Exécutez readelf sur la libc fichier de bibliothèque pour voir de quel type de fichier il s'agit. Comme il l'indique, il s'agit d'un DYN (fichier objet partagé) , ce qui signifie qu'il ne peut pas être exécuté directement par lui-même ; il doit être utilisé par un fichier exécutable qui utilise en interne toutes les fonctions mises à disposition par la bibliothèque.


[testdir]# readelf -h /lib64/libc.so.6
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: DYN (Shared object file)

taille :répertorie les tailles de section et la taille totale

La taille La commande ne fonctionne que sur les fichiers objet et exécutables, donc si vous essayez de l'exécuter sur un simple fichier ASCII, elle lancera une erreur disant Format de fichier non reconnu .


[testdir]# echo "test" > file1
[testdir]# cat file1
test
[testdir]# file file1
file1: ASCII text
[testdir]# size file1
size: file1: File format not recognized

Maintenant, exécutez taille sur le fichier objet et le fichier exécutable de l'exercice ci-dessus. Notez que le fichier exécutable (a.out ) contient beaucoup plus d'informations que le fichier objet (hello.o ), basé sur la sortie de la commande size :


[testdir]# size hello.o
text data bss dec hex filename
89 0 0 89 59 hello.o
[testdir]# size a.out
text data bss dec hex filename
1194 540 4 1738 6ca a.out

Mais qu'est-ce que le texte , données , et bss signifient ?

Le texte sections font référence à la section de code du binaire, qui contient toutes les instructions exécutables. Les données les sections sont là où se trouvent toutes les données initialisées, et bss est l'endroit où toutes les données non initialisées sont stockées.

Comparer la taille avec certains des autres binaires système disponibles.

Pour les ls commande :


[testdir]# size /bin/ls
text data bss dec hex filename
103119 4768 3360 111247 1b28f /bin/ls

Vous pouvez voir que gcc et gdb sont des programmes beaucoup plus gros que ls juste en regardant la sortie de la taille commande :


[testdir]# size /bin/gcc
text data bss dec hex filename
755549 8464 81856 845869 ce82d /bin/gcc
[testdir]# size /bin/gdb
text data bss dec hex filename
6650433 90842 152280 6893555 692ff3 /bin/gdb

chaînes :imprime les chaînes de caractères imprimables dans les fichiers

Il est souvent utile d'ajouter le -d drapeau aux chaînes commande pour afficher uniquement les caractères imprimables de la section de données.

bonjour.o est un fichier objet qui contient des instructions pour imprimer le texte Hello World . Par conséquent, la seule sortie des chaînes la commande est Hello World .


[testdir]# strings -d hello.o
Hello World

Exécution de chaînes sur a.out (un exécutable), d'autre part, affiche des informations supplémentaires qui ont été incluses dans le binaire lors de la phase de liaison :


[testdir]# strings -d a.out
/lib64/ld-linux-x86-64.so.2
!^BU
libc.so.6
puts
__libc_start_main
__gmon_start__
GLIBC_2.2.5
UH-0
UH-0
=(
[]A\A]A^A_
Hello World
;*3$"

Rappelez-vous que la compilation est le processus de conversion des instructions du code source en code machine. Le code machine se compose uniquement de 1 et de 0 et est difficile à lire pour les humains. Par conséquent, il est utile de présenter le code machine sous forme d'instructions en langage d'assemblage. À quoi ressemblent les langages d'assemblage ? N'oubliez pas que le langage d'assemblage est spécifique à l'architecture ; puisque j'utilise l'architecture Intel ou x86-64, les instructions seront différentes si vous utilisez l'architecture ARM pour compiler les mêmes programmes.

objdump :affiche les informations des fichiers objets

Un autre outil binutils qui peut vider les instructions du langage machine du binaire est appelé objdump .
Utilisez le -d option, qui désassemble toutes les instructions d'assemblage du binaire.


[testdir]# objdump -d hello.o
hello.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000
:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: bf 00 00 00 00 mov $0x0,%edi
9: e8 00 00 00 00 callq e

e: b8 00 00 00 00 mov $0x0,%eax
13: 5d pop %rbp
14: c3 retq

Cette sortie semble intimidante au début, mais prenez un moment pour la comprendre avant d'aller de l'avant. Rappelez-vous que le .text contient toutes les instructions du code machine. Les instructions de montage peuvent être vues dans la quatrième colonne (c'est-à-dire, pousser , mouvement , appelq , pop , retq ). Ces instructions agissent sur des registres, qui sont des emplacements de mémoire intégrés au CPU. Les registres de cet exemple sont rbp , rsp , édi , eax , etc., et chaque registre a une signification particulière.

Exécutez maintenant objdump sur le fichier exécutable (a.out ) et voyez ce que vous obtenez. La sortie de objdump sur l'exécutable peut être volumineux, je l'ai donc réduit au principal fonction utilisant le grep commande :


[testdir]# objdump -d a.out  | grep -A 9 main\>
000000000040051d
:
40051d: 55 push %rbp
40051e: 48 89 e5 mov %rsp,%rbp
400521: bf d0 05 40 00 mov $0x4005d0,%edi
400526: e8 d5 fe ff ff callq 400400
40052b: b8 00 00 00 00 mov $0x0,%eax
400530: 5d pop %rbp
400531: c3 retq

Notez que les instructions sont similaires au fichier objet hello.o , mais ils contiennent des informations supplémentaires :

  • Le fichier objet hello.o a l'instruction suivante :callq e
  • L'exécutable a.out se compose de l'instruction suivante avec une adresse et une fonction :callq 400400 <puts@plt>

L'instruction d'assemblage ci-dessus appelle un puts une fonction. N'oubliez pas que vous avez utilisé un printf fonction dans le code source. Le compilateur a inséré un appel aux puts fonction de bibliothèque pour afficher Hello World à l'écran.

Regardez l'instruction pour une ligne au-dessus de puts :

  • Le fichier objet hello.o a l'instruction mov :mov $0x0,%edi
  • L'instruction mov pour l'exécutable a.out a une adresse réelle ($0x4005d0 ) au lieu de $0x0 :mov $0x4005d0,%edi

Cette instruction déplace tout ce qui est présent à l'adresse $0x4005d0 dans le binaire au registre nommé edi .

Que pourrait-il y avoir d'autre dans le contenu de cet emplacement mémoire ? Oui, vous avez bien deviné :il ne s'agit que du texte Hello, World . Comment pouvez-vous en être sûr ?

Le lire La commande vous permet de vider n'importe quelle section du fichier binaire (a.out ) sur l'écran. Ce qui suit lui demande de vider le .rodata , qui sont des données en lecture seule, à l'écran :


[testdir]# readelf -x .rodata  a.out

Hex dump of section '.rodata':
0x004005c0 01000200 00000000 00000000 00000000 ....
0x004005d0 48656c6c 6f20576f 726c6400 Hello World.

Vous pouvez voir le texte Hello World à droite et son adresse en binaire à gauche. Correspond-elle à l'adresse que vous avez vue dans le mov consigne ci-dessus ? Oui, c'est le cas.

strip :supprime les symboles des fichiers objets

Cette commande est souvent utilisée pour réduire la taille du binaire avant de l'expédier aux clients.

N'oubliez pas que cela entrave le processus de débogage puisque les informations vitales sont supprimées du binaire; néanmoins, le binaire s'exécute parfaitement.

Exécutez-le sur votre a.out exécutable et notez ce qui se passe. Tout d'abord, assurez-vous que le binaire n'est pas supprimé en exécutant la commande suivante :


[testdir]# file a.out
a.out: ELF 64-bit LSB executable, x86-64, [......] not stripped

Gardez également une trace du nombre d'octets à l'origine dans le binaire avant d'exécuter la strip commande :


[testdir]# du -b a.out
8440 a.out

Exécutez maintenant le strip commande sur votre exécutable et assurez-vous qu'elle a fonctionné en utilisant le fichier commande :


[testdir]# strip a.out
[testdir]# file a.out a.out: ELF 64-bit LSB executable, x86-64, [......] stripped

Après avoir supprimé le binaire, sa taille est descendue à 6296 du précédent 8440 octets pour ce petit programme. Avec autant d'économies pour un petit programme, il n'est pas étonnant que de gros programmes soient souvent supprimés.


[testdir]# du -b a.out
6296 a.out

addr2line :convertit les adresses en noms de fichiers et numéros de ligne

La addr2line L'outil recherche simplement les adresses dans le fichier binaire et les associe aux lignes du programme de code source C. Plutôt cool, n'est-ce pas ?

Écrivez un autre programme de test pour cela; seulement cette fois assurez-vous de le compiler avec le -g drapeau pour gcc , qui ajoute des informations de débogage supplémentaires pour le binaire et aide également en incluant les numéros de ligne (fournis dans le code source ici) :


[testdir]# cat -n atest.c
1 #include <stdio.h>
2
3 int globalvar = 100;
4
5 int function1(void)
6 {
7 printf("Within function1\n");
8 return 0;
9 }
10
11 int function2(void)
12 {
13 printf("Within function2\n");
14 return 0;
15 }
16
17 int main(void)
18 {
19 function1();
20 function2();
21 printf("Within main\n");
22 return 0;
23 }

Compiler avec le -g drapeau et exécutez-le. Pas de surprise ici :


[testdir]# gcc -g atest.c
[testdir]# ./a.out
Within function1
Within function2
Within main

Utilisez maintenant objdump pour identifier les adresses mémoire où commencent vos fonctions. Vous pouvez utiliser le grep commande pour filtrer les lignes spécifiques que vous souhaitez. Les adresses de vos fonctions sont mises en évidence ci-dessous :


[testdir]# objdump -d a.out  | grep -A 2 -E 'main>:|function1>:|function2>:'
000000000040051d :
40051d: 55 push %rbp
40051e: 48 89 e5 mov %rsp,%rbp
--
0000000000400532 :
400532: 55 push %rbp
400533: 48 89 e5 mov %rsp,%rbp
--
0000000000400547
:
400547: 55 push %rbp
400548: 48 89 e5 mov %rsp,%rbp

Utilisez maintenant la addr2line outil pour mapper ces adresses du binaire pour qu'elles correspondent à celles du code source C :


[testdir]# addr2line -e a.out 40051d
/tmp/testdir/atest.c:6
[testdir]#
[testdir]# addr2line -e a.out 400532
/tmp/testdir/atest.c:12
[testdir]#
[testdir]# addr2line -e a.out 400547
/tmp/testdir/atest.c:18

Il dit que 40051d commence à la ligne numéro 6 dans le fichier source atest.c , qui est la ligne où l'accolade de départ ({ ) pour fonction1 départs. Faites correspondre la sortie pour function2 et principal .

nm :répertorie les symboles des fichiers objets

Utilisez le programme C ci-dessus pour tester le nm outil. Compilez-le rapidement en utilisant gcc et exécutez-le.


[testdir]# gcc atest.c
[testdir]# ./a.out
Within function1
Within function2
Within main

Exécutez maintenant nm et grep pour plus d'informations sur vos fonctions et variables :


[testdir]# nm a.out  | grep -Ei 'function|main|globalvar'
000000000040051d T function1
0000000000400532 T function2
000000000060102c D globalvar
U __libc_start_main@@GLIBC_2.2.5
0000000000400547 T main

Vous pouvez voir que les fonctions sont marquées T , qui représente les symboles dans le texte section, tandis que les variables sont marquées comme D , qui représente les symboles dans les données initialisées rubrique.

Imaginez à quel point il sera utile d'exécuter cette commande sur des binaires où vous n'avez pas de code source ? Cela vous permet de jeter un coup d'œil à l'intérieur et de comprendre quelles fonctions et variables sont utilisées. À moins, bien sûr, que les binaires aient été supprimés, auquel cas ils ne contiennent aucun symbole, et donc le nm commande ne serait pas très utile, comme vous pouvez le voir ici :


[testdir]# strip a.out
[testdir]# nm a.out | grep -Ei 'function|main|globalvar'
nm: a.out: no symbols

Conclusion

The GNU binutils tools offer many options for anyone interested in analyzing binaries, and this has only been a glimpse of what they can do for you. Read the man pages for each tool to understand more about them and how to use them.


Linux
  1. Les meilleurs outils Linux pour les écrivains

  2. Top 5 des outils de mise en cache Linux open source recommandés par les geeks

  3. L'utilisation de Rsync pendant la mise à jour de la source est-elle sûre ?

  4. Installation par défaut de Solaris (outils utilisateur) ?

  5. Commande mv sous Linux :7 exemples essentiels

Mettez à niveau le matériel de votre PC Linux à l'aide d'outils open source

4 outils open source pour exécuter un serveur Linux

5 alternatives modernes aux outils de ligne de commande Linux essentiels

Commande de source bash

Les 10 outils de navigation de fichiers open source pour le système Linux

Tracer le fichier .gnu avec gnuplot