GNU/Linux >> LINUX-Kenntnisse >  >> Linux

9 wesentliche GNU-Binutils-Tools

Stellen Sie sich vor, Sie hätten keinen Zugriff auf den Quellcode einer Software, könnten aber trotzdem verstehen, wie die Software implementiert ist, Schwachstellen darin finden und – noch besser – die Fehler beheben. All dies in binärer Form. Klingt nach Superkräften, nicht wahr?

Auch Sie können solche Superkräfte besitzen, und die GNU-Binärprogramme (Binutils) sind ein guter Ausgangspunkt. Die GNU-Binutils sind eine Sammlung von Binärwerkzeugen, die standardmäßig auf allen Linux-Distributionen installiert werden.

Die Binäranalyse ist die am meisten unterschätzte Fähigkeit in der Computerindustrie. Es wird hauptsächlich von Malware-Analysten, Reverse Engineers und

Personen verwendet, die an Low-Level-Software arbeiten.

Dieser Artikel untersucht einige der Tools, die über binutils verfügbar sind. Ich verwende RHEL, aber diese Beispiele sollten auf jeder Linux-Distribution laufen.


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

Beachten Sie, dass einige Paketierungsbefehle (wie rpm ) möglicherweise nicht auf Debian-basierten Distributionen verfügbar, verwenden Sie daher das entsprechende dpkg Befehl, wo zutreffend.

Softwareentwicklung 101

In der Open-Source-Welt konzentrieren sich viele von uns auf Software in Source-Form; Wenn der Quellcode der Software leicht verfügbar ist, ist es einfach, einfach eine Kopie des Quellcodes zu erhalten, Ihren bevorzugten Editor zu öffnen, eine Tasse Kaffee zu trinken und mit der Erkundung zu beginnen.

Aber der Quellcode ist nicht das, was auf der CPU ausgeführt wird; es sind die binären oder maschinensprachlichen Anweisungen, die auf der CPU ausgeführt werden. Die binäre oder ausführbare Datei erhalten Sie, wenn Sie den Quellcode kompilieren. Debugging-Experten erhalten oft einen Vorteil, wenn sie diesen Unterschied verstehen.

Zusammenstellung 101

Bevor Sie sich mit dem binutils-Paket selbst beschäftigen, ist es gut, die Grundlagen der Kompilierung zu verstehen.

Kompilierung ist der Prozess der Umwandlung eines Programms aus seiner Quelle oder Textform in einer bestimmten Programmiersprache (C/C++) in Maschinencode.

Maschinencode ist die Folge von Einsen und Nullen, die von einer CPU (oder Hardware im Allgemeinen) verstanden werden und daher von der CPU ausgeführt oder ausgeführt werden können. Dieser Maschinencode wird in einer Datei in einem bestimmten Format gespeichert, das häufig als ausführbare Datei oder Binärdatei bezeichnet wird. Unter Linux (und BSD, wenn Linux Binary Compatibility verwendet wird) heißt dies ELF (Executable and Linkable Format).

Der Kompilierungsprozess durchläuft eine Reihe komplizierter Schritte, bevor er eine ausführbare oder binäre Datei für eine bestimmte Quelldatei darstellt. Betrachten Sie dieses Quellprogramm (C-Code) als Beispiel. Öffnen Sie Ihren bevorzugten Editor und geben Sie dieses Programm ein:


#include <stdio.h>

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

Schritt 1:Vorverarbeitung mit cpp

Der C-Präprozessor (cpp ) wird verwendet, um alle Makros zu erweitern und die Header-Dateien einzuschließen. In diesem Beispiel die Header-Datei stdio.h wird im Quellcode enthalten sein. stdio.h ist eine Header-Datei, die Informationen zu einem printf enthält Funktion, die innerhalb des Programms verwendet wird. cpp läuft auf dem Quellcode, und die resultierenden Anweisungen werden in einer Datei namens hello.i gespeichert . Öffnen Sie die Datei mit einem Texteditor, um ihren Inhalt anzuzeigen. Der Quellcode zum Drucken von Hallo Welt befindet sich am Ende der Datei.


[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]#

