Catégories
Actualités Tech

Comment consulter des fichiers binaires à partir de la ligne de commande Linux

Un terminal Linux stylisé avec des lignes de texte vert sur un ordinateur portable.
fatmawati achmad zaenuri / Shutterstock

Vous avez un dossier mystère? Linux file La commande vous indiquera rapidement de quel type de fichier il s'agit. S'il s'agit d'un fichier binaire, vous pouvez en savoir plus à ce sujet. file a tout un tas de compagnons stables qui vous aideront à l'analyser. Nous allons vous montrer comment utiliser certains de ces outils.

Identification des types de fichiers

Les fichiers ont généralement des caractéristiques qui permettent aux progiciels d'identifier de quel type de fichier il s'agit, ainsi que ce que les données qu'il contient. Il ne serait pas logique d'essayer d'ouvrir un fichier PNG dans un lecteur de musique MP3, il est donc à la fois utile et pragmatique qu'un fichier porte avec lui une certaine forme d'identification.

Il peut s'agir de quelques octets de signature au tout début du fichier. Cela permet à un fichier d'être explicite sur son format et son contenu. Parfois, le type de fichier est déduit d'un aspect distinctif de l'organisation interne des données elle-même, connu sous le nom d'architecture de fichier.

Certains systèmes d'exploitation, comme Windows, sont entièrement guidés par l'extension d'un fichier. Vous pouvez l'appeler crédule ou de confiance, mais Windows suppose que tout fichier avec l'extension DOCX est vraiment un fichier de traitement de texte DOCX. Linux n'est pas comme ça, comme vous le verrez bientôt. Il veut une preuve et regarde à l'intérieur du fichier pour la trouver.

Les outils décrits ici étaient déjà installés sur les distributions Manjaro 20, Fedora 21 et Ubuntu 20.04 que nous avons utilisées pour rechercher cet article. Commençons notre enquête en utilisant le file commander.

Utilisation de la commande file

Nous avons une collection de différents types de fichiers dans notre répertoire actuel. Ils sont un mélange de documents, de code source, d'exécutables et de fichiers texte.

le ls La commande nous montrera ce qui se trouve dans le répertoire, et la -hl L'option (tailles lisibles par l'homme, liste longue) nous montrera la taille de chaque fichier:

ls -hl

ls -hl dans une fenêtre de terminal.

Essayons file sur quelques-uns d'entre eux et voir ce que nous obtenons:

file build_instructions.odt
file build_instructions.pdf
file COBOL_Report_Apr60.djvu

fichier build_instructions.odt dans une fenêtre de terminal.

Les trois formats de fichiers sont correctement identifiés. Lorsque c'est possible, file nous donne un peu plus d'informations. Le fichier PDF serait au format version 1.5.

Même si nous renommons le fichier ODT pour avoir une extension avec la valeur arbitraire de XYZ, le fichier est toujours correctement identifié, à la fois dans le Files navigateur de fichiers et sur la ligne de commande en utilisant file.

Fichier OpenDocument correctement identifié dans le navigateur de fichiers Files, même si son extension est XYZ.

Dans le Files navigateur de fichiers, il est donné l'icône correcte. Sur la ligne de commande, file ignore l'extension et regarde à l'intérieur du fichier pour déterminer son type:

file build_instructions.xyz

fichier build_instructions.xyz dans une fenêtre de terminal.

En utilisant file sur les supports, tels que les fichiers d'images et de musique, fournit généralement des informations concernant leur format, leur encodage, leur résolution, etc.:

file screenshot.png
file screenshot.jpg
file Pachelbel_Canon_In_D.mp3

fichier screenshot.png dans une fenêtre de terminal.

Fait intéressant, même avec des fichiers en texte brut, file ne juge pas le fichier par son extension. Par exemple, si vous disposez d'un fichier avec l'extension ".c", contenant du texte brut standard mais pas de code source, file ne le confond pas avec un véritable fichier de code source C:

file function+headers.h
file makefile
file hello.c

fonction de fichier + headers.h dans une fenêtre de terminal.

file identifie correctement le fichier d'en-tête («.h») dans le cadre d'une collection de fichiers de code source C, et il sait que le makefile est un script.

Utilisation de fichiers avec des fichiers binaires

Les fichiers binaires sont plus une «boîte noire» que les autres. Les fichiers image peuvent être visualisés, les fichiers son peuvent être lus et les fichiers de document peuvent être ouverts par le logiciel approprié. Les fichiers binaires, cependant, sont plus difficiles.

