Programowanie w asemblerze Uruchamianie programów

Transkrypt

Programowanie w asemblerze Uruchamianie programów
Programowanie w asemblerze
Uruchamianie programów
Zbigniew Jurkiewicz, Instytut Informatyki UW
17 stycznia 2017
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Uruchamianie programów
Przy uruchamianiu i badaniu zachowania programów
systemowych używa sie˛ wielu narz˛edzi.
Prostsze z ich, takie jak strace, pozwalaja˛ pasywnie
śledzić działanie programu.
Bardziej skomplikowane sa˛ debuggery, pozwalajace
˛
zatrzymywać program i analizować zawartość pamieci.
˛
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Programy śledzace
˛
Do śledzenia wywołań systemowych służy w Linuksie
program strace (w innych Uniksach istnieja˛ podobne
narz˛edzia, np. truss).
Informacje prezentowane przez strace można również
wydobyć z podsystemu /proc.
Majac
˛ „kultowy” program w C
#include <stdio.h>
int main (int argc, char **argv)
printf("Witaj świecie\n");
spróbujemy go prześledzić używajac
˛ strace.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Programy śledzace
˛
katastrofa:~/akpn$ gcc -static -o hello hello.c
katastrofa:~/akpn$ strace ./hello
execve("./hello", ["./hello"], [/* 54 vars */]) = 0
fcntl64(0, F_GETFD)
= 0
fcntl64(1, F_GETFD)
= 0
fcntl64(2, F_GETFD)
= 0
geteuid32()
= 1000
getuid32()
= 1000
getegid32()
= 100
getgid32()
= 100
brk(0)
= 0x80ad8f4
brk(0x80ae8f4)
= 0x80ae8f4
brk(0x80af000)
= 0x80af000
fstat64(1, st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
write(1, "Witaj \266wiecie\n", 14Witaj świecie
)
= 14
munmap(0x40000000, 4096)
= 0
exit_group(14)
= ?
katastrofa:~/akpn$
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Programy śledzace
˛
Uwaga: Opcja -static znaczaco
˛ zmniejsza długość
śladu, ale nie należy jej używać bez potrzeby.
strace pokazuje wszystkie wykonane wywołania
systemowe wraz z argumentami i zwracanymi wartościami.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Programy śledzace
˛
Popatrzmy na wersje˛ w asemblerze
section .text
global _start
msg
len
db
equ
;dla linkera (ld)
’Witaj świecie!’,0xa ;napis do wypisania
$ - msg
;i jego długość
_start:
;poczatek
˛
mov
mov
mov
mov
int
edx,len
ecx,msg
ebx,1
eax,4
0x80
;długość
;adres napisu
;deskryptor (stdout)
;wywołanie systemowe write
mov
int
eax,1
0x80
;wywołanie systemowe exit
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Programy śledzace
˛
Teraz ślad bedzie
˛
znacznie krótszy
katastrofa:~/akpn$ nasm -f elf hello.asm
katastrofa:~/akpn$ ld -o hello hello.o
katastrofa:~/akpn$ strace ./hello
execve("./hello", ["./hello"], [/* 54 vars */]) = 0
write(1, "Witaj świecie!\n", 15Witaj świecie!
)
= 15
_exit(1)
= ?
katastrofa:~/akpn$
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Programy śledzace
˛
Na zakończenie szef kuchni poleca
katastrofa:~/akpn$ strace strace ./hello
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Posługiwanie sie˛ debuggerem
Debugger jest to narz˛edzie, które pozwala zatrzymać
wykonywany program i zbadać lub zmienić jego stan.
Typowy debugger pozwala:
określać miejsca (nazywane przystankami — breakpoints),
w których program powinien zatrzymać sie˛ i przekazać
sterowanie debuggerowi;
wykonywać program krokowo, tzn. zatrzymywać sie˛
każdorazowo po wykonaniu pojedynczej instrukcji;
ogladać
˛
i zmieniać wartości zmiennych zatrzymanego
programu (a także instrukcje), po czym wznawiać jego
działanie.
Dodatkowo niektóre debuggery pozwalaja˛ określać
warunki, których spełnienie powoduje zatrzymanie
programu.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Posługiwanie sie˛ debuggerem
Modyfikacje programu przez debugger sa˛ dokonywane
bezpośrednio na obrazie programu w pamieci
˛ i nie
zmieniaja˛ kodu źródłowego.
Z NASMem dobrze współpracował program ald
przeznaczony przede wszystkim do znajdowania błedów
˛
w
programach systemowych.
Niestety od jakiegoś czasu (2004) nie jest już rozwijany i
pozostało nam gdb — ale nauczyło sie˛ już składni NASMa.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Posługiwanie sie˛ debuggerem
Wróćmy do naszego pliku hello.asm
Po zasemblowaniu i zlinkowaniu przystapimy
˛
do ogladania
˛
i modyfikacji naszego pliku.
$ nasm -g -f elf hello.asm
$ ld -o hello hello.o ...
Najpierw musimy wywołać program debuggera:
$ gdb hello
...
(gdb)
Debugger oczekuje na nasze polecenia, sygnalizujac
˛ to
promptem „gdb”.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Posługiwanie sie˛ debuggerem
Kończymy prace˛ poleceniem quit (wiele poleceń można
skracać, w tym przypadku wystarcza q)
(gdb) q
$ _
Moim ulubionym poleceniem jest help — goraco
˛ polecam
(bo podrecznik
˛
gdb ma kilkaset stron)!
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Posługiwanie sie˛ debuggerem
Do ogladania
˛
zawartości rejestrów służy polecenie
(gdb) info registers
eax
0x22c8e0 2279648
ecx
0x226070 2252912
edx
0x21bd10 2211088
ebx
0x22bfc4 2277316
esp
0xbffff2a0 0xbffff2a0
ebp
0x0 0x0
esi
0xbffff2ac -1073745236
edi
0x80481c0 134513088
eip
0x80481c4 0x80481c4 <_start+4>
eflags
0x200282 [ SF IF ID ]
cs
0x73 115
ss
0x7b 123
ds
0x7b 123
es
0x7b 123
fs
0x0 0
gs
0x33 51
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Posługiwanie sie˛ debuggerem
Polecenie info ma wiele innych przydatnych argumentów.
Zawartość rejestru można zmienić poleceniem
set $eax=1
Poleceniem p (print)
(gdb) print $xmm0
można dokładniej obejrzeć pojedynczy rejestr (zwłaszcza
gdy zawiera wartości upakowane).
Polecenie x (od examine) pozwala precyzyjnie sterować
wyświetlaniem.
Polecenie where wyświetla stos wywołań.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Programowanie
Podstawowym przeznaczeniem debuggera (dla nas) jest
testowanie niewielkich programów w jezyku
˛
wewnetrznym.
˛
Polecenie list pozwala obejrzeć kod (cz˛eści) programu:
parametr to numer środkowej linii w wyświetlanym ciagu
˛
instrukcji.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Programowanie
Używajac
˛
set disassembly-flavor intel
bedziemy
˛
mieli składnie˛ zbliżona˛ do NASM.
(gdb) list 8
3
section .data
4 output:
5
db ‘The processor Vendor ID is ’xxxxxxxxxxxx’\n‘
6 output_len equ $-output
7
8
section .text
9
global _start
10 _start:
11
mov eax,0
12
cpuid
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Uruchomienie wykonania
Do uruchamiania programu przeznaczone sa˛ nastepuj
˛ ace
˛
polecenia.
Polecenie
(gdb) run
powoduje uruchomienie wykonania programu od poczatku.
˛
Polecenie
(gdb) continue
kontynuuje wykonanie zatrzymanego program poczynajac
˛
od bieżacej
˛ instrukcji (zgodnie z bieżac
˛ a˛ zawartościa˛
rejestru EIP).
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Punkty zatrzymania
Wykonanie programu odbywa sie˛ do końca, chyba że
określono przystanki (breakpoints).
Sa˛ one adresami instrukcji, po dojściu do których
wykonywanie programu ma zostać przerwane, a
sterowanie powróci do debuggera.
Ustawiamy je poleceniem break, podajac
˛ np. numer
instrukcji (numery instrukcji można uzyskać poleceniem
list):
(gdb) break 25
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Tryb krokowy
Dodatkowe polecenia uruchamiajace
˛ sa˛ przeznaczone do
pracy w trybie krokowym.
Polecenie step w najprostszej postaci powoduje
wykonanie pojedynczej instrukcji.
(gdb) step
26
mov ebx, 0x8048231
Opcjonalny argument pozwala określić liczbe˛ rozkazów,
które maja˛ zostać wykonane.
(gdb) step 5
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Tryb krokowy
Uwaga: Jeśli bieżac
˛ a˛ instrukcja˛ jest int lub call, to
użycie polecenia step spowoduje wejście w śledzenie
procedury obsługi przerwania lub procedury bibliotecznej
(która bywa dłuuuuga).
Polecenie next pozwala uniknac
˛ wchodzenia „w głab”
˛
procedur.
(gdb) next 5
27
test eax, eax
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów
Dodatkowe informacje
Przystanki sa˛ realizowane zwykle przez wstawienie w
odpowiednie miejsca w programie poniższej instrukcji (z
zachowaniem poprzedniej zawartości i odtworzeniem jej
po jakimkolwiek zatrzymaniu).
int 3
Instrukcje˛ taka˛ warto również umieszczać na końcu
testowanego programu.
Śledzenie krokowe realizuje sie˛ przez ustawienie flagi TF
w procesorze.
Zbigniew Jurkiewicz, Instytut Informatyki UW
Programowanie w asemblerze Uruchamianie programów