Copy Link
Add to Bookmark
Report
Echo Magazine Issue 15 Phile 0x006
echo|zine, volume 4 issue 15
----------------------[ Strace untuk analisa eksekusi ]-------------------
--------------------------------------------------------------------------
-------------[ Anonymous-co-editor <anonymous at echo or id>]-------------
---// License
Lisensi: DWYWBDLMKIYCOCI (*singkatan baca di footer)
---// Pendahuluan
Sering dengar soal strace? Atau persisnya apa itu strace? Strace adalah
program untuk melacak system call yang dilakukan oleh suatu program.
Lebih detilnya, kita bisa tahu:
1. Nama system call yang dipanggil
2. Nilai parameter dari system call
3. Nilai kembalian dari system call
Lalu apa itu system call? Tidak lain adalah suatu prosedur untuk
meminta suatu layanan tertentu dari kernel. Prosedur ini dipanggil lewat
dua kemungkinan:
a. interrupt 80 hexa
b. instruksi SYSENTER/SYSCALL
Dengan kata lain, system call pada dasarnya adalah interrupt/exception
handler yang diijinkan untuk dipanggil oleh program, tidak eksklusif
untuk keperluan kernel itu sendiri. Detil teknis cara eksekusi system
call tidak dibahas disini.
========================================================================
Program --[fungsi C]--> glibc --[ system call ] --> kernel -- [fungsi internal kernel]
^ /^ /^ |
\ / \ / \ |
+-----------------+ + -----------------------+ +------------+
========================================================================
Gambar 1. Ilustrasi cara kerja system call
Strace memanfaatkan fungsi ptrace(), suatu feature untuk melacak
pemanggilan system call yang dilakukan oleh suatu proses. Secara garis
besar, proses pelacakan dilakukan sbb:
1. Pertama dipilih PID dari proses yang akan kita lacak. PID ini bisa
sembarang proses jika effective user id anda adalah root, jika tidak
maka anda hanya bisa melacak child process anda sendiri. Setelah
memilih PID target, ptrace() dipanggil dengan dua argumen yaitu PID
tujuan dan request PTRACE_ATTACH. Perhatian: suatu proses tidak bisa
dilacak oleh lebih dari satu proses.
2. Proses tujuan akan berhenti (karena menerima sinyal SIGSTOP). Dalam
keadaan ini, proses yang terlacak kita sebut child dan proses pelacak
disebut parent. Namun demikian child tetap "mengira" bahwa parent aslinya
bukanlah proses pelacak kita (bisa dicek dengan getppid()).
3. Dalam posisi relasi parent-child sudah terbentuk dan child sedang berhenti
bekerja, dipanggil lagi fungsi ptrace(). Kali ini dengan request
PTRACE_SYSCALL. Dengan demikian, secara efektif proses pelacakan dimulai. Proses
pelacakan baru berakhir jika dipanggil lagi fungsi ptrace() dengan request
PTRACE_DETACH.
4. Setiap kali proses child dihentikan karena memanggil suatu system call,
child akan dihentikan dan parent akan diberitahu akan keadaan ini (lewat
mekanisme signal). Selanjutnya, parent bisa menginspeksi isi memory child
guna menentukan properti dari system call. Biasanya yang dicek adalah stack
karena disinilah paramater system call (termasuk nomer fungsi system call)
disimpan. Request PTRACE_SYSCALL diulangi sebanyak yang diperlukan.
Dengan sekelumit dasar teori yang telah kita simak, pertanyaannya
seperti apa strace bekerja di lapangan? Kita coba dengan:
========================================================================
$ strace echo a
execve("/bin/echo", ["echo", "a"], [/* 32 vars */]) = 0
uname({sys="Linux", node="xxx", ...}) = 0
brk(0) = 0x804bc08
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
= 0x40016000
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=70497, ...}) = 0
old_mmap(NULL, 70497, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000
close(3) = 0
open("/lib/tls/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`V\1B4\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1531064, ...}) = 0
old_mmap(0x42000000, 1257224, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0)
= 0x42000000
[edited -- cut off]
write(1, "a\n", 2a
) = 2
munmap(0x40217000, 4096) = 0
exit_group(0) = ?
========================================================================
Listing 1. Cuplikan output strace echo
Phewww, so much for a single "echo"? Itulah kenyataannya. Banyak
sekali system call yang dilakukan suatu program, bahkan yang sekedar untuk
menampilkan huruf "a"! Tidak mudah memang untuk menangkap makna dari
urutan maupun apa maksud system call itu sendiri, tapi untungnya perintah
"man" bisa membantu. Contohnya, bingung apa itu execve()? ketik saja
$ man execve
Dengan membaca manual, kita relatif mudah mencari makna nilai kembalian
(angka di sebelah kanan tanda "="). Kadang strace bisa menerjemahkannya
untuk kita, tapi tetap saja yang dipakai adalah konstanta simbolik semacam
ENOENT, EPERM dst. Jangan lupa bahwa anda bisa mengecek langsung ke file
header yang bersangkutan (biasanya terletak di /usr/include) untuk men cross
check data yang didapat dari "man".
Diatas telah kita lihat bagaimana strace melacak program yang dijalankan
sebagai parameter, tapi strace sebenarnya bisa juga melacak program lain
yang sedang berjalan. Cukup gunakan option -p disertai pid
proses tujuan:
$ strace -p 12345
Iseng-iseng, bagaimana kalau kita lacak shell kita sendiri?
$ strace -p $$
wait4(-1, <unfinished ...>
Kelihatannya shell kita sedang "bengong" :) persisnya, sedang menunggu
sembarang child menghentikan eksekusi. Tidak usah ditunggu, karena ditunggu
pun shell akan tetap dalam posisi menunggu ini. Langsung tekan Ctrl-C
untuk mengakhiri sesi tracing.
Jangan lupakan option -o, karena dengan option ini anda bisa menyimpan hasil
trace ke dalam nama file sesuai argumen parameter. Efeknya, anda tidak bisa
melihat output strace di layar saat itu juga
---// Praktek Penggunaan
Dalam kesehariannya, strace paling banyak digunakan untuk keperluan:
a. runtime binary analysis.
b. trouble shooting.
Untuk contoh dari dua tipikal tugas diatas, akan diberikan dua contoh:
--------[ Runtime Binary analysis
Apa yang kita analisa? tentu saja bukan kode mesin dari program, tapi apa
saja system call yang digunakan dan untuk keperluan apa. Contoh mudahnya
adalah: library apa yang dipakai oleh "echo"? Perhatikan lagi
listing 1:
open("/lib/tls/libc.so.6", O_RDONLY) = 3
Jelas disini jawabannya adalah libc.so dan ini tidak mengherankan karena
library ini dibinding ke semua program Linux anda (kecuali program
dicompile sebagai static binary). Library lain tidak kita temukan dalam
output strace.
Bisa juga anda sedang penasaran, bagaimana sebenarnya perintah "who"
mencari tahu user yang sedang login. Biasanya, program UNIX mengambil
informasi yang berkaitan dengan sistem dengan membaca suatu file. Fakta
ini bisa kita gunakan untuk mempersempit analisa kita, caranya dengan
menggunakan option "-e trace=<nama fungsi>". Contohnya:
=========================================================================
$ strace -e trace=open -o trace.txt who
$ cat trace.txt
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
open("/lib/tls/libc.so.6", O_RDONLY) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
open("/var/run/utmp", O_RDWR) = -1 EACCES (Permission denied)
open("/var/run/utmp", O_RDONLY) = 3
open("/etc/localtime", O_RDONLY) = 3
open("/usr/lib/gconv/gconv-modules.cache", O_RDONLY) = 3
=========================================================================
Listing 2. Strace "who"
Terlihat ada 8 file yang dibuka oleh "who". Dua yang pertama bukan
file yang diperlukan oleh who (ini adalah file untuk keperluan runtime
linking). Insting biasanya mengarahkan kita pada nama file yang "unik", dalam
hal ini adalah /var/run/utmp. Patut dicatat bahwa file ini pertama hendak
dibuka dengan mode read/write (O_RDWR, namun kemudian diubah menjadi
read only (O_RDONLY). Penasaran apa itu file utmp?
=========================================================================
$ file /var/run/utmp
/var/run/utmp: GLS_BINARY_LSB_FIRST
$ strings /var/run/utmp
[edited-cut off]
mulyadi
pts/0
mulyadi
pts/1
mulyadi
=========================================================================
Listing 3. Menganalisa isi file utmp.
Yup, file utmp adalah sumber informasi login di Linux. Strings
mengkonfirmasikan bahwa di dalam utmp ada data login yang sangat mirip
dengan yang kita lihat di output "who".
Contoh sederhana lainnya misalnya: kenapa perintah "touch" itu bisa
membuat suatu file kosong? Apa bukan sekedar mengupdate timestamp dari
suatu file?
=========================================================================
$ strace touch coba.ahh
[edited -- cut off]
open("coba.ahh", O_WRONLY|O_NONBLOCK|O_CREAT|O_NOCTTY|O_LARGEFILE, 0666) = 3
close(3) = 0
utime("coba.ahh", NULL) = 0
[edited -- cut off]
=========================================================================
Listing 4. touch saat membuat file
Oh, ternyata rahasianya ada pada option O_CREAT saat open(). Berdasarkan
"man open" dijelaskan bahwa jika file yang hendak dibuka tidak ada, maka
file akan dibuat dengan nama sesuai parameter open().
--------[ Trouble shooting
Contoh berikut ini sebenarnya bisa juga dianggap analisa binary, karena
pada dasarnya troubleshooting tidak terlepas dari kemampuan menganalisa
tingkah laku suatu program. Disini "masalah" yang diajukan sederhana, lebih cepat
mana eksekusi suatu program dengan output mengarah standart output atau diredirect
ke file? Perintah berikut bisa memberikan jawabannya:
=========================================================================
$ time ls -R ~
real 0m1.420s
user 0m0.080s
sys 0m0.040s
$ time ls -R ~ > ./hasil.txt
real 0m0.066s
user 0m0.040s
sys 0m0.030s
=========================================================================
Listing 5. Waktu eksekusi ls
Hmm, ternyata menampilkan langsung ke standart output 21 kali lebih
lambat dibanding menyimpan file. Lalu apa yang menyebabkan kelambatan ini?
Dengan option -c, kita bisa mendapat statistik mengenai waktu eksekusi
masing-masing nama system call.
=========================================================================
$ strace -c ls -R > ./hasil.txt
execve("/bin/ls", ["ls", "-R"], [/* 30 vars */]) = 0
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
25.93 0.000329 4 79 lstat64
24.82 0.000315 315 1 uname
12.29 0.000156 7 22 getdents64
[edited -- cut off]
----- ----------- ----------- --------- --------- ----------------
100.00 0.001269 218 5 total
$ strace -c ls -R
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
93.17 0.015500 228 68 write
2.06 0.000343 4 78 lstat64
1.74 0.000290 13 22 getdents64
[edited -- cut off]
------ ----------- ----------- --------- --------- ----------------
100.00 0.016636 283 3 total
=========================================================================
Listing 6. Statistik pemanggilan system call oleh ls
Ternyata saat ditampilkan ke standart output, write() memakan waktu total
terlama dibanding system call lainnya. Bahkan total waktunya (0.0155 detik)
mengalahkan waktu total eksekusi lstat64() (0.000329 detik) saat output "ls"
diredirect ke suatu file. Secara persentase, write() juga mendominasi
sebanyak 93.17% dari keseluruhan system call, bandingkan lstat64() yang
hanya 25.93%. Kesimpulannya, jika anda ingin sedang dipepet waktu dan
perlu melist seluruh isi direktori (terutama jika isinya sangat banyak
dan/atau secara rekursif), redirect saja ke ke file!
Sering juga kita mengalami kebingungan, jika suatu program mengakses
file konfigurasi yang sama di beberapa tempat, mana yang dipakai terlebih
dahulu? Misalnya wget. Wget memiliki dua tempat kemungkinan konfigurasi,
yaitu di /etc/wgetrc dan ~/.wgetrc. Masalahnya, mana dulu yang diakses?
=========================================================================
$ strace -e trace=file -ff -v -o trace.txt wget
$ grep -i wgetrc trace.txt
access("/etc/wgetrc", F_OK) = 0
open("/etc/wgetrc", O_RDONLY) = 3
access("/home/mulyadi/.wgetrc", F_OK) = -1 ENOENT (No such file or directory)
=========================================================================
Listing 7. wget mengakses file konfigurasi
Ternyata cukup jelas, wget mengakses file konfigurasi global di /etc/,
baru kemudian mengakses konfigurasi user-specific. Dengan kata lain,
setting global akan dioverride oleh setting per-user. Contoh ini bisa
anda kembangkan untuk kasus lain yang lebih kompleks.
---// Catatan
Melihat dari contoh-contoh diatas, bisa dilihat bahwa ptrace()
(strace khususnya) bisa disalahgunakan untuk tujuan cracking. Untuk mencegah
hal ini, bisa digunakan patch kernel semisal grsecurity untuk menghilangkan
kemampuan suatu user ID melakukan ptrace(). Kemampuan (atau "capability")
yang dimaksud adalah CAP_SYS_PTRACE.
---// Kesimpulan
Strace cukup berguna sebagai salah satu alat bantu pelacakan program.
Kita bisa menggunakannya untuk analisa program dan/atau trouble shooting.
Strace sendiri hanya menganalisa pemanggilan system call, oleh karenanya
tidak bisa mengetahui secara persis apa yang dilakukan suatu program.
Strace bisa dianggap sebagai pendamping disassembler (misal biew) untuk
memperoleh gambaran cara kerja program.
---// Referensi
[a] man ptrace
[b] man strace
---// shoutz alias teriak-teriak ke:
- God
- All the echo staff
- Pembaca setia echo zine
(*) DWYWBDLMKIYCOCI : Do Whatever You Want But Do Let Me Know If You Copy
or Cite It.