So können Sie über die Linux-Befehlszeile in Binärdateien einsehen

Haben Sie eine mysteriöse Datei? Der Linux-Dateibefehl sagt Ihnen schnell, um welchen Dateityp es sich handelt. Wenn es sich jedoch um eine Binärdatei handelt, können Sie noch mehr darüber erfahren. file hat eine ganze Reihe von Stablemates, die Ihnen bei der Analyse helfen. Wir zeigen Ihnen, wie Sie einige dieser Tools verwenden.

Identifizieren von Dateitypen

Dateien weisen normalerweise Merkmale auf, die es Softwarepaketen ermöglichen, zu erkennen, um welchen Dateityp es sich handelt und was die darin enthaltenen Daten darstellen. Es würde keinen Sinn machen, eine PNG-Datei in einem MP3-Musikplayer zu öffnen, daher ist es sowohl nützlich als auch pragmatisch, dass eine Datei eine Form von ID mit sich führt.

Dies können einige Signaturbytes ganz am Anfang der Datei sein. Dies ermöglicht es einer Datei, ihr Format und ihren Inhalt eindeutig anzugeben. Manchmal wird der Dateityp aus einem bestimmten Aspekt der internen Organisation der Daten selbst abgeleitet, der als Dateiarchitektur bekannt ist.

Einige Betriebssysteme, wie Windows, richten sich vollständig nach der Erweiterung einer Datei. Sie können es leichtgläubig oder vertrauensvoll nennen, aber Windows geht davon aus, dass jede Datei mit der DOCX-Erweiterung wirklich eine DOCX-Textverarbeitungsdatei ist. Linux ist nicht so, wie Sie gleich sehen werden. Es will Beweise und sucht in der Datei, um es zu finden.

Die hier beschriebenen Tools wurden bereits auf den Distributionen Manjaro 20, Fedora 21 und Ubuntu 20.04 installiert, mit denen wir diesen Artikel recherchiert haben. Beginnen wir unsere Untersuchung mit der Dateibefehl.

Verwenden des Dateibefehls

Wir haben eine Sammlung verschiedener Dateitypen in unserem aktuellen Verzeichnis. Sie sind eine Mischung aus Dokument-, Quellcode-, ausführbaren und Textdateien.

Der Befehl ls zeigt uns, was sich im Verzeichnis befindet, und die Option -hl (für Menschen lesbare Größen, lange Auflistung) zeigt uns die Größe jeder Datei:

ls -hl

Lassen Sie uns einige davon mit Datei versuchen und sehen, was wir erhalten:

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

Die drei Dateiformate werden richtig erkannt. Wenn möglich, gibt uns die Datei etwas mehr Informationen. Die PDF-Datei befindet sich angeblich im Version 1.5-Format.

Selbst wenn wir die ODT-Datei umbenennen, um eine Erweiterung mit dem willkürlichen Wert von XYZ zu haben, wird die Datei immer noch korrekt identifiziert, sowohl im Dateibrowser für Dateien als auch in der Befehlszeile mit Datei.

Im Dateibrowser „Dateien“ wird ihm das richtige Symbol zugewiesen. Auf der Befehlszeile ignoriert file die Erweiterung und sieht in der Datei nach, um ihren Typ zu bestimmen:

file build_instructions.xyz

Die Verwendung von Dateien auf Medien wie Bild- und Musikdateien liefert normalerweise Informationen zu Format, Kodierung, Auflösung usw.:

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

Interessanterweise beurteilt file die Datei selbst bei Nur-Text-Dateien nicht nach ihrer Erweiterung. Wenn Sie beispielsweise eine Datei mit der Erweiterung „.c“ haben, die Standard-Klartext, aber keinen Quellcode enthält, wird die Datei nicht mit einem echten C . verwechselt Quellcodedatei:

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

file identifiziert die Header-Datei („.h“) korrekt als Teil einer C-Quellcodesammlung von Dateien und weiß, dass das Makefile ein Skript ist.

  So erstellen Sie eine Auslagerungsdatei unter Linux

Datei mit Binärdateien verwenden

Binärdateien sind eher eine „Black Box“ als andere. Bilddateien können angezeigt, Tondateien abgespielt und Dokumentdateien mit dem entsprechenden Softwarepaket geöffnet werden. Binäre Dateien sind jedoch eine größere Herausforderung.

Beispielsweise sind die Dateien „hello“ und „wd“ binäre ausführbare Dateien. Sie sind Programme. Die Datei „wd.o“ ist eine Objektdatei. Wenn Quellcode von einem Compiler kompiliert wird, werden eine oder mehrere Objektdateien erstellt. Diese enthalten den Maschinencode, den der Computer schließlich ausführen wird, wenn das fertige Programm ausgeführt wird, zusammen mit Informationen für den Linker. Der Linker überprüft jede Objektdatei auf Funktionsaufrufe an Bibliotheken. Es verknüpft sie mit allen Bibliotheken, die das Programm verwendet. Das Ergebnis dieses Prozesses ist eine ausführbare Datei.

Die Datei „watch.exe“ ist eine binäre ausführbare Datei, die für die Ausführung unter Windows querkompiliert wurde:

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

Nehmen wir das letzte zuerst, teilt uns die Datei mit, dass die Datei „watch.exe“ ein ausführbares PE32+-Konsolenprogramm für die x86-Prozessorfamilie unter Microsoft Windows ist. PE steht für Portable Executable Format, die 32- und 64-Bit-Versionen hat. PE32 ist die 32-Bit-Version und PE32+ ist die 64-Bit-Version.

Die anderen drei Dateien sind alle identifiziert als Ausführbares und verknüpfbares Format (ELF)-Dateien. Dies ist ein Standard für ausführbare Dateien und Dateien mit gemeinsam genutzten Objekten, wie z. B. Bibliotheken. Wir werden uns in Kürze das ELF-Header-Format ansehen.

Was Ihnen vielleicht auffallen könnte, ist, dass die beiden ausführbaren Dateien („wd“ und „hello“) identifiziert werden als Linux-Standardbasis (LSB) Shared Objects, und die Objektdatei „wd.o“ wird als LSB relocable identifiziert. Das Wort ausführbar ist in seiner Abwesenheit offensichtlich.

Objektdateien sind verschiebbar, was bedeutet, dass der darin enthaltene Code an jedem Ort in den Speicher geladen werden kann. Die ausführbaren Dateien werden als gemeinsam genutzte Objekte aufgelistet, da sie vom Linker aus den Objektdateien so erstellt wurden, dass sie diese Fähigkeit erben.

Dies ermöglicht die Randomisierung des Adressraum-Layouts (ASMR)-System, um die ausführbaren Dateien an Adressen seiner Wahl in den Speicher zu laden. Ausführbare Standarddateien haben in ihren Headern eine Ladeadresse codiert, die vorschreibt, wo sie in den Speicher geladen werden.

ASMR ist eine Sicherheitstechnik. Das Laden ausführbarer Dateien in den Speicher an vorhersagbaren Adressen macht sie anfällig für Angriffe. Dies liegt daran, dass Angreifern ihre Eintrittspunkte und die Standorte ihrer Funktionen immer bekannt sind. Positionsunabhängige ausführbare Dateien (PIE), die an einer zufälligen Adresse positioniert ist, überwinden diese Anfälligkeit.

Wenn wir Stellen Sie unser Programm zusammen Mit dem gcc-Compiler und der Option -no-pie generieren wir eine konventionelle ausführbare Datei.

  So verwenden Sie SpiderOak One unter Linux

Mit der Option -o (Ausgabedatei) können wir einen Namen für unsere ausführbare Datei angeben:

gcc -o hello -no-pie hello.c

Wir verwenden die Datei für die neue ausführbare Datei und sehen, was sich geändert hat:

file hello

Die Größe der ausführbaren Datei ist dieselbe wie zuvor (17 KB):

ls -hl hello

Die Binärdatei wird jetzt als ausführbare Standarddatei identifiziert. Wir tun dies nur zu Demonstrationszwecken. Wenn Sie Anwendungen auf diese Weise kompilieren, verlieren Sie alle Vorteile des ASMR.

Warum ist eine ausführbare Datei so groß?

Unser Beispiel-Hello-Programm ist 17 KB groß, kann also kaum als groß bezeichnet werden, aber dann ist alles relativ. Der Quellcode ist 120 Byte groß:

cat hello.c

Was sperrt die Binärdatei, wenn sie nur eine Zeichenfolge im Terminalfenster ausgibt? Wir wissen, dass es einen ELF-Header gibt, aber dieser ist für eine 64-Bit-Binärdatei nur 64 Byte lang. Offensichtlich muss es etwas anderes sein:

ls -hl hello

Lasst uns scannen Sie die Binärdatei mit dem strings-Befehl als einfachen ersten Schritt, um herauszufinden, was darin enthalten ist. Wir werden es in weniger umleiten:

strings hello | less

Es gibt viele Zeichenfolgen in der Binärdatei, abgesehen von „Hello, Geek world!“ aus unserem Quellcode. Die meisten von ihnen sind Bezeichnungen für Regionen innerhalb der Binärdatei sowie die Namen und Verknüpfungsinformationen gemeinsam genutzter Objekte. Dazu gehören die Bibliotheken und Funktionen innerhalb dieser Bibliotheken, von denen die Binärdatei abhängt.

Der ldd-Befehl zeigt uns die gemeinsamen Objektabhängigkeiten einer Binärdatei:

ldd hello

Die Ausgabe enthält drei Einträge, von denen zwei einen Verzeichnispfad enthalten (der erste nicht):

