Artikel ini menjelaskan tentang alat dan perintah yang dapat digunakan untuk merekayasa balik executable di lingkungan Linux.
Rekayasa terbalik adalah tindakan mencari tahu apa yang dilakukan perangkat lunak, di mana tidak ada kode sumber yang tersedia. Rekayasa terbalik mungkin tidak memberi Anda detail yang tepat dari perangkat lunak. Namun Anda dapat memahami dengan baik tentang bagaimana perangkat lunak diimplementasikan.
Rekayasa balik melibatkan tiga langkah dasar berikut:
- Mengumpulkan Info
- Menentukan perilaku Program
- Mencegah panggilan perpustakaan
Aku. Mengumpulkan Info
Langkah pertama adalah mengumpulkan informasi tentang program sasaran dan apa yang dilakukan. Untuk contoh kita, kita akan mengambil perintah 'siapa'. Perintah 'who' mencetak daftar pengguna yang sedang login.
1. Perintah String
Strings adalah perintah yang mencetak string karakter yang dapat dicetak dalam file. Jadi sekarang mari kita gunakan ini untuk melawan perintah target (siapa).
# strings /usr/bin/who
Beberapa string penting adalah,
users=%lu EXIT COMMENT IDLE TIME LINE NAME /dev/ /var/log/wtmp /var/run/utmp /usr/share/locale Michael Stone David MacKenzie Joseph Arceneaux
Dari output about, kita dapat mengetahui bahwa ‘who’ menggunakan 3 file (/var/log/wtmp, /var/log/utmp, /usr/share/locale).
Baca lebih lanjut:Contoh Perintah String Linux (Teks Pencarian di File Biner UNIX)
2. Perintah nm
Perintah nm, digunakan untuk membuat daftar simbol dari program target. Dengan menggunakan nm, kita dapat mengetahui fungsi local dan library serta variabel global yang digunakan. nm tidak dapat bekerja pada program yang bergaris menggunakan perintah 'strip'.
Catatan:Secara default perintah 'siapa' dilucuti. Untuk contoh ini, saya mengkompilasi perintah 'who' sekali lagi.
# nm /usr/bin/who
Ini akan mencantumkan yang berikut:
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
Pada keluaran di atas:
- t|T – Simbol ada di bagian .text code
- b|B – Simbol ada di bagian .data yang diinisialisasi PBB
- D|d – Simbol ada di bagian .data yang Diinisialisasi.
Huruf Kapital atau Kecil, menentukan apakah simbol itu lokal atau global.
Dari output about, kita dapat mengetahui hal berikut,
- Memiliki fungsi global (main,set_program_name,usage,dll..)
- Ini memiliki beberapa fungsi lokal (print_user,time_string dll..)
- Ini memiliki variabel yang diinisialisasi global (Versi,exit_failure)
- Ini memiliki variabel yang diinisialisasi PBB (format_waktu, lebar_format_waktu, dll.)
Terkadang, dengan menggunakan nama fungsi, kita dapat menebak apa yang akan dilakukan oleh fungsi tersebut.
Baca selengkapnya:10 Contoh Perintah Linux nm Praktis
Perintah lain yang dapat digunakan untuk mendapatkan informasi adalah
- perintah ldd
- perintah fuser
- perintah lsof
- /sistem file proc
II. Menentukan Perilaku Program
3. Perintah ltrace
Ini melacak panggilan ke fungsi perpustakaan. Itu mengeksekusi program dalam proses itu.
# ltrace /usr/bin/who
Outputnya ditunjukkan di bawah ini.
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)
Anda dapat mengamati bahwa ada satu set panggilan ke getutxent dan keluarga fungsi perpustakaannya. Anda juga dapat mencatat bahwa ltrace memberikan hasil dalam urutan fungsi yang dipanggil dalam program.
Sekarang kita tahu bahwa perintah 'who' bekerja dengan memanggil getutxent dan keluarga fungsinya untuk mendapatkan pengguna yang login.
4. strace Perintah
perintah strace digunakan untuk melacak panggilan sistem yang dibuat oleh program. Jika sebuah program tidak menggunakan fungsi library apa pun, dan hanya menggunakan panggilan sistem, maka menggunakan ltrace biasa, kami tidak dapat melacak eksekusi program.
# 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
Anda dapat mengamati bahwa setiap kali fungsi malloc dipanggil, ia memanggil panggilan sistem brk(). Fungsi perpustakaan getutxent sebenarnya memanggil panggilan sistem 'terbuka' untuk membuka '/var/run/utmp' dan menempatkan kunci baca dan membaca konten kemudian melepaskan kunci.
Sekarang kami mengkonfirmasi bahwa perintah who membaca file utmp untuk menampilkan output.
Baik 'strace' dan 'ltrace' memiliki serangkaian opsi bagus yang dapat digunakan.
- -p pid – Menempel pada pid yang ditentukan. Berguna jika program sudah berjalan dan Anda ingin mengetahui perilakunya.
- -n 2 – Indentasi setiap panggilan bersarang dengan 2 spasi.
- -f – Ikuti garpu
Baca lebih lanjut:7 Contoh Strace untuk Debug Eksekusi Program di Linux
III. Mencegat panggilan perpustakaan
5. LD_PRELOAD &LD_LIBRARY_PATH
LD_PRELOAD memungkinkan kita untuk menambahkan perpustakaan ke eksekusi program tertentu. Fungsi di perpustakaan ini akan menimpa fungsi perpustakaan yang sebenarnya.
Catatan:Kami tidak dapat menggunakan ini dengan program yang disetel dengan bit 'suid'.
Mari kita ikuti program berikut.
#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"); } }
Kompilasi dan jalankan program.
# cc -o my_prg my_prg.c # ./my_prg
Ini akan mencetak “String tidak cocok”.
Sekarang kita akan menulis perpustakaan kita sendiri dan kita akan melihat bagaimana kita dapat mencegat fungsi perpustakaan.
#include <stdio.h> int strcmp(const char *s1, const char *s2) { // Always return 0. return 0; }
Kompilasi dan setel variabel LD_LIBRARY_PATH ke direktori saat ini.
# cc -o mylibrary.so -shared library.c -ldl # LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
Sekarang sebuah file bernama 'library.so' akan dibuat.
Setel variabel LD_PRELOAD ke file ini dan jalankan program perbandingan string.
# LD_PRELOAD=mylibrary.so ./my_prg
Sekarang ia akan mencetak 'Strings are matched' karena menggunakan fungsi strcmp versi kami.
Catatan:Jika Anda ingin mencegat fungsi pustaka apa pun, maka fungsi pustaka Anda sendiri harus memiliki prototipe yang sama dengan fungsi pustaka asli.
Kami baru saja membahas hal-hal yang sangat mendasar yang diperlukan untuk merekayasa balik sebuah program.
Bagi mereka yang ingin mengambil langkah selanjutnya dalam rekayasa balik, memahami format file ELF dan Program Bahasa Perakitan akan lebih membantu.