Par exemple, les fichiers «hello» et «wd» sont des exécutables binaires. Ce sont des programmes. Le fichier appelé «wd.o» est un fichier objet. Lorsque le code source est compilé par un compilateur, un ou plusieurs fichiers objets sont créés. Ceux-ci contiennent le code machine que l'ordinateur exécutera éventuellement lors de l'exécution du programme terminé, ainsi que des informations pour l'éditeur de liens. L'éditeur de liens vérifie chaque fichier objet pour les appels de fonction aux bibliothèques. Il les relie à toutes les bibliothèques utilisées par le programme. Le résultat de ce processus est un fichier exécutable.

Le fichier «watch.exe» est un exécutable binaire qui a été compilé de manière croisée pour s'exécuter sur Windows:

file wd
file wd.o
file hello
file watch.exe

fichier wd dans une fenêtre de terminal.

Prenant le dernier en premier, file nous indique que le fichier «watch.exe» est un exécutable PE32 +, programme de console, pour la famille de processeurs x86 sous Microsoft Windows. PE signifie format exécutable portable, qui a des versions 32 et 64 bits. Le PE32 est la version 32 bits et le PE32 + est la version 64 bits.

Les trois autres fichiers sont tous identifiés comme des fichiers ELF (Executable and Linkable Format). Il s'agit d'une norme pour les fichiers exécutables et les fichiers d'objets partagés, tels que les bibliothèques. Nous examinerons le format d'en-tête ELF sous peu.

Ce qui pourrait attirer votre attention, c'est que les deux exécutables («wd» et «hello») sont identifiés comme des objets partagés Linux Standard Base (LSB), et le fichier objet «wd.o» est identifié comme un LSB déplaçable. Le mot exécutable est évident en son absence.

Les fichiers objets sont déplaçables, ce qui signifie que le code qu'ils contiennent peut être chargé en mémoire à n'importe quel emplacement. Les exécutables sont répertoriés en tant qu'objets partagés car ils ont été créés par l'éditeur de liens à partir des fichiers objets de telle manière qu'ils héritent de cette capacité.

Cela permet au système ASMR (Address Space Layout Randomization) de charger les exécutables en mémoire aux adresses de son choix. Les exécutables standard ont une adresse de chargement codée dans leurs en-têtes, qui dictent où ils sont chargés en mémoire.

L'ASMR est une technique de sécurité. Le chargement d'exécutables en mémoire à des adresses prévisibles les rend vulnérables aux attaques. En effet, leurs points d'entrée et l'emplacement de leurs fonctions seront toujours connus des attaquants. Les exécutables indépendants de position (PIE) positionnés à une adresse aléatoire surmontent cette vulnérabilité.

Si nous compilons notre programme avec le gcc compilateur et fournir le -no-pie option, nous allons générer un exécutable classique.

le -o (fichier de sortie) nous permet de donner un nom à notre exécutable:

gcc -o hello -no-pie hello.c

Nous utiliserons file sur le nouvel exécutable et voyez ce qui a changé:

file hello

La taille de l'exécutable est la même qu'avant (17 Ko):

ls -hl hello

gcc -o hello -no-pie hello.c dans une fenêtre de terminal.

Le binaire est maintenant identifié comme un exécutable standard. Nous le faisons uniquement à des fins de démonstration. Si vous compilez des applications de cette façon, vous perdrez tous les avantages de l'ASMR.

Pourquoi un exécutable est-il si gros?

Notre exemple hello programme est de 17 Ko, donc on pourrait difficilement l’appeler grand, mais alors, tout est relatif. Le code source est de 120 octets:

cat hello.c

Qu'est-ce qui gonfle le binaire s'il ne fait qu'imprimer une chaîne dans la fenêtre du terminal? Nous savons qu'il y a un en-tête ELF, mais cela ne fait que 64 octets pour un binaire 64 bits. En clair, ça doit être autre chose:

ls -hl hello

cat hello.c dans une fenêtre de terminal.

Analysons le binaire avec le strings comme une première étape simple pour découvrir ce qu'il contient. Nous allons le canaliser dans less:

strings hello | less

cordes bonjour | moins dans une fenêtre de terminal.

Il existe de nombreuses chaînes à l'intérieur du binaire, en plus du "Bonjour, monde Geek!" à partir de notre code source. La plupart d'entre eux sont des étiquettes pour les régions au sein du binaire, et les noms et informations de liaison des objets partagés. Il s'agit notamment des bibliothèques et des fonctions au sein de ces bibliothèques, dont le binaire dépend.

le ldd La commande nous montre les dépendances des objets partagés d'un binaire:

ldd hello

ldd bonjour dans une fenêtre de terminal.