linux-vdso.so: Virtual Dynamic Shared Object (VDSO) ist ein Kernel-Mechanismus, der den Zugriff auf eine Reihe von Kernel-Space-Routinen durch eine User-Space-Binärdatei ermöglicht. Dies vermeidet den Overhead eines Kontextwechsels aus dem Benutzer-Kernel-Modus. Gemeinsam genutzte VDSO-Objekte halten sich an das Format Executable and Linkable Format (ELF), sodass sie zur Laufzeit dynamisch mit der Binärdatei verknüpft werden können. Das VDSO wird dynamisch zugewiesen und nutzt ASMR. Die VDSO-Fähigkeit wird vom Standard bereitgestellt GNU C-Bibliothek wenn der Kernel das ASMR-Schema unterstützt.
libc.so.6: Die GNU C-Bibliothek gemeinsames Objekt.
/lib64/ld-linux-x86-64.so.2: Dies ist der dynamische Linker, den die Binärdatei verwenden möchte. Der dynamische Linker fragt die Binärdatei ab, um herauszufinden, welche Abhängigkeiten sie hat. Es startet diese gemeinsam genutzten Objekte in den Speicher. Es bereitet die Binärdatei darauf vor, ausgeführt zu werden und die Abhängigkeiten im Speicher zu finden und darauf zuzugreifen. Dann startet es das Programm.

Der ELF-Header

Wir können den ELF-Header untersuchen und decodieren Verwenden des Dienstprogramms readelf und der Option -h (Dateiheader):

readelf -h hello

Der Header wird für uns interpretiert.

Das erste Byte aller ELF-Binärdateien wird auf den Hexadezimalwert 0x7F gesetzt. Die nächsten drei Bytes werden auf 0x45, 0x4C und 0x46 gesetzt. Das erste Byte ist ein Flag, das die Datei als ELF-Binärdatei identifiziert. Um dies kristallklar zu machen, buchstabieren die nächsten drei Bytes „ELF“ in ASCII:

  Die 5 besten Steam Game Store-Alternativen für Linux

Klasse: Gibt an, ob die Binärdatei eine ausführbare 32- oder 64-Bit-Datei ist (1=32, 2=64).
Daten: Zeigt die Endianität in Benutzung. Die Endian-Kodierung definiert die Art und Weise, wie Multibyte-Zahlen gespeichert werden. Bei der Big-Endian-Kodierung wird eine Zahl mit ihren höchstwertigen Bits zuerst gespeichert. Bei der Little-Endian-Kodierung wird die Zahl mit ihren am wenigsten signifikanten Bits zuerst gespeichert.
Version: Die Version von ELF (derzeit ist es 1).
OS/ABI: Repräsentiert den Typ von Anwendung binäre Schnittstelle in Benutzung. Dies definiert die Schnittstelle zwischen zwei binären Modulen, beispielsweise einem Programm und einer gemeinsam genutzten Bibliothek.
ABI-Version: Die Version des ABI.
Typ: Der Typ der ELF-Binärdatei. Die gemeinsamen Werte sind ET_REL für eine verschiebbare Ressource (wie eine Objektdatei), ET_EXEC für eine ausführbare Datei, die mit dem -no-pie-Flag kompiliert wurde, und ET_DYN für eine ASMR-fähige ausführbare Datei.
Maschine: Die Befehlssatzarchitektur. Dies gibt die Zielplattform an, für die die Binärdatei erstellt wurde.
Version: Für diese Version von ELF immer auf 1 setzen.
Einstiegspunktadresse: Die Speicheradresse innerhalb der Binärdatei, an der die Ausführung beginnt.

Die anderen Einträge sind Größen und Anzahlen von Regionen und Abschnitten innerhalb der Binärdatei, damit ihre Positionen berechnet werden können.

Ein kurzer Blick auf die ersten acht Bytes der Binärdatei mit Hexdump zeigt das Signaturbyte und die „ELF“-Zeichenfolge in den ersten vier Bytes der Datei an. Die Option -C (kanonisch) gibt uns die ASCII-Darstellung der Bytes zusammen mit ihren hexadezimalen Werten, und die Option -n (Zahl) lässt uns angeben, wie viele Bytes wir sehen möchten:

hexdump -C -n 8 hello

objdump und die Granular View

Wenn Sie die Details sehen möchten, können Sie den Befehl objdump mit der Option -d (disassemble) verwenden:

objdump -d hello | less

Dadurch wird der ausführbare Maschinencode zerlegt und in hexadezimalen Bytes neben dem Assemblersprachäquivalent angezeigt. Die Adressposition des ersten Byes in jeder Zeile wird ganz links angezeigt.

Dies ist nur nützlich, wenn Sie Assembler lesen können oder neugierig sind, was hinter dem Vorhang passiert. Es gibt viel Output, also haben wir es in weniger gesteckt.

Kompilieren und verlinken

Es gibt viele Möglichkeiten, eine Binärdatei zu kompilieren. Beispielsweise entscheidet der Entwickler, ob Debuginformationen hinzugefügt werden sollen. Auch die Art und Weise, wie die Binärdatei verlinkt ist, spielt eine Rolle für Inhalt und Größe. Wenn die Binärreferenzen Objekte als externe Abhängigkeiten teilen, ist sie kleiner als eine, auf die die Abhängigkeiten statisch verweisen.

Die meisten Entwickler kennen die hier behandelten Befehle bereits. Für andere bieten sie jedoch einige einfache Möglichkeiten, herumzustöbern und zu sehen, was sich in der binären Blackbox befindet.