Copy Link
Add to Bookmark
Report

Echo Magazine Issue 23 Phile 0x008

eZine's profile picture
Published in 
Echo Magazine
 · 21 Aug 2020

  

) ) ) ( )
( ( /( ( /( ( /( )\ ) ( /(
( )\ )\()) )\()) )\())(()/( )\()) (
)\ (((_) ((_)\ ((_)\ ((_)\ /(_))((_)\ )\
((_) )\___ _((_) ((_) _((_)(_)) _((_)((_)
| __|((/ __|| || | / _ \|_ / |_ _| | \| || __|
| _| | (__ | __ || (_) |/ / | | | .` || _|
|___| \___||_||_| \___//___| |___| |_|\_||___|



ECHO MAGAZINE VOLUME VIII, ISSUE XXIII, PHILE 0x008.TXT

Stack Tracing di executable (contoh kasus: binary Linux) - Mulyadi "the-hydra" Santosa
the-hydra/at/echo/dot/or/dot/id

-----[ Pendahuluan

Apa sih itu stack trace? Secara umum, dalam suatu program, sebelum suatu fungsi
dipanggil, dipersiapkan suatu area memory di stack yang disebut "frame". Di
Linux 32 bit, alamat stack dimulai dari alamat yang cukup besar, mendekati 3 GiB
(Gigabyte) atau 0xC000000. Sifat stack pada processor Intel *86 adalah data baru
dimasukkan pada alamat yang semakin menurun.

Pengetahuan ini berguna untuk analisa program secara low level dan membantu juga
analisa teknik exploit semacam buffer overflow.

Untuk mempermudah pemahaman, kita misalkan kita punya source code C berikut:

#include<stdio.h>
#include<string.h>

void printgue(char nih[], int n)
{
if ((nih) && (strlen(nih) > 0)) {
printf("%d %s\n", n, nih);
printgue(++nih, ++n);

} else {
printf("Finish\n");

}
}

void main()
{
printgue("bandung", 1);

}

Program ini adalah contoh sederhana program yang memiliki fungsi rekursif alias
memanggil dirinya sendiri. Perhatikan fungsi printgue() yang akan memanggil
dirinya sendiri. Dia baru berhenti jika string yang akan dicetak adalah kosong.
if ((nih) && (strlen(nih) > 0))


Program diatas dicompile dengan gcc di Linux:
$ gcc -g -W print-recur.c -o print-recur
Option -g memungkinkan program ini dengan mudah didebug nantinya.

Dan kita coba jalankan:
$ ./print-recur
1 bandung
2 andung
3 ndung
4 dung
5 ung
6 ng
7 g
Finish

Bagaimana sebenarnya proses pemanggilan fungsi printgue() yang berulang ini?
Kita coba minta bantuan gdb (GNU debugger):

Reading symbols from print-recur...done.
(gdb) l 1
1 #include<stdio.h>
2 #include<string.h>
3
4 void printgue(char nih[], int n)
5 {
6 if ((nih) && (strlen(nih) > 0)) {
7 printf("%d %s\n", n, nih);
8 printgue(++nih, ++n);
9
10 } else {
(gdb)
11 printf("Finish\n");
12
13 }
14 }
15
16 void main()
17 {
18 printgue("bandung", 1);
19
20 }

(gdb) break 8

Kita hentikan program di baris 12, karena pada saat inilah kita tahu rekursi
mencapai final (tidak ada lagi yang dicetak).

(gdb) r
Starting program: print-recur
1 bandung
2 andung
3 ndung
4 dung
5 ung
6 ng
7 g

Breakpoint 2, printgue (nih=0x8048566 "", n=8) at print-recur.c:11
11 printf("Finish\n");

Nah, pada saat ini, bagaimana alur pemanggilan fungsinya?
(gdb) bt
#0 printgue (nih=0x8048566 "", n=8) at print-recur.c:11
#1 0x0804845f in printgue (nih=0x8048566 "", n=8) at print-recur.c:8
#2 0x0804845f in printgue (nih=0x8048565 "g", n=7) at print-recur.c:8
#3 0x0804845f in printgue (nih=0x8048564 "ng", n=6) at print-recur.c:8
#4 0x0804845f in printgue (nih=0x8048563 "ung", n=5) at print-recur.c:8
#5 0x0804845f in printgue (nih=0x8048562 "dung", n=4) at print-recur.c:8
#6 0x0804845f in printgue (nih=0x8048561 "ndung", n=3) at print-recur.c:8
#7 0x0804845f in printgue (nih=0x8048560 "andung", n=2) at print-recur.c:8
#8 0x0804848c in main () at print-recur.c:18


Dari output diatas, kita bisa simpulkan beberapa hal:
1. Pemanggilan fungsi berawal dari eksekusi di alamat beberapa byte sebelum
0x0804848c (dalam hexadecimal)

2. Data yang dipassing ke fungsi dimulai dari alamat 0x804860. Nah, kok cuma
"andung"? Kan "bandung" mestinya? Ada yang tidak terlihat disini:
(gdb) disassemble main
Dump of assembler code for function main:
0x0804846f <+0>: push %ebp
0x08048470 <+1>: mov %esp,%ebp
0x08048472 <+3>: and $0xfffffff0,%esp
0x08048475 <+6>: sub $0x10,%esp
0x08048478 <+9>: movl $0x1,0x4(%esp)
0x08048480 <+17>: movl $0x804855f,(%esp)
0x08048487 <+24>: call 0x8048414 <printgue>
0x0804848c <+29>: leave
0x0804848d <+30>: ret
End of assembler dump.

(gdb) x/s 0x804855f
0x804855f: "bandung"

Aha, data string lengkap ternyata ada di alamat 0x804855f. Dari sini bisa
disimpulkan passing parameter berupa passing by reference, karena alamat data
yang dikirim, bukan nilai string itu sendiri.

Sudah dapat gambaran rekursif nya? Sekarang kita fokus pada isi stack dan
sekaligus cara fungsi dipanggil. Mari kita ulangi pemanggilan print-recur lewat
gdb:

$ gdb -q print-recur
Reading symbols from print-recur...done.
(gdb) break printgue
Breakpoint 1 at 0x804841a: file print-recur.c, line 6.
(gdb) r
Starting program: print-recur

Breakpoint 1, printgue (nih=0x804855f "bandung", n=1) at print-recur.c:6
6 if ((nih) && (strlen(nih) > 0)) {
(gdb) c
Continuing.
1 bandung

Breakpoint 1, printgue (nih=0x8048560 "andung", n=2) at print-recur.c:6
6 if ((nih) && (strlen(nih) > 0)) {
(gdb) c
Continuing.
2 andung

Breakpoint 1, printgue (nih=0x8048561 "ndung", n=3) at print-recur.c:6
6 if ((nih) && (strlen(nih) > 0)) {
(gdb) c

Continuing.
3 ndung
Breakpoint 1, printgue (nih=0x8048562 "dung", n=4) at print-recur.c:6
6 if ((nih) && (strlen(nih) > 0)) {




OK, kita disini mencapai titi dimana printgue() akan dikerjakan untuk kali
ke-4. Parameter n sengaja dipakai untuk memudahkan pelacakan :)

berarti urutan pemanggilan kita sejauh ini sebagai berikut:
(gdb) bt
#0 printgue (nih=0x8048562 "dung", n=4) at print-recur.c:6
#1 0x0804845f in printgue (nih=0x8048562 "dung", n=4) at print-recur.c:8
#2 0x0804845f in printgue (nih=0x8048561 "ndung", n=3) at print-recur.c:8
#3 0x0804845f in printgue (nih=0x8048560 "andung", n=2) at print-recur.c:8
#4 0x0804848c in main () at print-recur.c:18

Persisnya saat ini kita ada di
(gdb) print $eip
$1 = (void (*)()) 0x804841a <printgue+6>

(gdb) disassemble printgue
Dump of assembler code for function printgue:
0x08048414 <+0>: push %ebp
0x08048415 <+1>: mov %esp,%ebp
0x08048417 <+3>: sub $0x18,%esp
=> 0x0804841a <+6>: cmpl $0x0,0x8(%ebp) --> kita disini
0x0804841e <+10>: je 0x8048461 <printgue+77>
0x08048420 <+12>: mov 0x8(%ebp),%eax

Nah, waktunya kita cek isi stack frame kita:
(gdb) info frame
Stack level 0, frame at 0xbfffee20:
eip = 0x804841a in printgue (print-recur.c:6); saved eip 0x804845f
called by frame at 0xbfffee40
source language c.
Arglist at 0xbfffee18, args: nih=0x8048562 "dung", n=4
Locals at 0xbfffee18, Previous frame's sp is 0xbfffee20
Saved registers:
ebp at 0xbfffee18, eip at 0xbfffee1c


Jadi saat printgue() hendak mencetak "dung", keadaanya adalah:
- Kita hendak mengeksekusi perintah di alamat 0x804841a (eip =)
Dimana persisnya alamat ini?
(gdb) l *0x804841a
0x804841a is in printgue (print-recur.c:6).
........
6 if ((nih) && (strlen(nih) > 0)) {

- Kita dipanggil dari suatu perintah lain di alamat 0x804845f (saved eip)
(gdb) disassemble printgue
.....
0x08048454 <+64>: mov 0x8(%ebp),%eax
0x08048457 <+67>: mov %eax,(%esp)
0x0804845a <+70>: call 0x8048414 <printgue>
0x0804845f <+75>: jmp 0x804846d <printgue+89> --> akan berlanjut kesini
0x08048461 <+77>: movl $0x8048558,(%esp)
0x08048468 <+84>: call 0x8048350 <puts@plt>
......

Posisi diatas kira-kira pada instruksi persis setelah baris ke-8.

- Data yang dipassing sebagai parameter, dalam hal ini string "dung" bisa
ditemukan referensinya mulai alamat 0xbfffee18. Sementara string "dung"
sendiri ada mulai alamat 0x8048562 ("ban" terpotong)

- Awal stack frame di alamat 0xbfffee20. Satu persatu parameter akan disimpan
pada alamat yang semakin kecil.

Visualisasinya:

+-----------------------+
0xbfffee24: | 0x00000004 | => isi variabel "n" = 4
+-----------------------+
0xbfffee20 | 0x08048562 | => alamat "dung" (substring "bandung")
+-----------------------+
0xbfffee1c | 0x0804845f | => return address (instruksi berikut yang
+-----------------------+ dikerjakan)
0xbfffee18: | 0xbfffee38 | => data frame pemanggil
+-----------------------+
| |
| |
| (Variable local) |
| |
| |
0xbfffee00 +-----------------------+

Antara string yang dipassing dan variabel "n" alamatnya berselisih 4 byte,
karena pointer di sistem 32 bit besarnya juga 32 bit (4 byte).

Sebagai tambahan kita coba sekali lagi menterjemahkan frame sebelumnya:
(gdb) info frame 1
Stack frame at 0xbfffee40:
eip = 0x804845f in printgue (print-recur.c:8); saved eip 0x804845f
called by frame at 0xbfffee60, caller of frame at 0xbfffee20
source language c.
Arglist at 0xbfffee38, args: nih=0x8048562 "dung", n=4
Locals at 0xbfffee38, Previous frame's sp is 0xbfffee40
Saved registers:
ebp at 0xbfffee38, eip at 0xbfffee3c

Ini adalah keadaan sesaat printgue() akan dipanggil.
Visualisasinya:

+-----------------------+
0xbfffee44: | 0x00000004 | => isi variabel "n" = 4
+-----------------------+
0xbfffee40 | 0x08048562 | => alamat "dung" (substring "bandung")
+-----------------------+
0xbfffee3c | 0x0804845f | => return address (instruksi berikut yang
+-----------------------+ dikerjakan)
0xbfffee38: | 0xbfffee58 | => data frame pemanggil
+-----------------------+
| |
| |
| (Variable local) |
| |
| |
0xbfffee20 +-----------------------+

-----[ Penutup:

Mempelajari cara kerja internal suatu program memang tidak semudah membalik
telapak tangan, namun dengan ketelatenan dan tools yang tepat (misal gdb), kita
akan mudah mempelajarinya.

+---------------------------------------------------------+
| ECHO MAGAZINE VOLUME VIII, ISSUE XXIII, PHILE 0x008.TXT |
+---------------------------------------------------------+
| 31 DESEMBER 2010 | http://ezine.echo.or.id |
+---------------------------------------------------------+

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT