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

Reverse-Engineering-Tools unter Linux – Strings, nm, ltrace, strace, LD_PRELOAD

Dieser Artikel erläutert die Tools und Befehle, die zum Reverse Engineering einer ausführbaren Datei in einer Linux-Umgebung verwendet werden können.

Reverse Engineering ist der Vorgang, herauszufinden, was eine Software tut, für die kein Quellcode verfügbar ist. Reverse Engineering gibt Ihnen möglicherweise nicht die genauen Details der Software. Aber man kann ziemlich gut nachvollziehen, wie eine Software implementiert wurde.

Das Reverse Engineering umfasst die folgenden drei grundlegenden Schritte:

  1. Sammeln der Informationen
  2. Ermitteln des Programmverhaltens
  3. Abfangen der Bibliotheksaufrufe

Ich. Sammeln der Informationen

Der erste Schritt besteht darin, Informationen über das Zielprogramm und seine Aufgaben zu sammeln. Für unser Beispiel nehmen wir den Befehl „who“. Der Befehl „who“ druckt die Liste der derzeit angemeldeten Benutzer.

1. Strings-Befehl

Strings ist ein Befehl, der die Zeichenfolgen druckbarer Zeichen in Dateien druckt. Lassen Sie uns dies jetzt gegen unseren Befehl target (who) verwenden.

# strings /usr/bin/who

Einige der wichtigen Strings sind,

users=%lu
EXIT
COMMENT
IDLE
TIME
LINE
NAME
/dev/
/var/log/wtmp
/var/run/utmp
/usr/share/locale
Michael Stone
David MacKenzie
Joseph Arceneaux

Aus der about-Ausgabe können wir erkennen, dass „who“ etwa 3 Dateien verwendet (/var/log/wtmp, /var/log/utmp, /usr/share/locale).

Lesen Sie mehr:Linux-Strings-Befehlsbeispiele (Text in UNIX-Binärdateien suchen)

2. nm-Befehl

nm-Befehl, wird verwendet, um die Symbole aus dem Zielprogramm aufzulisten. Durch die Verwendung von nm können wir die lokalen und Bibliotheksfunktionen sowie die verwendeten globalen Variablen kennenlernen. nm kann nicht mit einem Programm arbeiten, das mit dem Befehl „strip“ gestreift ist.

Hinweis:Standardmäßig ist der Befehl „who“ entfernt. Für dieses Beispiel habe ich den ‚who‘-Befehl noch einmal kompiliert.

# nm /usr/bin/who

Dies wird Folgendes auflisten:

08049110 t print_line
08049320 t time_string
08049390 t print_user
08049820 t make_id_equals_comment
080498b0 t who
0804a170 T usage
0804a4e0 T main
0804a900 T set_program_name
08051ddc b need_runlevel
08051ddd b need_users
08051dde b my_line_only
08051de0 b time_format
08051de4 b time_format_width
08051de8 B program_name
08051d24 D Version
08051d28 D exit_failure

In der obigen Ausgabe:

  • t|T – Das Symbol ist im .text-Codeabschnitt vorhanden
  • b|B – Das Symbol befindet sich im UN-initialisierten .data-Abschnitt
  • D|d – Das Symbol befindet sich im initialisierten .data-Abschnitt.

Der Groß- oder Kleinbuchstabe bestimmt, ob das Symbol lokal oder global ist.

Aus der about-Ausgabe können wir Folgendes wissen:

  • Es hat die globale Funktion (main,set_program_name,usage,etc..)
  • Es hat einige lokale Funktionen (print_user,time_string etc..)
  • Es hat global initialisierte Variablen (Version,exit_failure)
  • Es hat die UN-initialisierten Variablen (time_format, time_format_width, etc..)

Manchmal können wir anhand der Funktionsnamen erraten, was die Funktionen tun werden.

Weiterlesen:10 praktische Linux-nm-Befehlsbeispiele

Die anderen Befehle, die zum Abrufen von Informationen verwendet werden können, sind

  • ldd-Befehl
  • Fuser-Befehl
  • lsof-Befehl
  • /proc Dateisystem

II. Bestimmen des Programmverhaltens

3. ltrace-Befehl

Es verfolgt die Aufrufe der Bibliotheksfunktion. Es führt das Programm in diesem Prozess aus.

# ltrace /usr/bin/who

Die Ausgabe wird unten gezeigt.

