GNU/Linux >> Linux Esercitazione >  >> Linux

10 modi per analizzare i file binari su Linux

"Ci sono 10 tipi di persone in questo mondo:quelli che capiscono il binario e quelli che non lo capiscono."

Più risorse Linux

  • Comandi Linux cheat sheet
  • Cheat sheet sui comandi avanzati di Linux
  • Corso online gratuito:Panoramica tecnica RHEL
  • Cheat sheet della rete Linux
  • Cheat sheet di SELinux
  • Cheat sheet dei comandi comuni di Linux
  • Cosa sono i container Linux?
  • I nostri ultimi articoli su Linux

Lavoriamo con i binari ogni giorno, ma ne capiamo così poco. Per binari intendo i file eseguibili che esegui quotidianamente, dagli strumenti della riga di comando alle applicazioni a tutti gli effetti.

Linux fornisce un ricco set di strumenti che rende l'analisi dei binari un gioco da ragazzi! Qualunque sia il tuo ruolo lavorativo, se lavori su Linux, conoscere le basi di questi strumenti ti aiuterà a capire meglio il tuo sistema.

In questo articolo, tratteremo alcuni dei più popolari di questi strumenti e comandi Linux, la maggior parte dei quali sarà disponibile in modo nativo come parte della tua distribuzione Linux. In caso contrario, puoi sempre utilizzare il tuo gestore di pacchetti per installarli ed esplorarli. Ricorda:imparare a usare lo strumento giusto nell'occasione giusta richiede molta pazienza e pratica.

file

Che cosa fa:aiuta a determinare il tipo di file.

Questo sarà il tuo punto di partenza per l'analisi binaria. Lavoriamo con i file ogni giorno. Non tutto è un tipo eseguibile; c'è un'intera vasta gamma di tipi di file là fuori. Prima di iniziare, è necessario comprendere il tipo di file che viene analizzato. È un file binario, un file di libreria, un file di testo ASCII, un file video, un file immagine, un PDF, un file di dati, ecc.?

Il file comando ti aiuterà a identificare il tipo di file esatto con cui hai a che fare.

$ file /bin/ls
/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=94943a89d17e9d373b2794dcb1f7e38c95b66c86, stripped
$
$ file /etc/passwd
/etc/passwd: ASCII text
$

ldd

Che cosa fa:stampa le dipendenze degli oggetti condivisi.

Se hai già utilizzato il file comando sopra su un binario eseguibile, non puoi perdere il messaggio "collegato dinamicamente" nell'output. Cosa significa?

Quando il software viene sviluppato, cerchiamo di non reinventare la ruota. Esistono una serie di attività comuni richieste dalla maggior parte dei programmi software, come stampare l'output o leggere da standard in o aprire file, ecc. Tutte queste attività comuni sono astratte in una serie di funzioni comuni che tutti possono quindi utilizzare invece di scrivere le proprie varianti. Queste funzioni comuni vengono inserite in una libreria chiamata libcglibc .

Come si trova da quali librerie dipende l'eseguibile? Ecco dove ldd il comando entra in scena. L'esecuzione su un binario collegato dinamicamente mostra tutte le sue librerie dipendenti e i loro percorsi.

$ ldd /bin/ls
        linux-vdso.so.1 =>  (0x00007ffef5ba1000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fea9f854000)
        libcap.so.2 => /lib64/libcap.so.2 (0x00007fea9f64f000)
        libacl.so.1 => /lib64/libacl.so.1 (0x00007fea9f446000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fea9f079000)
        libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fea9ee17000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fea9ec13000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fea9fa7b000)
        libattr.so.1 => /lib64/libattr.so.1 (0x00007fea9ea0e000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fea9e7f2000)
$

traccia

Che cosa fa:un tracciatore di chiamate in libreria.

Ora sappiamo come trovare le librerie da cui dipende un programma eseguibile usando ldd comando. Tuttavia, una libreria può contenere centinaia di funzioni. Tra queste centinaia, quali sono le effettive funzioni utilizzate dal nostro binario?

La traccia comando visualizza tutte le funzioni che vengono chiamate in fase di esecuzione dalla libreria. Nell'esempio seguente, puoi vedere i nomi delle funzioni chiamati, insieme agli argomenti passati a quella funzione. Puoi anche vedere cosa è stato restituito da quelle funzioni all'estrema destra dell'output.

