in

Comment jeter un œil à l’intérieur des fichiers binaires à partir de la ligne de commande Linux

fatmawati achmad zaenuri / Shutterstock

Vous avez un fichier mystère? Le 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 toute une série de partenaires 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’ils contiennent. Cela n’aurait aucun sens 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 forme d’identité.

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 elles-mêmes, 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 confiant, 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 le 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 la file commander.

Utilisation de la commande de fichier

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 qu’il y a dans le répertoire, et le -hl (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 voyez 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 dans le format de la 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 Fichiers, même si son extension est XYZ.

Dans le Files navigateur de fichiers, il reçoit 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’image et de musique, donne 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

file 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 avez un fichier avec l’extension «.c», contenant du texte brut standard mais pas du code source, file ne le confond pas avec un véritable C fichier de code source:

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») comme faisant partie d’une collection de fichiers de code source C, et il sait que le makefile est un script.

Utilisation d’un fichier avec des fichiers binaires

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

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 lorsque le programme terminé s’exécutera, 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 sous 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 dit que le fichier «watch.exe» est un exécutable PE32 +, programme 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 Format exécutable et liable (ELF). Il s’agit d’une norme pour les fichiers exécutables et les fichiers d’objets partagés, tels que les bibliothèques. Nous allons bientôt jeter un œil au format d’en-tête ELF.

Ce qui pourrait attirer votre attention, c’est que les deux exécutables («wd» et «hello») sont identifiés comme Base standard Linux (LSB) objets partagés, et le fichier d’objets «wd.o» est identifié comme un LSB relocalisable. 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 endroit. 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 sorte qu’ils héritent de cette capacité.

Cela permet au Randomisation de la disposition de l’espace d’adressage (ASMR) pour 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 dicte où ils sont chargés en mémoire.

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 les emplacements de leurs fonctions seront toujours connus des attaquants. Positionner les exécutables indépendants (PIE) positionné à une adresse aléatoire surmonte cette susceptibilité.

Si nous compiler notre programme avec le gcc compilateur et fournissez le -no-pie option, nous allons générer un exécutable conventionnel.

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’auparavant (17 Ko):

ls -hl hello

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

Le binaire est maintenant identifié comme un exécutable standard. Nous faisons cela à des fins de démonstration uniquement. 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 il pourrait difficilement être appelé grand, mais alors, tout est relatif. Le code source est de 120 octets:

cat hello.c

Qu’est-ce qui gonfle le binaire si tout ce qu’il fait est imprimer une chaîne dans la fenêtre du terminal? Nous savons qu’il existe un en-tête ELF, mais il 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.

Allons scannez le binaire avec le strings commande 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 y a beaucoup de chaînes dans le binaire, en plus du « Hello, Geek world! » à partir de notre code source. La plupart d’entre eux sont des étiquettes pour les régions dans le binaire, ainsi que les noms et les informations de liaison des objets partagés. Celles-ci incluent les bibliothèques et les fonctions de ces bibliothèques, dont dépend le binaire.

le ldd commander nous montre les dépendances d’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 ne le fait pas):

  • linux-vdso.so: Objet partagé dynamique virtuel (VDSO) est un mécanisme de noyau qui permet d’accéder à un ensemble de routines d’espace noyau par un binaire d’espace utilisateur. Ce évite la surcharge d’un changement de contexte depuis le mode noyau 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 norme Bibliothèque GNU C si le noyau prend en charge le schéma ASMR.
  • libc.so.6: le Bibliothèque GNU C objet partagé.
  • /lib64/ld-linux-x86-64.so.2: Il s’agit de l’éditeur de liens dynamique que le binaire souhaite utiliser. L’éditeur de liens dynamique interroge le binaire pour découvrir quelles dépendances il a. Il lance ces objets partagés en mémoire. Il prépare le binaire à s’exécuter et à 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 le -h (en-tête de fichier) option:

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 bonjour 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 en tant que binaire ELF. Pour rendre cela parfaitement clair, les trois octets suivants épellent «ELF» dans ASCII:

  • Classe: Indique si le binaire est un exécutable 32 ou 64 bits (1 = 32, 2 = 64).
  • Les données: Indique le endianité utilisé. Le codage endian définit la façon dont les nombres multi-octets sont stockés. Dans l’encodage big-endian, un nombre est stocké avec ses bits les plus significatifs en premier. Dans le codage little-endian, le nombre est stocké avec ses bits les moins significatifs en premier.
  • Version: La version d’ELF (actuellement, c’est 1).
  • OS / ABI: Représente le type de interface binaire d’application utilisé. 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: Le type de binaire ELF. Les valeurs communes sont ET_REL pour une ressource relocalisable (comme un fichier objet), ET_EXEC pour un exécutable compilé avec le -no-pie drapeau, et ET_DYN pour un exécutable compatible ASMR.
  • Machine: le Architecture d’ensemble 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: L’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 aperçu rapide des 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 (canonique) nous donne la représentation ASCII des octets avec leurs valeurs hexadécimales, et le -n L’option (number) nous permet de spécifier le nombre d’octets que 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 les détails, vous pouvez utiliser le objdumpcommande avec la -d (démonter) option:

objdump -d hello | less

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

Cela désassemble le code machine exécutable et l’affiche en octets hexadécimaux à côté de l’équivalent en langage assembleur. L’emplacement de l’adresse du premier bye dans 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 ce qui se passe derrière le rideau. Il y a beaucoup de sortie, donc nous l’avons insérée 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 manière 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 se lient 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 *

5 appareils Smarthome que vous ne devriez pas acheter

5 appareils Smarthome que vous ne devriez pas acheter

Comment suivre les hyperliens dans Outlook 2013 sans maintenir la touche Ctrl enfoncée

Comment suivre les hyperliens dans Outlook 2013 sans maintenir la touche Ctrl enfoncée