SPIS TRE¥CI

Transkrypt

SPIS TRE¥CI
SPIS TRECI
PRZE DMOWA
11
P ODZI KO W ANI A
12
0X100
WPROWAD ZENIE
13
0X200
PR OGR AMOWANIE
19
0x210 Istota programowania .....................................................................................20
0x220 Pseudokod ......................................................................................................22
0x230 Struktury sterujce ...........................................................................................22
0x231 If-Then-Else ........................................................................................22
0x232 Ptle While/Until ................................................................................24
0x233 Ptle for .............................................................................................25
0x240 Podstawowe pojcia programistyczne .............................................................26
0x241 Zmienne .............................................................................................26
0x242 Operatory arytmetyczne .....................................................................27
0x243 Operatory porównania .......................................................................28
0x244 Funkcje ...............................................................................................30
0x250 Zaczynamy brudzi sobie rce ..........................................................................34
0x251 Wikszy obraz ....................................................................................35
0x252 Procesor x86 ......................................................................................38
0x253 Jzyk asembler ...................................................................................40
0x260 Wracamy do podstaw .....................................................................................53
0x261 acuchy ............................................................................................53
0x262 Ze znakiem, bez znaku, duga i krótka ................................................57
0x263 Wskaniki ...........................................................................................59
0x264 acuchy formatujce .........................................................................63
0x265 Rzutowanie typów .............................................................................67
0x266 Argumenty wiersza polece ...............................................................74
0x267 Zasig zmiennych ...............................................................................78
0x270 Segmentacja pamici .......................................................................................85
0x271 Segmenty pamici w jzyku C ............................................................92
0x272 Korzystanie ze sterty ...........................................................................94
0x273 Funkcja malloc() ze sprawdzaniem bdów .........................................96
0x280 Budujemy na podstawach ...............................................................................98
0x281 Dostp do plików ...............................................................................98
0x282 Prawa dostpu do plików .................................................................104
0x283 Identyfikatory uytkowników ............................................................105
0x284 Struktury ..........................................................................................114
0x285 Wskaniki do funkcji .........................................................................117
0x286 Liczby pseudolosowe ........................................................................118
0x287 Gry hazardowe .................................................................................120
0X300
NADUYCI A
133
0x310 Uogólnione techniki naduy .........................................................................136
0x320 Przepenienia bufora ......................................................................................137
0x321 Luki zwizane z przepenieniem stosowym .......................................140
0x330 Eksperymenty z BASH ....................................................................................152
0x331 Wykorzystanie rodowiska ...............................................................161
0x340 Przepenienia w innych segmentach ..............................................................170
0x341 Podstawowe przepenienie na stercie ...............................................170
0x342 Przepenienia wskaników funkcyjnych .............................................176
0x350 acuchy formatujce ....................................................................................187
0x351 Parametry formatujce .....................................................................188
0x352 Podatno
cigów formatujcych na ataki ........................................190
0x353 Odczyt spod dowolnych adresów pamici ........................................192
0x354 Zapis pod dowolnymi adresami pamici ...........................................193
0x355 Bezpo
redni dostp do parametrów .................................................201
0x356 Uycie zapisu krótkich liczb cakowitych ...........................................203
0x357 Obej
cia z uyciem sekcji dtors .........................................................205
0x358 Kolejny saby punkt programu notesearch ........................................210
0x359 Nadpisywanie globalnej tabeli przesuni ........................................212
0X400
SIEC I
217
0x410 Model OSI .....................................................................................................217
0x420 Gniazda .........................................................................................................220
0x421 Funkcje obsugujce gniazda ............................................................221
0x422 Adresy gniazd ..................................................................................223
0x423 Sieciowa kolejno
bajtów ................................................................224
0x424 Konwersja adresu internetowego .....................................................225
6
Hacking. Sztuka penetracji
0x430
0x440
0x450
0x460
0x470
0x480
0X500
0x425 Prosty przykad serwera ....................................................................226
0x426 Przykad klienta WWW .....................................................................230
0x427 Maleki serwer WWW ......................................................................235
Zagldamy do niszych warstw .....................................................................240
0x431 Warstwa cza danych ......................................................................240
0x432 Warstwa sieci ...................................................................................242
0x433 Warstwa transportowa .....................................................................244
Podsuchiwanie w sieci ..................................................................................247
0X441 Sniffer surowych pakietów ...............................................................249
0x442 Sniffer libpcap ..................................................................................251
0x443 Dekodowanie warstw ......................................................................253
0x444 Aktywne podsuchiwanie .................................................................263
Odmowa usugi .............................................................................................276
0x451 Zalew pakietów SYN (SYN Flooding) .................................................276
0x452 Atak Ping of Death ...........................................................................281
0x453 Atak Teardrop ..................................................................................281
0x454 Zalew pakietów ping (Ping Flooding) ................................................281
0x455 Atak ze wzmocnieniem (Amplification) .............................................282
0x456 Rozproszony atak DoS ......................................................................283
Przejcie TCP/IP .............................................................................................283
0x461 Rozczanie za pomoc RST ..............................................................283
0x462 Przejcie z kontynuacj .....................................................................289
Skanowanie portów ......................................................................................289
0x471 Ukryte skanowanie SYN ....................................................................289
0x472 Skanowanie FIN, X-mas i Null ...........................................................290
0x473 Skanowanie z ukrycia .......................................................................290
0x474 Skanowanie z uyciem bezczynnego komputera ...............................291
0x475 Zabezpieczenie wyprzedzajce (Shroud) ...........................................293
Id i wam si gdzie
.....................................................................................298
0x481 Analiza z uyciem GDB .....................................................................299
0x482 Prawie ma znaczenie ........................................................................301
0x483 Shellcode wicy port .....................................................................304
SHE LLCODE
309
0x510 Asembler a C .................................................................................................309
0x511 Linuksowe wywoania systemowe w asemblerze .............................312
0x520 cieka do shellcode ......................................................................................315
0x521 Instrukcje asemblera wykorzystujce stos .........................................315
0x522 Badanie za pomoc GDB ..................................................................317
0x523 Usuwanie bajtów zerowych ..............................................................319
0x530 Kod wywoujcy powok ..............................................................................324
0x531 Kwestia uprawnie ...........................................................................328
0x532 Skracamy jeszcze bardziej .................................................................331
Spis treci
7
0x540 Kod powoki wicy port .............................................................................332
0x541 Duplikowanie standardowych deskryptorów plików .........................336
0x542 Rozgaziajce struktury sterujce ....................................................338
0x550 Shellcode nawizujcy poczenie powrotne .................................................343
0X600
R OD KI ZAPOBI EGAWC ZE
349
0x610 rodki zapobiegawcze, które wykrywaj .......................................................350
0x620 Demony systemowe ......................................................................................351
0x621 Byskawiczne wprowadzenie do sygnaów .......................................352
0x622 Demon tinyweb ................................................................................354
0x630 Narzdzia pracy .............................................................................................358
0x631 Narzdzie naduywajce tinywebd ...................................................359
0x640 Pliki dziennika ...............................................................................................364
0x641 Wmieszaj si w tum ........................................................................365
0x650 Przeoczywszy oczywiste ................................................................................366
0x651 Krok po kroku ...................................................................................367
0x652 Ponowne skadanie wszystkiego ......................................................371
0x653 Robocze procesy potomne ................................................................377
0x660 Zaawansowana sztuka kamuflau .................................................................378
0x661 Faszowanie rejestrowanego adresu IP .............................................379
0x662 Nierejestrowane naduycie ...............................................................383
0x670 Caa infrastruktura .........................................................................................386
0x671 Ponowne wykorzystanie gniazda ......................................................386
0x680 Przemyt adunku ............................................................................................390
0x681 Kodowanie acuchów .....................................................................391
0x682 Jak ukry puapk? ...........................................................................394
0x690 Ograniczenia bufora ......................................................................................395
0x691 Polimorficzny kod powoki z drukowalnymi znakami ASCII ...............397
0x6a0 Zabezpieczenia wzmacniajce .......................................................................408
0x6b0 Niewykonywalny stos ....................................................................................408
0x6b1 Powracanie do funkcji biblioteki libc .................................................409
0x6b2 Powrót do funkcji system() ...............................................................409
0x6c0 Randomizacja przestrzeni stosu .....................................................................411
0x6c1 Badanie za pomoc BASH i GDB .......................................................413
0x6c2 Odbijanie od linux-gate ....................................................................417
0x6c3 Wiedza stosowana ...........................................................................420
0x6c4 Pierwsza próba .................................................................................421
0x6c5 Gramy w ko
ci ..................................................................................422
0X700
KRYP TOLOGIA
425
0x710 Teoria informacji ............................................................................................426
0x711 Bezwarunkowe bezpieczestwo .......................................................426
0x712 Szyfr z kluczem jednorazowym .........................................................426
8
Hacking. Sztuka penetracji
0x720
0x730
0x740
0x750
0x760
0x770
0x780
0X800
0x713 Kwantowa dystrybucja kluczy ...........................................................427
0x714 Bezpieczestwo obliczeniowe ...........................................................428
Rozlego
algorytmów .................................................................................429
0x721 Notacja asymptotyczna .....................................................................430
Szyfrowanie symetryczne ...............................................................................430
0x731 Kwantowy algorytm przeszukiwania autorstwa Lova Grovera ...........432
Szyfrowanie asymetryczne .............................................................................432
0x741 RSA ..................................................................................................433
0x742 Kwantowy algorytm rozkadu na czynniki autorstwa Petera Shora ....437
Szyfry hybrydowe ..........................................................................................438
0x751 Ataki z ukrytym po
rednikiem ...........................................................439
0x752 „Odciski palców” komputerów w protokole SSH ...............................443
0x753 Rozmyte „odciski palców” ................................................................447
amanie hase ...............................................................................................451
0x761 Ataki sownikowe .............................................................................453
0x762 Ataki na zasadzie penego przegldu ................................................455
0x763 Tablica wyszukiwania skrótów .........................................................457
0x764 Macierz prawdopodobiestwa hase ................................................457
Szyfrowanie w sieci bezprzewodowej 802.11b ..............................................467
0x771 Protokó Wired Equivalent Privacy (WEP) ..........................................468
0x772 Szyfr strumieniowy RC4 ....................................................................469
Ataki na WEP ................................................................................................470
0x781 Ataki na zasadzie penego przegldu w trybie offline .......................470
0x782 Ponowne uycie strumienia klucza ....................................................471
0x783 Tablice sownikowe z wektorami IV ..................................................472
0x784 Przekierowanie IP .............................................................................473
0x785 Atak Fluhrera, Mantina i Shamira (FMS) ............................................474
P ODSUMOWANIE
485
0x810 Bibliografia i dodatkowe informacje ..............................................................486
0x820 Kody ródowe ..............................................................................................487
O P YC IE C D
489
SKOR OWID Z
491
Spis treci
9
0x200
PROGRAMOWANIE
Haker to termin uywany do okrelenia zarówno piszcych kod, jak
i tych, którzy wykorzystuj znajdujce si w nim bdy. Cho obie
grupy hakerów maj róne cele, to i jedni, i drudzy stosuj podobne
techniki rozwizywania problemów. A ze wzgldu na fakt, e umiejtno programowania pomaga tym, którzy wykorzystuj bdy w kodzie,
za zrozumienie metod zastosowania pomaga tym, którzy programuj,
wielu hakerów robi obie rzeczy jednoczenie. Interesujce rozwizania istniej
i w zakresie technik pisania eleganckiego kodu, i technik sucych do wykorzystywania saboci programów. Hakerstwo to w rzeczywistoci akt znajdowania pomysowych i nieintuicyjnych rozwiza problemów.
Metody stosowane w narzdziach wykorzystujcych saboci programów zwykle s zwizane z zastosowaniem regu funkcjonowania komputera w sposób, którego nigdy nie brano pod uwag — pozornie magiczne rezultaty osiga si zwykle
po skupieniu si na omijaniu zaimplementowanych zabezpiecze. Metody uywane podczas pisania programów s podobne, bo równie wykorzystuj reguy funkcjonowania komputera w nowy i pomysowy sposób, jednak w tym przypadku kocowym celem jest osignicie najbardziej wydajnego lub najkrótszego kodu ródowego.
Istnieje nieskoczenie wiele programów, które mona napisa w celu wykonania dowolnego zadania, jednak wikszo z istniejcych rozwiza jest niepotrzebnie
obszerna, zoona i niedopracowana. Pozostaa niewielka ich liczba to programy
niedue, wydajne i estetyczne. Ta waciwo programów nosi nazw elegancji, za
przemylane i pomysowe rozwizania prowadzce do takiej wydajnoci nazywane
s sztuczkami (ang. hacks). Hakerzy stojcy po obu stronach metodyki programowania
doceniaj zarówno pikno eleganckiego kodu, jak i geniusz pomysowych sztuczek.
W wiecie biznesu mniejsz wag przykada si do przemylanych metod programowania i eleganckiego kodu, a wiksz do tworzenia funkcjonalnego kodu w
jak najkrótszym czasie i jak najtaniej. Ze wzgldu na gwatowny wzrost mocy obliczeniowej oraz dostpnoci pamici powicenie dodatkowych piciu godzin na
opracowanie nieco szybszego i mniej wymagajcego pod wzgldem pamici fragmentu kodu po prostu nie opaca si, gdy mamy do czynienia z nowoczesnymi komputerami dysponujcymi gigahercowymi cyklami procesora i gigabajtami pamici.
Podczas gdy optymalizacje czasu i wykorzystania pamici pozostaj niezauwaone
przez wikszo (z wyjtkiem wyrafinowanych) uytkowników, nowa funkcjonalno ma potencja marketingowy. Kiedy najwaniejszym kryterium s pienidze,
powicanie czasu na opracowywanie przemylanych sztuczek optymalizacyjnych
nie ma po prostu sensu.
Elegancj programowania pozostawiono hakerom, hobbistom komputerowym,
których celem nie jest zarabianie, lecz wycinicie wszystkiego, co si da, z ich starych maszyn Commodore 64, autorom programów wykorzystujcych bdy, piszcym niewielkie i niesamowite fragmenty kodu, dziki którym potrafi przedostawa
si przez wskie szczeliny systemów zabezpiecze, oraz wszystkim innym doceniajcym warto szukania i znajdowania najlepszych moliwych rozwiza. S to osoby
zachwycajce si moliwociami oferowanymi przez programowanie i dostrzegajce
pikno eleganckich fragmentów kodu oraz geniusz przemylanych sztuczek. Poznanie
istoty programowania jest niezbdne do zrozumienia, w jaki sposób mona wykorzystywa saboci programów, dlatego te programowanie to naturalny punkt wyjcia
dla naszych rozwaa.
0x210 Istota programowania
Programowanie to pojcie niezmiernie naturalne i intuicyjne. Program jest tylko
seri instrukcji zapisanych w okrelonym jzyku. Programy wystpuj wszdzie
i nawet technofoby codziennie korzystaj z programów. Opisywanie drogi dojazdu
w jakie miejsce, przepisy kulinarne, rozgrywki pikarskie i spirale DNA to s rónego typu programy. Typowy „program” opisu dojazdu samochodem w konkretne
miejsce moe wyglda tak:
Najpierw zjed w dó Alej Kociuszki, prowadzc na wschód. Jed ni
do momentu, kiedy zobaczysz po prawej stronie koció. Jeeli ulica bdzie
zablokowana ze wzgldu na prowadzone roboty, skr w prawo w ul. Moniuszki,
potem skr w lewo w ul. Prusa i w kocu skr w prawo w ul. Orzeszkowej.
W przeciwnym razie moesz po prostu kontynuowa jazd Alej Kociuszki
i skrci w ul. Orzeszkowej. Jed ni i skr w ul. Docelow. Jed ni
jakie 8 kilometrów — nasz dom bdzie si znajdowa po prawej stronie.
Dokadny adres to ul. Docelowa 743.
Kady, kto zna jzyk polski, pojmie podane instrukcje i bdzie móg kierowa
si nimi podczas jazdy. Bez wtpienia nie zapisano ich zbyt byskotliwie, ale s jednoznaczne i zrozumiae.
20
0x200
Jednak komputer nie zna jzyka polskiego — rozumie jedynie jzyk maszynowy
(ang. machine language). W celu poinstruowania go, aby wykona pewn czynno,
instrukcj t naley zapisa w zrozumiaym dla niego jzyku. Jednake jzyk maszynowy jest bardzo ezoteryczny i trudny do opanowania. Skada si z serii bitów
oraz bajtów i róni si, w zalenoci od danej architektury komputerowej. Tak wic
w celu napisania programu w jzyku maszynowym dla procesora z rodziny Intel x86
naley okreli warto zwizan z kad instrukcj, sposób interakcji poszczególnych
instrukcji oraz mnóstwo innych niskopoziomowych szczegóów. Tego rodzaju programowanie jest bardzo niewdzicznym oraz kopotliwym zadaniem i z pewnoci
nie jest intuicyjne.
Do pokonania komplikacji zwizanych z pisaniem programów w jzyku maszynowym potrzebny jest translator. Jedn z form translatora jzyka maszynowego jest
asembler. To program, który tumaczy jzyk asemblera na kod zapisany w jzyku
maszynowym. Jzyk asemblera jest bardziej przystpny od kodu maszynowego,
poniewa dla rónych instrukcji i zmiennych wykorzystuje nazwy (mnemoniki), a nie
same wartoci liczbowe. Jednak jzyk asemblera wci jest daleki od intuicyjnego.
Nazwy instrukcji s ezoteryczne, a sam jzyk wci jest zaleny od architektury.
Oznacza to, e tak samo, jak jzyk maszynowy dla procesorów Intel x86 róni si
od jzyka maszynowego dla procesorów Sparc, jzyk asemblera x86 jest róny od
jzyka asemblera Sparc. aden program napisany przy uyciu jzyka asemblera dla
architektury jednego procesora nie bdzie dziaa w architekturze innego procesora.
Jeeli program zosta napisany w jzyku asemblera x86, musi zosta przepisany, aby
mona byo uruchomi go w architekturze Sparc. Ponadto w celu napisania wydajnego programu w jzyku asemblera naley zna wiele niskopoziomowych szczegóów, dotyczcych architektury danego procesora.
Problemów tych mona unikn przy uyciu kolejnej formy translatora, noszcego nazw kompilatora (ang. compiler). Kompilator konwertuje kod zapisany w jzyku wysokopoziomowym do postaci kodu maszynowego. Jzyki wysokopoziomowe s
o wiele bardziej intuicyjne od jzyka asemblera i mog by konwertowane do wielu
rónych typów jzyka maszynowego dla odmiennych architektur procesorów. Oznacza to, e jeli zapisze si program w jzyku wysokiego poziomu, ten sam kod moe
zosta skompilowany przez kompilator do postaci kodu maszynowego dla rónych
architektur. C, C++ lub FORTRAN to przykady jzyków wysokopoziomowych.
Program napisany w takim jzyku jest o wiele bardziej czytelny1 od jzyka asemblera lub maszynowego, cho wci musi by zgodny z bardzo surowymi reguami
dotyczcymi sposobu wyraania instrukcji, poniewa w przeciwnym razie kompilator nie bdzie w stanie ich zrozumie.
1
Oczywicie pod warunkiem, e przez czytelny rozumiemy taki, który przypomina jzyk angielski
— przyp. tum.
Programowanie
21
0x220 Pseudokod
Programici dysponuj jeszcze jednym rodzajem jzyka programowania, noszcym
nazw pseudokodu. Pseudokod (ang. pseudo-code) to po prostu wyraenia zapisane
w jzyku naturalnym, ustawione w struktur ogólnie przypominajc jzyk wysokopoziomowy. Nie jest on rozumiany przez kompilatory, asemblery ani komputery, ale stanowi przydatny sposób skadania instrukcji. Pseudokod nie jest dobrze
zdefiniowany. W rzeczywistoci wiele osób zapisuje pseudokod odmiennie. Jest to
rodzaj nieokrelonego, brakujcego ogniwa midzy jzykami naturalnymi, takimi
jak angielski lub polski, a wysokopoziomowymi jzykami programowania, takimi
jak C lub Pascal. Pseudokod stanowi wietne wprowadzenie do uniwersalnych
zaoe programistycznych.
0x230 Struktury sterujce
Bez struktur sterujcych program byby jedynie seri kolejno wykonywanych instrukcji. Jest to wystarczajce w przypadku bardzo prostych programów, ale wikszo
programów, takich jak np. opis dojazdu, taka nie jest. Prezentowany opis dojazdu
zawiera instrukcje: Jed ni do momentu, kiedy zobaczysz po prawej stronie koció.
Jeeli ulica bdzie zablokowana ze wzgldu na prowadzone roboty… Instrukcje te
nazywamy strukturami sterujcymi, poniewa zmieniaj przebieg wykonywania
programu z prostego, sekwencyjnego, na bardziej zoony i uyteczny.
0x231
If-Then-Else
W naszym opisie dojazdu Aleja Kociuszki moe by w remoncie. Do rozwizania
problemu wynikajcego z tej sytuacji potrzebujemy specjalnego zestawu instrukcji.
Jeeli zdefiniowane okolicznoci nie wystpi (remont), bd wykonywane standardowe instrukcje. Tego rodzaju specjalne przypadki mona wzi pod uwag w programie, korzystajc z jednej z najbardziej naturalnych struktur sterujcych: if-then-else
(jeeli-wówczas-w przeciwnym przypadku). Ogólnie wyglda to mniej wicej tak:
If (warunek) then
{
Zestaw instrukcji do wykonania, gdy warunek jest speniony;
}
Else
{
Zestaw instrukcji do wykonania, gdy warunek nie jest speniony;
}
W tej ksice bdziemy posugiwali si pseudokodem przypominajcym jzyk C,
wic kada instrukcja bdzie zakoczona rednikiem, a ich zbiory bd grupowane
za pomoc nawiasów klamrowych i wyróniane wciciem. Pseudokod struktury
if-then-else dla wskazówek dojazdu mógby przedstawia si nastpujco:
22
0x200
Jed w dó Alej Kociuszki;
Jeeli (ulica zablokowana)
{
Skr w prawo w ul. Moniuszki;
Skr w lewo w ul. Prusa;
Skr w prawo w ul. Orzeszkowej;
}
else
{
Skr w prawo w ul. Orzeszkowej;
}
Kada instrukcja znajduje si w osobnym wierszu, a róne zestawy instrukcji
warunkowych s zgrupowane wewntrz nawiasów klamrowych i dla zwikszenia
czytelnoci zastosowano dla nich wcicie. W C i wielu innych jzykach programowania sowo kluczowe then jest domniemane i tym samym pomijane, wic pominlimy je równie w pseudokodzie.
Oczywicie, istniej jzyki, których skadnia wymaga zastosowania sowa kluczowego then — przykadami mog tu by BASIC, Fortran, a nawet Pascal. Tego
typu rónice syntaktyczne midzy jzykami programowania s niemal nieistotne,
jako e podstawowa struktura jest taka sama. Gdy programista zrozumie zaoenia
tych jzyków, nauczenie si rónych odmian syntaktycznych jest stosunkowo atwe.
Poniewa w dalszych rozdziaach bdziemy programowali w jzyku C, prezentowany pseudokod bdzie przypomina ten jzyk. Naley jednak pamita, e moe
on przyjmowa róne postaci.
Inn powszechn regu skadni podobnej do C jest sytuacja, w której zestaw
instrukcji umieszczonych w nawiasach klamrowych zawiera tylko jedn instrukcj.
Dla zwikszenia czytelnoci wci warto dla takiej instrukcji zastosowa wcicie,
ale nie jest ono wymagane. Zgodnie z t regu mona wic napisa wczeniej przedstawiony opis dojazdu tak, aby uzyska równowany fragment pseudokodu:
Jed w dó Alej Kociuszki;
Jeeli (ulica zablokowana)
{
Skr w prawo w ul. Moniuszki;
Skr w lewo w ul. Prusa;
Skr w prawo w ul. Orzeszkowej;
}
else
Skr w prawo w ul. Orzeszkowej;
Regua mówica o zestawach instrukcji dotyczy wszystkich struktur sterujcych
opisywanych w niniejszej ksice, a sam regu równie mona zapisa w pseudokodzie.
Programowanie
23
If (w zestawie instrukcji znajduje si tylko jedna instrukcja)
Uycie nawiasów klamrowych do zgrupowania instrukcji jest opcjonalne;
Else
{
Uycie nawiasów klamrowych jest niezbdne;
Poniewa musi istnie logiczny sposób zgrupowania tych instrukcji;
}
Nawet opis skadni mona potraktowa jak program. Istnieje wiele odmian struktury if-then-else, np. instrukcje select i case, ale logika pozostaje taka sama: jeeli
stanie si tak, zrób to i to, w przeciwnym przypadku wykonaj, co poniej (a poniej
mog si znajdowa kolejne instrukcje if-then).
0x232
Ptle While/Until
Innym podstawowym pojciem programistycznym jest struktura sterujca while,
bdca rodzajem ptli. Programista czsto chce wykona zestaw instrukcji kilka razy.
Program moe wykona to zadanie, stosujc ptl, ale wymaga to okrelenia warunków jej zakoczenia, aby nie trwaa w nieskoczono. Ptla while wykonuje podany
zestaw instrukcji, dopóki warunek jest prawdziwy. Prosty program dla godnej
myszy mógby wyglda nastpujco:
While (jeste godna)
{
Znajd jakie jedzenie;
Zjedz jedzenie;
}
Te dwa zestawy instrukcji za instrukcj while bd powtarzane, dopóki mysz
bdzie godna. Ilo poywienia odnalezionego przez mysz przy kadej próbie moe
waha si od maego okruszka po cay bochen chleba. Podobnie liczba wykona
zestawu instrukcji w ptli while zaley od iloci poywienia znalezionego przez mysz.
Odmian ptli while jest ptla until, wykorzystywana w Perlu (w C skadnia
ta nie jest uywana). Ptla until jest po prostu ptl while z odwróconym warunkiem.
Ten sam program dla myszy zapisany z uyciem ptli until wygldaby nastpujco:
Until (nie jeste godna)
{
Znajd jakie jedzenie;
Zjedz jedzenie;
}
Logicznie kada instrukcja podobna do until moe by przeksztacona w ptl
while. Opis dojazdu zawiera zdanie: „Jed ni do momentu, kiedy zobaczysz po
prawej stronie koció”. Moemy to w prosty sposób przeksztaci w standardow
ptl while — wystarczy, e odwrócimy warunek:
24
0x200
While (nie ma kocioa po prawej stronie)
Jed Alej Kociuszki;
0x233
Ptle for
Kolejn struktur sterujc jest ptla for, wykorzystywana wtedy, gdy programista
chce, aby ptla wykonaa okrelon liczb iteracji. Instrukcja dojazdu: „Jed ni
jakie 8 kilometrów” przeksztacona do ptli for mogaby wyglda nastpujco:
For (8 iteracji)
Jed prosto 1 kilometr;
W rzeczywistoci ptla for jest po prostu ptl while z licznikiem. Powysz
instrukcj mona równie dobrze zapisa tak:
Ustaw licznik na 0
While (licznik jest mniejszy od 8)
{
Jed prosto 1 kilometr;
Dodaj 1 do licznika;
}
Skadnia pseudokodu dla ptli for sprawia, e jest to jeszcze bardziej widoczne:
For (i=0; i<8; i++)
Jed prosto 1 kilometr;
W tym przypadku licznik nosi nazw i, a instrukcja for jest podzielona na trzy
sekcje oddzielone rednikami. Pierwsza sekcja deklaruje licznik i ustawia jego pocztkow warto — w tym przypadku 0. Druga sekcja przypomina instrukcj while
z licznikiem — dopóki licznik spenia podany warunek, kontynuuj ptl. Trzecia
i ostatnia sekcja opisuje, co ma si dzia z licznikiem przy kadej iteracji. W tym
przypadku i++ jest skróconym sposobem na powiedzenie: „Dodaj 1 do licznika
o nazwie i”.
Zastosowanie wszystkich omówionych struktur sterujcych umoliwia przeksztacenie opisu dojazdu ze strony 6 w poniszy pseudokod przypominajcy jzyk C:
Rozpocznij jazd na wschód Alej Kociuszki;
While (po prawej stronie nie ma kocioa)
Jed Alej Kociuszki;
If (ulica zablokowana)
{
Skr w prawo w ul. Moniuszki);
Skr w lewo w ul. Prusa);
Skr w prawo w ul. Orzeszkowej);
}
Programowanie
25
Else
Skr(prawo, ul. Orzeszkowej);
Skr(lewo, ul. Docelowa);
For (i=0; i<8; i++)
{
Jed prosto 1 kilometr;
}
Zatrzymaj si przy ul. Docelowej 743;
0x240 Podstawowe pojcia programistyczne
W kolejnych punktach zostan przedstawione podstawowe pojcia programistyczne.
S one wykorzystywane w wielu jzykach programowania, róni si jedynie pod
wzgldem syntaktycznym. Podczas prezentacji bd integrowane z przykadami
w pseudokodzie przypominajcym skadni jzyka C. Na kocu pseudokod bdzie
bardzo przypomina kod napisany w jzyku C.
0x241
Zmienne
Licznik wykorzystywany w ptli for jest rodzajem zmiennej. O zmiennej moemy
myle jak o obiekcie przechowujcym dane, które mog by zmieniane — std
nazwa. Istniej równie zmienne, których wartoci nie s zmieniane, i trafnie nazywa
si je staymi. W przykadzie motoryzacyjnym jako zmienn moemy potraktowa
prdko samochodu, natomiast kolor lakieru samochodu byby sta. W pseudokodzie zmienne s po prostu pojciami abstrakcyjnymi, natomiast w C (i wielu innych jzykach) zmienne przed wykorzystaniem musz by zadeklarowane i naley
okreli ich typ. Jest tak dlatego, e program napisany w C bdzie kompilowany
do postaci programu wykonywalnego. Podobnie jak przepis kucharski, w którym
przed faktycznymi instrukcjami wymieniane s wszystkie potrzebne skadniki, deklaracje zmiennych pozwalaj dokona przygotowa przed przejciem do faktycznej
treci programu. Ostatecznie wszystkie zmienne s przechowywane gdzie w pamici,
a ich deklaracje pozwalaj kompilatorowi wydajniej uporzdkowa pami. Jednak
na koniec, mimo wszystkich deklaracji typów zmiennych, wszystko odbywa si
w pamici.
W jzyku C dla kadej zmiennej okrelany jest typ, opisujcy informacje, które
bd przechowywane w tej zmiennej. Do najczciej stosowanych typów zaliczamy int (liczby cakowite), float (liczby zmiennoprzecinkowe) i char (pojedyncze
znaki). Zmienne s deklarowane poprzez uycie tych sów kluczowych przed nazw
zmiennej, co zobrazowano na poniszym przykadzie.
int a, b;
float k;
char z;
Zmienne a i b s zadeklarowane jako liczby cakowite, k moe przyjmowa wartoci zmiennoprzecinkowe (np. 3,14), a z powinna przechowywa warto znakow,
26
0x200
tak jak A lub w. Wartoci mona przypisywa zmiennym podczas deklaracji lub
w dowolnym póniejszym momencie. Suy do tego operator =.
int a = 13, b;
float k;
char z = 'A';
k = 3.14;
z = 'w';
b = a + 5;
Po wykonaniu powyszych instrukcji zmienna a bdzie posiadaa warto 13,
zmienna k bdzie zawieraa liczb 3,14, a zmienna b bdzie miaa warto 18, poniewa 13 plus 5 równa si 18. Zmienne s po prostu sposobem pamitania wartoci,
jednak w jzyku C naley najpierw zadeklarowa typ kadej zmiennej.
0x242
Operatory arytmetyczne
Instrukcja b = a + 7 jest przykadem bardzo prostego operatora arytmetycznego.
W jzyku C dla rónych operacji matematycznych wykorzystywane s ponisze
symbole.
Pierwsze cztery operatory powinny wyglda znajomo. Redukcja modulo moe
stanowi nowe pojcie, ale jest to po prostu reszta z dzielenia. Jeeli a ma warto
13, to 13 podzielone przez 5 równa si 2, a reszta wynosi 3, co oznacza, e a % 5 = 3.
Ponadto skoro zmienne a i b s liczbami cakowitymi, po wykonaniu instrukcji
b = a / 5, zmienna b bdzie miaa warto 2, poniewa jest to cz wyniku bdca
liczb cakowit. Do przechowania bardziej poprawnego wyniku, czyli 2,6, musi
zosta uyta zmienna typu float (zmiennoprzecinkowa).
Operacja
Symbol
Przykad
Dodawanie
+
b = a + 5
Odejmowanie
-
b = a - 5
Mnoenie
*
b = a * 5
Dzielenie
/
b = a / 5
Redukcja modulo
%
b = a % 5
Aby program korzysta z tych zaoe, musimy mówi w jego jzyku. Jzyk C
dostarcza kilku skróconych form zapisu tych operacji arytmetycznych. Jedna z nich
zostaa przedstawiona wczeniej i jest czsto wykorzystywana w ptlach for.
Pene wyraenie
Skrót
Wyjanienie
i = i + 1
i++ lub ++i
Dodaje 1 do wartoci zmiennej.
i = i - 1
i-- lub i--
Odejmuje 1 od wartoci zmiennej.
Programowanie
27
Te skrócone wyraenia mog by czone z innymi operacjami arytmetycznymi
w celu utworzenia bardziej zoonych wyrae. Wówczas staje si widoczna rónica
midzy i++ i ++i. Pierwsze wyraenie oznacza: „Zwiksz warto i o 1 po przeprowadzeniu operacji arytmetycznej”, natomiast drugie: „Zwiksz warto i o 1 przed
przeprowadzeniem operacji arytmetycznej”. Poniszy przykad pozwoli to lepiej
zrozumie.
int a, b;
a = 5
b = a++ * 6;
Po wykonaniu tego zestawu instrukcji zmienna b bdzie miaa warto 30, natomiast zmienna a bdzie miaa warto 6, poniewa skrócony zapis b = a++ * 6 jest
równowany poniszym instrukcjom.
b = a * 6;
a = a + 1;
Jeeli jednak zostanie uyta instrukcja b = ++a * 6, kolejno dodawania zostanie
zmieniona i otrzymamy odpowiednik poniszych instrukcji.
a = a + 1;
b = a * 6;
Poniewa kolejno wykonywania dziaa zmienia si, w powyszym przypadku zmienna b bdzie miaa warto 36, natomiast warto zmiennej a wci bdzie
wynosia 6.
Czsto w programach musimy zmodyfikowa zmienn w danym miejscu. Przykadowo moemy doda dowoln warto, tak jak 12, i przechowa wynik w tej
samej zmiennej (np. i = i + 12) Zdarza si to tak czsto, e równie utworzono
skrócon form tej instrukcji.
Pene wyraenie
Skrót
Wyjanienie
i = i + 12
i+=12
Dodaje okrelon warto do wartoci zmiennej.
i = i - 12
i-=12
Odejmuje okrelon warto od wartoci zmiennej.
i = i * 12
i*=12
Mnoy okrelon warto przez warto zmiennej.
i = i / 12
i/=12
Dzieli warto zmiennej przez okrelon warto.
0x243
Operatory porównania
Zmienne s czsto wykorzystywane w instrukcjach warunkowych wczeniej opisanych
struktur sterujcych, a te instrukcje warunkowe opieraj si na jakiego rodzaju
porównaniu. W jzyku C operatory porównania wykorzystuj skrócon skadni,
która jest stosunkowo powszechna w innych jzykach programowania.
28
0x200
Warunek
Symbol
Przykad
Mniejsze ni
<
(a < b)
Wiksze ni
>
(a < b)
Mniejsze lub równe
<=
(a <= b)
Wiksze lub równe
>=
(a >= b)
Równe
==
(a == b)
Nie równa si
!=
(a != b)
Wikszo z tych operatorów jest zrozumiaa, jednake naley zauway, e
w skrócie porównania równoci wykorzystywane s dwa znaki równoci. Jest to wane
rozrónienie, poniewa podwójny znak równoci jest stosowany do sprawdzenia
równoci, natomiast jeden znak równoci jest uywany do przypisania wartoci
zmiennej. Instrukcja a = 7 oznacza: „Umie w zmiennej a warto 7”, natomiast
a == 7 oznacza: „Sprawd, czy warto zmiennej a wynosi 7”. (Niektóre jzyki programowania, takie jak Pascal, wykorzystuj do przypisywania wartoci zmiennym
nastpujcy znak — := — aby wyeliminowa ryzyko bdnego zrozumienia kodu).
Naley te zwróci uwag, e wykrzyknik zwykle oznacza nie. Ten symbol moe by
wykorzystany do odwrócenia dowolnego wyraenia.
!(a < b)
jest równowane
(a >= b)
Operatory porównania mog by czone ze sob za pomoc skrótów dla operatorów logicznych OR i AND.
Operator logiczny
Symbol
Przykad
OR
||
((a < b) || (a < c))
AND
&&
((a < b) && !(a < c))
Przykadowa instrukcja zawierajca dwa mniejsze warunki poczone operatorem OR zostanie oszacowana jako prawdziwa, jeeli warto zmiennej a bdzie mniejsza od wartoci zmiennej b LUB warto zmiennej a bdzie mniejsza od wartoci
zmiennej c. Podobnie przykadowa instrukcja zawierajca dwa mniejsze warunki
poczone operatorem AND zostanie oszacowana jako prawdziwa, jeeli warto
zmiennej a bdzie mniejsza od wartoci zmiennej b I warto zmiennej a nie bdzie
mniejsza od wartoci zmiennej c. Takie instrukcje powinny by grupowane za pomoc
nawiasów i mog zawiera wiele rónych odmian.
Wiele rzeczy moemy sprowadzi do zmiennych, operatorów porównania i struktur sterujcych. W przykadzie myszy poszukujcej poywienia gód moemy przedstawi jako zmienn boolowsk o wartoci true lub false (prawda lub fasz). Oczywicie, 1 oznacza true, a 0 — false.
Programowanie
29
While (gód == 1)
{
Szukaj poywienia;
Zjedz poywienie;
}
Oto kolejny skrót czsto wykorzystywany przez programistów i hakerów. Jzyk
C nie zawiera adnych operatorów boolowskich, wic kada warto niezerowa jest
uznawana za true, a instrukcja jest szacowana jako false, gdy zawiera 0. W rzeczywistoci operatory porównania faktycznie zwracaj 1, jeeli porównanie jest spenione, a w przeciwnym przypadku zwracaj 0. Sprawdzenie, czy zmienna gód ma
warto 1, zwróci 1, jeeli gód ma warto 1, lub zwróci 0, jeeli gód ma warto 0.
Poniewa program wykorzystuje tylko te dwa przypadki, mona w ogóle pomin
operator porównania.
While (gód)
{
Szukaj poywienia;
Zjedz poywienie;
}
Sprytniejszy program sterujcy zachowaniem myszy z uyciem wikszej liczby
danych wejciowych to przykad, jak mona czy operatory porównania ze zmiennymi.
While ((gód) && !(kot_obecny))
{
Szukaj poywienia;
If (!(poywienie_znajduje_si_w_puapce_na_myszy;
Zjedz poywienie;
}
W tym przykadzie zaoono, e dostpne s równie zmienne opisujce obecno kota oraz pooenie poywienia z wartociami 1 dla spenienia warunku i 0 dla
przypadku przeciwnego. Wystarczy pamita, e warto niezerowa oznacza prawd,
a warto 0 jest szacowana jako fasz.
0x244
Funkcje
Czasami zdarzaj si zestawy instrukcji, których programista bdzie potrzebowa
kilka razy. Takie instrukcje mona zgrupowa w mniejszym podprogramie, zwanym
funkcj. W innych jzykach funkcje nazywane s take procedurami i podprocedurami. Przykadowo wykonanie skrtu samochodem skada si z kilku mniejszych
instrukcji: wcz odpowiedni kierunkowskaz, zwolnij, sprawd, czy nie nadjedaj
inne samochody, skr kierownic w odpowiednim kierunku itd. Opis trasy z pocztku tego rozdziau zawiera cakiem sporo skrtów, jednake wymienianie wszystkich
30
0x200
drobnych instrukcji przy kadym skrcie byoby mudne (i zmniejszaoby czytelno kodu). W celu zmodyfikowania sposobu dziaania funkcji moemy przesya do
niej zmienne jako argumenty. W tym przypadku do funkcji jest przesyany kierunek
skrtu.
Function Skrt(zmienna_kierunku)
{
Wcz zmienna_kierunku kierunkowskaz;
Zwolnij;
Sprawd, czy nie nadjedaj inne samochody;
while (nadjedaj inne samochody)
{
Zatrzymaj si;
Obserwuj nadjedajce samochody;
}
Obró kierownic w zmienna_kierunku;
while (skrt nie jest ukoczony)
{
if (prdko < 8 km/h)
Przyspiesz;
}
Obró kierownic do pierwotnego pooenia;
Wycz zmienna_kierunku kierunkowskaz;
}
Funkcja ta zawiera wszystkie instrukcje konieczne do wykonania skrtu. Gdy
program, w którym funkcja taka si znajduje, musi wykona skrt, moe po prostu
wywoa t funkcj. Gdy zostaje wywoana, znajdujce si w niej instrukcje s wykonywane z przesanymi argumentami. Nastpnie wykonywanie programu powraca
do momentu za wywoaniem funkcji. Do tej funkcji mona przesa warto lewo
albo prawo, co spowoduje wykonanie skrtu w tym kierunku.
Domylnie w jzyku C funkcje mog zwraca wartoci do elementu wywoujcego. Dla osób znajcych matematyk jest to zupenie zrozumiae. Wyobramy sobie
funkcj obliczajc silni — jest oczywiste, e zwraca wynik.
W jzyku C funkcje nie s oznaczane sowem kluczowym function. Zamiast tego
s deklarowane przez typ danych zwracanej zmiennej. Taki format bardzo przypomina deklaracj zmiennej. Jeeli funkcja w zamierzeniu ma zwraca liczb cakowit (moe to by np. funkcja obliczajca silni jakiej liczby x), jej kod mógby
wyglda nastpujco:
int factorial(int x)
{
int i;
for(i=1; i < x; i++)
x *= i;
return x;
}
Programowanie
31
Funkcja ta jest deklarowana liczb cakowit, poniewa mnoy kad warto
od 1 do x i zwraca wynik, który jest liczb cakowit. Instrukcja return na kocu
funkcji przesya zawarto zmiennej x i koczy wykonywanie funkcji. Funkcj obliczajc silni mona wówczas wykorzysta jak kad zmienn typu integer w gównej
czci kadego programu, który „wie” o tej funkcji:
int a=5, b;
b = factorial(a);
Po ukoczeniu wykonywania tego krótkiego programu zmienna b bdzie miaa
warto 120, poniewa funkcja factorial() zostanie wywoana z argumentem 5
i zwróci 120.
Ponadto w jzyku C kompilator musi „wiedzie” o funkcji, zanim jej uyje. W tym
celu mona po prostu napisa ca funkcj przed póniejszym wykorzystaniem jej
w programie lub skorzysta z prototypu funkcji. Prototyp funkcji jest prostym sposobem poinformowania kompilatora, i powinien spodziewa si funkcji o podanej
nazwie, zwracajcej okrelony typ danych i przyjmujcej argumenty o okrelonym
typie danych. Faktyczny kod funkcji mona umieci pod koniec programu, ale mona
j wykorzysta w dowolnym miejscu, poniewa kompilator bdzie ju wiedzia o jej
istnieniu. Przykadowy prototyp funkcji factorial() mógby wyglda nastpujco:
int factorial(int);
Zwykle prototypy funkcji s umieszczane blisko pocztku programu. W prototypie nie ma potrzeby definiowania nazw zmiennych, poniewa dzieje si to w samej funkcji. Kompilator musi jedynie zna nazw funkcji, typ zwracanych danych
i typy danych argumentów funkcjonalnych.
Jeeli funkcja nie posiada wartoci, któr moe zwróci (np. wczeniej opisana
funkcja skr()), powinna by zadeklarowana jako typ void. Jednake funkcja skr()
nie posiada jeszcze caej funkcjonalnoci wymaganej przez nasz opis dojazdu. Kady
skrt w opisie dojazdu zawiera zarówno kierunek, jak i nazw ulicy. To oznacza, e
funkcja skrcajca powinna przyjmowa dwie zmienne: kierunek skrtu oraz nazw
ulicy, w któr naley skrci. To komplikuje funkcj skrcajc, poniewa przed
wykonaniem skrtu naley zlokalizowa ulic. Bardziej kompletna funkcja skrcajca,
w której wykorzystano skadni podobn do jzyka C, jest zamieszczona w pseudokodzie poniszego listingu.
void skr(zmienna_kierunku, nazwa_docelowej_ulicy)
{
Szukaj tabliczki z nazw ulicy;
nazwa_biecego_skrzyowania = odczytaj tabliczk z nazw ulicy;
while (nazwa_biecego_skrzyowania != nazwa_docelowej_ulicy)
{
Szukaj innej tabliczki z nazw ulicy;
nazwa_biecego_skrzyowania = odczytaj tabliczk z nazw ulicy;
}
32
0x200
Wcz zmienna_kierunku kierunkowskaz;
Zwolnij;
Sprawd, czy nie nadjedaj inne samochody;
while (nadjedaj inne samochody)
{
Zatrzymaj si;
Obserwuj nadjedajce samochody;
}
Obró kierownic w zmienna_kierunku;
while (skrt nie jest ukoczony)
{
if (prdko < 8 km/h)
Przyspiesz;
}
Obró kierownic do pierwotnego pooenia;
Wycz zmienna_kierunku kierunkowskaz;
}
Funkcja ta zawiera sekcj, która wyszukuje odpowiednie skrzyowanie poprzez
odnajdywanie tabliczki z nazw ulicy, odczytywanie z niej nazwy i przechowywanie
jej w zmiennej o nazwie nazwa_biecego_skrzyowania. Wyszukiwanie i odczytywanie tabliczek z nazwami odbywa si do momentu odnalezienia docelowej ulicy.
Wówczas wykonywane s pozostae instrukcje skrtu. Moemy teraz zmieni pseudokod z instrukcjami dojazdu, aby wykorzystywa funkcj skrtu.
Rozpocznij jazd na wschód Alej Kociuszki;
while (po prawej stronie nie ma kocioa)
{
Jed Alej Kociuszki;
}
If (ulica zablokowana)
{
Skr(prawo, ul. Moniuszki);
Skr(lewo, ul. Prusa);
Skr(prawo, ul. Orzeszkowej);
}
else
{
Skr(prawo, ul. Orzeszkowej);
}
Skr(lewo, ul. Docelowa);
For (i=0; i<5; i++)
{
Jed prosto 1 kilometr;
}
Zatrzymaj si przy ul. Docelowej 743;
Funkcje nie s czsto wykorzystywane w pseudokodzie, bo zwykle jest on stosowany przez programistów do zarysowania zaoe programu przed napisaniem
kompilowalnego kodu. Poniewa pseudokod nie musi dziaa, nie trzeba wypisywa
penych funkcji — wystarczy jedynie krótki zapisek: Tu zrób co zoonego. Jednak
Programowanie
33
w jzyku programowania, takim jak C, funkcje s czsto wykorzystywane. Wikszo
prawdziwej uytecznoci C pochodzi ze zbiorów istniejcych funkcji, zwanych
bibliotekami.
0x250 Zaczynamy brudzi sobie rce
Gdy zaznajomilimy si ju troch ze skadni jzyka C i wyjanilimy kilka podstawowych poj programistycznych, przejcie do faktycznego programowania w C
nie jest niczym nadzwyczajnym. Kompilatory jzyka C istniej dla niemal kadego
systemu operacyjnego i architektury procesora, jednak w niniejszej ksice bdziemy
wykorzystywali wycznie system Linux i procesor z rodziny x86. Linux jest darmowym systemem operacyjnym, do którego kady ma dostp, a procesory x86 s
najpopularniejszymi procesorami konsumenckimi na wiecie. Poniewa hacking
wie si gównie z eksperymentowaniem, najlepiej bdzie, jeeli w trakcie lektury
bdziesz dysponowa kompilatorem jzyka C.
Dziki dyskowi LiveCD doczonemu do niniejszej ksiki moesz praktycznie
stosowa przekazywane informacje, jeeli tylko dysponujesz procesorem z rodziny
x86. Wystarczy woy dysk CD do napdu i ponownie uruchomi komputer. Uruchomione zostanie rodowisko linuksowe, które nie narusza istniejcego systemu operacyjnego. W tym rodowisku moesz wypróbowywa przykady z ksiki, a take
przeprowadza wasne eksperymenty.
Przejdmy do rzeczy. Program firstprog.c jest prostym fragmentem kodu w C,
wypisujcym 10 razy „Hello world!”.
Listing 2.1. firstprog.c
#include <stdio.h>
int main()
{
int i;
for(i=0; i < 10; i++) // Wykonaj ptl 10 razy.
{
puts("Hello, world!\n"); // Przeka acuch do wyjcia.
}
return 0; // Poinformuj OS, e program zakoczy si bez bdów.
}
Gówne wykonywanie programu napisanego w C rozpoczyna si w funkcji main().
Tekst znajdujcy si za dwoma ukonikami jest komentarzem ignorowanym przez
kompilator.
Pierwszy wiersz moe wprawia w zakopotanie, ale jest to jedynie skadnia
informujca kompilator, aby doczy nagówki dla standardowej biblioteki wejcia-wyjcia o nazwie stdio. Ten plik nagówkowy jest dodawany do programu
podczas kompilacji. Znajduje si on w /usr/include/stdio.h i definiuje kilka staych
i prototypów funkcji dla odpowiednich funkcji w standardowej bibliotece we-wy.
Poniewa funkcja main() wykorzystuje funkcj printf() ze standardowej biblioteki
34
0x200
we-wy, wymagany jest prototyp funkcji printf() przed jej zastosowaniem. Ten
prototyp funkcji (a take wiele innych) znajduje si w pliku nagówkowym stdio.h.
Sporo moliwoci jzyka C to pochodne jego zdolnoci do rozbudowy oraz wykorzystania bibliotek. Reszta kodu powinna by zrozumiaa i przypomina wczeniej
prezentowany pseudokod. S nawet nawiasy klamrowe, które mona byo pomin.
To, co bdzie si dziao po uruchomieniu tego programu, powinno by oczywiste,
ale skompilujmy go za pomoc GCC, aby si upewni.
GNU Compiler Collection jest darmowym kompilatorem jzyka C, tumaczcym
jzyk C na jzyk maszynowy, zrozumiay dla komputera. W wyniku tumaczenia
powstaje wykonywalny plik binarny, któremu domylnie nadawana jest nazwa a.out.
Czy skompilowany program wykonuje to, co chcielimy?
reader@hacking:~/booksrc $
reader@hacking:~/booksrc $
-rwxr-xr-x 1 reader reader
reader@hacking:~/booksrc $
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
reader@hacking:~/booksrc $
0x251
gcc firstprog.c
ls -l a.out
6621 2007-09-06 22:16 a.out
./a.out
Wikszy obraz
Dobrze, tego wszystkiego mona si nauczy na podstawowym kursie programowania — s to podstawy, ale niezbdne. Wikszo pocztkowych kursów programowania uczy jedynie, jak czyta i pisa w jzyku C. Prosz mnie le nie zrozumie
— pynna znajomo C jest bardzo przydatna i wystarczy do stania si dobrym
programist, ale jest to tylko cz wikszej caoci. Wikszo programistów uczy
si jzyka z góry do dou i nigdy nie ma szerszego obrazu. Hakerzy s tak dobrzy,
poniewa wiedz, jak wszystkie czci tego wikszego obrazu wchodz ze sob
w interakcje. Aby w programowaniu patrze szerzej, wystarczy sobie zda spraw, e
kod w jzyku C bdzie kompilowany. Kod nie moe nic zrobi, dopóki nie zostanie
skompilowany do wykonywalnego pliku binarnego. Mylenie o kodzie ródowym
w C jak o programie jest czstym bdem wykorzystywanym codziennie przez hakerów. Instrukcje w pliku a.out s zapisane w jzyku maszynowym — podstawowym
jzyku zrozumiaym dla procesora. Kompilatory tumacz jzyk kodu C na jzyk
maszynowy rónych architektur procesorów. W tym przypadku procesor pochodzi z rodziny wykorzystujcej architektur x86. Istniej równie architektury procesorów Sparc (uywane w stacjach roboczych Sun) oraz architektura PowerPC
Programowanie
35
(wykorzystywana w macintoshach przed zastosowaniem procesorów Intela). Kada
architektura wymaga innego jzyka maszynowego, wic kompilator jest porednikiem — tumaczy kod w jzyku C na jzyk maszynowy docelowej architektury.
Dopóki skompilowany program dziaa, przecitny programista skupia si wycznie na kodzie ródowym. Jednak haker zdaje sobie spraw, e w rzeczywistoci
wykonywany jest program skompilowany. Majc dogbn wiedz na temat dziaania
procesora, haker moe manipulowa dziaajcymi programami. Widzielimy kod
ródowy naszego pierwszego programu i skompilowalimy go do pliku wykonywalnego dla architektury x86. Ale jak wyglda ten wykonywalny plik binarny? Narzdzia programistyczne GNU zawieraj program o nazwie objdump, który moe by
uyty do badania skompilowanych plików binarnych. Rozpocznijmy od poznania
kodu maszynowego, na który zostaa przetumaczona funkcja main().
reader@hacking:~/booksrc $ objdump -D a.out | grep -A20 main.:
08048374 <main>:
8048374: 55 push %ebp
8048375: 89 e5 mov %esp,%ebp
8048377: 83 ec 08 sub $0x8,%esp
804837a: 83 e4 f0 and $0xfffffff0,%esp
804837d: b8 00 00 00 00 mov $0x0,%eax
8048382: 29 c4 sub %eax,%esp
8048384: c7 45 fc 00 00 00 00 movl $0x0,0xfffffffc(%ebp)
804838b: 83 7d fc 09 cmpl $0x9,0xfffffffc(%ebp)
804838f: 7e 02 jle 8048393 <main+0x1f>
8048391: eb 13 jmp 80483a6 <main+0x32>
8048393: c7 04 24 84 84 04 08 movl $0x8048484,(%esp)
804839a: e8 01 ff ff ff call 80482a0 <printf@plt>
804839f: 8d 45 fc lea 0xfffffffc(%ebp),%eax
80483a2: ff 00 incl (%eax)
80483a4: eb e5 jmp 804838b <main+0x17>
80483a6: c9 leave
80483a7: c3 ret
80483a8: 90 nop
80483a9: 90 nop
80483aa: 90 nop
reader@hacking:~/booksrc $
Program objdump zwróci zbyt wiele wierszy wynikowych, aby rozsdnie si im
przyjrze, wic przekazalimy wyjcie do grep z opcj powodujc wywietlenie
tylko 20 wierszy za wyraeniem regularnym main.:. Kady bajt jest reprezentowany
w notacji szesnastkowej, systemie liczbowym o podstawie 16. System liczbowy, do
którego jestemy najbardziej przyzwyczajeni, wykorzystuje podstaw 10, a przy
liczbie 10 naley umieci dodatkowy symbol. W systemie szesnastkowym stosowane s cyfry od 0 do 9 reprezentujce 0 do 9, ale take litery A do F, które reprezentuj w nim liczby od 10 do 15. Jest to wygodny zapis, poniewa bajt zawiera 8 bitów, z których kady moe mie warto prawda lub fasz. To oznacza, e bajt ma
256 (28) moliwych wartoci, wic kady bajt moe by opisany za pomoc dwóch
cyfr w systemie szesnastkowym.
36
0x200
Liczby szesnastkowe — rozpoczynajc od 0x8048374 po lewej stronie — s adresami pamici. Bity jzyka maszynowego musz by gdzie przechowywane, a tym
„gdzie” jest pami. Pami jest po prostu zbiorem bajtów z tymczasowej przestrzeni
magazynowej, którym przypisano adresy liczbowe.
Podobnie jak rzd domów przy lokalnej ulicy, z których kady posiada wasny
adres, pami moemy traktowa jako rzd bajtów, z których kady ma wasny adres.
Do kadego bajta pamici mona uzyska dostp za pomoc jego adresu, a w tym
przypadku procesor siga do tej czci pamici, aby pobra instrukcje jzyka maszynowego skadajce si na skompilowany program. Starsze procesory Intela z rodziny x86 wykorzystuj 32-bitowy schemat adresacji, natomiast nowsze — 64-bitowy.
Procesory 32-bitowe posiadaj 232 (lub te 4 294 967 296) moliwych adresów, natomiast procesory 64-bitowe dysponuj iloci adresów równ 264 (1,84467441 × 1019).
Procesory 64-bitowe mog pracowa w trybie kompatybilnoci 32-bitowej, co umoliwia im szybkie wykonywanie kodu 32-bitowego.
Bajty szesnastkowe znajdujce si porodku powyszego listingu s instrukcjami
w jzyku maszynowym procesora x86. Oczywicie, te wartoci szesnastkowe s
jedynie reprezentacjami binarnych jedynek i zer, które rozumie procesor. Ale poniewa 0101010110001001111001011000001111101100111100001… przydaje si
tylko procesorowi, kod maszynowy jest wywietlany jako bajty szesnastkowe, a kada
instrukcja jest umieszczana w osobnym wierszu, co przypomina podzia akapitu
na zdania.
Gdy si zastanowimy, okae si, e bajty szesnastkowe same w sobie te nie s
przydatne — tutaj dochodzi do gosu jzyk asembler. Instrukcje po prawej stronie
s zapisane w asemblerze. Asembler jest po prostu zbiorem mnemoników dla odpowiednich instrukcji jzyka maszynowego. Instrukcja ret jest znacznie atwiejsza
do zapamitania i zrozumienia ni 0xc3 czy te 11000011. W przeciwiestwie do C
i innych jzyków kompilowanych, instrukcje asemblera wystpuj w relacji jedendo-jednego z instrukcjami odpowiednimi jzyka maszynowego. Oznacza to, e skoro
kada architektura procesora posiada wasny zestaw instrukcji jzyka maszynowego,
kada z nich ma równie wasn odmian asemblera. Jzyk asemblera jest dla programistów tylko sposobem reprezentacji instrukcji jzyka maszynowego podawanych procesorowi. Dokadny sposób reprezentacji tych instrukcji maszynowych jest
tylko kwesti konwencji i preferencji. Cho teoretycznie mona stworzy wasn
skadni asemblera dla architektury x86, wikszo osób wykorzystuje jeden z gównych typów — skadni AT&T lub Intel. Kod asemblera przedstawiony na stronie 21
jest zgodny ze skadni AT&T, poniewa niemal wszystkie dezasemblery w Linuksie
domylnie wykorzystuj t skadni. Skadni AT&T mona atwo rozpozna dziki kakofonii symboli % i $ umieszczanych przed wszystkimi elementami (prosz
ponownie spojrze na stron 21). Ten sam kod mona wywietli w skadni Intela,
dodajc do polecenia objdump dodatkow opcj -M:
reader@hacking:~/booksrc $ objdump -M intel -D a.out | grep -A20 main.:
08048374 <main>:
8048374: 55 push ebp
8048375: 89 e5 mov ebp,esp
Programowanie
37
8048377: 83 ec 08 sub esp,0x8
804837a: 83 e4 f0 and esp,0xfffffff0
804837d: b8 00 00 00 00 mov eax,0x0
8048382: 29 c4 sub esp,eax
8048384: c7 45 fc 00 00 00 00 mov DWORD PTR [ebp-4],0x0
804838b: 83 7d fc 09 cmp DWORD PTR [ebp-4],0x9
804838f: 7e 02 jle 8048393 <main+0x1f>
8048391: eb 13 jmp 80483a6 <main+0x32>
8048393: c7 04 24 84 84 04 08 mov DWORD PTR [esp],0x8048484
804839a: e8 01 ff ff ff call 80482a0 <printf@plt>
804839f: 8d 45 fc lea eax,[ebp-4]
80483a2: ff 00 inc DWORD PTR [eax]
80483a4: eb e5 jmp 804838b <main+0x17>
80483a6: c9 leave
80483a7: c3 ret
80483a8: 90 nop
80483a9: 90 nop
80483aa: 90 nop
reader@hacking:~/booksrc $
Uwaam, e skadnia Intela jest znacznie bardziej czytelna i atwiejsza do zrozumienia, wic bdzie ona stosowana w niniejszej ksice. Niezalenie od reprezentacji jzyka asemblera, polecenia rozumiane przez procesor s cakiem proste.
Instrukcje te skadaj si z operacji i czasem dodatkowych operacji opisujcych cel
i (lub) ródo operacji. Operacje te przenosz dane w pamici, aby wykona jakiego rodzaju podstawowe dziaania matematyczne, lub przerywaj dziaanie procesora, by wykona co innego. To jest wszystko, co procesor potrafi wykona. Jednak,
podobnie jak za pomoc stosunkowo niewielkiego alfabetu napisano miliony ksiek,
tak samo, korzystajc z niewielkiego zbioru instrukcji maszynowych, mona utworzy
nieskoczenie wiele programów.
Procesory posiadaj równie wasny zestaw specjalnych zmiennych zwanych
rejestrami. Wikszo instrukcji wykorzystuje rejestry do odczytywania lub zapisywania danych, wic ich poznanie jest niezbdne do zrozumienia instrukcji. Duy
obraz wci si powiksza…
0x252
Procesor x86
Procesor 8086 by pierwszym procesorem w architekturze x86. Zosta opracowany
i wyprodukowany przez firm Intel, która póniej wprowadzaa na rynek bardziej
zaawansowane procesory z tej rodziny: 80186, 80286m 80386 i 80486. Jeeli pamitasz, e w latach 80. i 90. ubiegego wieku mówiono o procesorach 386 i 486, chodzio
wówczas o te wanie ukady.
Procesor x86 posiada kilka rejestrów, które s dla niego czym w rodzaju wewntrznych zmiennych. Mógbym teraz przeprowadzi abstrakcyjny wykad na temat
rejestrów, ale uwaam, e lepiej zobaczy co na wasne oczy. Narzdzia programistyczne GNU zawieraj równie debuger o nazwie GDB. Debugery s uywane
przez programistów do wykonywania skompilowanych programów krok po kroku,
badania pamici programu i przegldania rejestrów procesora. Programista, który
38
0x200
nigdy nie korzysta z debugera do przyjrzenia si wewntrznym mechanizmom
pracy programu, jest jak siedemnastowieczny badacz, który nigdy nie uywa mikroskopu. Debuger, tak jak mikroskop, pozwala hakerowi na precyzyjne przyjrzenie si kodowi maszynowemu, ale moliwoci debugera wykraczaj daleko poza t
metafor. W przeciwiestwie do mikroskopu, debuger umoliwia obejrzenie wykonywania programu ze wszystkich stron, wstrzymanie go oraz zmian wszelkich
parametrów.
Poniej GDB zosta uyty do wywietlenia stanu rejestrów procesora tu przed
rozpoczciem programu:
reader@hacking:~/booksrc $ gdb -q ./a.out
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) break main
Breakpoint 1 at 0x804837a
(gdb) run
Starting program: /home/reader/booksrc/a.out
Breakpoint 1, 0x0804837a in main ()
(gdb) info registers
eax 0xbffff894 -1073743724
ecx 0x48e0fe81 1222704769
edx 0x1 1
ebx 0xb7fd6ff4 -1208127500
esp 0xbffff800 0xbffff800
ebp 0xbffff808 0xbffff808
esi 0xb8000ce0 -1207956256
edi 0x0 0
eip 0x804837a 0x804837a <main+6>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) quit
The program is running. Exit anyway? (y or n) y
reader@hacking:~/booksrc $
Punkt wstrzymania zosta ustawiony przy funkcji main(), wic wykonywanie
programu zostanie zatrzymane tu przed uruchomieniem naszego kodu. Nastpnie
GDB uruchamia program, zatrzymuje si w punkcie wstrzymania i wywietla wszystkie rejestry procesora i ich biecy stan.
Pierwsze cztery rejestry (EAX, ECX, EDX i EBX) s rejestrami ogólnego przeznaczenia. S to odpowiednio rejestry: akumulatorowy, licznika, danych i bazowy.
S one wykorzystywane do rónych celów, ale su gównie jako zmienne tymczasowe dla procesora, gdy wykonuje instrukcje kodu maszynowego.
Programowanie
39
Kolejne cztery rejestry (ESP, EBP, ESI i EDI) s równie rejestrami ogólnego
przeznaczenia, ale czasem nazywa si je wskanikami i indeksami. S to odpowiednio:
wskanik, wskanik bazowy, ródo i przeznaczenie. Pierwsze dwa rejestry s nazywane wskanikami, poniewa przechowuj 32-bitowe adresy, wskazujce do miejsc
w pamici. Te rejestry s istotne dla wykonywania programu i zarzdzania pamici.
Póniej omówi je dokadniej. Kolejne dwa rejestry, z technicznego punktu widzenia
równie wskanikowe, czsto wykorzystywane s do wskazywania róda i celu, gdy
dane musz by odczytane lub zapisane. Istniej instrukcje odczytujce i zapisujce,
które uywaj tych rejestrów, ale przewanie mona je traktowa jak zwyke rejestry
ogólnego przeznaczenia.
Rejestr EIP jest rejestrem wskanika instrukcji, wskazujcym aktualnie wykonywan instrukcj. Tak jak dziecko podczas czytania pokazuje sobie palcem poszczególne sowa, tak procesor odczytuje konkretne instrukcje, korzystajc z EIP jak z palca.
Oczywicie, rejestr ten jest bardzo istotny i bdzie czsto wykorzystywany podczas
debugowania. Aktualnie wskazuje adres pamici 0x804838a.
Pozostay rejestr, EFLAGS, zawiera kilka flag bitowych wykorzystywanych do
porówna oraz segmentacji pamici. Pami jest dzielona na kilka rónych segmentów, co opisz póniej, a rejestr ten pozwala je ledzi. Przewanie moemy je
zignorowa, poniewa rzadko konieczny jest bezporedni dostp do nich.
0x253
Jzyk asembler
Poniewa w niniejszej ksice wykorzystujemy skadni Intela w jzyku asembler,
musimy odpowiednio skonfigurowa narzdzia. W GDB skadni dezasemblera
mona ustawi na intelowsk, wpisujc po prostu set disassembly intel lub w skrócie
set dis intel. Moemy skonfigurowa GDB tak, aby to ustawienie byo aktywne
przy kadym uruchomieniu, umieszczajc to polecenie w pliku .gdbinit w katalogu
domowym.
reader@hacking:~/booksrc
(gdb) set dis intel
(gdb) quit
reader@hacking:~/booksrc
reader@hacking:~/booksrc
set dis intel
reader@hacking:~/booksrc
$ gdb -q
$ echo "set dis intel" > ~/.gdbinit
$ cat ~/.gdbinit
$
Po skonfigurowaniu GDB tak, eby wykorzystywa skadni Intela, zapoznajmy
si z ni. W skadni Intela instrukcje asemblera maj zwykle ponisz posta:
operacja <cel>, <ródo>
Wartociami docelowymi i ródowymi s rejestr, adres pamici lub warto.
Operacje s zwykle intuicyjnie rozumianymi mnemonikami. Operacja mov przenosi
warto ze róda do celu, sub odejmuje, inc inkrementuje itd. Przykadowo ponisze
instrukcje przenosz warto z ESP do EBP, a nastpnie odejmuj 8 od ESP (zapisujc wynik w ESP).
40
0x200
8048375: 89 e5 mov ebp,esp
8048377: 83 ec 08 sub esp,0x8
Dostpne s równie operacje wykorzystywane do sterowanie przebiegiem
wykonywania. Operacja cmp suy do porównywania wartoci, a niemal wszystkie
operacje, których nazwa rozpoczyna si liter j, su do przeskakiwania do innej
czci kodu (w zalenoci od wyniku porównania). W poniszym przykadzie najpierw
4-bajtowa warto znajdujca w ERB minus 4 jest porównywana z liczb 9. Nastpna
instrukcja jest skrótem od przeskocz, jeeli mniejsze lub równe ni (ang. jump if less
than or equal to), odnoszcym si do wyniku wczeniejszego porównania. Jeeli
warto ta jest mniejsza lub równa 9, wykonywanie przeskoczy do instrukcji znajdujcej si pod adresem 0x8048393. W przeciwnym przypadku wykonywana bdzie
kolejna instrukcja z bezwarunkowym przeskokiem. Jeeli warto nie bdzie mniejsza
ani równa 9, procesor przeskoczy do 0x80483a6.
804838b: 83 7d fc 09 cmp DWORD PTR [ebp-4],0x9
804838f: 7e 02 jle 8048393 <main+0x1f>
8048391: eb 13 jmp 80483a6 <main+0x32>
Przykady te pochodz z naszej poprzedniej dezasemblacji. Skonfigurowalimy
debuger do wykorzystywania skadni Intela, wic wykorzystajmy go do kroczenia
przez nasz pierwszy program na poziomie instrukcji asemblera.
W kompilatorze GCC mona poda opcj –g, dziki czemu zostan doczone
dodatkowe informacje debugowania, co da GDB dostp do kodu ródowego.
reader@hacking:~/booksrc $ gcc -g firstprog.c
reader@hacking:~/booksrc $ ls -l a.out
-rwxr-xr-x 1 matrix users 11977 Jul 4 17:29 a.out
reader@hacking:~/booksrc $ gdb -q ./a.out
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) list
1 #include <stdio.h>
2
3 int main()
4 {
5 int i;
6 for(i=0; i < 10; i++)
7 {
8 printf("Hello, world!\n");
9 }
10 }
(gdb) disassemble main
Dump of assembler code for function main():
0x08048384 <main+0>: push ebp
0x08048385 <main+1>: mov ebp,esp
0x08048387 <main+3>: sub esp,0x8
0x0804838a <main+6>: and esp,0xfffffff0
0x0804838d <main+9>: mov eax,0x0
0x08048392 <main+14>: sub esp,eax
Programowanie
41
0x08048394 <main+16>: mov DWORD PTR [ebp-4],0x0
0x0804839b <main+23>: cmp DWORD PTR [ebp-4],0x9
0x0804839f <main+27>: jle 0x80483a3 <main+31>
0x080483a1 <main+29>: jmp 0x80483b6 <main+50>
0x080483a3 <main+31>: mov DWORD PTR [esp],0x80484d4
0x080483aa <main+38>: call 0x80482a8 <_init+56>
0x080483af <main+43>: lea eax,[ebp-4]
0x080483b2 <main+46>: inc DWORD PTR [eax]
0x080483b4 <main+48>: jmp 0x804839b <main+23>
0x080483b6 <main+50>: leave
0x080483b7 <main+51>: ret
End of assembler dump.
(gdb) break main
Breakpoint 1 at 0x8048394: file firstprog.c, line 6.
(gdb) run
Starting program: /hacking/a.out
Breakpoint 1, main() at firstprog.c:6
6 for(i=0; i < 10; i++)
(gdb) info register eip
eip 0x8048394 0x8048394
(gdb)
Najpierw wypisywany jest kod ródowy oraz dezasemblacja funkcji main().
Nastpnie na pocztku main() ustawiany jest punkt wstrzymania i program jest
uruchamiany. Ten punkt wstrzymania po prostu informuje debuger, aby wstrzyma
wykonywanie programu, gdy dojdzie do tego punktu. Poniewa punkt wstrzymania
zosta ustawiony na pocztku funkcji main(), program dociera do punktu wstrzymania i zatrzymuje si przed wykonaniem jakichkolwiek instrukcji z funkcji main().
Nastpnie wywietlana jest warto EIP (wskanika instrukcji).
Naley zwróci uwag, e EIP zawiera adres pamici, wskazujcy instrukcj
w dezasemblacji funkcji main() (pogrubionej na listingu). Wczeniejsze instrukcje
(zapisane kursyw), razem nazywane prologiem funkcji, s generowane przez kompilator w celu ustawienia pamici dla reszty zmiennych lokalnych funkcji main().
Jednym z powodów koniecznoci deklaracji zmiennych w jzyku C jest pomoc
w konstrukcji tej czci kodu. Debuger „wie”, e ta cz kodu jest generowana
automatycznie i potrafi j pomin. Prologiem funkcji zajmiemy si póniej, a na
razie, wzorujc si na GDB, pominiemy go.
Debuger GDB zawiera bezporedni metod badania pamici za pomoc polecenia x, które jest skrótem od examine. Badanie pamici jest bardzo wan umiejtnoci kadego hakera. Wikszo technik hakerskich przypomina sztuczki magiczne
— wydaj si niesamowite i magiczne, dopóki nie poznasz oszustwa. Zarówno
w magii, jak i hakerstwie, jeeli popatrzymy w odpowiednie miejsce, sztuczka stanie
si oczywista. Jest to jeden z powodów, dla których dobry magik nigdy nie wykonuje
dwa razy tego samego triku w czasie jednego wystpu. Jednak za pomoc debugera,
takiego jak GDB, moemy dokadnie zbada kady aspekt wykonywania programu,
wstrzyma go, przej krok dalej i powtórzy tyle razy, ile potrzeba. Poniewa
dziaajcy program to w wikszoci procesor i segmenty pamici, zbadanie pamici
jest pierwsz z metod przekonania si, co naprawd si dzieje.
42
0x200
Polecenie examine w GDB moe zosta uyte do przyjrzenia si okrelonym adresom pamici na róne sposoby. To polecenie oczekuje dwóch argumentów: miejsca
w pamici, które ma by zbadane, oraz okrelenia sposobu wywietlania zawartoci.
Do okrelania formatu wywietlania równie uywany jest jednoliterowy skrót,
który mona poprzedzi liczb elementów do zbadania. Najczciej wykorzystywane
s ponisze skróty formatujce:
o — wywietla w formacie ósemkowym,
x — wywietla w formacie szesnastkowym,
u — wywietla w standardowym systemie dziesitnym, bez znaków,
t — wywietla w formacie binarnym.
Liter tych mona uy z poleceniem examine do zbadania okrelonego adresu
pamici. W poniszym przykadzie wykorzystano biecy adres z rejestru EIP.
Skrócone polecenia s czsto wykorzystywane w GDB i nawet info register eip
mona skróci do i r eip.
(gdb) i r
eip
(gdb) x/o
0x8048384
(gdb) x/x
0x8048384
(gdb) x/u
0x8048384
(gdb) x/t
0x8048384
(gdb)
eip
0x8048384
0x8048384
<main+16>:
$eip
<main+16>:
$eip
<main+16>:
$eip
<main+16>:
0x8048384 <main+16>
077042707
0x00fc45c7
16532935
00000000111111000100010111000111
Pami, któr wskazuje rejestr EIP, moe by zbadana za pomoc adresu przechowywanego w EIP. Debuger umoliwia bezporednie odwoywanie si do rejestrów, wic $eip jest równowane wartoci przechowywanej aktualnie w EIP. Warto
077042707 w systemie ósemkowym jest tym samym, co 0x00fc45c7 w systemie szesnastkowym, co jest tym samym, co 16532935 w systemie dziesitnym, co z kolei jest
tym samym, co 00000000111111000100010111000111 w systemie dwójkowym.
Format polecenia examine mona równie poprzedzi cyfr, okrelajc liczb
badanych jednostek w docelowym adresie.
(gdb) x/2x $eip
0x8048384 <main+16>:
(gdb) x/12x $eip
0x8048384 <main+16>:
0x8048394 <main+32>:
0x80483a4 <main+48>:
(gdb)
0x00fc45c7 0x83000000
0x00fc45c7 0x83000000 0x7e09fc7d 0xc713eb02
0x84842404 0x01e80804 0x8dffffff 0x00fffc45
0xc3c9e5eb 0x90909090 0x90909090 0x5de58955
Programowanie
43
Domylnym rozmiarem jednej jednostki jest 4-bajtowa jednostka zwana sowem.
Rozmiar jednostek wywietlania dla polecenia examine moe by zmieniany poprzez
dodanie odpowiedniej litery za liter formatujc. Poprawnymi literami rozmiaru s:
b — pojedynczy bajt,
h — pósowo o rozmiarze dwóch bajtów,
w — sowo o rozmiarze czterech bajtów,
g — sowo podwójne o rozmiarze omiu bajtów.
Wprowadza to pewien zamt, poniewa czasami termin sowo okrela równie
wartoci 2-bajtowe. W takim przypadku podwójne sowo lub DWORD oznacza warto 4-bajtow. W ksice tej terminy „sowo” i DWORD bd okrelay wartoci
4-bajtowe. Do okrelania wartoci 2-bajtowych bdzie uywany termin „pósowo”.
Ponisze wyjcie z GDB obrazuje pami wywietlan w rónych rozmiarach.
(gdb) x/8xb $eip
0x8048384 <main+16>:
(gdb) x/8xh $eip
0x8048384 <main+16>:
(gdb) x/8xw $eip
0x8048384 <main+16>:
0x8048394 <main+32>:
(gdb)
0xc7
0x45
0xfc
0x00
0x00
0x00
0x00
0x83
0x45c7 0x00fc 0x0000 0x8300 0xfc7d 0x7e09 0xeb02 0xc713
0x00fc45c7
0x84842404
0x83000000
0x01e80804
0x7e09fc7d
0x8dffffff
0xc713eb02
0x00fffc45
Jeeli dobrze si przyjrzysz, zauwaysz w powyszych danych co dziwnego.
Pierwsze polecenie examine pokazuje pierwsze osiem bajtów i, oczywicie, polecenia examine wykorzystujce wiksze jednostki wywietlaj wicej danych. Jednake
wynik pierwszego polecenia pokazuje, e pierwszymi dwoma bajtami s 0xc7 i 0x45,
ale gdy badane jest pósowo w dokadnie tym samym adresie pamici, pokazywana
jest warto 0x45c7, czyli bajty s odwrócone. Ten sam efekt moemy zauway
w przypadku penego 4-bajtowego sowa wywietlanego jako 0x00fc45c7. Jeeli
jednak pierwsze cztery bajty s wywietlane bajt po bajcie maj one kolejno 0xc7,
0x45m 0xfc i 0x00.
Jest tak, poniewa w procesorze x86 wartoci s przechowywane w kolejnoci
little endian, co oznacza, e najmniej istotny bajt jest przechowywany jako pierwszy.
Jeeli np. cztery bajty maj by interpretowane jako jedna warto, bajty musz by
uyte w odwrotnej kolejnoci. Debuger GDB jest na tyle mdry, e wie, jak przechowywane s wartoci, wic gdy badane jest sowo lub pósowo, bajty musz by
odwrócone w celu wywietlenia poprawnych wartoci w ukadzie szesnastkowym.
Przejrzenie tych wywietlanych wartoci w systemie szesnastkowym i jako liczb
dziesitnych bez znaku moe uatwi zrozumienie tej kwestii.
(gdb) x/4xb $eip
0x8048384 <main+16>:
(gdb) x/4ub $eip
0x8048384 <main+16>:
(gdb) x/1xw $eip
44
0x200
0xc7
0x45
0xfc
0x00
199
69
252
0
0x8048384 <main+16>:
0x00fc45c7
(gdb) x/1uw $eip
0x8048384 <main+16>:
16532935
(gdb) quit
The program is running. Exit anyway? (y or n) y
reader@hacking:~/booksrc $ bc -ql
199*(256^3) + 69*(256^2) + 252*(256^1) + 0*(256^0)
3343252480
0*(256^3) + 252*(256^2) + 69*(256^1) + 199*(256^0)
16532935
quit
reader@hacking:~/booksrc $
Pierwsze cztery bajty s pokazane zarówno w notacji szesnastkowej, jak i standardowej, dziesitnej. Kalkulator dziaajcy w wierszu polece bc posuy do wykazania, e jeeli bajty zostan zinterpretowane w nieprawidowej kolejnoci, otrzymamy w wyniku zupenie nieprawidow warto 3343252480. Kolejno bajtów
w danej architekturze jest istotnym czynnikiem, którego naley by wiadomym.
Cho wikszo narzdzi debugujcych i kompilatorów zajmuje si automatycznie
szczegóami zwizanymi z kolejnoci bajtów, czasem bdziesz musia samodzielnie
manipulowa pamici.
Oprócz konwersji kolejnoci bajtów, GDB moe za pomoc polecenia examine
wykonywa inne konwersje. Widzielimy ju, e GDB moe dezasemblowa instrukcje jzyka maszynowego na zrozumiae dla czowieka instrukcje asemblera. Polecenie examine przyjmuje równie liter formatujc i (skrót od instruction), co powoduje wywietlenie pamici jako dezasemblowanych instrukcji jzyka asembler.
reader@hacking:~/booksrc $ gdb -q ./a.out
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) break main
Breakpoint 1 at 0x8048384: file firstprog.c, line 6.
(gdb) run
Starting program: /home/reader/booksrc/a.out
Breakpoint 1, main () at firstprog.c:6
6
for(i=0; i < 10; i++)
(gdb) i r $eip
eip
0x8048384
0x8048384 <main+16>
(gdb) x/i $eip
0x8048384 <main+16>: mov DWORD PTR [ebp-4],0x0
(gdb) x/3i $eip
0x8048384 <main+16>: mov DWORD PTR [ebp-4],0x0
0x804838b <main+23>: cmp DWORD PTR [ebp-4],0x9
0x804838f <main+27>: jle 0x8048393 <main+31>
(gdb) x/7xb $eip
0x8048384 <main+16>: 0xc7 0x45 0xfc 0x00 0x00
(gdb) x/i $eip
0x8048384 <main+16>: mov DWORD PTR [ebp-4],0x0
(gdb)
0x00
0x00
Programowanie
45
W powyszym przykadzie program a.out zosta uruchomiony w GDB z punktem
wstrzymania ustawionym w main(). Poniewa rejestr EIP wskazuje na pami, która
faktycznie zawiera instrukcje w jzyku maszynowym, adnie poddaj si one procesowi dezasemblacji.
Wczeniejsza dezasemblacja za pomoc objectdump potwierdza, e siedem bajtów,
które wskazuje EIP, to faktycznie jzyk maszynowy dla odpowiedniej instrukcji
asemblera.
8048384:
c7 45 fc 00 00 00 00
mov
DWORD PTR [ebp-4],0x0
Ta instrukcja asemblera przenosi warto 0 do pamici znajdujcej si pod adresem przechowywanym w rejestrze EBP minus 4. To wanie w tym miejscu pamici przechowywana jest zmienna i z jzyka C. Zmienna ta zostaa zadeklarowana
jako liczba cakowita (int), wykorzystujca 4 bajty pamici w przypadku procesora
x86. To polecenie po prostu zeruje zmienn i dla ptli for. Gdybymy teraz zbadali
t pami, zawieraaby ona tylko przypadkowe mieci. Pami w tej lokacji mona
zbada na róne sposoby:
(gdb) i r ebp
ebp
0xbffff808
(gdb) x/4xb $ebp - 4
0xbffff804:
0xc0
0x83
(gdb) x/4xb 0xbffff804
0xbffff804:
0xc0
0x83
(gdb) print $ebp - 4
$1 = (void *) 0xbffff804
(gdb) x/4xb $1
0xbffff804:
0xc0
0x83
(gdb) x/xw $1
0xbffff804:
0x080483c0
(gdb)
0xbffff808
0x04
0x08
0x04
0x08
0x04
0x08
Rejestr EBP zawiera adres 0xbffff808, a instrukcja asemblera bdzie zapisywaa
do adresu o tej wartoci pomniejszonej o 4 — 0xbfffff804. Polecenie examine moe
zbada bezporednio ten adres pamici lub wykona odpowiednie obliczenia w locie.
Za pomoc polecenia print równie mona wykona proste dziaania arytmetyczne,
a wynik zostanie zapisany w tymczasowej zmiennej w debugerze. Zmienna ta nosi
nazw $1 i moe póniej zosta uyta do szybkiego uzyskania dostpu do okrelonego
miejsca w pamici. Kada z przedstawionych wyej metod pozwala uzyska ten
sam rezultat: wywietlenie znalezionych w pamici 4 bajtów mieci, które zostan
wyzerowane po wykonaniu biecej instrukcji.
Uruchommy biec instrukcj, korzystajc z polecenia nexti, bdcego skrótem
dla next instruction (nastpna instrukcja). Procesor odczyta instrukcj z EIP, wykona
j i przestawi EIP na kolejn instrukcj.
46
0x200
(gdb) nexti
0x0804838b
6
for(i=0; i < 10; i++)
(gdb) x/4xb $1
0xbffff804:
0x00 0x00
0x00
0x00
(gdb) x/dw $1
0xbffff804:
0
(gdb) i r eip
eip
0x804838b
0x804838b <main+23>
(gdb) x/i $eip
0x804838b <main+23>:
cmp DWORD PTR [ebp-4],0x9
(gdb)
Zgodnie z oczekiwaniami, poprzednia instrukcja zeruje 4 bajty odnalezione
w adresie EBP minus 4, czyli pamici przydzielonej zmiennej i z kodu C. EIP
przechodzi do kolejnej instrukcji. Omówienie kolejnych instrukcji warto przeprowadzi cznie.
(gdb) x/10i $eip
0x804838b <main+23>:
0x804838f <main+27>:
0x8048391 <main+29>:
0x8048393 <main+31>:
0x804839a <main+38>:
0x804839f <main+43>:
0x80483a2 <main+46>:
0x80483a4 <main+48>:
0x80483a6 <main+50>:
0x80483a7 <main+51>:
(gdb)
cmp
jle
jmp
mov
call
lea
inc
jmp
leave
ret
DWORD PTR [ebp-4],0x9
0x8048393 <main+31>
0x80483a6 <main+50>
DWORD PTR [esp],0x8048484
0x80482a0 <printf@plt>
eax,[ebp-4]
DWORD PTR [eax]
0x804838b <main+23>
Pierwsza instrukcja, cmp, jest instrukcj porównania, która porównuje zawarto
pamici wykorzystywanej przez zmienn i z kodu C z wartoci 9. Kolejna instrukcja,
jle, oznacza przeskocz, jeeli mniejsze lub równe (ang. jump if less than equal to).
Wykorzystuje ona wynik poprzedniego porównania (który jest przechowywany
w rejestrze EFLAGS) do przestawienia EIP tak, aby wskazywa inn cz kodu,
jeeli cel poprzedniej operacji porównania jest mniejszy lub równy ródu. W tym
przypadku instrukcja powoduje przeskok do adresu 0x8048393, jeeli warto
przechowywana w pamici dla zmiennej i jest mniejsza lub równa wartoci 9. W przeciwnym przypadku EIP przejdzie do kolejnej instrukcji, która jest instrukcj przeskoku bezwarunkowego. Powoduje ona przeskok EIP do adresu 0x80483a6. Te trzy
instrukcje cznie tworz struktur sterujc if-then-else: jeeli i jest mniejsze lub
równe 9, przejd do instrukcji pod adresem 0x8048393; w przeciwnym przypadku
przejd do instrukcji pod adresem 0x80483a6. Pierwszy adres, 0x804893 (pogrubiony),
jest po prostu sta instrukcj przeskoku, a drugi adres — 0x80483a6 (zapisany
kursyw) — znajduje si na kocu funkcji.
Poniewa wiemy, e w lokacji pamici porównywanej z 9 przechowywane jest 0
i wiemy, e 0 jest mniejsze lub równe 9, po wykonaniu kolejnych dwóch instrukcji
EIP powinien wskazywa na 0x8048393.
Programowanie
47
(gdb) nexti
0x0804838f
6
(gdb) x/i $eip
0x804838f <main+27>:
(gdb) nexti
8
printf("Hello
(gdb) i r eip
eip
0x8048393
(gdb) x/2i $eip
0x8048393 <main+31>:
0x804839a <main+38>:
(gdb)
for(i=0; i < 10; i++)
jle
0x8048393 <main+31>
world!\n");
0x8048393 <main+31>
mov
call
DWORD PTR [esp],0x8048484
0x80482a0 <printf@plt>
Zgodnie z oczekiwaniami, wczeniejsze dwie instrukcje przeniosy wykonywanie
programu do 0x8048393, co doprowadza do kolejnych dwóch instrukcji. Pierwsza
z nich jest kolejn instrukcj mov, która zapisze adres 0x8048484 do adresu pamici
znajdujcego si w rejestrze ESP. Ale co wskazuje ESP?
(gdb) i r esp
esp
0xbffff800 0xbffff800
(gdb)
W tej chwili ESP wskazuje adres pamici 0xbffff800, wic po wykonaniu instrukcji
mov zostanie tutaj zapisany adres 0x8048484. Ale dlaczego? Co takiego specjalnego
jest w adresie pamici 0x8048484? Jest tylko jeden sposób, aby si o tym przekona.
(gdb) x/2xw 0x8048484
0x8048484:
0x6c6c6548
0x6f57206f
(gdb) x/6xb 0x8048484
0x8048484:
0x48
0x65 0x6c
0x6c 0x6f 0x20
(gdb) x/6ub 0x8048484
0x8048484:
72
101 108
108 111 32
(gdb)
Dowiadczony czytelnik moe zauway w tej pamici co szczególnego, zwaszcza w zakresie bajtów. Po nabraniu dowiadczenia w badaniu pamici tego typu
wzorce stan si bardziej oczywiste. Bajty te zawieraj si w zakresie znaków ASCII.
ASCII jest uzgodnionym standardem, który odwzorowuje wszystkie znaki z klawiatury (a take kilka innych) na stae liczby. Bajty 0x48, 0x65 i 0x6f odpowiadaj literom
alfabetu przedstawionym w tabeli znaków ASCII (tabela 2.1). Tabel mona znale
na stronach podrcznika ASCII dostpnego w wikszoci systemów Unix po wpisaniu man ascii.
Na szczcie, polecenie examine w GDB posiada równie moliwo przyjrzenia
si tego typu pamici. Litera formatujca c umoliwia automatyczne sprawdzenie
bajta w tablicy ASCII, a litera formatujca s powoduje wywietlenie caego acucha
danych znakowych.
48
0x200
Tabela 2.1. Tablica znaków ASCII
Oct
Dec
Hex
Char
Oct
Dec
Hex
Char
000
0
00
NUL '\0'
100
64
40
@
001
1
01
SOH
101
65
41
A
002
2
02
STX
102
66
42
B
003
3
03
ETX
103
67
43
C
004
4
04
EOT
104
68
44
D
005
5
05
ENQ
105
69
45
E
006
6
06
ACK
106
70
46
F
007
7
07
BEL '\a'
107
71
47
G
010
8
08
BS '\b'
110
72
48
H
011
9
09
HT '\t'
111
73
49
I
012
10
0A
LF '\n'
112
74
4A
J
013
11
0B
VT '\v'
113
75
4B
K
014
12
0C
FF '\f'
114
76
4C
L
015
13
0D
CR '\r'
115
77
4D
M
016
14
0E
SO
116
78
4E
N
017
15
0F
SI
117
79
4F
O
020
16
10
DLE
120
80
50
P
021
17
11
DC1
121
81
51
Q
022
18
12
DC2
122
82
52
R
023
19
13
DC3
123
83
53
S
024
20
14
DC4
124
84
54
T
025
21
15
NAK
125
85
55
U
026
22
16
SYN
126
86
56
V
027
23
17
ETB
127
87
57
W
030
24
18
CAN
130
88
58
X
031
25
19
EM
131
89
59
Y
032
26
1A
SUB
132
90
5A
Z
033
27
1B
ESC
133
91
5B
[
034
28
1C
FS
134
92
5C
\ '\\'
035
29
1D
GS
135
93
5D
]
036
30
1E
RS
136
94
5E
^
037
31
1F
US
137
95
5F
_
040
32
20
SPACE
140
96
60
041
33
21
!
141
97
61
a
042
34
22
"
142
98
62
b
043
35
23
#
143
99
63
c
Programowanie
49
Tabela 2.1. Tablica znaków ASCII — cig dalszy
Oct
Dec
Hex
Char
Oct
Dec
Hex
Char
044
36
24
$
144
100
64
d
045
37
25
%
145
101
65
e
046
38
26
&
146
102
66
f
047
39
27
'
147
103
67
g
050
40
28
(
150
104
68
h
051
41
29
)
151
105
69
i
052
42
2A
*
152
106
6A
j
053
43
2B
+
153
107
6B
k
054
44
2C
,
154
108
6C
l
055
45
2D
-
155
109
6D
m
056
46
2E
.
156
110
6E
n
057
47
2F
/
157
111
6F
o
060
48
30
0
160
112
70
p
061
49
31
1
161
113
71
q
062
50
32
2
162
114
72
r
063
51
33
3
163
115
73
s
064
52
34
4
164
116
74
t
065
53
35
5
165
117
75
u
066
54
36
6
166
118
76
v
067
55
37
7
167
119
77
w
070
56
38
8
170
120
78
x
071
57
39
9
171
121
79
y
072
58
3A
:
172
122
7A
z
073
59
3B
;
173
123
7B
{
074
60
3C
<
174
124
7C
|
075
61
3D
=
175
125
7D
}
076
62
3E
>
176
126
7E
~
077
63
3F
?
177
127
7F
DEL
(gdb) x/6cb 0x8048484
0x8048484:
72 'H' 101 'e' 108 'l' 108 'l' 111 'o' 32 ' '
(gdb) x/s 0x8048484
0x8048484:
"Hello, world!\n"
(gdb)
Polecenia te pokazuj, e pod adresem 0x8048484 przechowywany jest acuch
danych "Hello, world!\n". Ten acuch jest argumentem funkcji printf(), co wskazuje, e przeniesienie adresu tego acucha do adresu przechowywanego w ESP
50
0x200
(0x8048484) ma co wspólnego z t funkcj. Poniszy wynik z gdb pokazuje adres
acucha danych przenoszony do adresu, który wskazuje ESP.
(gdb) x/2i $eip
0x8048393 <main+31>:
mov
DWORD PTR [esp],0x8048484
0x804839a <main+38>:
call
0x80482a0 <printf@plt>
(gdb) x/xw $esp
0xbffff800:
0xb8000ce0
(gdb) nexti
0x0804839a
8
printf("Hello, world!\n");
(gdb) x/xw $esp
0xbffff800:
0x08048484
(gdb)
Kolejna instrukcja wywouje funkcj printf(). Wypisuje ona acuch danych.
Wczeniej instrukcje stanowiy przygotowania do wywoania tej funkcji, a wynik
tego wywoania zosta pogrubiony poniej.
(gdb) x/i $eip
0x804839a <main+38>:
call
0x80482a0 <printf@plt>
(gdb) nexti
Hello, world!
6
for(i=0; i < 10; i++)
(gdb)
Korzystajc dalej z debugera, przyjrzyjmy si kolejnym dwóm instrukcjom.
Ponownie wikszy sens ma opisanie ich razem.
(gdb) x/2i $eip
0x804839f <main+43>: lea eax,[ebp-4]
0x80483a2 <main+46>: inc DWORD PTR [eax]
(gdb)
Te dwie instrukcje po prostu zwikszaj warto zmiennej i o 1. Instrukcja lea,
akronim od Load Effective Address (wczytaj efektywny adres), wczytuje znany ju
adres EBP minus 4 do rejestru EAX. Wykonanie tej instrukcji przedstawiono poniej.
(gdb) x/ i $eip
0x804839f <main+43>:
lea
eax,[ebp-4]
(gdb) print $ebp - 4
$2 = (void *) 0xbffff804
(gdb) x/x $2
0xbffff804:
0x00000000
(gdb) i r eax
eax
0xd
13
(gdb) nexti
0x080483a2
6
for(i=0; i < 10; i++)
(gdb) i r eax
eax
0xbffff804
-1073743868
(gdb) x/xw $eax
Programowanie
51
0xbffff804:
(gdb) x/dw $eax
0xbffff804:
(gdb)
0x00000000
0
Kolejna instrukcja, inc, zwikszy warto znalezion pod tym adresem (przechowywan teraz w rejestrze EAX) o 1. Wykonanie tej instrukcji równie przedstawiono
poniej.
(gdb) x/i $eip
0x80483a2 <main+46>: inc
DWORD PTR [eax]
(gdb) x/dw $eax
0xbffff804:
0
(gdb) nexti
0x080483a4
6
for(i=0; i < 10; i++)
(gdb) x/dw $eax
0xbffff804:
1
(gdb)
W wyniku warto przechowywana pod adresem pamici EBP minus 4 (0xbfffff804)
jest powikszana o 1. To zachowanie odpowiada fragmentowi kodu C, w którym
warto zmiennej i jest powikszana w ptli for.
Kolejn instrukcj jest bezwarunkowy przeskok.
(gdb) x/i $eip
0x80483a4 <main+48>: jmp 0x804838b <main+23>
(gdb)
Instrukcja po wykonaniu przekieruje program z powrotem do instrukcji spod
adresu 0x804838b. Dokonuje tego poprzez proste ustawienie EIP na t warto.
Spogldajc ponownie na pen dezasemblacj, powiniene stwierdzi, które
czci kodu C zostay skompilowane do których instrukcji jzyka maszynowego.
(gdb) disass main
Dump of assembler code for function main:
0x08048374 <main+0>:
push
ebp
0x08048375 <main+1>:
mov
ebp,esp
0x08048377 <main+3>:
sub
esp,0x8
0x0804837a <main+6>:
and
esp,0xfffffff0
0x0804837d <main+9>:
mov
eax,0x0
0x08048382 <main+14>:
sub
esp,eax
0x08048384 <main+16>:
mov
DWORD PTR [ebp-4],0x0
0x0804838b <main+23>:
cmp
DWORD PTR [ebp-4],0x9
0x0804838f <main+27>:
jle
0x8048393 <main+31>
0x08048391 <main+29>:
jmp
0x80483a6 <main+50>
0x08048393 <main+31>:
mov
DWORD PTR [esp],0x8048484
0x0804839a <main+38>:
call
0x80482a0 <printf@plt>
0x0804839f <main+43>:
lea
eax,[ebp-4]
0x080483a2 <main+46>:
inc
DWORD PTR [eax]
0x080483a4 <main+48>:
jmp
0x804838b <main+23>
52
0x200

Podobne dokumenty