GNU/Linux >> Tutoriels Linux >  >> Linux

10 façons d'analyser des fichiers binaires sous Linux

"Il y a 10 types de personnes dans ce monde :celles qui comprennent le binaire et celles qui ne le comprennent pas."

Plus de ressources Linux

  • Aide-mémoire des commandes Linux
  • Aide-mémoire des commandes Linux avancées
  • Cours en ligne gratuit :Présentation technique de RHEL
  • Aide-mémoire sur le réseau Linux
  • Aide-mémoire SELinux
  • Aide-mémoire sur les commandes courantes de Linux
  • Que sont les conteneurs Linux ?
  • Nos derniers articles Linux

Nous travaillons quotidiennement avec des fichiers binaires, mais nous comprenons si peu de choses à leur sujet. Par binaires, j'entends les fichiers exécutables que vous exécutez quotidiennement, depuis vos outils de ligne de commande jusqu'aux applications complètes.

Linux fournit un riche ensemble d'outils qui facilitent l'analyse des binaires ! Quel que soit votre rôle, si vous travaillez sur Linux, connaître les bases de ces outils vous aidera à mieux comprendre votre système.

Dans cet article, nous couvrirons certains des outils et commandes Linux les plus populaires, dont la plupart seront disponibles en mode natif dans le cadre de votre distribution Linux. Sinon, vous pouvez toujours utiliser votre gestionnaire de packages pour les installer et les explorer. N'oubliez pas :apprendre à utiliser le bon outil au bon moment nécessite beaucoup de patience et de pratique.

fichier

Action :aide à déterminer le type de fichier.

Ce sera votre point de départ pour l'analyse binaire. Nous travaillons quotidiennement avec des fichiers. Tout n'est pas un type exécutable; il existe toute une gamme de types de fichiers. Avant de commencer, vous devez comprendre le type de fichier en cours d'analyse. S'agit-il d'un fichier binaire, d'un fichier de bibliothèque, d'un fichier texte ASCII, d'un fichier vidéo, d'un fichier image, d'un PDF, d'un fichier de données, etc. ?

Le fichier La commande vous aidera à identifier le type de fichier exact auquel vous avez affaire.

$ file /bin/ls
/bin/ls :exécutable LSB 64 bits ELF, x86-64, version 1 (SYSV), lié dynamiquement (utilise des bibliothèques partagées), pour GNU/Linux 2.6.32 , BuildID[sha1]=94943a89d17e9d373b2794dcb1f7e38c95b66c86, supprimé
$
$ fichier /etc/passwd
/etc/passwd :texte ASCII
$

ldd

Ce qu'il fait :imprime les dépendances d'objets partagés.

Si vous avez déjà utilisé le fichier commande ci-dessus sur un binaire exécutable, vous ne pouvez pas manquer le message "lié dynamiquement" dans la sortie. Qu'est-ce que cela signifie ?

Lors du développement d'un logiciel, nous essayons de ne pas réinventer la roue. Il existe un ensemble de tâches courantes requises par la plupart des logiciels, comme l'impression d'une sortie ou la lecture à partir d'un fichier standard, ou l'ouverture de fichiers, etc. Toutes ces tâches courantes sont résumées dans un ensemble de fonctions communes que tout le monde peut ensuite utiliser au lieu d'écrire. leurs propres variantes. Ces fonctions courantes sont placées dans une bibliothèque appelée libc ou glibc .

Comment trouver de quelles bibliothèques dépend l'exécutable ? C'est là que ldd commande entre dans l'image. L'exécuter sur un binaire lié dynamiquement montre toutes ses bibliothèques dépendantes et leurs chemins.

$ ldd /bin/ls
        linux-vdso.so.1 =>  (0x00007ffef5ba1000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fea9f854000)
        libcap.so.2 => /lib64/libcap.so.2 (0x00007fea9f64f000)
        libacl.so.1 => /lib64/libacl.so.1 (0x00007fea9f446000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fea9f079000)
        libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fea9ee17000)
        libdl.so.2 => /lib64/libdl .so.2 (0x00007fea9ec13000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fea9fa7b000)
        libattr.so.1 => /lib64/libattr.so.1 (0x00007fea9ea0e000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fea9e7f2000)
