Copy Link
Add to Bookmark
Report

Echo Magazine Issue 23 Phile 0x008

eZine's profile picture
Published in 
Echo Magazine
 · 4 years ago

  

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



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

guest's profile picture
@guest
12 Nov 2024
It is very remarkable that the period of Atlantis’s destruction, which occurred due to earthquakes and cataclysms, coincides with what is co ...

guest's profile picture
@guest
12 Nov 2024
Plato learned the legend through his older cousin named Critias, who, in turn, had acquired information about the mythical lost continent fr ...

guest's profile picture
@guest
10 Nov 2024
الاسم : جابر حسين الناصح - السن :٤٢سنه - الموقف من التجنيد : ادي الخدمه - خبره عشرين سنه منهم عشر سنوات في كبرى الشركات بالسعوديه وعشر سنوات ...

lostcivilizations's profile picture
Lost Civilizations (@lostcivilizations)
6 Nov 2024
Thank you! I've corrected the date in the article. However, some websites list January 1980 as the date of death.

guest's profile picture
@guest
5 Nov 2024
Crespi died i april 1982, not january 1980.

guest's profile picture
@guest
4 Nov 2024
In 1955, the explorer Thor Heyerdahl managed to erect a Moai in eighteen days, with the help of twelve natives and using only logs and stone ...

guest's profile picture
@guest
4 Nov 2024
For what unknown reason did our distant ancestors dot much of the surface of the then-known lands with those large stones? Why are such cons ...

guest's profile picture
@guest
4 Nov 2024
The real pyramid mania exploded in 1830. A certain John Taylor, who had never visited them but relied on some measurements made by Colonel H ...

guest's profile picture
@guest
4 Nov 2024
Even with all the modern technologies available to us, structures like the Great Pyramid of Cheops could only be built today with immense di ...

lostcivilizations's profile picture
Lost Civilizations (@lostcivilizations)
2 Nov 2024
In Sardinia, there is a legend known as the Legend of Tirrenide. Thousands of years ago, there was a continent called Tirrenide. It was a l ...
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