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