$ ltrace ls
__libc_start_main(0x4028c0, 1, 0x7ffd94023b88, 0x412950 <unfinished ...>
strrchr("ls", '/')                                                                  = nil
setlocale(LC_ALL, "")                                                               = "en_US.UTF-8"
bindtextdomain("coreutils", "/usr/share/locale")                                    = "/usr/share/locale"
textdomain("coreutils")                                                             = "coreutils"
__cxa_atexit(0x40a930, 0, 0, 0x736c6974756572)                                      = 0
isatty(1)                                                                           = 1
getenv("QUOTING_STYLE")                                                             = nil
getenv("COLUMNS")                                                                   = nil
ioctl(1, 21523, 0x7ffd94023a50)                                                     = 0
<< snip >>
fflush(0x7ff7baae61c0)                                                              = 0
fclose(0x7ff7baae61c0)                                                              = 0
+++ exited (status 0) +++
$

Hexdump

Che cosa fa:Visualizza il contenuto del file in ASCII, decimale, esadecimale o ottale.

Spesso capita di aprire un file con un'applicazione che non sa cosa fare con quel file. Prova ad aprire un file eseguibile o un file video usando vim; vedrai solo parole senza senso sullo schermo.

L'apertura di file sconosciuti in Hexdump ti aiuta a vedere cosa contiene esattamente il file. Puoi anche scegliere di vedere la rappresentazione ASCII dei dati presenti nel file utilizzando alcune opzioni della riga di comando. Questo potrebbe aiutarti a darti alcuni indizi su che tipo di file è.

$ hexdump -C /bin/ls | head
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  d4 42 40 00 00 00 00 00  |..>......B@.....|
00000020  40 00 00 00 00 00 00 00  f0 c3 01 00 00 00 00 00  |@...............|
00000030  00 00 00 00 40 00 38 00  09 00 40 00 1f 00 1e 00  |[email protected]...@.....|
00000040  06 00 00 00 05 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000050  40 00 40 00 00 00 00 00  40 00 40 00 00 00 00 00  |@.@.....@.@.....|
00000060  f8 01 00 00 00 00 00 00  f8 01 00 00 00 00 00 00  |................|
00000070  08 00 00 00 00 00 00 00  03 00 00 00 04 00 00 00  |................|
00000080  38 02 00 00 00 00 00 00  38 02 40 00 00 00 00 00  |8.......8.@.....|
00000090  38 02 40 00 00 00 00 00  1c 00 00 00 00 00 00 00  |8.@.............|
$

stringhe

Che cosa fa:stampa le stringhe di caratteri stampabili nei file.

Se Hexdump sembra un po' eccessivo per il tuo caso d'uso e stai semplicemente cercando caratteri stampabili all'interno di un binario, puoi utilizzare le stringhe comando.

Durante lo sviluppo del software, viene aggiunta una varietà di messaggi di testo/ASCII, come la stampa di messaggi informativi, informazioni di debug, messaggi di aiuto, errori e così via. A condizione che tutte queste informazioni siano presenti nel file binario, verranno scaricate sullo schermo utilizzando stringhe .

$ strings /bin/ls

rifarsi

Che cosa fa:mostra informazioni sui file ELF.

ELF (Executable and Linkable File Format) è il formato di file dominante per eseguibili o binari, non solo su Linux ma anche su una varietà di sistemi UNIX. Se hai utilizzato strumenti come il comando file, che ti dice che il file è in formato ELF, il passaggio logico successivo sarà quello di utilizzare il readelf comando e le sue varie opzioni per analizzare ulteriormente il file.

Avere a portata di mano un riferimento della specifica ELF effettiva quando si utilizza readelf può essere molto utile. Puoi trovare le specifiche qui.

$ 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)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x4042d4
  Start of program headers:          64 (bytes into file)
  Start of section headers:          115696 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 30
$

objdump

Che cosa fa:Visualizza le informazioni da un file oggetto.

I binari vengono creati quando scrivi codice sorgente che viene compilato utilizzando uno strumento chiamato, ovviamente, compilatore. Questo compilatore genera istruzioni in linguaggio macchina equivalenti al codice sorgente, che possono quindi essere eseguite dalla CPU per eseguire un determinato compito. Questo codice del linguaggio macchina può essere interpretato tramite mnemonici chiamati linguaggio assembly. Un linguaggio assembly è un insieme di istruzioni che aiutano a comprendere le operazioni eseguite dal programma e infine eseguite sulla CPU.

objdump l'utilità legge il file binario o eseguibile e scarica le istruzioni in linguaggio assembly sullo schermo. La conoscenza dell'assemblaggio è fondamentale per comprendere l'output di objdump comando.

Ricorda:il linguaggio assembly è specifico dell'architettura.

$ objdump -d /bin/ls | head

/bin/ls:     file format elf64-x86-64


Disassembly of section .init:

0000000000402150 <_init@@Base>:
  402150:       48 83 ec 08             sub    $0x8,%rsp
  402154:       48 8b 05 6d 8e 21 00    mov    0x218e6d(%rip),%rax        # 61afc8 <__gmon_start__>
  40215b:       48 85 c0                test   %rax,%rax
$

traccia

Che cosa fa:traccia le chiamate e i segnali di sistema.

