GNU/Linux >> Belajar Linux >  >> Linux

9 alat binutils GNU penting

Bayangkan tidak memiliki akses ke kode sumber perangkat lunak tetapi masih dapat memahami bagaimana perangkat lunak diimplementasikan, menemukan kerentanan di dalamnya, dan—lebih baik lagi—memperbaiki bug. Semua ini dalam bentuk biner. Kedengarannya seperti memiliki kekuatan super, bukan?

Anda juga dapat memiliki kekuatan super seperti itu, dan utilitas biner GNU (binutils) adalah titik awal yang baik. Binutils GNU adalah kumpulan alat biner yang diinstal secara default di semua distribusi Linux.

Analisis biner adalah keterampilan yang paling diremehkan dalam industri komputer. Ini sebagian besar digunakan oleh analis malware, reverse engineer, dan orang-orang

yang bekerja pada perangkat lunak tingkat rendah.

Artikel ini mengeksplorasi beberapa alat yang tersedia melalui binutils. Saya menggunakan RHEL tetapi contoh-contoh ini harus dijalankan di semua distribusi Linux.


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

Perhatikan bahwa beberapa perintah pengemasan (seperti rpm ) mungkin tidak tersedia di distribusi berbasis Debian, jadi gunakan dpkg yang setara perintah jika berlaku.

Pengembangan perangkat lunak 101

Di dunia open source, banyak dari kita berfokus pada perangkat lunak dalam bentuk sumber; ketika kode sumber perangkat lunak sudah tersedia, Anda dapat dengan mudah mendapatkan salinan kode sumber, membuka editor favorit Anda, minum secangkir kopi, dan mulai menjelajah.

Tetapi kode sumbernya bukanlah yang dieksekusi pada CPU; itu adalah instruksi bahasa biner atau mesin yang dieksekusi pada CPU. File biner atau yang dapat dieksekusi adalah apa yang Anda dapatkan ketika Anda mengkompilasi kode sumber. Orang yang ahli dalam debugging sering mendapatkan keunggulan mereka dengan memahami perbedaan ini.

Kompilasi 101

Sebelum menggali ke dalam paket binutils itu sendiri, ada baiknya untuk memahami dasar-dasar kompilasi.

Kompilasi adalah proses mengubah program dari sumbernya atau bentuk teks dalam bahasa pemrograman tertentu (C/C++) menjadi kode mesin.

Kode mesin adalah urutan 1 dan 0 yang dipahami oleh CPU (atau perangkat keras secara umum) dan oleh karena itu dapat dieksekusi atau dijalankan oleh CPU. Kode mesin ini disimpan ke file dalam format tertentu yang sering disebut sebagai file yang dapat dieksekusi atau file biner. Di Linux (dan BSD, saat menggunakan Linux Binary Compatibility), ini disebut ELF (Executable and Linkable Format).

Proses kompilasi melewati serangkaian langkah rumit sebelum menyajikan file yang dapat dieksekusi atau biner untuk file sumber yang diberikan. Pertimbangkan program sumber ini (kode C) sebagai contoh. Buka editor favorit Anda dan ketik program ini:


#include <stdio.h>

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

Langkah 1:Praproses dengan cpp

Praprosesor C (cpp ) digunakan untuk memperluas semua makro dan menyertakan file header. Dalam contoh ini, file header stdio.h akan dimasukkan ke dalam kode sumber. stdio.h adalah file header yang berisi informasi tentang printf fungsi yang digunakan dalam program. cpp berjalan pada kode sumber, dan instruksi yang dihasilkan disimpan dalam file bernama hello.i . Buka file dengan editor teks untuk melihat isinya. Kode sumber untuk mencetak hello world ada di bagian bawah file.


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

Langkah 2:Kompilasi dengan gcc

Ini adalah tahap di mana kode sumber praproses dari Langkah 1 diubah menjadi instruksi bahasa rakitan tanpa membuat file objek. Ia menggunakan Koleksi Kompilator GNU (gcc ). Setelah menjalankan gcc perintah dengan -S opsi di hello.i file, itu membuat file baru bernama hello.s . File ini berisi instruksi bahasa assembly untuk program C.

Anda dapat melihat konten menggunakan editor apa pun atau kucing perintah.


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

Langkah 3:Merakit dengan as

Tujuan dari assembler adalah untuk mengubah instruksi bahasa assembly menjadi kode bahasa mesin dan menghasilkan file objek yang memiliki .o perpanjangan. Gunakan perakit GNU sebagai yang tersedia secara default di semua platform Linux.


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