utmpxname(0x8050c6c, 0xb77068f8, 0, 0xbfc5cdc0, 0xbfc5cd78)          = 0
setutxent(0x8050c6c, 0xb77068f8, 0, 0xbfc5cdc0, 0xbfc5cd78)          = 1
getutxent(0x8050c6c, 0xb77068f8, 0, 0xbfc5cdc0, 0xbfc5cd78)          = 0x9ed5860
realloc(NULL, 384)                                                   = 0x09ed59e8
getutxent(0, 384, 0, 0xbfc5cdc0, 0xbfc5cd78)                         = 0x9ed5860
realloc(0x09ed59e8, 768)                                             = 0x09ed59e8
getutxent(0x9ed59e8, 768, 0, 0xbfc5cdc0, 0xbfc5cd78)                 = 0x9ed5860
realloc(0x09ed59e8, 1152)                                            = 0x09ed59e8
getutxent(0x9ed59e8, 1152, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
realloc(0x09ed59e8, 1920)                                            = 0x09ed59e8
getutxent(0x9ed59e8, 1920, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
getutxent(0x9ed59e8, 1920, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
realloc(0x09ed59e8, 3072)                                            = 0x09ed59e8
getutxent(0x9ed59e8, 3072, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
getutxent(0x9ed59e8, 3072, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
getutxent(0x9ed59e8, 3072, 0, 0xbfc5cdc0, 0xbfc5cd78)

Sie können beobachten, dass es eine Reihe von Aufrufen von getutxent und seiner Familie von Bibliotheksfunktionen gibt. Beachten Sie auch, dass ltrace die Ergebnisse in der Reihenfolge ausgibt, in der die Funktionen im Programm aufgerufen werden.

Jetzt wissen wir, dass der Befehl „who“ funktioniert, indem er getutxent und seine Funktionsfamilie aufruft, um die angemeldeten Benutzer abzurufen.

4. strace-Befehl

Der Befehl strace wird verwendet, um die Systemaufrufe des Programms zu verfolgen. Wenn ein Programm keine Bibliotheksfunktion verwendet und nur Systemaufrufe verwendet, können wir die Programmausführung nicht verfolgen, wenn wir einfaches ltrace verwenden.

# strace /usr/bin/who
[b76e7424] brk(0x887d000)               = 0x887d000
[b76e7424] access("/var/run/utmpx", F_OK) = -1 ENOENT (No such file or directory)
[b76e7424] open("/var/run/utmp", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
.
.
.
[b76e7424] fcntl64(3, F_SETLKW, {type=F_RDLCK, whence=SEEK_SET, start=0, len=0}) = 0
[b76e7424] read(3, "\10\325"..., 384) = 384
[b76e7424] fcntl64(3, F_SETLKW, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0

Sie können beobachten, dass bei jedem Aufruf der malloc-Funktion der Systemaufruf brk() aufgerufen wird. Die getutxent-Bibliotheksfunktion ruft tatsächlich den „open“-Systemaufruf auf, um „/var/run/utmp“ zu öffnen, und setzt eine Lesesperre und liest den Inhalt und gibt dann die Sperren frei.

Jetzt haben wir bestätigt, dass der Befehl who die utmp-Datei liest, um die Ausgabe anzuzeigen.

Sowohl „strace“ als auch „ltrace“ haben eine Reihe guter Optionen, die verwendet werden können.

  • -p pid – Hängt an die angegebene pid an. Nützlich, wenn das Programm bereits läuft und Sie wissen möchten, wie es sich verhält.
  • -n 2 – Jeden verschachtelten Aufruf um 2 Leerzeichen einrücken.
  • -f – Gabelung folgen

Weiterlesen:7 Strace-Beispiele zum Debuggen der Ausführung eines Programms unter Linux

III. Abfangen der Bibliotheksanrufe

5. LD_PRELOAD &LD_LIBRARY_PATH

LD_PRELOAD ermöglicht es uns, einer bestimmten Ausführung des Programms eine Bibliothek hinzuzufügen. Die Funktion in dieser Bibliothek überschreibt die eigentliche Bibliotheksfunktion.

Hinweis:Wir können dies nicht mit Programmen verwenden, die mit dem „suid“-Bit gesetzt sind.

Nehmen wir das folgende Programm.

#include <stdio.h>
int main() {
  char str1[]="TGS";
  char str2[]="tgs";
  if(strcmp(str1,str2)) {
    printf("String are not matched\n");
  }
  else {
    printf("Strings are matched\n");
  }
}

Kompilieren Sie das Programm und führen Sie es aus.

# cc -o my_prg my_prg.c
# ./my_prg

Es wird „Strings are not matched“ ausgeben.

Jetzt werden wir unsere eigene Bibliothek schreiben und sehen, wie wir die Bibliotheksfunktion abfangen können.

#include <stdio.h>
int strcmp(const char *s1, const char *s2) {
  // Always return 0.
  return 0;
}

Kompilieren und setzen Sie die Variable LD_LIBRARY_PATH auf das aktuelle Verzeichnis.

# cc -o mylibrary.so -shared library.c -ldl
# LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

Jetzt wird eine Datei namens „library.so“ erstellt.
Setzen Sie die Variable LD_PRELOAD auf diese Datei und führen Sie das String-Vergleichsprogramm aus.

# LD_PRELOAD=mylibrary.so ./my_prg

Jetzt wird „Strings are matched“ ausgegeben, da unsere Version der strcmp-Funktion verwendet wird.

Hinweis:Wenn Sie eine Bibliotheksfunktion abfangen möchten, sollte Ihre eigene Bibliotheksfunktion denselben Prototyp wie die ursprüngliche Bibliotheksfunktion haben.

Wir haben gerade die sehr grundlegenden Dinge behandelt, die zum Reverse Engineering eines Programms erforderlich sind.

Für diejenigen, die den nächsten Schritt im Reverse Engineering machen möchten, wird das Verständnis des ELF-Dateiformats und des Assembler-Sprachprogramms in größerem Umfang hilfreich sein.


Linux
  1. 5 Rust-Tools, die es wert sind, auf der Linux-Befehlszeile ausprobiert zu werden

  2. 5 Befehlszeilen-Tools zum schnellen Auffinden von Dateien unter Linux

  3. Linux-mv-Befehl

  4. Linux-IP-Befehl

  5. Linux-cd-Befehl

Linux-Strings-Befehls-Tutorial für Anfänger (5 Beispiele)

Linux strace Command Tutorial für Anfänger (8 Beispiele)

So verfolgen Sie die Programmausführung mit dem Linux-Strace-Befehl

11 Strace-Befehl mit Beispiel in Linux

Sysadmin-Tools:11 Möglichkeiten, den Befehl ls unter Linux zu verwenden

So verwenden Sie den Linux-Strace-Befehl