Se hai utilizzato trace , menzionato in precedenza, pensa a strace essere simili. L'unica differenza è che, invece di chiamare una libreria, la strace l'utilità traccia le chiamate di sistema. Le chiamate di sistema sono il modo in cui ti interfaccia con il kernel per portare a termine il lavoro.

Per fare un esempio, se vuoi stampare qualcosa sullo schermo, utilizzerai printfput funzione dalla libreria standard libc; tuttavia, sotto il cofano, alla fine, una chiamata di sistema denominata scrivi sarà fatto per stampare effettivamente qualcosa sullo schermo.

$ strace -f /bin/ls
execve("/bin/ls", ["/bin/ls"], [/* 17 vars */]) = 0
brk(NULL)                               = 0x686000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f967956a000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=40661, ...}) = 0
mmap(NULL, 40661, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f9679560000
close(3)                                = 0
<< snip >>
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9679569000
write(1, "R2  RH\n", 7R2  RH
)                 = 7
close(1)                                = 0
munmap(0x7f9679569000, 4096)            = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
$

nm

Che cosa fa:elenca i simboli dai file oggetto.

Se stai lavorando con un file binario che non è stato rimosso, nm comando ti fornirà le preziose informazioni che sono state incorporate nel binario durante la compilazione. non può aiutarti a identificare variabili e funzioni dal binario. Puoi immaginare quanto sarebbe utile se non avessi accesso al codice sorgente del binario analizzato.

Per mostrare nm , scriveremo rapidamente un piccolo programma e lo compileremo con -g opzione, e vedremo anche che il binario non viene rimosso utilizzando il comando file.

$ cat hello.c 
#include <stdio.h>

int main() {
    printf("Hello world!");
    return 0;
}
$
$ gcc -g hello.c -o hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=3de46c8efb98bce4ad525d3328121568ba3d8a5d, not stripped
$
$ ./hello
Hello world!$
$


$ nm hello | tail
0000000000600e20 d __JCR_END__
0000000000600e20 d __JCR_LIST__
00000000004005b0 T __libc_csu_fini
0000000000400540 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
000000000040051d T main
                 U printf@@GLIBC_2.2.5
0000000000400490 t register_tm_clones
0000000000400430 T _start
0000000000601030 D __TMC_END__
$

gb

Che cosa fa:il debugger GNU.

Bene, non tutto nel binario può essere analizzato staticamente. Abbiamo eseguito alcuni comandi che eseguivano il binario, come ltracestrace; tuttavia, il software è costituito da una varietà di condizioni che potrebbero portare all'esecuzione di vari percorsi alternativi.

L'unico modo per analizzare questi percorsi è in fase di esecuzione, avendo la possibilità di interrompere o mettere in pausa il programma in una determinata posizione ed essere in grado di analizzare le informazioni e quindi spostarsi più in basso.

È qui che arrivano i debugger nell'immagine e su Linux, gdb è il debugger di fatto. Ti aiuta a caricare un programma, impostare punti di interruzione in punti specifici, analizzare memoria e registro della CPU e fare molto di più. Completa gli altri strumenti sopra menzionati e ti consente di eseguire molte più analisi di runtime.

Una cosa da notare è che una volta caricato un programma utilizzando gdb , ti verrà presentato il suo (gdb) richiesta. Tutti gli altri comandi verranno eseguiti in questo gdb prompt dei comandi finché non esci.

Utilizzeremo il programma "ciao" che abbiamo compilato in precedenza e utilizzeremo gdb per vedere come funziona.

$ gdb -q ./hello
Reading symbols from /home/flash/hello...done.
(gdb) break main
Breakpoint 1 at 0x400521: file hello.c, line 4.
(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400521 in main at hello.c:4
(gdb) run
Starting program: /home/flash/./hello

Breakpoint 1, main () at hello.c:4
4           printf("Hello world!");
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.6.x86_64
(gdb) bt
#0  main () at hello.c:4
(gdb) c
Continuing.
Hello world![Inferior 1 (process 29620) exited normally]
(gdb) q
$

Conclusione

Una volta che avrai acquisito familiarità con l'utilizzo di questi strumenti di analisi binaria nativi di Linux e compreso l'output che forniscono, potrai passare a strumenti di analisi binaria open source più avanzati e professionali come radare2.


Linux
  1. 3 modi per elencare gli utenti in Linux

  2. Comandi di ricerca di Linux

  3. File di registro di Linux

  4. Comando Linux ls - Elenca file

  5. Comando mcopy in Linux

I 6 modi migliori per visualizzare i file in Linux

Esegui file binari in Linux

3 modi per trovare file e directory in Linux

Strumenti Sysadmin:11 modi per usare il comando ls in Linux

4 modi per trasferire file e directory su Linux

comando ls in Linux/UNIX