Schritt 2:Kompilierung mit gcc

Dies ist die Phase, in der vorverarbeiteter Quellcode aus Schritt 1 in Anweisungen in Assemblersprache konvertiert wird, ohne eine Objektdatei zu erstellen. Es verwendet die GNU Compiler Collection (gcc ). Nach dem Ausführen von gcc Befehl mit dem -S Option auf hello.i -Datei erstellt es eine neue Datei namens hello.s . Diese Datei enthält die Anweisungen in Assemblersprache für das C-Programm.

Sie können den Inhalt mit einem beliebigen Editor oder der Katze anzeigen Befehl.


[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]#

Schritt 3:Zusammenbau mit as

Der Zweck eines Assemblers besteht darin, Assembleranweisungen in Maschinensprachencode umzuwandeln und eine Objektdatei mit einem .o zu generieren Verlängerung. Verwenden Sie den GNU-Assembler as die standardmäßig auf allen Linux-Plattformen verfügbar ist.


[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]#

Sie haben jetzt Ihre erste Datei im ELF-Format; Sie können ihn jedoch noch nicht ausführen. Später werden Sie den Unterschied zwischen einer Objektdatei sehen und eine ausführbare Datei .


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

Schritt 4:Verknüpfung mit ld

Dies ist die letzte Stufe der Kompilierung, wenn die Objektdateien verknüpft werden, um eine ausführbare Datei zu erstellen. Eine ausführbare Datei erfordert normalerweise externe Funktionen, die häufig aus Systembibliotheken (libc ).

Sie können den Linker direkt mit ld aufrufen Befehl; Dieser Befehl ist jedoch etwas kompliziert. Stattdessen können Sie gcc verwenden Compiler mit dem -v (verbose) Flag, um zu verstehen, wie die Verknüpfung erfolgt. (Mit dem ld Befehl zum Verknüpfen ist eine Übung, die Sie noch erkunden können.)


[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]#

Nachdem Sie diesen Befehl ausgeführt haben, sollten Sie eine ausführbare Datei mit dem Namen a.out sehen :