Anda sekarang memiliki file pertama Anda dalam format ELF; namun, Anda belum dapat menjalankannya. Nanti, Anda akan melihat perbedaan antara file objek dan file yang dapat dieksekusi .


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

Langkah 4:Menautkan dengan ld

Ini adalah tahap akhir kompilasi, ketika file objek ditautkan untuk membuat file yang dapat dieksekusi. Eksekusi biasanya memerlukan fungsi eksternal yang sering kali berasal dari pustaka sistem (libc ).

Anda dapat langsung memanggil penaut dengan ld memerintah; namun, perintah ini agak rumit. Sebagai gantinya, Anda dapat menggunakan gcc kompiler dengan -v (verbose) bendera untuk memahami bagaimana menghubungkan terjadi. (Menggunakan ld perintah untuk menautkan adalah latihan yang tersisa untuk Anda jelajahi.)


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

Setelah menjalankan perintah ini, Anda akan melihat file yang dapat dieksekusi bernama a.out :


[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

Menjalankan file perintah di a.out menunjukkan bahwa itu memang ELF yang dapat dieksekusi:


[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

Jalankan file yang dapat dieksekusi untuk melihat apakah file tersebut sesuai dengan instruksi kode sumber:


[testdir]# ./a.out
Hello World

Itu benar! Begitu banyak yang terjadi di balik layar hanya untuk mencetak Hello World di layar. Bayangkan apa yang terjadi pada program yang lebih rumit.

Jelajahi alat binutils

Latihan ini memberikan latar belakang yang baik untuk menggunakan alat-alat yang ada dalam paket binutils. Sistem saya memiliki binutils versi 2.27-34; Anda mungkin memiliki versi yang berbeda tergantung pada distribusi Linux Anda.


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

Alat-alat berikut tersedia dalam paket binutils:


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

Latihan kompilasi di atas telah menjelajahi dua alat ini:sebagai perintah digunakan sebagai assembler, dan ld perintah digunakan sebagai penghubung. Baca terus untuk mengetahui tentang tujuh alat paket binutils GNU lainnya yang disorot dalam huruf tebal di atas.

readelf:Menampilkan informasi tentang file ELF

Latihan di atas menyebutkan istilah file objek dan file yang dapat dieksekusi . Menggunakan file dari latihan itu, masukkan readelf menggunakan -h (header) opsi untuk membuang header ELF file di layar Anda. Perhatikan bahwa file objek yang diakhiri dengan .o ekstensi ditampilkan sebagai Jenis:REL (File yang dapat dipindahkan) :


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

Jika Anda mencoba menjalankan file ini, Anda akan mendapatkan pesan kesalahan yang mengatakan bahwa itu tidak dapat dijalankan. Ini berarti bahwa ia belum memiliki informasi yang diperlukan untuk dieksekusi pada CPU.

Ingat, Anda perlu menambahkan x atau bit yang dapat dieksekusi pada file objek terlebih dahulu menggunakan chmod perintah atau Anda akan mendapatkan Izin ditolak kesalahan.


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

Jika Anda mencoba perintah yang sama di a.out file, Anda melihat bahwa jenisnya adalah EXEC (File yang dapat dieksekusi) .


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

Seperti yang terlihat sebelumnya, file ini dapat langsung dieksekusi oleh CPU:


[testdir]# ./a.out
Hello World

baca sendiri perintah memberikan banyak informasi tentang biner. Di sini, ia memberi tahu Anda bahwa itu dalam format ELF64-bit, yang berarti itu hanya dapat dijalankan pada CPU 64-bit dan tidak akan bekerja pada CPU 32-bit. Ini juga memberitahu Anda bahwa itu dimaksudkan untuk dieksekusi pada arsitektur X86-64 (Intel/AMD). Titik masuk ke biner berada di alamat 0x400430, yang merupakan alamat utama fungsi dalam program sumber C.

Coba readelf perintah pada binari sistem lain yang Anda tahu, seperti ls . Perhatikan bahwa keluaran Anda (terutama Jenis: ) mungkin berbeda pada sistem RHEL 8 atau Fedora 30 ke atas karena perubahan posisi yang dapat dijalankan secara independen (PIE) yang dibuat untuk alasan keamanan.


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

Pelajari apa pustaka sistem ls perintah bergantung pada penggunaan ldd perintah, sebagai berikut:


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

Jalankan readelf di libc library file untuk melihat jenis file itu. Seperti yang ditunjukkan, ini adalah DYN (File objek bersama) , yang berarti tidak dapat langsung dieksekusi sendiri; itu harus digunakan oleh file yang dapat dieksekusi yang secara internal menggunakan fungsi apa pun yang disediakan oleh perpustakaan.


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

size:Mencantumkan ukuran bagian dan ukuran total

ukuran perintah hanya berfungsi pada objek dan file yang dapat dieksekusi, jadi jika Anda mencoba menjalankannya pada file ASCII sederhana, itu akan menimbulkan kesalahan yang mengatakan Format file tidak dikenali .


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

Sekarang, jalankan ukuran pada file objek dan file yang dapat dieksekusi dari latihan di atas. Perhatikan bahwa file yang dapat dieksekusi (a.out ) memiliki lebih banyak informasi daripada file objek (hello.o ), berdasarkan output dari perintah ukuran:


[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

Tapi apa gunanya teks , data , dan bss bagian berarti?

teks bagian merujuk ke bagian kode biner, yang memiliki semua instruksi yang dapat dieksekusi. data bagian adalah tempat semua data yang diinisialisasi, dan bss adalah tempat semua data yang tidak diinisialisasi disimpan.

Bandingkan ukuran dengan beberapa binari sistem lain yang tersedia.

Untuk ls perintah:


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

Anda dapat melihat gcc . itu dan gdb adalah program yang jauh lebih besar daripada ls hanya dengan melihat output dari size perintah:


[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:Mencetak string karakter yang dapat dicetak dalam file

Seringkali berguna untuk menambahkan -d tandai ke string perintah untuk hanya menampilkan karakter yang dapat dicetak dari bagian data.

halo.o adalah file objek yang berisi instruksi untuk mencetak teks Hello World . Oleh karena itu, satu-satunya keluaran dari string perintahnya adalah Halo Dunia .


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

Menjalankan string di a.out (yang dapat dieksekusi), di sisi lain, menunjukkan informasi tambahan yang disertakan dalam biner selama fase penautan:


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

Ingatlah bahwa kompilasi adalah proses mengubah instruksi kode sumber menjadi kode mesin. Kode mesin hanya terdiri dari 1 dan 0 dan sulit dibaca manusia. Oleh karena itu, ada baiknya menyajikan kode mesin sebagai instruksi bahasa rakitan. Seperti apa bahasa assembly itu? Ingat bahwa bahasa assembly adalah khusus arsitektur; karena saya menggunakan arsitektur Intel atau x86-64, instruksinya akan berbeda jika Anda menggunakan arsitektur ARM untuk mengkompilasi program yang sama.

objdump:Menampilkan informasi dari file objek

Alat binutils lain yang dapat membuang instruksi bahasa mesin dari biner disebut objdump .
Gunakan -d opsi, yang membongkar semua instruksi perakitan dari biner.


[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

Output ini tampak menakutkan pada awalnya, tetapi luangkan waktu sejenak untuk memahaminya sebelum melanjutkan. Ingatlah bahwa .text bagian memiliki semua instruksi kode mesin. Instruksi perakitan dapat dilihat di kolom keempat (yaitu, push , pindah , callq , muncul , retq ). Instruksi ini bekerja pada register, yang merupakan lokasi memori yang dibangun ke dalam CPU. Register dalam contoh ini adalah rbp , rsp , diedit , eax , dll., dan setiap register memiliki arti khusus.

Sekarang jalankan objdump pada file yang dapat dieksekusi (a.out ) dan lihat apa yang Anda dapatkan. Keluaran objdump pada executable bisa menjadi besar, jadi saya telah mempersempitnya menjadi utama fungsi menggunakan grep perintah:


[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

Perhatikan bahwa instruksinya mirip dengan file objek hello.o , tetapi mereka memiliki beberapa informasi tambahan di dalamnya:

  • File objek hello.o memiliki instruksi berikut:callq e
  • a.out yang dapat dieksekusi terdiri dari instruksi berikut dengan alamat dan fungsi:callq 400400 <puts@plt>

Instruksi perakitan di atas memanggil puts fungsi. Ingat bahwa Anda menggunakan printf fungsi dalam kode sumber. Kompilator menyisipkan panggilan ke put fungsi library untuk menampilkan Hello World ke layar.

Lihat instruksi untuk baris di atas puts :

  • File objek hello.o memiliki instruksi mov :mov $0x0,%edi
  • Petunjuk pindah untuk a.out yang dapat dieksekusi memiliki alamat sebenarnya ($0x4005d0 ) bukannya $0x0 :mov $0x4005d0,%edi

Instruksi ini memindahkan apapun yang ada di alamat $0x4005d0 dalam biner ke register bernama edi .

Apa lagi yang bisa ada di konten lokasi memori itu? Ya, tebakan Anda benar:tidak lain adalah teks Halo, Dunia . Bagaimana Anda bisa yakin?

baca sendiri perintah memungkinkan Anda untuk membuang bagian mana pun dari file biner (a.out ) ke layar. Berikut ini memintanya untuk membuang .rodata , yang merupakan data hanya-baca, ke layar:


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

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

Anda dapat melihat teks Hello World di sisi kanan dan alamatnya dalam biner di sisi kiri. Apakah itu cocok dengan alamat yang Anda lihat di mov instruksi di atas? Ya, benar.

strip:Membuang simbol dari file objek

Perintah ini sering digunakan untuk memperkecil ukuran biner sebelum mengirimkannya ke pelanggan.

Ingat bahwa itu menghalangi proses debugging karena informasi penting dihapus dari biner; namun, biner dijalankan dengan sempurna.

Jalankan di a.out . Anda dieksekusi dan perhatikan apa yang terjadi. Pertama, pastikan biner tidak dilucuti dengan menjalankan perintah berikut:


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

Juga, lacak jumlah byte yang awalnya dalam biner sebelum menjalankan strip perintah:


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

Sekarang jalankan strip perintah pada executable Anda dan pastikan itu berfungsi menggunakan file perintah:


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

Setelah menghapus biner, ukurannya turun menjadi 6296 dari 8440 sebelumnya byte untuk program kecil ini. Dengan penghematan sebesar ini untuk program kecil, tidak heran program besar sering dilucuti.


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

addr2line:Mengubah alamat menjadi nama file dan nomor baris

addr2line alat hanya mencari alamat dalam file biner dan mencocokkannya dengan baris dalam program kode sumber C. Cukup keren, bukan?

Tulis program pengujian lain untuk ini; hanya saja kali ini pastikan Anda mengompilasinya dengan -g tandai untuk gcc , yang menambahkan informasi debug tambahan untuk biner dan juga membantu dengan memasukkan nomor baris (disediakan dalam kode sumber di sini):


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

Kompilasi dengan -g bendera dan jalankan. Tidak ada kejutan di sini:


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

Sekarang gunakan objdump untuk mengidentifikasi alamat memori tempat fungsi Anda dimulai. Anda dapat menggunakan grep perintah untuk menyaring baris tertentu yang Anda inginkan. Alamat untuk fungsi Anda disorot di bawah ini:


[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

Sekarang gunakan addr2line alat untuk memetakan alamat ini dari biner agar sesuai dengan kode sumber C:


[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

Dikatakan bahwa 40051d dimulai pada baris nomor 6 dalam file sumber atest.c , yang merupakan garis tempat kurung kurawal awal ({ ) untuk fungsi1 dimulai. Cocokkan output untuk function2 dan utama .

nm:Mencantumkan simbol dari file objek

Gunakan program C di atas untuk menguji nm alat. Kompilasi dengan cepat menggunakan gcc dan jalankan.


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

Sekarang jalankan nm dan grep untuk informasi tentang fungsi dan variabel Anda:


[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

Anda dapat melihat bahwa fungsinya ditandai T , yang berarti simbol dalam teks bagian, sedangkan variabel ditandai sebagai D , yang berarti simbol dalam data . yang diinisialisasi bagian.

Bayangkan betapa bergunanya menjalankan perintah ini pada binari di mana Anda tidak memiliki kode sumber? Ini memungkinkan Anda untuk mengintip ke dalam dan memahami fungsi dan variabel mana yang digunakan. Kecuali, tentu saja, biner telah dilucuti, dalam hal ini tidak mengandung simbol, dan oleh karena itu nm perintah tidak akan sangat membantu, seperti yang Anda lihat di sini:


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

Kesimpulan

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. Alat Linux teratas untuk penulis

  2. Apakah Menggunakan Rsync Saat Sumber Diperbarui Aman?

  3. Instal Default Solaris (Alat pengguna)?

  1. Redirect Ke File Yang Sama Dengan File Sumber yang Diproses Oleh Perintah?

  2. GDB tidak akan memuat file sumber

  3. conda source deactivate :bash:deactivate:Tidak ada file atau direktori seperti itu

  1. 5 Alat Caching Linux Sumber Terbuka Teratas yang Direkomendasikan oleh Geeks

  2. Perintah mv di Linux:7 Contoh Penting

  3. GNU nano 2:Format DOS atau Format Mac di Linux