$

ltrace

Ce qu'il fait :un traceur d'appels de bibliothèque.

Nous savons maintenant comment trouver les bibliothèques dont dépend un programme exécutable en utilisant le ldd commande. Cependant, une bibliothèque peut contenir des centaines de fonctions. Parmi ces centaines, quelles sont les fonctions réellement utilisées par notre binaire ?

La ltrace La commande affiche toutes les fonctions appelées au moment de l'exécution à partir de la bibliothèque. Dans l'exemple ci-dessous, vous pouvez voir les noms de fonction appelés, ainsi que les arguments passés à cette fonction. Vous pouvez également voir ce qui a été renvoyé par ces fonctions à l'extrême droite de la sortie.

 $ LTRACE LS 
__LIBC_START_MAIN (0x4028C0, 1, 0x7FFD94023B88, 0x412950
Strrchr ("LS", '/') =NIL
SETLOCALE (LC_ALL, " ") =" EN_US.UTF-8 "
BindTextDomain (" Coreutils "," / USR / Share / locale ") =" / USR / Share / locale "
TextDomain (" Coreutils ") =" Coretutils "
__cxa_atexit (0x40A930, 0, 0, 0x736c6974756572) =0
ISATTY (1) =1
getenv (" citant_style ") =nil
getenv (" colonnes ") =néant
ioctl(1, 21523, 0x7ffd94023a50)                                                     =0
< >
f Flush (0x7FF7BAAE61C0) =0
FCLOSE (0x7FF7BAAE61C0) =0
+++ EXITÉ (Statut 0) +++
$

Hexdump

Ce qu'il fait :afficher le contenu du fichier au format ASCII, décimal, hexadécimal ou octal.

Souvent, il arrive que vous ouvriez un fichier avec une application qui ne sait pas quoi faire avec ce fichier. Essayez d'ouvrir un fichier exécutable ou un fichier vidéo à l'aide de vim ; tout ce que vous verrez, c'est du charabia jeté sur l'écran.

L'ouverture de fichiers inconnus dans Hexdump vous aide à voir exactement ce que contient le fichier. Vous pouvez également choisir de voir la représentation ASCII des données présentes dans le fichier à l'aide de certaines options de ligne de commande. Cela pourrait vous donner des indices sur le type de fichier dont il s'agit.

$ hexdump -C /bin/ls | tête
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  d4 42 40 00 00 00 00 00  |..>......B@.....|
00000020  40 00 00 00 00 00 00 00  f0 c3 01 00 00 00 00 00  |@ ...............|
00000030  00 00 00 00 40 00 38 00  09 00 40 00 1f 00 1e 00  |[email protected]...@. ....|
00000040  06 00 00 00 05 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000050  40 00 40 00 00 00 00 00  40 00 40 00 00 00 00 00  |@.@.....@.@.....|
00000060  f8 01 00 00 00 00 00 00  f8 01 00 00 00 00 00 00  |................|
00000070  08 00 00 00 00 00 00 00  03 00 00 00 04 00 00 00  |...... ..........|
00000080  38 02 00 00 00 00 00 00  38 02 40 00 00 00 00 00  |8.......8.@.....|
00000090  38 02 40 00 00 00 00 00  1c 00 00 00 00 00 00 00  |8.@.............|
$

chaînes

Ce qu'il fait :imprimer les chaînes de caractères imprimables dans les fichiers.

Si Hexdump semble un peu exagéré pour votre cas d'utilisation et que vous recherchez simplement des caractères imprimables dans un binaire, vous pouvez utiliser les chaînes commande.

Lors du développement d'un logiciel, divers messages texte/ASCII y sont ajoutés, tels que l'impression de messages d'information, des informations de débogage, des messages d'aide, des erreurs, etc. Si toutes ces informations sont présentes dans le binaire, elles seront transférées à l'écran à l'aide de chaînes .

$ strings /bin/ls 

lisez-vous

Fonction :affiche des informations sur les fichiers ELF.

ELF (Executable and Linkable File Format) est le format de fichier dominant pour les exécutables ou les binaires, non seulement sur Linux mais également sur une variété de systèmes UNIX. Si vous avez utilisé des outils comme la commande de fichier, qui vous indique que le fichier est au format ELF, la prochaine étape logique sera d'utiliser le readelf commande et ses différentes options pour analyser le fichier plus en détail.

Avoir une référence de la spécification ELF réelle à portée de main lors de l'utilisation de readelf peut être très utile. Vous pouvez trouver la spécification ici.

$ readelf -h /bin/ls
En-tête ELF :
  Magie :  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Classe :                            ELF64
Données:Complément 2 de 2 Endian
Version:1 (Courant)
OS / ABI:UNIX - Système V
Version ABI:0
Type:Exec (fichier exécutable)
Machine:Dispositifs micro avancés X86-64
Version:0x1
Adresse du point d'entrée:0x4042D4
Début des en-têtes de programme:64 (octets dans le fichier)
Début des en-têtes :         115696 (octets dans le fichier)
  Indicateurs :                            0x0
  Taille de cet en-tête :              64 (octets)
  Taille des en-têtes de programme :           56 (octets)      Nombre d'en-têtes de programme 
 

  Taille des en-têtes de section :          64 (octets)
 Nombre d'en-têtes de section :        31
  Index de table de chaînes d'en-tête de section :30
$

vidage obj

Ce qu'il fait :afficher des informations à partir d'un fichier objet.

Les binaires sont créés lorsque vous écrivez du code source qui est compilé à l'aide d'un outil appelé, sans surprise, un compilateur. Ce compilateur génère des instructions en langage machine équivalentes au code source, qui peuvent ensuite être exécutées par le CPU pour effectuer une tâche donnée. Ce code en langage machine peut être interprété via des mnémoniques appelés langage d'assemblage. Un langage d'assemblage est un ensemble d'instructions qui vous aident à comprendre les opérations effectuées par le programme et finalement exécutées sur le CPU.

objdump L'utilitaire lit le fichier binaire ou exécutable et affiche les instructions du langage d'assemblage à l'écran. La connaissance de l'assemblage est essentielle pour comprendre la sortie de objdump commande.

N'oubliez pas :le langage d'assemblage est spécifique à l'architecture.

$ objdump -d /bin/ls | head

/bin/ls :    format de fichier elf64-x86-64


Démontage de la section .init :

0000000000402150 <_init @@ base>:
402150:48 83 EC 08 SUB $ 08,% RSP
402154:48 8B 05 6D 8E 21 00 MOV 0x218E6D (% RIP),% Rax # 61Afc8 <__ Gmon_Start __>
  40215b :      48 85 c0                test   %rax,%rax
$

tracer

Ce qu'il fait :suivre les appels et les signaux du système.

Si vous avez utilisé ltrace , mentionné précédemment, pensez à strace étant similaire. La seule différence est qu'au lieu d'appeler une bibliothèque, la strace l'utilitaire suit les appels système. Les appels système vous permettent d'interagir avec le noyau pour faire le travail.

Pour donner un exemple, si vous voulez imprimer quelque chose à l'écran, vous utiliserez le printf ou place fonction de la bibliothèque standard libc; cependant, sous le capot, finalement, un appel système nommé write sera fait pour réellement imprimer quelque chose à l'écran.

$ strace -f /bin/ls
execve("/bin/ls", ["/bin/ls"], [/* 17 vars */]) =0
brk( NULL)                               =0x686000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =0x7f967956a000
access("/etc/ld.so.preload", R_OK) =  preload", R_OK) 1 ENOENT (Aucun fichier ou répertoire de ce type)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) =3
fstat(3, {st_mode=S_IFREG|0644, st_size=40661 , ...}) =0
mmap(NULL, 40661, PROT_READ, MAP_PRIVATE, 3, 0) =0x7f9679560000
close(3)                              =0
< >
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) =0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 ) =0x7f9679569000
Écrire (1, "R2 RH \ N", 7R2 RH Fermer (1) =0
MUNMAP (0x7f9679569000, 4096) =0
fermer(2)                                =0
exit_group(0)                                                     = 0 + sorti avec 0 +++
$

nm

Ce qu'il fait :répertorier les symboles des fichiers d'objets.

Si vous travaillez avec un binaire qui n'est pas dépouillé, le nm La commande vous fournira les informations précieuses qui ont été intégrées dans le binaire lors de la compilation. nm peut vous aider à identifier les variables et les fonctions du binaire. Vous pouvez imaginer à quel point cela serait utile si vous n'aviez pas accès au code source du binaire en cours d'analyse.

Pour présenter nm , nous allons rapidement écrire un petit programme et le compiler avec le -g option, et nous verrons également que le binaire n'est pas supprimé en utilisant la commande file.

$ cat hello.c 
#include

int main() {
    printf("Hello world!");
    return 0 ;
}
$
$ gcc -g bonjour.c -o bonjour
$
$ fichier bonjour
bonjour :ELF 64 bits LSB exécutable , x86-64, version 1 (SYSV), lié dynamiquement (utilise des bibliothèques partagées), pour GNU/Linux 2.6.32, BuildID[sha1]=3de46c8efb98bce4ad525d3328121568ba3d8a5d, non supprimé
$
$ ./hello
Bonjour le monde !$
$


$ nm bonjour | queue
0000000000600E20 D __JC_CSU_FINI1
0000000000006005B0 t __libc_csu_fini
u __libc_start_main @@ glibc_2.2.5
0000000000004005D T Main gdb

Ce qu'il fait :le débogueur GNU.

Eh bien, tout dans le binaire ne peut pas être analysé statiquement. Nous avons exécuté certaines commandes qui exécutaient le binaire, comme ltrace et trace; cependant, le logiciel se compose d'une variété de conditions qui pourraient conduire à l'exécution de divers chemins alternatifs.

La seule façon d'analyser ces chemins est au moment de l'exécution en ayant la possibilité d'arrêter ou de mettre en pause le programme à n'importe quel endroit donné et d'être capable d'analyser les informations puis de descendre plus bas.

C'est là que les débogueurs viennent dans l'image, et sous Linux, gdb est le débogueur de facto. Il vous aide à charger un programme, à définir des points d'arrêt à des endroits spécifiques, à analyser la mémoire et le registre du processeur, et bien plus encore. Il complète les autres outils mentionnés ci-dessus et vous permet de faire beaucoup plus d'analyses d'exécution.

Une chose à noter est qu'une fois que vous avez chargé un programme à l'aide de gdb , vous serez présenté avec son propre (gdb) rapide. Toutes les autres commandes seront exécutées dans ce gdb invite de commande jusqu'à ce que vous quittiez.

Nous allons utiliser le programme "hello" que nous avons compilé précédemment et utiliser gdb pour voir comment cela fonctionne.

$ gdb -q ./hello
Lecture des symboles depuis /home/flash/hello...done.
(gdb) break main
Point d'arrêt 1 à 0x400521 :fichier hello.c , ligne 4.
(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400521 in main at hello.c:4
(gdb) run
Programme de démarrage :/home/flash/./hello

Point d'arrêt 1, main () à hello.c:4
4           printf("Hello world !");
 Infos de débogage séparées manquantes, utilisez :debuginfo-install glibc-2.17-260.el7_6.6.x86_64
(gdb) bt
#0  main () sur hello.c:4
(gdb ) c
Continuant.
Bonjour tout le monde ![Inférieur 1 (processus 29620) s'est terminé normalement]
(gdb) q
$

Conclusion

Une fois que vous êtes à l'aise avec l'utilisation de ces outils d'analyse binaire Linux natifs et que vous comprenez le résultat qu'ils fournissent, vous pouvez ensuite passer à des outils d'analyse binaire open source plus avancés et professionnels comme radare2.


Linux
  1. 3 façons de lister les utilisateurs sous Linux

  2. Commandes de recherche Linux

  3. Commande mcopy sous Linux

  4. Comment comparer des fichiers binaires sous Linux ?

  5. Diff/correctif binaire pour les fichiers volumineux sous Linux ?

Les 6 meilleures façons d'afficher des fichiers sous Linux

Exécuter des fichiers binaires sous Linux

3 façons de trouver des fichiers et des répertoires sous Linux

Outils Sysadmin :11 façons d'utiliser la commande ls sous Linux

4 façons de transférer des fichiers et des répertoires sous Linux

Commande ls sous Linux/UNIX