[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

Ausführen der Datei Befehl auf a.out zeigt, dass es sich tatsächlich um eine ausführbare ELF-Datei handelt:


[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

Führen Sie Ihre ausführbare Datei aus, um zu sehen, ob sie den Anweisungen des Quellcodes entspricht:


[testdir]# ./a.out
Hello World

Es tut! Hinter den Kulissen passiert so viel, nur um Hello World zu drucken auf dem Bildschirm. Stellen Sie sich vor, was in komplizierteren Programmen passiert.

Entdecken Sie die binutils-Tools

Diese Übung lieferte einen guten Hintergrund für die Verwendung der Tools, die im binutils-Paket enthalten sind. Mein System hat binutils Version 2.27-34; Abhängig von Ihrer Linux-Distribution haben Sie möglicherweise eine andere Version.


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

Die folgenden Tools sind in den binutils-Paketen verfügbar:


[~]# 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

Die obige Kompilierungsübung hat bereits zwei dieser Tools untersucht:das as Befehl wurde als Assembler verwendet und der ld Befehl wurde als Linker verwendet. Lesen Sie weiter, um mehr über die anderen sieben GNU-Binutils-Paket-Tools zu erfahren, die oben fett hervorgehoben sind.

readelf:Zeigt Informationen über ELF-Dateien an

In der obigen Übung wurden die Begriffe Objektdatei verwendet und ausführbare Datei . Geben Sie unter Verwendung der Dateien aus dieser Übung readelf ein mit dem -h (Header) Option, um den ELF-Header der Dateien auf Ihrem Bildschirm auszugeben. Beachten Sie, dass die Objektdatei mit .o endet Erweiterung wird als Typ:REL (Relocatable file) angezeigt :


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

Wenn Sie versuchen, diese Datei auszuführen, erhalten Sie eine Fehlermeldung, die besagt, dass sie nicht ausgeführt werden kann. Das bedeutet einfach, dass es noch nicht über die Informationen verfügt, die für die Ausführung auf der CPU erforderlich sind.

Denken Sie daran, dass Sie das x hinzufügen müssen oder ausführbares Bit auf der Objektdatei zuerst mit chmod andernfalls erhalten Sie eine Berechtigung verweigert Fehler.


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

Wenn Sie denselben Befehl auf a.out versuchen Datei, sehen Sie, dass ihr Typ eine EXEC (ausführbare Datei) ist .


[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)

Wie zuvor gesehen, kann diese Datei direkt von der CPU ausgeführt werden:


[testdir]# ./a.out
Hello World

Das Readelf Der Befehl gibt eine Fülle von Informationen über eine Binärdatei. Hier sagt es Ihnen, dass es im ELF64-Bit-Format ist, was bedeutet, dass es nur auf einer 64-Bit-CPU ausgeführt werden kann und nicht auf einer 32-Bit-CPU funktioniert. Es sagt Ihnen auch, dass es auf der X86-64 (Intel/AMD)-Architektur ausgeführt werden soll. Der Einstiegspunkt in die Binärdatei befindet sich an der Adresse 0x400430, die nur die Adresse der Hauptdatei ist Funktion innerhalb des C-Quellprogramms.

Probieren Sie das Readelf aus Befehl auf den anderen Systembinärdateien, die Sie kennen, wie ls . Beachten Sie, dass Ihre Ausgabe (insbesondere Type: ) kann auf RHEL 8- oder Fedora 30-Systemen und höher aufgrund von aus Sicherheitsgründen vorgenommenen Änderungen an Position Independent Executable (PIE) abweichen.


[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)

Erfahren Sie, welche Systembibliotheken die ls Der Befehl ist abhängig von der Verwendung von ldd Befehl wie folgt:


[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)

Führen Sie readelf aus auf der libc Bibliotheksdatei, um zu sehen, um welche Art von Datei es sich handelt. Wie bereits erwähnt, handelt es sich um eine DYN (Shared Object File) , was bedeutet, dass es nicht direkt ausgeführt werden kann; es muss von einer ausführbaren Datei verwendet werden, die intern alle Funktionen verwendet, die von der Bibliothek zur Verfügung gestellt werden.


[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)

Größe:Listet Abschnittsgrößen und die Gesamtgröße auf

Die Größe Der Befehl funktioniert nur mit Objekt- und ausführbaren Dateien. Wenn Sie also versuchen, ihn mit einer einfachen ASCII-Datei auszuführen, wird er einen Fehler mit der Meldung Dateiformat nicht erkannt ausgeben .


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

Führen Sie nun Größe aus in der Objektdatei und die ausführbare Datei aus der Übung oben. Beachten Sie, dass die ausführbare Datei (a.out ) enthält erheblich mehr Informationen als die Objektdatei (hello.o ), basierend auf der Ausgabe des Größenbefehls:


[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

Aber was macht der Text , Daten und bss Abschnitte bedeuten?

Der Text Abschnitte beziehen sich auf den Codeabschnitt der Binärdatei, der alle ausführbaren Anweisungen enthält. Die Daten Abschnitten befinden sich alle initialisierten Daten und bss Hier werden alle nicht initialisierten Daten gespeichert.

Größe vergleichen mit einigen der anderen verfügbaren Systembinärdateien.

Für die ls Befehl:


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

Sie können diesen gcc sehen und gdb sind viel größere Programme als ls indem Sie sich einfach die Ausgabe der Größe ansehen Befehl:


[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

Strings:Druckt die Strings druckbarer Zeichen in Dateien

Oft ist es sinnvoll, das -d hinzuzufügen Flag zu den Strings Befehl, nur die druckbaren Zeichen aus dem Datenabschnitt anzuzeigen.

Hallo.o ist eine Objektdatei, die Anweisungen zum Ausdrucken des Textes Hello World enthält . Daher die einzige Ausgabe der Strings Befehl ist Hello World .


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

Ausführen von Strings auf a.out (eine ausführbare Datei) hingegen zeigt zusätzliche Informationen, die während der Verknüpfungsphase in die Binärdatei aufgenommen wurden:


[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$"

Denken Sie daran, dass die Kompilierung der Prozess der Konvertierung von Quellcodeanweisungen in Maschinencode ist. Maschinencode besteht nur aus Einsen und Nullen und ist für Menschen schwer lesbar. Daher ist es hilfreich, Maschinencode als Anweisungen in Assemblersprache darzustellen. Wie sehen Assemblersprachen aus? Denken Sie daran, dass die Assemblersprache architekturspezifisch ist; Da ich die Intel- oder x86-64-Architektur verwende, sind die Anweisungen anders, wenn Sie die ARM-Architektur verwenden, um dieselben Programme zu kompilieren.

objdump:Zeigt Informationen aus Objektdateien an

Ein weiteres Binutils-Tool, das die Maschinensprachenanweisungen aus der Binärdatei ausgeben kann, heißt objdump .
Verwenden Sie das -d Option, die alle Assembler-Anweisungen aus der Binärdatei zerlegt.


[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

Diese Ausgabe wirkt zunächst einschüchternd, aber nehmen Sie sich einen Moment Zeit, um sie zu verstehen, bevor Sie fortfahren. Denken Sie daran, dass die Datei .text Abschnitt enthält alle Maschinencode-Anweisungen. Die Montageanleitung ist in der vierten Spalte zu sehen (also push , Bewegung , callq , Pop , retq ). Diese Befehle wirken auf Register, die in die CPU eingebaute Speicherstellen sind. Die Register in diesem Beispiel sind rbp , rsp , edi , eax , etc., und jedes Register hat eine besondere Bedeutung.

Führen Sie nun objdump aus auf der ausführbaren Datei (a.out ) und sehen, was Sie bekommen. Die Ausgabe von objdump auf der ausführbaren Datei kann groß sein, also habe ich es auf die Hauptdatei eingegrenzt Funktion mit grep Befehl:


[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

Beachten Sie, dass die Anweisungen denen der Objektdatei hello.o ähneln , aber sie enthalten einige zusätzliche Informationen:

  • Die Objektdatei hello.o hat die folgende Anweisung:callq e
  • Die ausführbare Datei a.out besteht aus der folgenden Anweisung mit einer Adresse und einer Funktion:callq 400400 <puts@plt>

Die obige Assembler-Anweisung ruft ein puts auf Funktion. Denken Sie daran, dass Sie ein printf verwendet haben Funktion im Quellcode. Der Compiler hat einen Aufruf an die puts eingefügt Bibliotheksfunktion zur Ausgabe von Hello World zum Bildschirm.

Sehen Sie sich die Anweisung für eine Zeile über puts an :

  • Die Objektdatei hello.o hat die Anweisung mov :mov $0x0,%edi
  • Die Anweisung mov für die ausführbare Datei a.out hat eine tatsächliche Adresse ($0x4005d0 ) statt $0x0 :mov $0x4005d0,%edi

Diese Anweisung verschiebt alles, was an der Adresse $0x4005d0 vorhanden ist innerhalb der Binärdatei in das Register mit dem Namen edi .

Was könnte sich sonst noch im Inhalt dieses Speicherplatzes befinden? Ja, Sie haben richtig geraten:Es ist nichts als der Text Hello, World . Wie können Sie sicher sein?

Das Readelf Mit dem Befehl können Sie jeden Abschnitt der Binärdatei (a.out ) auf den Bildschirm. Im Folgenden wird es aufgefordert, die .rodata zu sichern , bei denen es sich um schreibgeschützte Daten handelt, auf dem Bildschirm:


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

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

Sie können den Text Hello World sehen auf der rechten Seite und seine binäre Adresse auf der linken Seite. Stimmt es mit der Adresse überein, die Sie im mov gesehen haben? Anleitung oben? Ja, das tut es.

strip:Verwirft Symbole aus Objektdateien

Dieser Befehl wird häufig verwendet, um die Größe der Binärdatei zu reduzieren, bevor sie an Kunden versendet wird.

Denken Sie daran, dass es den Debugging-Prozess behindert, da wichtige Informationen aus der Binärdatei entfernt werden; nichtsdestotrotz wird die Binärdatei fehlerfrei ausgeführt.

Führen Sie es auf Ihrem a.out aus ausführbar und beobachten Sie, was passiert. Stellen Sie zunächst sicher, dass die Binärdatei nicht entfernt ist indem Sie den folgenden Befehl ausführen:


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

Verfolgen Sie außerdem die Anzahl der ursprünglich in der Binärdatei enthaltenen Bytes, bevor Sie den Strip ausführen Befehl:


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

Führen Sie nun den Strip aus Befehl auf Ihrer ausführbaren Datei und stellen Sie sicher, dass es funktioniert hat, indem Sie die Datei verwenden Befehl:


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

Nach dem Entfernen der Binärdatei ging ihre Größe auf 6296 zurück vom vorherigen 8440 Bytes für dieses kleine Programm. Bei so vielen Einsparungen für ein winziges Programm ist es kein Wunder, dass große Programme oft abgeschafft werden.


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

addr2line:Konvertiert Adressen in Dateinamen und Zeilennummern

Die addr2line Das Tool sucht einfach nach Adressen in der Binärdatei und gleicht sie mit Zeilen im C-Quellcodeprogramm ab. Ziemlich cool, nicht wahr?

Schreiben Sie dazu ein weiteres Testprogramm; Stellen Sie nur dieses Mal sicher, dass Sie es mit -g kompilieren Flag für gcc , das zusätzliche Debugging-Informationen für die Binärdatei hinzufügt und auch hilft, indem es die Zeilennummern enthält (hier im Quellcode bereitgestellt):


[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 }

Mit -g kompilieren kennzeichnen und ausführen. Keine Überraschungen hier:


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

Verwenden Sie nun objdump um Speicheradressen zu identifizieren, an denen Ihre Funktionen beginnen. Sie können das grep verwenden Befehl, um bestimmte Zeilen herauszufiltern, die Sie möchten. Die Adressen für Ihre Funktionen sind unten hervorgehoben:


[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

Verwenden Sie nun die addr2line Werkzeug, um diese Adressen aus der Binärdatei so abzubilden, dass sie mit denen des C-Quellcodes übereinstimmen:


[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

Da steht, dass 40051d beginnt in Zeile 6 in der Quelldatei atest.c , das ist die Zeile, in der die beginnende geschweifte Klammer ({ ) für Funktion1 beginnt. Passen Sie die Ausgabe für Funktion2 an und Haupt .

nm:Listet Symbole aus Objektdateien auf

Verwenden Sie das obige C-Programm, um nm zu testen Werkzeug. Kompilieren Sie es schnell mit gcc und ausführen.


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

Führen Sie nun nm aus und grep Informationen zu Ihren Funktionen und Variablen:


[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

Sie sehen, dass die Funktionen mit T gekennzeichnet sind , was für Symbole im Text steht Abschnitt, während Variablen als D gekennzeichnet sind , was für Symbole in den initialisierten Daten steht Abschnitt.

Stellen Sie sich vor, wie nützlich es wäre, diesen Befehl für Binärdateien auszuführen, für die Sie keinen Quellcode haben? Auf diese Weise können Sie einen Blick hineinwerfen und verstehen, welche Funktionen und Variablen verwendet werden. Es sei denn natürlich, die Binärdateien wurden entfernt, in diesem Fall enthalten sie keine Symbole und daher das nm Befehl wäre nicht sehr hilfreich, wie Sie hier sehen können:


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

Schlussfolgerung

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. Top 5 der von Geeks empfohlenen Open-Source-Linux-Caching-Tools

  2. Ist die Verwendung von Rsync während der Aktualisierung der Quelle sicher?

  3. Solaris-Standardinstallation (Benutzertools)?

  4. mv-Befehl unter Linux:7 wesentliche Beispiele

  5. Wo finde ich die buildinfo.sh-Datei?

Rüsten Sie Ihre Linux-PC-Hardware mit Open-Source-Tools auf

4 Open-Source-Tools zum Ausführen eines Linux-Servers

5 moderne Alternativen zu wichtigen Linux-Befehlszeilentools

Top-Linux-Tools für Autoren

Bash-Quellbefehl

Die 10 Open-Source-Dateinavigationstools für Linux-Systeme