Il y a trois entrées dans la sortie, et deux d'entre elles incluent un chemin de répertoire (la première n'en a pas):

  • linux-vdso.so: L'objet partagé dynamique virtuel (VDSO) est un mécanisme de noyau qui permet à un ensemble de routines de l'espace noyau d'être accédé par un binaire de l'espace utilisateur. Cela évite la surcharge d'un changement de contexte du mode noyau de l'utilisateur. Les objets partagés VDSO adhèrent au format ELF (Executable and Linkable Format), ce qui leur permet d'être liés dynamiquement au binaire lors de l'exécution. Le VDSO est alloué dynamiquement et tire parti de l'ASMR. La capacité VDSO est fournie par la bibliothèque GNU C standard si le noyau prend en charge le schéma ASMR.
  • libc.so.6: L'objet partagé GNU C Library.
  • /lib64/ld-linux-x86-64.so.2: C'est l'éditeur de liens dynamique que le binaire veut utiliser. L'éditeur de liens dynamique interroge le binaire pour découvrir ses dépendances. Il lance ces objets partagés en mémoire. Il prépare le binaire à s'exécuter et à pouvoir trouver et accéder aux dépendances en mémoire. Ensuite, il lance le programme.

L'en-tête ELF

Nous pouvons examiner et décoder l'en-tête ELF en utilisant le readelf utilitaire et -h Option (en-tête de fichier):

readelf -h hello

readelf -h bonjour dans une fenêtre de terminal.

L'en-tête est interprété pour nous.

Sortie de readelf -h hello dans une fenêtre de terminal.

Le premier octet de tous les binaires ELF est défini sur la valeur hexadécimale 0x7F. Les trois octets suivants sont définis sur 0x45, 0x4C et 0x46. Le premier octet est un indicateur qui identifie le fichier comme un binaire ELF. Pour que ce soit clair, les trois octets suivants indiquent «ELF» en ASCII:

  • Classe: Indique si le binaire est un exécutable 32 ou 64 bits (1 = 32, 2 = 64).
  • Les données: Indique l'endianité en cours d'utilisation. Le codage endian définit la façon dont les nombres multi-octets sont stockés. Dans le codage big-endian, un nombre est d'abord stocké avec ses bits les plus significatifs. Dans le codage petit-boutien, le nombre est d'abord stocké avec ses bits les moins significatifs.
  • Version: La version d'ELF (actuellement, c'est 1).
  • OS / ABI: Représente le type d'interface binaire d'application utilisée. Cela définit l'interface entre deux modules binaires, tels qu'un programme et une bibliothèque partagée.
  • Version ABI: La version de l'ABI.
  • Type: Type de fichier binaire ELF. Les valeurs communes sont ET_REL pour une ressource déplaçable (comme un fichier objet), ET_EXEC pour un exécutable compilé avec le -no-pie drapeau, et ET_DYN pour un exécutable prenant en charge ASMR.
  • Machine: L'architecture du jeu d'instructions. Cela indique la plate-forme cible pour laquelle le binaire a été créé.
  • Version: Toujours défini sur 1, pour cette version d'ELF.
  • Adresse du point d'entrée: Adresse mémoire dans le binaire à laquelle l'exécution commence.

Les autres entrées sont des tailles et des nombres de régions et de sections dans le binaire afin que leurs emplacements puissent être calculés.

Un coup d'œil rapide sur les huit premiers octets du binaire avec hexdump affichera l'octet de signature et la chaîne «ELF» dans les quatre premiers octets du fichier. le -C (canonical) option nous donne la représentation ASCII des octets à côté de leurs valeurs hexadécimales, et le -n L'option (nombre) nous permet de spécifier combien d'octets nous voulons voir:

hexdump -C -n 8 hello

hexdump -C -n 8 bonjour dans une fenêtre de terminal.

objdump et la vue granulaire

Si vous voulez voir le détail, vous pouvez utiliser le objdumpcommande avec le -d option (démonter):

objdump -d hello | less

objdump -d bonjour | moins dans une fenêtre de terminal.

Cela démonte le code machine exécutable et l'affiche en octets hexadécimaux à côté de l'équivalent du langage assembleur. L'emplacement de l'adresse du premier bye de chaque ligne est affiché à l'extrême gauche.

Cela n'est utile que si vous pouvez lire le langage d'assemblage ou si vous êtes curieux de savoir ce qui se passe derrière le rideau. Il y a beaucoup de sortie, nous l'avons donc canalisé dans less.

Putput de objdump -d bonjour | moins dans une fenêtre de terminal.

Compilation et liaison

Il existe de nombreuses façons de compiler un binaire. Par exemple, le développeur choisit d'inclure ou non les informations de débogage. La façon dont le binaire est lié joue également un rôle dans son contenu et sa taille. Si les références binaires partagent des objets en tant que dépendances externes, il sera plus petit que celui auquel les dépendances sont liées statiquement.

La plupart des développeurs connaissent déjà les commandes que nous avons couvertes ici. Pour d'autres, cependant, ils offrent des moyens faciles de fouiller et de voir ce qui se trouve à l'intérieur de la boîte noire binaire.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *