Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
IDZ DO
IDZ DO
KATALOG KSI¥¯EK
KATALOG KSI¥¯EK
TWÓJ KOSZYK
TWÓJ KOSZYK
CENNIK I INFORMACJE
CENNIK I INFORMACJE
CZYTELNIA
CZYTELNIA
Hack Wars. Tom 1.
Na tropie hakerów
Autor: John Chirillo
T³umaczenie: Pawe³ Koronkiewicz, Leonard Milcin
ISBN: 83-7197-599-6
Tytu³ orygina³u:
Format: B5, stron: 736
Zawiera CD-ROM
Hack Attacks Revealed: A Complete
Reference with Custom Security Hacking Toolkit
Ekspert w dziedzinie zabezpieczeñ, John Chirillo, zachêca Czytelnika do poznania
mrocznego i tajemniczego œwiata hakerów. Czerpi¹c z bogatego doœwiadczenia we
wspó³pracy z firmami Fortune 1000, Chirillo przedstawia ró¿ne sposoby wykorzystania
przez hakerów luk w zabezpieczeniach sieci oraz metody rozpoznawania tego rodzaju
zagro¿eñ. Uzupe³nieniem jest szczegó³owy opis pakietu TigerBox, umo¿liwiaj¹cego
hakerom przeprowadzanie skutecznych w³amañ, a administratorowi sieci — zyskanie
pewnoœci, ¿e jest w³aœciwie chroniona.
W tej prowokacyjnej ksi¹¿ce znajdziemy:
"
"
"
"
Opis protoko³ów sieciowych i technologii komunikacyjnych z punktu widzenia
hakera
Pe³ny opis stosowanych metod w³amañ, wyjaœniaj¹cy, jak dzia³aj¹ hakerzy,
crackerzy, "phreaks" i cyberpunki
Narzêdzia do gromadzenia informacji i skanowania sieci, umo¿liwiaj¹ce wykrycie
i przeanalizowanie przypadków naruszenia bezpieczeñstwa systemu
Dok³adne instrukcje, jak pos³ugiwaæ siê pakietem typu TigerBox
i wykorzystywaæ go do wykrywania ataków.
O Autorze .............................................................................................................9
Wstęp ................................................................................................................11
Rozdział 1. Protokoły komunikacyjne .................................................................15
Krótka historia Internetu ...................................................................................................15
IP — Internet Protocol ................................................................................................16
Datagramy IP — transportowanie, rozmiar i fragmentacja ........................................18
Adresy IP, klasy i maski podsieci ...............................................................................21
VLSM — krótka instrukcja tworzenia podsieci i odczytywania adresu IP ................22
ARP/RARP — rozpoznawanie adresu sprzętowego.........................................................31
ARP — transportowanie, budowa nagłówka pakietu .................................................31
RARP — transportowanie, dokonywanie transakcji ..................................................33
Usługa RARP..............................................................................................................33
TCP — Transmission Control Protocol ............................................................................33
Sekwencje oraz okna...................................................................................................34
Budowa nagłówka pakietu TCP..................................................................................35
Porty, końcówki, nawiązywanie połączenia ...............................................................37
UDP — User Datagram Protocol ......................................................................................37
Budowa i transportowanie datagramów UDP.............................................................38
Multiplexing, demultiplexing oraz porty UDP ...........................................................39
ICMP — Internet Control Message Protocol....................................................................39
Budowa i transportowanie pakietów ICMP ................................................................39
Komunikaty ICMP, wyszukiwanie maski podsieci ....................................................40
Przykłady datagramów ICMP.....................................................................................42
Rozdział 2. NetWare oraz NetBIOS .....................................................................43
NetWare — wprowadzenie ...............................................................................................43
IPX — Internetwork Packet Exchange .......................................................................44
SPX — Sequenced Packet Exchange .........................................................................48
Budowa i przykłady nagłówków SPX ........................................................................49
Zarządzanie połączeniami, przerywanie .....................................................................49
Algorytm Watchdog....................................................................................................50
Korekcja błędów, ochrona przed zatorami .................................................................51
NetBIOS — wprowadzenie...............................................................................................51
Konwencje nazywania, przykładowe nagłówki..........................................................51
Usługi NetBIOS ..........................................................................................................52
4
Hack Wars. Na tropie hakerów
NetBEUI — wprowadzenie ..............................................................................................53
Związki z NetBIOS.....................................................................................................54
Okna i liczniki.............................................................................................................54
Rozdział 3. Porty standardowe oraz związane z nimi usługi..................................55
Przegląd portów.................................................................................................................55
Porty TCP oraz UDP...................................................................................................56
Luki w bezpieczeństwie związane z portami standardowymi ....................................57
Niezidentyfikowane usługi................................................................................................69
Rozdział 4. Techniki rozpoznania i skanowania ...................................................99
Rozpoznanie ......................................................................................................................99
Katalog Whois ..........................................................................................................100
PING .........................................................................................................................102
Serwisy wyszukiwawcze ..........................................................................................105
Social Engineering ....................................................................................................106
Skanowanie portów .........................................................................................................107
Techniki skanowania portów ....................................................................................107
Popularne skanery portów.........................................................................................108
Przykładowy skan ...........................................................................................................120
Rozdział 5. Niezbędnik hakera..........................................................................127
Pojęcia związane z siecią ................................................................................................127
Model warstwowy — Open Systems Interconnection Model ..................................127
Rodzaje okablowania — przepustowość oraz maksymalna długość........................129
Konwersje pomiędzy postaciami dwójkowymi, dziesiątkowymi
i szesnastkowymi liczb .......................................................................................129
Funkcje wydajnościowe protokołów ........................................................................140
Technologie sieciowe ......................................................................................................141
Adresowanie MAC i kody producentów ..................................................................141
Ethernet .....................................................................................................................141
Token Ring................................................................................................................148
Sieci Token Ring i mostkowanie trasy nadawcy ......................................................149
Sieci Token Ring i translacyjne mostkowanie trasy nadawcy..................................153
Sieci FDDI ................................................................................................................155
Protokoły wybierania tras................................................................................................157
Protokoły wektorowo-odległościowe i protokoły stanów przyłączy........................157
Protokół RIP..............................................................................................................159
Protokół IGRP...........................................................................................................160
Protokół RTMP sieci Appletalk................................................................................161
Protokół OSPF ..........................................................................................................161
Ważne polecenia .............................................................................................................162
append .......................................................................................................................162
assign.........................................................................................................................164
attrib ..........................................................................................................................164
backup .......................................................................................................................165
break..........................................................................................................................166
chcp ...........................................................................................................................166
chdir (cd) ...................................................................................................................167
chkdsk .......................................................................................................................168
cls ..............................................................................................................................168
command...................................................................................................................168
comp..........................................................................................................................169
copy...........................................................................................................................170
ctty.............................................................................................................................171
Spis treści
5
date ............................................................................................................................171
del (erase)..................................................................................................................172
dir ..............................................................................................................................172
diskcomp ...................................................................................................................173
diskcopy ....................................................................................................................174
exe2bin ......................................................................................................................174
exit.............................................................................................................................175
fastopen .....................................................................................................................175
fc ...............................................................................................................................175
fdisk...........................................................................................................................177
find ............................................................................................................................177
format ........................................................................................................................178
graftabl ......................................................................................................................179
Graphics ....................................................................................................................179
join ............................................................................................................................180
keyb...........................................................................................................................181
label...........................................................................................................................182
mkdir (md) ................................................................................................................182
mode..........................................................................................................................183
more ..........................................................................................................................186
nlsfunc.......................................................................................................................186
path............................................................................................................................187
print ...........................................................................................................................187
prompt .......................................................................................................................188
recover.......................................................................................................................189
rename (ren) ..............................................................................................................190
replace .......................................................................................................................190
restore........................................................................................................................191
rmdir (rd)...................................................................................................................192
select .........................................................................................................................192
set ..............................................................................................................................193
share ..........................................................................................................................194
sort.............................................................................................................................194
subst ..........................................................................................................................195
sys .............................................................................................................................196
time ...........................................................................................................................196
tree.............................................................................................................................197
type............................................................................................................................197
ver .............................................................................................................................197
verify .........................................................................................................................198
vol .............................................................................................................................198
xcopy.........................................................................................................................198
Rozdział 6. Podstawy programowania dla hakerów ...........................................201
Język C ............................................................................................................................201
Wersje języka C ........................................................................................................202
Klasyfikowanie języka C ..........................................................................................203
Struktura języka C ...........................................................................................................203
Komentarze ...............................................................................................................205
Biblioteki...................................................................................................................205
Tworzenie programów ....................................................................................................205
Kompilacja ................................................................................................................205
Typy danych..............................................................................................................206
Operatory ..................................................................................................................210
6
Hack Wars. Na tropie hakerów
Funkcje......................................................................................................................212
Polecenia preprocesora C..........................................................................................216
Instrukcje sterujące ...................................................................................................219
Wejście-wyjście ........................................................................................................223
Wskaźniki .................................................................................................................226
Struktury ...................................................................................................................229
Operacje na plikach...................................................................................................234
Ciągi ..........................................................................................................................244
Obsługa tekstu...........................................................................................................250
Data i godzina ...........................................................................................................253
Pliki nagłówkowe......................................................................................................259
Debugowanie programu............................................................................................259
Błędy wartości zmiennoprzecinkowych ...................................................................260
Obsługa błędów ........................................................................................................260
Konwersja typów zmiennych....................................................................................263
Prototypy...................................................................................................................265
Wskaźniki do funkcji ................................................................................................266
Sizeof........................................................................................................................267
Przerwania.................................................................................................................267
Funkcja signal() ........................................................................................................270
Dynamiczne alokowanie pamięci .............................................................................271
Funkcja atexit() .........................................................................................................273
Wydajność.................................................................................................................274
Przeszukiwanie katalogów........................................................................................275
Dostęp do pamięci rozbudowanej .............................................................................278
Dostęp do pamięci rozszerzonej ...............................................................................282
Tworzenie programów TSR......................................................................................290
Rozdział 7. Metody przeprowadzania ataków ....................................................319
Streszczenie przypadku ...................................................................................................319
„Tylne wejścia” (backdoors) ...........................................................................................320
Zakładanie „tylnego wejścia” ...................................................................................322
Typowe techniki „tylnego wejścia” ................................................................................323
Filtry pakietów ..........................................................................................................323
Filtry stanowe............................................................................................................328
Bramy proxy i poziomu aplikacji .............................................................................333
Przeciążanie (flooding) ...................................................................................................333
Zacieranie śladów (log bashing) .....................................................................................342
Zacieranie śladów aktywności online .......................................................................343
Unikanie rejestrowania wciśnięć klawiszy ...............................................................344
Bomby pocztowe, spam i podrabianie korespondencji ...................................................355
Łamanie haseł (password cracking) ................................................................................357
Deszyfrowanie i krakowanie.....................................................................................357
Zdalne przejęcie kontroli.................................................................................................362
Krok 1. Rozpoznanie ................................................................................................363
Krok 2. Przyjazna wiadomość email ........................................................................363
Krok 3. Kolejna ofiara ..............................................................................................364
Monitorowanie komunikacji (sniffing) ...........................................................................366
Podrabianie IP i DNS (spoofing) ....................................................................................374
Studium przypadku ...................................................................................................375
Konie trojańskie ..............................................................................................................382
Infekcje wirusowe ...........................................................................................................388
Wardialing .......................................................................................................................391
„Złamanie” strony WWW (Web page hack)...................................................................392
Spis treści
7
Krok 1. Rozpoznanie ................................................................................................394
Krok 2. Uszczegółowienie danych ...........................................................................394
Krok 3. Rozpoczęcie właściwego ataku ...................................................................397
Krok 4. Poszerzenie wyłomu ....................................................................................397
Krok 5. „Hakowanie” strony.....................................................................................397
Rozdział 8. Bramy, routery oraz demony usług internetowych ............................401
Bramy i routery ...............................................................................................................401
3Com.........................................................................................................................402
Ascend/Lucent ..........................................................................................................409
Cabletron/Enterasys ..................................................................................................416
Cisco .........................................................................................................................423
Intel ...........................................................................................................................431
Nortel/Bay.................................................................................................................438
Demony serwerów internetowych...................................................................................442
Apache HTTP ...........................................................................................................443
Lotus Domino ...........................................................................................................445
Microsoft Internet Information Server......................................................................446
Netscape Enterprise Server .......................................................................................448
Novell Web Server....................................................................................................451
O’Reilly Web Site Professional ................................................................................454
Rozdział 9. Systemy operacyjne .......................................................................459
UNIX.........................................................................................................................460
AIX ...........................................................................................................................462
BSD...........................................................................................................................470
HP-UX ......................................................................................................................484
IRIX ..........................................................................................................................494
Linux .........................................................................................................................497
Macintosh..................................................................................................................522
Microsoft Windows ..................................................................................................527
Novell NetWare ........................................................................................................543
OS/2 ..........................................................................................................................552
SCO...........................................................................................................................566
Solaris .......................................................................................................................568
Rozdział 10. Serwery proxy i zapory firewall........................................................573
Bramy międzysieciowe ...................................................................................................573
BorderWare...............................................................................................................573
FireWall-1 .................................................................................................................577
Gauntlet.....................................................................................................................581
NetScreen ..................................................................................................................585
PIX ............................................................................................................................589
Raptor........................................................................................................................596
WinGate ....................................................................................................................599
Rozdział 11. TigerSuite — kompletny pakiet narzędzi do badania i ochrony sieci ...605
Terminologia ...................................................................................................................605
Wprowadzenie.................................................................................................................607
Instalacja ...................................................................................................................610
Moduły ............................................................................................................................613
Moduły grupy System Status ....................................................................................614
TigerBox Tookit..............................................................................................................619
TigerBox Tools .........................................................................................................619
TigerBox Scanners....................................................................................................624
8
Hack Wars. Na tropie hakerów
TigerBox Penetrators ................................................................................................626
TigerBox Simulators .................................................................................................627
Przykładowy scenariusz włamania..................................................................................628
Krok 1. Badanie celu.................................................................................................629
Krok 2. Rozpoznanie ................................................................................................631
Krok 3. Socjotechnika...............................................................................................633
Krok 4. Atak..............................................................................................................635
Podsumowanie ................................................................................................................635
Dodatek A Klasy adresów IP oraz podział na podsieci.......................................637
Dodatek B Porty standardowe .........................................................................641
Dodatek C Pełna lista portów specjalnych .......................................................645
Dodatek D Porty usług niepożądanych .............................................................685
Dodatek E Zawartość płyty CD .......................................................................691
Skorowidz .........................................................................................................701
Rozdział 6.
Język C
Dla każdego hakera, młodego czy starego, mniej lub bardziej doświadczonego, zna-
jomość języka C jest jednym z fundamentów wiedzy. Niemal wszystkie narzędzia
i programy, stosowane w trakcie analiz sieci i włamań, powstają właśnie w tym języ-
ku. Również w niniejszej książce większość przedstawianego kodu to właśnie kod
źródłowy w języku C. Programy te można modyfikować, dostosowywać do własnych
potrzeb i odpowiednio kompilować.
W pracy nad niniejszym rozdziałem wykorzystano obszerne fragmenty pracy guru
programowania Matthew Proberta. Mają one pełnić funkcję wprowadzenia do pro-
gramowania w języku C i umożliwić stosowanie przedstawianych w książce (i załą-
czonych na CD-ROM-ie) listingów programów. Pełny kurs języka znajdziesz w nie-
jednej książce wydawnictwa Helion.
Język C wyróżniają następujące cechy, które omawiamy niżej.
J
J
J
J
Blokowe konstrukcje sterowania wykonywaniem programu (typowe dla
większości języków wysokiego poziomu).
J
J
J
J
Swobodne operowanie podstawowymi obiektami „maszynowymi” (takimi jak
bajty) i możliwość odwoływania się do nich przy użyciu dowolnej, wymaganej
w danej sytuacji, perspektywy obiektowej (typowe dla języków asemblerowych).
J
J
J
J
Możliwość wykonywania operacji zarówno wysokiego poziomu (na przykład
arytmetyka zmiennoprzecinkowa), jak i niskiego poziomu (zbliżonych
do instrukcji języka maszynowego), co umożliwia tworzenie kodu wysoce
zoptymalizowanego bez utraty jego przenośności.
202
Hack Wars. Na tropie hakerów
Przedstawiony w niniejszym rozdziale opis języka C bazować będzie na funkcjach
oferowanych przez większość kompilatorów dla komputerów PC. Powinien dzięki
temu umożliwić rozpoczęcie tworzenia prostych programów osobom nieposiadają-
cym szerokiej wiedzy o języku (uwzględnimy między innymi funkcje zapisane w pa-
mięci ROM i funkcje DOS-u).
Przyjmujemy założenie, że masz, drogi Czytelniku, dostęp do kompilatora C i od-
powiedniej dokumentacji funkcji bibliotecznych. Programy przykładowe powstały
w Turbo C firmy Borland; większość elementów niestandardowych tego narzędzia
uwzględniono również w późniejszych edycjach Microsoft C.
Wersje języka C
W pierwotnej edycji języka C (jeszcze przed publikacją Kernighana i Ritchie’ego,
The C Programming Language, Prentice-Hall 1988 (polskie wydanie: Język ANSI C,
Wydawnictwa Naukowo-Techniczne 1994)) zintegrowane operatory przypisania (+=,
*= itd.) definiowane były odwrotnie (tj. =+, =* itd.). Znakomicie utrudniało to inter-
pretację wyrażeń takich jak:
co mogłoby znaczyć
lub
Ritchie szybko zauważył dwuznaczność takiego zapisu i zmodyfikował go do postaci
znanej dzisiaj (+=, *= itd.). Mimo to wciąż stosowanych jest wiele odmian będących
rodzajem wypośrodkowania między pierwotną wersją języka C Kernighana i Ritchie’ego
a językiem ANSI C. Różnice między nimi dotyczą przede wszystkim:
J
J
J
J
wprowadzenia prototypów funkcji i zmiany preambuły definicji funkcji,
aby dostosować ją do stylu prototypów,
J
J
J
J
wprowadzenia znaku wielokropka (...) do oznaczenia list argumentów o zmiennej
długości,
J
J
J
J
wprowadzenia słowa kluczowego
(dla funkcji, które nie zwracają wartości)
i typu
dla ogólnych zmiennych wskaźnikowych,
J
J
J
J
wprowadzenie w preprocesorze mechanizmów scalania ciągów, wklejania
elementu (token-pasting) i zamiany na ciąg (string-izing),
J
J
J
J
dodanie w preprocesorze translacji „trygrafów” (trigraph) — trójznakowych
sekwencji reprezentujących znaki specjalne,
J
J
J
J
dodanie w preprocesorze dyrektywy
i formalizacja pseudofunkcji
,
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
203
J
J
J
J
wprowadzenie ciągów i znaków wielobajtowych, zapewniających obsługę
języków narodowych,
J
J
J
J
wprowadzenie słowa kluczowego
(jako uzupełnienie słowa
,
stosowane w deklaracjach liczb całkowitych) i jednoargumentowego
operatora plus (
).
Klasyfikowanie języka C
Szerokie możliwości języka C, dopuszczenie bezpośredniego operowania na adresach
i danych w pamięci oraz strukturalne podejście do programowania sprawiają, że język
ten klasyfikuje się jako „język programowania średniego poziomu”. Znajduje to wyraz
w mniejszej liczbie gotowych rozwiązań niż w językach wysokiego poziomu, takich
jak BASIC, ale wyższym poziomie strukturalnym niż niskopoziomowy Assembler.
Słowa kluczowe
Pierwotna edycja języka C definiuje 27 słów kluczowych. Komitet ANSI dodał do
nich 5 nowych. Wynikiem są dwa standardy języka, choć norma ANSI przejęła więk-
szość elementów od Kerninghana i Ritchie’ego. Oto lista:
Warto zwrócić uwagę, że niektóre kompilatory C wprowadzają dodatkowe słowa klu-
czowe, specyficzne dla środowiska sprzętowego. Warto zapoznać się z nimi.
Struktura języka C
Język C wymaga programowania strukturalnego. Oznacza to, że na program składa
się pewna grupa nawzajem wywołujących się bloków kodu. Dostępne są różnorodne
polecenia służące do konstruowania pętli i sprawdzania warunków:
Blok programu w języku C ujmowany jest w nawiasy klamrowe (
). Może on być
kompletną procedurą, nazywaną funkcją lub częścią kodu funkcji. Przyjrzyjmy się
przykładowi:
204
Hack Wars. Na tropie hakerów
!
"
#
!#
$
Instrukcje wewnątrz nawiasów klamrowych wykonane zostaną tylko wtedy, gdy speł-
niony zostanie warunek
.
Jako kolejny przykład przedstawimy pełny blok kodu funkcji, zawierający wewnątrz
blok pętli:
%&'()
"
#
"
*+, ! !*#
*-*.#
$
!//0 !#
#
$
Zwróćmy uwagę, że każdy wiersz instrukcji zakończony jest średnikiem, o ile nie jest
sygnałem początku bloku kodu (w takim przypadku kolejnym znakiem jest nawias
klamrowy). Język C rozpoznaje wielkość liter, ale nie bierze pod uwagę białych zna-
ków. Odstępy między poleceniami są pomijane, stąd konieczność użycia średnika,
aby oznaczyć koniec wiersza. Tego rodzaju podejście powoduje, że następujące pole-
cenia interpretowane są jako identyczne:
!#
!#
!#
Ogólna postać programu w języku C jest następująca:
J
J
J
J
instrukcje preprocesora kompilacji,
J
J
J
J
globalne deklaracje danych.
J
J
J
J
deklaracje i definicje funkcji (włączając w to zawartość programu):
"
$
"
$
1
"
$
2
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
205
"
$
Komentarze
Podobnie jak większość języków, C pozwala umieszczać w kodzie programu komen-
tarze. Ich ogranicznikami są symbole
i
:
34'5 56 743
(Równie często korzysta się z komentarzy jednoliniowych, otrzymywanych poprzez
sekwencję //, np.:
33' 85 92:2
Biblioteki
Programy w języku C kompiluje się i łączy z funkcjami bibliotecznymi, dostarcza-
nymi wraz z kompilatorem. Na biblioteki składają się funkcje standardowe, których
działanie zdefiniowane zostało w normie ANSI. Ich powiązanie z konkretnym kom-
pilatorem zapewnia dostosowanie do platformy sprzętowej. Wynika stąd, że standardowa
funkcja biblioteczna
działa tak samo w systemach DEC VAX i IBM PC,
choć różni się jej, zapisany w bibliotece, kod maszynowy. Programista C nie musi za-
głębiać się w zawartość bibliotek, wymagana jest jedynie umiejętność ich stosowania
i znajomość działania funkcji, które pozostają niezmienne na każdym komputerze.
Tworzenie programów
Kompilacja
Zanim zajmiemy się funkcjami, poleceniami, sekwencjami i innymi zaawansowanymi
zagadnieniami, przyjrzyjmy się praktycznemu przykładowi, w którym doprowadzimy
do skompilowania kodu. Kompilowanie programów C jest stosunkowo prostą czyn-
nością, jednak różni się zależnie od stosowanego kompilatora. Kompilatory wyposażone
w menu umożliwią skompilowanie, skonsolidowanie i uruchomienie programu jed-
nym wciśnięciem klawisza. Podchodząc jednak do zagadnienia możliwie uniwersal-
nie i tradycyjnie, przeprowadzimy poniżej całą procedurę w oparciu o wiersz poleceń.
W dowolnym edytorze wprowadzamy poniższy fragment kodu i zapisujemy plik jako
przyklad.c:
34
;
43
206
Hack Wars. Na tropie hakerów
< 20
"
*=>+*#
$
Kolejnym krokiem jest skompilowanie kodu do postaci pliku programu — dopiero
wtedy można będzie go uruchomić (czy też wykonać). W wierszu poleceń w tym sa-
mym katalogu, w którym zapisaliśmy plik przyklad.c, wprowadzamy następujące po-
lecenie kompilacji:
2
Nie wolno zapominać, że składnia polecenia kompilacji zależy od kompilatora. Nasz
przykład opiera się na standardzie języka C. Współcześnie jednak popularne jest sto-
sowanie składni wywodzącej się z kompilatora GNU C:
2
Po wykonaniu takiego polecenia nasz kod jest już skompilowany i ma postać pliku
programu, który możemy uruchomić. Wynik jego działania łatwo wydedukować
z prostego kodu:
=>
9
To wszystko! Kompilowanie małych programów w C nie jest trudne, należy jedynie
mieć świadomość szkodliwych niekiedy efektów ich działania. Programy przedsta-
wiane na stronach tej książki i załączone na CD-ROM-ie są oczywiście znacznie bar-
dziej skomplikowane, jednak zasady pozostają te same.
Typy danych
W języku C wyróżnia się cztery podstawowe typy danych: znakowy, całkowity,
zmiennoprzecinkowy i nieokreślony. Odpowiadają im słowa kluczowe:
,
,
i
. Dalsze typy danych tworzy się na tej podstawie, dodając modyfikatory:
(ze znakiem),
(bez znaku),
(długa) i
(krótka). Modyfika-
tor
jest elementem domyślnym, co sprawia, że jego użycie może się okazać
konieczne jedynie w wypadku gdy zastosowano przełącznik kompilacji nakazujący
domyślne korzystanie ze zmiennych bez znaku. Rozmiar każdego typu danych zależy
od platformy sprzętowej, jednak norma ANSI wyznacza pewne zakresy minimalne,
zestawione w tabeli 6.1.
W praktyce tak określone konwencje oznaczają, że typ danych
nadaje się najle-
piej do przechowywania zmiennych typu znacznikowego, takich jako kody stanu,
o ograniczonym zakresie wartości. Można również korzystać z typu
. Gdy jednak
zakres wartości nie przekracza 127 (lub 255 dla
), każda deklarowana
w ten sposób zmienna przyczynia się do niepotrzebnego obciążania pamięci.
Natomiast trudniejsze jest pytanie o to, z którego typu liczb rzeczywistych korzystać
—
,
czy
. Gdy wymagana jest dokładność, na przykład
w aplikacji stosowanej w księgowości, instynktownie powinniśmy użyć typu
,
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
207
Tabela 6.1.
Rozmiary i zakresy typów danych języka C
Typ
Rozmiar
Zakres
8
–128 do 127
8
0 do 255
16
–32 768 do 32 767
16
0 do 65 535
32
–2 147 483 648 do 2 147 483 647
32
0 do 4 294 967 295
32
precyzja 6-cyfrowa
64
precyzja 10-cyfrowa
80
precyzja 10-cyfrowa
wiąże się to jednak z wykorzystaniem przez każdą zmienną 10 bajtów. Obliczenia na
liczbach rzeczywistych nie są tak dokładne jak na liczbach całkowitych, warto więc
zawsze rozważyć użycie typu
i „obejście” problemu. Typ danych
nie jest
zbyt dobry, gdyż jego 6-cyfrowa precyzja nie zapewnia dokładności, na której zawsze
będziemy mogli polegać. Ogólną zasadą jest korzystanie z typów całkowitych tak
szeroko, jak tylko jest to możliwe, a gdy pojawia się konieczność użycia liczb rze-
czywistych, wprowadzenie typu
.
Deklarowanie zmiennej
Każda zmienna musi zostać zadeklarowana przed użyciem. Ogólną postacią deklara-
cji zmiennej jest:
#
Aby więc przykładowo zadeklarować zmienną
typu
, przeznaczoną do przecho-
wywania wartości z zakresu od –32 768 do 32 767, użyjemy instrukcji:
#
Ciągi znakowe deklarować można jako tabele znaków:
? @#
Deklaracja ciągu o nazwie
!"#
i długości 30 znaków, wyglądać będzie następująco:
?A!@#
Tablice danych innych typów mogą mieć więcej niż jeden wymiar. Oto deklaracja
dwuwymiarowej tablicy liczb całkowitych:
? !@? !@#
Elementy tablicy wywołujemy jako:
?!@?!@
?!@? @
?@?@
208
Hack Wars. Na tropie hakerów
Wyróżnia się trzy poziomy dostępu do zmiennych: lokalny, na poziomie modułu
i globalny. Zmienna deklarowana wewnątrz bloku kodu będzie dostępna wyłącznie
dla instrukcji wewnątrz tego bloku. Zmienna deklarowana poza blokami kodu funkcji,
ale poprzedzona modyfikatorem
, będzie dostępna wyłącznie instrukcjom we-
wnątrz modułu kodu źródłowego. Zmienna deklarowana poza blokami kodu funkcji
i niepoprzedzona modyfikatorem będzie dostępna dla dowolnych instrukcji w dowol-
nym module programu. Na przykład:
#
#
7 5; 5 B5
5 B 6 8 B
5 55C5 5 2D CE8 6
; 5692:2
"
#
#
$
5
"
34F5 G!43
!
"
#
!# 1!# HH
*+=,*#
$
$
W powyższym przykładzie zmienna
jest dostępna dla wszystkich, kompilowa-
nych jako jeden program, modułów kodu źródłowego. Zmienna
jest osiągalna dla
wszystkich instrukcji w funkcjach
i
#$
, ale pozostaje niewidoczna
z poziomu innych modułów. Zmienne
i
%
są dostępne wyłącznie instrukcjom we-
wnątrz funkcji
. Z kolei zmienna
może być użyta wyłącznie przez instrukcje
wewnątrz bloku kodu po instrukcji
.
Jeżeli drugi blok kodu faktycznie ma skorzystać ze zmiennej
, wymagane będzie
umieszczenie w nim deklaracji zmiennej globalnej
:
#
5
"
$
Język C nie stawia szczególnych przeszkód w przypisywaniu do siebie różnych typów
danych. Przykładowo możemy zadeklarować zmienną typu
, co spowoduje przy-
pisanie do przechowywania jej wartości jednego bajtu danych. Można podjąć próbę
przypisania do niej wartości spoza tego zakresu:
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
209
"
I!!!#
$
Zmienna
może przechowywać wartości z zakresu od –127 do 128, a więc wartość 5000
nie zostanie przypisana.
przyjmie jednak wartość 136.
Potrzeba przypisania różnych typów danych nie jest niczym oryginalnym. Aby po-
wstrzymać kompilator od generowania ostrzeżeń o takich operacjach, można skorzy-
stać z instrukcji konwersji (cast statement), informując kompilator o tym, że operacja
wykonywana jest świadomie. Instrukcję taką budujemy, umieszczając przed zmienną
lub wyrażeniem nazwę typu danych ujętą w nawiasy:
"
#
#
!!31I#
#
$
Operacja rzutowania
informuje kompilator o konieczności konwersji wartości
zmiennej zmiennoprzecinkowej
do liczby całkowitej, zanim ta zostanie przypisana
do zmiennej
%
.
Parametry formalne
Funkcja w języku C może przyjmować parametry przekazywane przez funkcję wy-
wołującą. Parametry te deklaruje się podobnie jak zmienne, podając ich nazwy we-
wnątrz towarzyszących nazwie funkcji nawiasów:
JKLM
"
34MGE 8 43
4#
$
"
#
#
#
I#
N#
JKLM #
*--G6-+* #
$
210
Hack Wars. Na tropie hakerów
Modyfikatory dostpu
Stosuje się dwa modyfikatory dostępu:
i
. Wartość zmiennej zadekla-
rowanej jako
nie może zostać zmieniona przez program, wartość zmiennej za-
deklarowanej jako
może zostać zmieniona przez program. Dodatkowo, za-
deklarowanie zmiennej jako
uniemożliwia kompilatorowi zaalokowanie jej
do rejestru i ogranicza przeprowadzaną na niej optymalizację.
Typy klas przechowywania zmiennych
Język C przewiduje cztery rodzaje przechowywania zmiennych:
,
,
i
. Typ
umożliwia modułowi kodu źródłowego dostęp do zmiennej
zadeklarowanej w innym module. Zmienne
dostępne są wyłącznie z poziomu
bloku kodu, w którym zostały zadeklarowane. Dodatkowo, jeżeli zmienna ma zasięg
lokalny, zachowuje swoją wartość między kolejnymi wywołaniami bloku kodu.
Zmienne rejestrowe (
) są, gdy tylko jest to możliwe, przechowywane w reje-
strach procesora. Zapewnia to najszybszy dostęp do ich wartości. Typ
stosuje się
wyłącznie w odniesieniu do zmiennych lokalnych. Nakazuje on zachowywanie war-
tości zmiennej lokalnej. Ponieważ jest to modyfikator domyślny, rzadko można spo-
tkać go w programach.
Operatory
Operatory to elementy kodu, które nakazują wykonanie obliczeń na zmiennych. W ję-
zyku C dostępne są następujące:
.
adres,
4
pośredniość,
H
plus jednoargumentowy,
minus jednoargumentowy,
O
dopełnienie bitowe,
>
negacja logiczna,
HH
jako prefiks — preinkrementacja, jako sufiks — postinkrementacja,
PP
jako prefiks — predekrementacja, jako sufiks — postdekrementacja,
H
dodawanie,
odejmowanie,
4
mnożenie,
3
dzielenie,
-
reszta z dzielenia (modulo),
przesunięcie w lewo,
00
przesunięcie w prawo,
.
bitowa operacja AND,
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
211
/
bitowa operacja OR,
Q
bitowa operacja XOR,
..
logiczna operacja AND,
//
logiczna operacja OR,
przypisanie,
4
przypisanie iloczynu,
3
przypisanie ilorazu,
-
przypisanie reszty (modułu),
H
przypisanie sumy,
przypisanie różnicy,
przypisanie przesunięcia w lewo,
00
przypisanie przesunięcia w prawo,
.
przypisanie wyniku bitowej operacji AND,
/
przypisanie wyniku bitowej operacji OR,
Q
przypisanie wyniku bitowej operacji XOR,
mniejsze niż,
0
większe niż,
mniejsze lub równe,
0
większe lub równe,
równe,
>
różne od,
2
bezpośredni selektor składnika,
0
pośredni selektor składnika,
RS
jeżeli
to prawda, to , w przeciwnym razie %&
?@
definiowanie tablic,
nawiasy oddzielają warunki i wyrażenia,
222
wielokropek wykorzystuje się w listach parametrów formalnych prototypów
funkcji do deklarowania zmiennej liczby parametrów lub parametrów
zmiennych typów.
Aby zilustrować sposób korzystania z podstawowych operatorów, przyjrzyjmy się
krótkiemu programowi:
"
#
#
#
I#3495 BI43
31#3495 B5143
41#3495 B 85143
34F CC BE543
212
Hack Wars. Na tropie hakerów
*M5 *#
*M5 *#
$
Typowym sposobem zwiększenia wartości zmiennej o 1 jest wiersz:
H
Język C dostarcza operatora inkrementacji, wystarczy więc napisać:
HH
W podobny sposób korzystamy z operatora dekrementacji, czyli zmniejszania war-
tości o 1:
Pozostałe operatory matematyczne wykorzystujemy podobnie. Warto jednak pamię-
tać o wprowadzanych przez język C możliwościach zapisu skróconego:
Zapis typowy
Zapis w j+zyku C
H HH
41
41
3
3
-I
-I
Funkcje
Funkcje to procedury kodu źródłowego tworzące program w języku C. Ich ogólną po-
stacią jest:
"
$
to typ zwracanej przez funkcję wartości:
,
,
,
itp.
Kod wewnątrz funkcji C pozostaje niewidoczny dla innych funkcji C. Nie można wy-
konywać skoków z jednej funkcji do wnętrza innej. Funkcje mogą jedynie wywoły-
wać inne funkcje. Nie wolno również definiować funkcji wewnątrz innych funkcji.
Definicja musi zostać umieszczona bezpośrednio na poziomie modułu kodu.
Parametry przekazywane są do funkcji jako wartości lub jako odwołania (wskaźniki).
Gdy parametr jest przekazywany jako wartość, funkcja otrzymuje kopię tej wartości.
Parametr przekazywany jako odwołanie jest jedynie wskaźnikiem do właściwego pa-
rametru. Pozwala to na zmianę jego wartości z poziomu wywołanej funkcji. W poniż-
szym przykładzie przekazujemy dwa parametry jako wartość do funkcji
#$
,
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
213
która następnie podejmuje próbę zmiany wartości przekazanych zmiennych. Drugim
krokiem jest przekazanie tych samych parametrów do funkcji
#$
, która rów-
nież podejmuje próbę zmiany wartości zmiennych:
< 20
5
"
34T 55 5 5 B43
41#
41#
*+, BE 5-2, BE 5-*#
#
$
5 4 4
"
34T 55 5 5;43
4441#
4441#
*+, BE 5 -2, BE 5 -*44#
4#
$
"
#
#
#
I#
N#
5#
5 ..#
*+, BE- BE- BE-*#
$
#$
nie zmienia wartości otrzymanych parametrów. Modyfikowana jest za-
wartość wskazywanych parametrami adresów pamięci. O ile
#$
otrzymuje
z funkcji
wartości zmiennych
i
%
,
#$
otrzymuje z funkcji
ich
adresy w pamięci.
Przekazywanie tablicy do funkcji
Następujący program przekazuje do funkcji tablicę, a funkcja nadaje wartości ele-
mentom tablicy:
214
Hack Wars. Na tropie hakerów
< 20
5 ?@
"
#
!# !!#HH
?@#
$
"
? !!@#
#
5 #
!# !!#HH
*+, BE --* ?@#
$
Parametr funkcji,
'(
, jest tablicą dowolnej długości. Deklaracja taka jest możli-
wa, ponieważ kompilator przekazuje jedynie adres początkowy tablicy, a nie wartości
poszczególnych jej elementów. Konsekwencją tego jest fakt, że funkcja może zmie-
niać wartości elementów tablicy. Aby uniemożliwić funkcji wprowadzanie modyfika-
cji, konieczne jest użycie typu
:
5 ?@
"
$
Przy takiej deklaracji wiersz zmieniający zawartość tablicy wywołałby błąd kompila-
cji. Określenie parametru jako wartości stałej nie likwiduje jednak pośredniości jego
przekazania. Ilustruje to poniższy program:
< 20
5 ?@
"
4 #
#
34' 5 8U U43
34 5V43
345 V 43
#
!# !!#HH
"
4 #
HH#
$
$
"
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
215
? !!@#
#
5 #
!# !!#HH
*+, BE --* ?@#
$
Przekazywanie parametrów funkcji main()
Język C umożliwia przekazanie parametrów do uruchamianego programu z poziomu
systemu operacyjnego. Do ich odczytania wykorzystuje się zmienne
i
'(
:
< 20
4?@
"
#
!##HH
*+, - -*?@#
$
Parametr
przechowuje liczbę przekazanych programowi parametrów. W tablicy
'(
zapisane są ich adresy;
'(
jest zawsze nazwą uruchamianego programu.
Mechanizm ten ma szczególne znaczenie dla aplikacji wymagających dostępu do pli-
ków systemowych i danych. Rozważmy następującą sytuację: mała aplikacja obsługi
baz danych przechowuje swoje dane w pojedynczym pliku dane.dat; aplikacja ta musi
zostać tak zaprojektowana, aby można było uruchomić ją z dowolnego katalogu, czy
to na dysku twardym, czy dyskietce; musi również zapewnić uruchamianie za pośred-
nictwem ścieżki wyszukiwania DOS-u (
). Do poprawnej pracy aplikacji jest więc
wymagane, aby zawsze mogła odnaleźć plik dane.dat. Rozwiązanie takie zapewni
przyjęcie założenia, że plik danych jest zawsze w identycznym katalogu co sam pro-
gram. Poniższy fragment ilustruje wykorzystanie parametrów
i
w celu utwo-
rzenia ścieżki do pliku danych aplikacji:
< 20
( ? W!@#
4?@
"
4(*XD'D2XD'*#
4#
( ?!@#
( *2*# ;
å 92:2
KYZZ
"
349 5 27LJ43
216
Hack Wars. Na tropie hakerów
( *2*#
$
34, 5 B43
4 >U++U
#
(#
$
Przedstawiony program tworzy i zapisuje w zmiennej
!" )#
ciąg postaci ścież-
ka\dane.dat. Jeżeli więc przykładową nazwą pliku uruchomieniowego będzie test.exe
i zostanie on umieszczony w katalogu \borlandc, zmiennej
!" )#
przypisany
zostanie ciąg
* * +
.
Wyj#cie z funkcji
Polecenie
powoduje natychmiastowe wyjście z funkcji. Jeżeli w deklaracji
funkcji podano typ zwracanej wartości, w poleceniu
należy użyć parametru te-
go samego typu.
Prototypy funkcji
Prototypy funkcji umożliwiają kompilatorowi C sprawdzanie poprawności przekazy-
wanych, do i z funkcji, danych. Ma to istotne znaczenie jako zabezpieczenie przed
przekroczeniem zakresu zaalokowanego dla zmiennej obszaru pamięci. Prototyp funkcji
umieszcza się na początku programu po poleceniach preprocesora (takich jak
)
i przed deklaracjami funkcji.
Polecenia preprocesora C
W języku C w treści kodu źródłowego można umieszczać polecenia dla kompilatora.
Określa się je terminem polecenia preprocesora. Norma ANSI definiuje następujące:
<
<
<
<
<
<
<
<
<
<
<
<
Wszystkie polecenia preprocesora rozpoczyna znak krzyżyka (hash), czyli #. Każde
wymaga osobnego wiersza kodu (uzupełnionego ewentualnie komentarzem). Poniżej
przedstawiamy krótkie omówienie.
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
217
#define
Polecenie
tworzy identyfikator, który kompilator zastąpi podanym ciągiem
w danym module kodu źródłowego. Na przykład:
<TDZF&!
<'[Y&>TDZF&
Kompilator zastąpi wszystkie dalsze wystąpienia ciągu
,-./0
znakiem
, a wszystkie
dalsze wystąpienia ciągu
1230
— ciągiem
4
. Zastępowaniu nie podlegają identyfi-
katory wewnątrz znaków cudzysłowu, a więc wiersz:
*'[Y&*#
nie zostanie zmieniony, ale
*-*TDZF&#
podlega modyfikacji.
Polecenie
może również zostać użyte do definiowania makr, także makr z pa-
rametrami. Do zapewnienia poprawności zastąpień zaleca się ujmowanie parametrów
w nawiasy. W poniższym przykładzie deklarujemy makro o nazwie
, przyj-
mujące dwa parametry i zwracające ten z nich, którego wartość jest większa.
< 20
< 0 RS
"
*+-5 6*IN#
$
#error
Polecenie
powoduje przerwanie procesu kompilacji i wyświetlenie podanego
tekstu, na przykład:
<F\LJ9]ZL,DK&XLJLXYZY:
powoduje zatrzymanie kompilacji i wyświetlenie:
F\LJ9]ZL,DK&XLJLXYZY:
#include
Polecenie
nakazuje kompilatorowi odczytanie i przetworzenie zawartości
dodatkowego pliku źródłowego. Nazwa pliku musi zostać ujęta w cudzysłów lub
wstawiona między znaki
5
, na przykład:
< * 12*
< 20
Jeżeli nazwa pliku została wpisana między znaki
5
, kompilator wyszukuje go w kata-
logu określonym w konfiguracji. Jest to zasada ogólna.
218
Hack Wars. Na tropie hakerów
#if, #else, #elif, #endif
Grupa poleceń
dostarcza mechanizmu kompilacji warunkowej. Stosowana jest
dość typowa składnia:
<
<
<
Polecenie
to skrócona postać
:
<
<
<
#ifdef, #ifndef
Rozwinięciem tych poleceń jest
(jeżeli zdefiniowano) i
(jeżeli nie zdefiniowano). Konstrukcje składniowe są następujące:
<
<
<
<
<
<
to identyfikator utworzony za pomocą deklaracji
.
#undef
Polecenie
usuwa definicję makra utworzonego przy użyciu wcześniejszej in-
strukcji
.
#line
Polecenie
modyfikuje zmienne globalne kompilatora
)).670))
i
)),6.0))
.
Ogólną postacią instrukcji jest:
< * *
Wartość
zostaje umieszczona w zmiennej
)).670))
, a
8 8
— w zmien-
nej
)),6.0))
.
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
219
#pragma
Umożliwia korzystanie z poleceń specyficznych dla kompilatora.
Instrukcje steruj"ce
Jak w każdym języku programowania, również w C, znajdziemy instrukcje spraw-
dzające wartość wyrażenia. Wynikiem takiego sprawdzenia jest wartość
1230
lub
,-./0
.
Wartości
,-./0
odpowiada liczba
, a
1230
— liczba różna od zera.
Instrukcje wykonania warunkowego
Podstawową instrukcją wykonania warunkowego jest
o następującej składni:
gdzie
może być instrukcją pojedynczą lub ujętym w nawiasy klamrowe
blokiem kodu. Element
jest opcjonalny. Jeżeli wartością
jest
1230
,
wykonywana jest instrukcja podana bezpośrednio po nim. W pozostałych przypadkach
wykonywana jest instrukcja podana po słowie
(o ile ta część składni została użyta).
Alternatywą dla konstrukcji
+++
jest polecenie
9:
w postaci:
Jeżeli wartością wyrażenia jest
1230
, wykonywana jest pierwsza instrukcja. W pozo-
stałych przypadkach wykonywana jest instrukcja druga. Ilustruje to przykład:
< 20
"
#
W#
*+ -*-1!R* *S* *#
$
Język C oferuje również instrukcję
"
, ułatwiającą porównywanie wyrażenia
z pewną listą wartości. Wykonywane są instrukcje powiązane z pierwszą dopasowaną
wartością listy. Składnia polecenia
"
jest następująca:
"
S
#
!S
#
2
2
220
Hack Wars. Na tropie hakerów
S
#
S
$
Użycie instrukcji
#
nie jest wymagane, ale jej pominięcie powoduje dalsze po-
równywanie wyrażenia z kolejnymi elementami listy wartości.
< 20
"
#
W#
"
!S *+G6*#
#
S *+G65*#
#
1S *+G6*#
#
AS *+G6 *#
#
S *+5 6 *#
$
$
Instrukcje
"
można zagnieżdżać.
Instrukcje iteracji
W języku C stosuje się trzy instrukcje pętli (iteracji):
,
"
i
;"
. Składnia
pętli
jest następująca:
# #
Jest ona szczególnie przydatna, gdy korzystamy z licznika, jak w poniższym przykła-
dzie wyświetlającym zestaw znaków ASCII:
< 20
"
#
A1# 1^#HH
*-+ -+ *#
$
Dopuszczalna jest również nieskończona pętla
:
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
221
##
"
$
Język C pozwala używać też pustych instrukcji. Poniższa pętla usuwa z ciągu począt-
kowe znaki odstępu:
#4 UU# HH
#
Warto zwrócić uwagę na średniki odpowiadające inicjalizacji pętli i pustej instrukcji.
Pętla
"
ma konstrukcję nieco prostszą:
Instrukcja lub blok instrukcji (ujęty w nawiasy klamrowe) będą powtarzane do czasu,
gdy wyrażenie warunku przyjmie wartość
,-./0
. Jeżeli wyrażenie nie jest prawdziwe
jeszcze przed wejściem do pętli, instrukcje nie będą wykonywane w ogóle. Jest to
istotna różnica w stosunku do pętli
;"
, która zawsze zostaje wykonana co naj-
mniej raz. Jej składnia to:
"
$
#
Instrukcje skoku
Instrukcja
pozwala powrócić z funkcji wykonywanej do funkcji, z której ta zo-
stała wywołana. W zależności od zadeklarowanego typu wartości zwracanej przez
funkcję instrukcja
może wymagać odpowiedniego parametru:
JYZ'
"
4#
$
lub
5
"
*+=,*#
# 5 92:2
$
Instrukcja
#
służy do wychodzenia z pętli lub instrukcji
"
. W przypadku pę-
tli powoduje to jej przedwczesne zakończenie, jak w poniższym przykładzie:
< 20
"
#
222
Hack Wars. Na tropie hakerów
!#1IW#HH
"
!!
#
*-+ *#
$
$
Uzupełnieniem
#
jest polecenie
, wymuszające przeprowadzenie następ-
nej iteracji pętli. Kolejną wykonywaną instrukcją jest w tym przypadku instrukcja pę-
tli (dalsze instrukcje w iterowanym bloku są pomijane). Dostępna jest również funk-
cja przedwczesnego zakończenia wykonywania programu —
. Można za jej
pomocą przekazać wartość zwracaną do programu wywołującego:
#
Continue
Słowo kluczowe
nakazuje skok do instrukcji kontrolnej pętli. W przypadku
pętli zagnieżdżonych jest to instrukcja pętli wewnętrznej (
"
,
+++"
). To spo-
sób na łagodne zakończenie pętli jak w poniższym przykładzie, gdzie odczytujemy
zapisane w pliku ciągi:
< 20
"
T]Z&4#
4#
? !!@#
*2 ***#
KYZZ
"
*K8 E 2 *#
!#
$
"
!!#
KYZZ
34, 5B43
#
#
$
#
$
W przypadku pętli
instrukcja
powoduje najpierw wykonanie wyrażenia
inkrementacji, a dopiero po nim następuje sprawdzenie warunku zakończenia.
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
223
Wej#cie-wyj#cie
Pobieranie danych
Program w języku C może pobierać dane z konsoli (która jest standardowym urzą-
dzeniem wejściowym), pliku lub portu. Ogólnym poleceniem odczytu danych ze stan-
dardowego strumienia wejściowego
jest
. Skanuje ono po jednym znaku
kolejne pola wejściowe. Podlegają one formatowaniu zgodnie z pierwszym z przekaza-
nych funkcji
parametrów. Następnie pole zostaje zapisane pod adresem prze-
kazanym jako kolejny parametr wywołania funkcji. Przykładowy program odczytuje
pojedynczą liczbę całkowitą ze strumienia
:
"
#
*-*.#
$
Warto zwrócić uwagę na operator użyty jako prefiks zmiennej
na liście parametrów
wywołania funkcji
. Funkcja ta zapisuje bowiem wartość pod określonym ad-
resem, nie posługując się mechanizmem przypisywania wartości zmiennej. Ciągiem
formatującym jest ciąg znakowy, który może zawierać trzy typy danych: znaki odstępu
(spacja, tabulator, przejście do nowego wiersza), znaki właściwe (wszystkie znaki ASCII
z wyjątkiem znaku %) i specyfikatory formatowania. Specyfikatory te mają następu-
jącą składnię:
-?4@? @?//Z@
Oto przykład:
< 20
"
?A!@#
#
*95*#
*-A!-*.#
*+--*#
$
Zwróćmy uwagę na wiersz
+5
— nakazuje on kompilatorowi prze-
twarzanie pliku nagłówkowego stdio.h, w którym zawarte są prototypy funkcji
i
. Po uruchomieniu tego prostego programu łatwo przekonamy się, że użycie
znaku odstępu przerwie wprowadzanie pierwszego pola danych.
Alternatywną funkcją pobierania danych jest
, odczytująca ciąg znaków ze stru-
mienia
do momentu napotkania znaku nowego wiersza. W ciągu docelowym znak
nowego wiersza zastąpiony zostaje znakiem
73..
. Charakterystyczna dla tej funkcji
jest możliwość odczytywania znaków odstępu. Oto nowa wersja powyższego programu
(korzystająca z
w miejsce
):
224
Hack Wars. Na tropie hakerów
< 20
< 20
< 20
"
?^!@#
4#
?A!@#
#
*+95*#
34L C 43
#
345 V C 43
.? @#
34Y 5_ 6 5C5KYZZ43
4UU"
4!#
#
$
34Z 5 C56C 43
UU#
34L 5 643
#
34, _C 43
4!#
34\ 5C543
#
34,B 543
*+KS-S-*#
$
Wyprowadzanie danych
Podstawową funkcją wyprowadzania danych jest
. Jest ona podobna do
z tą różnicą, że zapisuje dane do standardowego strumienia wyjściowego
.
Funkcja pobiera listę pól danych wyjściowych, odpowiednio stosuje specyfikatory
formatowania i wyprowadza wynik. Można stosować takie same przekształcenia for-
matujące jak w przypadku funkcji
, jak również dodatkowe znaczniki:
;
wyrównuje dane wyjściowe do lewej, uzupełniając je z prawej strony
znakami odstępu międzywyrazowego (spacji),
wymusza poprzedzanie liczb znakiem.
Nieco odmienna jest także postać specyfikatora szerokości. Jest on rozbudowany o ele-
ment określający precyzję:
2
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
225
Aby więc wyświetlić liczbę zmiennoprzecinkową z dokładnością do trzech miejsc
dziesiętnych, piszemy:
*-2A*#
Poniżej przedstawiamy listę specjalnych stałych znakowych, które mogą pojawić się
na liście parametrów funkcji
:
+
nowy wiersz (NL),
+
powrót karetki (CR),
+
tabulator,
+
znak cofania (backspace),
+
znak nowej strony,
+
tabulator pionowy,
++
ukośnik odwrotny (backslash),
+U
apostrof,
+*
cudzysłów,
+R
znak zapytania,
+
ciąg w notacji ósemkowej,
+
ciąg w notacji szesnastkowej.
Kolejny program ilustruje, w jaki sposób wyświetlić liczbę całkowitą w postaci dzie-
siętnej, szesnastkowej i ósemkowej. Liczba
<
po znaku procentów (
=
) w instrukcji
nakazuje kompilatorowi dopełnienie wyświetlanej liczby do szerokości co
najmniej czterech cyfr:
349 5 6 43
34 5G543
< 20
"
#
"
*+95 6 ! _E*#
*-*.#
*-!`-!`)-!`*#
$
>!#
$
Do funkcji pokrewnych
należy
, której prototyp ma postać:
T]Z&44 ? 222@#
Jej zadaniem jest przesyłanie sformatowanych danych wyjściowych do określonego
strumienia plikowego.
226
Hack Wars. Na tropie hakerów
Kolejną tego rodzaju funkcją jest
o prototypie:
44 ? 222@#
Alternatywą dla
jest
, funkcja przesyłająca prosty ciąg do strumienia
. Przesyłany ciąg zostaje automatycznie uzupełniony znakiem nowego wiersza.
Jest to rozwiązanie szybsze od
, jednak jego możliwości są ograniczone.
Bezpo#rednia wymiana danych z konsol*
Do przesyłania i odczytu danych z konsoli (klawiatury i ekranu) można wykorzysty-
wać również bezpośrednie funkcje we-wy. Wyróżnia je litera „c” na początku — od-
powiednikiem
jest więc
, a odpowiednikiem
— funkcja
. Różnice między funkcjami bezpośredniej wymiany danych a funkcjami
standardowymi są następujące.
J
J
J
J
Nie są wykorzystywane strumienie predefiniowane, nie można więc przekierować
danych przesyłanych funkcjami komunikacji bezpośredniej.
J
J
J
J
Funkcji bezpośrednich nie można przenosić między różnymi systemami
operacyjnymi (m.in. nie można z nich korzystać w programach dla Windows).
J
J
J
J
Funkcje bezpośrednie są szybsze niż standardowe.
J
J
J
J
Nie zapewniają współpracy ze wszystkimi trybami wyświetlania (zwłaszcza
trybami graficznymi VESA).
Wska%niki
Wskaźnik to zmienna, która przechowuje adres elementu danych w pamięci. Deklara-
cja wskaźnika jest podobna do deklaracji zwykłej zmiennej, ale nazwa poprzedzana
jest znakiem gwiazdki (
), na przykład:
4#
Powyższy wiersz deklaruje zmienną
jako wskaźnik do zmiennej typu
.
Wykorzystanie wskaźników dostarcza szerokich możliwości, wymaga jednak szcze-
gólnej uwagi. Skutki przypisania błędnego adresu są najczęściej nieprzewidywalne.
Oto przykład prostego programu, w którym wykorzystywany jest wskaźnik:
< 20
"
#
4#
345 V 43
!!#
.#
*+M 5 BE--2*#
$
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
227
Wartości wskaźników można zwiększać i zmniejszać, dopuszczalne są również inne
operacje matematyczne. Typowym zastosowaniem wskaźników jest zapewnienie dy-
namicznego przydziału pamięci. W trakcie pracy programu często pojawia się potrze-
ba przejściowego (tymczasowego) zaalokowania bloku pamięci. Korzystamy wów-
czas z funkcji
:
" # #
Funkcja
zwraca wskaźnik typu
, co oznacza, że może on wskazywać
dane dowolnego typu —
,
,
itd. W poniższym przykładzie alokujemy
pamięć dla tabeli 1000 liczb całkowitych.
< 20
< 20
"
4#
#
345 V 43
34' 6 !!! C43
34 5 43
34 5 G55 43
!!!4 #
34F5 ;43
KYZZ
"
*+K8E6 !!! 5 B
å *#
!#
$
349 5 BG 43
!# !!!#HH
"
4#
HH#
$
349 BE C 43
!!!#
34,B B 43
!# !!!#HH"
*+& - 5 BE-*4#
HH#
$
349 8 5 643
#
$
228
Hack Wars. Na tropie hakerów
Wskaźniki wykorzystuje się również w odniesieniu do tablic znaków, czyli ciągów
(strings). Ponieważ wszystkie ciągi w programach C kończy bajt o wartości 0, korzy-
stając ze wskaźnika, możemy policzyć znaki w ciągu:
< 20
< 20
"
4#
? !!@#
#
34]5 5CU U43
*'5 *#
34Y BE5C 43
#
34]5 5C; BE43
!#
34M5 43
4
"
HH#
HH#
$
34,B 43
*+X S-* #
$
Wymaganą do zaadresowania 1 MB pamięci 20-bitową liczbę dzieli się na dwie warto-
ści: przesunięcie (offset) i segment (każdy segment to 64 kB). Do przechowywania nu-
merów segmentów pamięci komputer IBM PC wykorzystuje tzw. rejestry segmentowe.
Konsekwencją takiego rozwiązania są w języku C trzy dodatkowe słowa kluczowe:
J
J
J
J
— wskaźniki „bliskie” mają rozmiar 16 bitów i umożliwiają dostęp
do danych bieżącego segmentu,
J
J
J
J
— wskaźniki „dalekie” obejmują wartości określające przesunięcie
i segment, umożliwiając dostęp do dowolnego adresu w pamięci,
J
J
J
J
— wskaźniki „ogromne” to odmiana wskaźników dalekich, zapewniająca
możliwość zwiększania i zmniejszania wartości w całym zakresie 1 MB
(kompilator generuje odpowiedni kod modyfikujący wartość przesunięcia).
Nie będzie zapewne zaskakujące stwierdzenie, że przetwarzanie programu korzystają-
cego ze wskaźników typu
będzie szybsze niż w przypadku programu, w którym
zastosowano wskaźniki
. Wskaźniki
są oczywiście największym obciążeniem.
Kompilatory C wyposażone są w makro zwracające adres odpowiadający podanym
wartościom numeru segmentu i przesunięcia:
4J\(T9 #
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
229
Struktury
Język C oferuje technikę grupowania zmiennych pod jedną nazwą, dostarczając w ten
sposób wygodnego sposobu przechowywania powiązanych ze sobą informacji i struktu-
ralizowania ich. Składnia definicji struktury jest następująca:
"
#
$
2
2
2
$
#
Używanie zmiennych strukturalnych jest niezbędne przy korzystaniu z plików, w któ-
rych występuje uporządkowanie oparte na rekordach danych. W poniższym przykła-
dzie operować będziemy na prostym pliku z listą adresów. Rozpoczniemy od deklara-
cji struktury
, złożonej z sześciu pól:
!"#
,
,
,
"$ "!"
,
!
i
:
"
?A!@#
?A!@#
?A!@#
5 ?A!@#
?W@#
? I@#
$
#
Odwołania do pól zmiennej strukturalnej mają postać:
2 #
Nie ma ograniczenia liczby pól struktury, nie jest również wymagane, aby typy pól
były takie same lub podobne, na przyklad:
"
?A!@#
#
4 #
$
#
Jest to poprawna deklaracja struktury obejmująca: pole tablicy znakowej, pole liczby
całkowitej i pole wskaźnika do zmiennej znakowej. Aby przekazać zmienną struktu-
ralną jako parametr, korzystamy z jej adresu — poprzedzamy nazwę zmiennej ope-
ratorem
>
. Oto przykładowy program wykorzystujący struktury w celu wykonania
prostych operacji na pliku listy adresów:
230
Hack Wars. Na tropie hakerów
< 20
< 20
< 20
< 20
< 20
< + 20
34 ( 43
< (1I
"
?A!@#
?A!@#
?A!@#
5 ?A!@#
?W@#
? I@#
$
#
#
#
349 543
DXX([&7#
7ZF#
X]F9XD'D#
TD'DZ4#
%&'XD'D#
J&KY#
L9&KXD'D#
F&D[7=#
7ZF
"
#
!# (#HH
**#
$
TD'DZ4
"
*+: S-* #
!#
$
L9&KXD'D
"
34FV 52a8 G243
34a8 G _ 243
*2 *L([X,[/L(D99&KXF(],[]'&#
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
231
"
*2 *L([X,[/L(7[&D'F(],[]'&#
TD'DZ*K8 E *#
$
$
%&'XD'D
"
349 43
7ZF#
*K*#
2#
*+D*#
2#
*+J *#
2 #
*+,5G *#
25 #
*+\ *#
2#
*+K *#
2 #
$
X]F9XD'D
"
34,B 43
?I@#
7ZF#
*K-*2#
*+D-*2#
*+J -*2 #
*+,5G -*25 #
*+\ -*2#
*+K -++*2 #
*,B5&K'&[*#
#
$
DXX([&7
"
34X;C 43
#
.#
TD'DZ*M 8*#
$
F&D[7=
"
232
Hack Wars. Na tropie hakerów
? !!@#
#
*,VG *#
#
4 !
#
34M 5C 43
!F&&\(F&'#
"
34M; 5643
.#
0!
"
349 543
2 >KYZZ
#
2 >KYZZ
#
2 >KYZZ
#
25 >KYZZ
#
2 >KYZZ
#
2 >KYZZ
#
$
$
0!#
!#
$
J&KY
"
5#
? !@#
"
7ZF#
*++ + + , 56*#
*+++ + + X5*#
*+++ + + 19 *#
*+++ + + A,5B*#
*+++++*#
#
5 #
5
"
S%&'XD'D#
349;C 5V_ 43
!F&&\(&KX#
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
233
DXX([&7#
#
1SF&D[7=
X]F9XD'D#
"
*K]&MKDZ&M]LK&>*#
*,B5&K'&[*#
#
$
#
AS #
$
$
5>A#
$
"
7ZF#
L9&KXD'D#
J&KY#
$
Pola bitowe
Język C przewiduje możliwość korzystania w strukturach ze zmiennych o rozmiarze
mniejszym niż 8 bitów. Określa się je mianem pól bitowych, a ich rozmiar może być
dowolny, od 1 bitu wzwyż. Deklaracja pola bitowego wygląda następująco:
S #
Przykładem może być deklaracja kilku jednobitowych znaczników stanu:
"
S #
S #
S #
S #
$
#
#
Zmienna
! !#
będzie zajmować w pamięci tylko 4 bity, mimo że składa się z 4 pól,
z których każde dostępne jest jako osobne pole struktury.
Union
Kolejnym ułatwieniem języka C, pozwalającym zapewnić optymalne wykorzystanie
dostępnej pamięci, jest struktura
, czyli zbiór zmiennych, współużytkujących je-
den adres pamięci. Oznacza to, oczywiście, że w danym momencie dostępna jest tyl-
ko jedna ze zmiennych składowych. Deklaracja
ma następującą postać:
234
Hack Wars. Na tropie hakerów
"
#
#
2
2
2
#
$#
Wyliczenia
Wyliczenie (enumeracja) to przypisanie liście symboli rosnących wartości całkowi-
tych. Wyliczenie deklarujemy:
" $ %#
Przykładem może być definicja listy kolorów:
\LZL[b
"
7MD[Kb
K]&:]&F\]
M]&ZLKb
7M&[,LKb
:[DML,b
aDFKLFMD[b
7]&JKLFMD[b
aDFKLK]&:]&F\]
aDFKLM]&ZLKb
aDFKL7M&[,LKb
MLZ'b
:]DZb
$#
Operacje na plikach
W operacjach dostępu do plików język C posługuje się buforowanymi strumieniami
plikowymi. Niektóre z platform języka, jak UNIX i DOS, oferują również niebuforo-
wane uchwyty plików.
Strumienie buforowane
Dostęp do strumieni buforowanych realizowany jest za pośrednictwem wskaźnika do
zmiennej typu
,6.0
. Ten szczególny typ danych zdefiniowany został w nagłówku st-
dio.h. Aby więc zadeklarować wskaźnik do pliku, wprowadzamy:
< 20
T]Z&4 #
Aby otworzyć strumień, używamy funkcji
. Pobiera ona dwa parametry: na-
zwę otwieranego pliku oraz tryb dostępu. Oto lista trybów dostępu.
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
235
Tryb
Opis
otwórz tylko do odczytu (plik musi istnieć),
utwórz do zapisu; zastąp, jeżeli plik o podanej nazwie istnieje,
otwórz do dołączania danych (dopisywania na końcu pliku); utwórz nowy plik,
jeżeli plik o podanej nazwie nie istnieje,
H
otwórz istniejący plik do odczytu i zapisu (plik musi istnieć),
H
utwórz do odczytu i zapisu; zastąp, jeżeli istnieje,
H
otwórz do czytania i dołączania danych; utwórz nowy plik, jeżeli nie istnieje.
Aby określić tryb tekstowy lub binarny, do opisu trybu można dołączyć
lub
. W przy-
padku pominięcia tego znacznika strumień zostanie otwarty w trybie określanym
zmienną globalną
)
. Odczyt i zapis danych do strumieni plikowych w trybie tek-
stowym wiąże się z konwersją — podczas zapisu znaki CR i LF zamieniane są na pary
CR LF, a przy odczycie pary CR LF ulegają zamianie na pojedynczy znak LF. Tego
rodzaju operacje nie są wykonywane w trybie binarnym.
Jeżeli funkcja
nie będzie mogła otworzyć pliku, zwróci w miejsce wskaźnika
wartość
73..
(zdefiniowaną w stdio.h). Poniższy program utworzy nowy plik dane.txt
i udostępni go do odczytu i zapisu:
< 20
"
T]Z&4#
*2 **H*#
$
Aby zamknąć strumień, używamy funkcji
, wymagającej podania wskaźnika
do pliku.
#
Jeżeli podczas zamykania strumienia wystąpi błąd, funkcja
zwróci wartość
niezerową (znacznik EOF — End Of File). Do przesyłania i odbierania danych ze
strumieni służą cztery podstawowe funkcje:
,
,
i
.
Funkcja
odczytuje pojedynczy znak z określonego strumienia wejściowego
(przekształcany do liczby całkowitej):
T]Z&4#
Jej odwrotnością jest
, zapisująca pojedynczy znak do określonego strumienia
wyjściowego:
T]Z&4#
Funkcja
odczytuje ze strumienia wejściowego ciąg:
4 4 T]Z&4#
236
Hack Wars. Na tropie hakerów
Odczyt zostaje przerwany po pobraniu
znaków lub znaku nowego
wiersza (również wstawianego do tablicy). Do odczytanego ciągu
dołączany jest koń-
czący znak
73..! % #? @A*A
. W przypadku wystąpienia błędów
funkcja zwraca
73..
.
Funkcja
zapisuje do strumienia ciąg zakończony znakiem
73..
(inaczej
A*A
):
4 T]Z&4#
Wszystkie opisywane funkcje zwracają w przypadku błędów wartość
0B,
(zdefinio-
waną w stdio.h) z wyjątkiem funkcji
, która w przypadku wystąpienia błędu
zwraca
73..
. Poniższy program tworzy kopię pliku dane.dat, o nazwie dane.old, ilu-
strując zarazem użycie wszystkich czterech funkcji:
< 20
"
T]Z&4#
T]Z&4 #
* 2 ***#
KYZZ
"
*+K8 E 2 *#
!#
$
*2**H*#
KYZZ
"
*+K8 E 2*#
!#
$
349 5 5 5 G43
348 <43
>
#
34M5 43
#
#
!#
$
W kolejnym przykładowym programie używamy funkcji
do kopiowania tekstu
ze strumienia
(zazwyczaj oznacza to znaki wprowadzane z klawiatury) do no-
wego pliku dane.txt:
< 20
"
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
237
T]Z&4#
? !!@#
*2 **H*#
"
#
#
$
4 #
#
$
Swobodny dostp do danych strumieni
Dostęp swobodny do danych dostarczanych za pośrednictwem strumieni zapewnia
funkcja
#
o prototypie:
T]Z&4 #
Funkcja zmienia pozycję wskaźnika pliku skojarzonego ze strumieniem otwartym
wcześniej przez
. Wskaźnik ustawiany jest na
za (lub przed
w przypadku wartości ujemnej) pozycją
. Tą ostatnią może być początek
pliku, bieżące położenie wskaźnika lub koniec pliku. Pozycje te symbolizują stałe
/00C)/01
,
/00C)D32
i
/00C)07E
. Udaną operację
#
sygnalizuje zwrócenie warto-
ści
. Uzupełnieniem
#
jest funkcja
, zwracająca wartość bieżącej pozy-
cji wskaźnika pliku:
T]Z&4#
Funkcja zwraca pozycję wskaźnika pliku, określoną jako ilość bajtów od początku
pliku, lub
;
w przypadku błędu.
Uchwyty
Uchwyty plików (handles) otwiera funkcja
o prototypie:
4 &? @#
Udaną operację sygnalizuje zwrócenie numeru uchwytu. W pozostałych przypadkach
zwracane jest
F
. Na wartość
składają się połączone bitową operacją OR stałe
symboliczne, odpowiadające deklaracjom w pliku fcntl.h. Różnią się one w zalezności
od kompilatora. Do typowych należą:
L(D99&KX
przed każdym zapisem wskaźnik pliku będzie ustawiany na końcu pliku,
L(7[&D'
jeżeli plik nie istnieje, zostanie utworzony,
L('[YK7
obcina istniejący plik do długości 0 bajtów,
L(&)7Z
używane w połączeniu z
B)D20-1,
L(:]KD[b
otwiera plik w trybie binarnym,
L('&)'
otwiera plik w trybie tekstowym.
238
Hack Wars. Na tropie hakerów
Po przypisaniu uchwytu pliku za pomocą polecenia
można korzystać z funkcji
i
"
. Prototyp
jest następujący:
% 4 #
Funkcja podejmuje próbę odczytu podanej liczby bajtów i zwraca liczbę bajtów fak-
tycznie pobranych przez uchwyt pliku. Odczytane dane umieszczane są w bloku pa-
mięci określonym parametrem
. Funkcja
działa podobnie, nie różni się
również jej prototyp i sposób generowania wartości zwracanej. Zapisuje ona podaną
ilość bajtów z określonego wskaźnikiem bloku pamięci. Pliki otwierane funkcją
zamykamy funkcją
:
% #
Funkcja
zwraca
w przypadku operacji udanej, a
F
w przypadku wystąpie-
nia błędów.
Dostęp swobodny zapewnia funkcja
#
, bardzo podobna do
#
, ale pobie-
rająca jako parametr numer uchwytu, a nie wskaźnik strumienia
,6.0
. W poniższym
przykładzie wykorzystujemy uchwyt pliku do zapisu danych z
(czyli klawiatu-
ry) do nowego pliku o nazwie dane.txt:
< 20
< 20
< + 20
"
#
? !!@#
*2 *L([X,[/L(7[&D'/L('[YK7F(],[]'&#
"
#
. #
$
4 #
#
$
Przegl*d funkcji plikowych
Norma ANSI definiuje związane z plikami operacje we-wy przy użyciu strumieni,
opisując różnorodne funkcje. Prototyp funkcji
ma postać:
T]Z&4 4 4#
Funkcja podejmuje próbę otwarcia strumienia łączącego z plikiem o podanej nazwie
w określonym trybie. Udana operacja kończy się zwróceniem wskaźnika typu
,6.0
.
W przypadku niepowodzenia funkcji zwraca
73..
. Na wcześniejszych stronach przed-
stawiony został opis parametru
.
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
239
Funkcja
służy do zamykania strumienia otwartego wcześniejszym wywoła-
niem
:
T]Z&4#
Udana operacja
kończy się opróżnieniem wszystkich buforów pliku i zwró-
ceniem wartości
. W przypadku błędów zwracana jest wartość
0B,
.
Wiele komputerów korzysta z buforowanego dostępu do plików. Oznacza to, że dane,
zapisywane do strumienia, wstępnie umieszczane są w pamięci, a faktyczny zapis na-
stępuje dopiero po przekroczeniu pewnej granicznej ilości bajtów. Jeżeli w czasie,
gdy dane nie zostały jeszcze faktycznie zapisane do strumienia, nastąpi awaria zasila-
nia, dane zostaną utracone. Zabezpiecza przed tym funkcja
, wymuszająca
zapisanie wszystkich danych oczekujących:
T]Z&4#
Jeżeli wywołanie
jest udane, związane ze strumieniem bufory zostają opróż-
nione i zwracana jest wartość
. W przypadku błędów funkcja zwraca wartość
0B,
.
Kolejną funkcją jest
zwracająca lokalizację wskaźnika pliku:
T]Z&4#
Funkcja zwraca przesunięcie wskaźnika pliku w stosunku do początku pliku lub
F.
w przypadku błędów. Przesunięcie wskaźnika pliku do nowej pozycji umożliwia
#
:
T]Z&4 #
Funkcja podejmuje próbę przesunięcia wskaźnika pliku o
bajtów od pozycji
, określonej jedną ze stałych:
F&&\(F&'
początek pliku,
F&&\(7Y[
bieżąca pozycja wskaźnika pliku,
F&&\(&KX
koniec pliku.
Przesunięcie (
) może być wartością dodatnią (przesuwanie wskaźnika w stronę
końca pliku) lub ujemną (przesuwanie wskaźnika w stronę początku pliku). Aby
szybko przenieść wskaźnik do początku pliku i usunąć wcześniejsze odwołania do
błędów, C dostarcza funkcji
"
:
T]Z&4#
Funkcja ta działa podobnie jak
#&.&/00C)/01
. Jednak
#
usuwa znacz-
nik
0B,
, a
" #"
wszystkie sygnały błędów. Informacje o błędach funk-
cji plikowych można pobrać przy użyciu funkcji
:
T]Z&4#
Funkcja zwraca wartość niezerową, jeżeli w określonym strumieniu wystąpił błąd. Po
sprawdzeniu wartości
należy zadbać o usunięcie sygnałów błędów za po-
mocą funkcji
:
T]Z&4#
240
Hack Wars. Na tropie hakerów
Sprawdzenie, czy spełniony jest warunek osiągnięcia końca pliku, realizuje predefi-
niowane makro
:
T]Z&4#
Makro zwraca wartość niezerową, gdy dla danego strumienia stwierdzono osiągnięcie
końca pliku. W pozostałych przypadkach zwracaną wartością jest
.
Dostępnych jest kilka funkcji realizujących odczyt danych ze strumienia plikowego.
Pojedyncze znaki można odczytywać funkcją
:
T]Z&4#
zwraca wartość ASCII pobranego znaku lub znak
0B,
w przypadku wystąpie-
nia błędu. Odczyt ciągu danych umożliwia funkcja
, odczytująca ciąg zakoń-
czony znakiem nowego wiersza:
4 ' T]Z&4#
W wyniku udanego wywołania funkcji w zmiennej
umieszczany jest ciąg zakoń-
czony znakiem nowego wiersza lub zawierający
znaków. Funkcja zachowuje
kończący ciąg znak nowego wiersza, dołączając do ciągu
bajt
73..
. W przypadku
nieudanego wywołania zwracany jest wskaźnik pusty. Ciągi zapisujemy do strumie-
nia funkcją
:
4 T]Z&4#
Funkcja
zapisuje wszystkie znaki ciągu
, z wyjątkiem końcowego bajtu
73..
, do strumienia
. Standardowo funkcja zwraca ostatni zapisany znak, a w przy-
padku wystąpienia błędów —
0B,
. Dostępna jest również funkcja zapisująca do stru-
mienia pojedynczy znak
:
T]Z&4#
Funkcja zwraca zapisany znak lub, w przypadku wystąpienia błędów, znak
0B,
.
Aby odczytać ze strumienia duży blok danych lub rekord, można posłużyć się funkcją
:
( 4( ( T]Z&4#
Funkcja podejmuje próbę odczytu
elementów, z których każdy ma długość
,
ze strumienia plikowego
do bloku pamięci określonego wskaźnikiem
. Aby
ustalić, czy operacja przebiegła bez zakłóceń, korzystamy z funkcji
.
Siostrzaną funkcją
jest
"
:
( 4( ( T]Z&4#
Funkcja zapisuje
elementów o długości
z obszaru pamięci określonego
wskaźnikiem
do strumienia
.
Funkcja
umożliwia odczyt danych formatowanych:
T]Z&4 4?222@#
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
241
Funkcja zwraca liczbę faktycznie odczytanych pól, a
0B,
w przypadku końca pliku.
Poniższy przykład ilustruje użyteczność funkcji
podczas odczytywania ze
strumienia liczb:
< 20
"
T]Z&4#
#
#
#
#
#
? !!@#
*2 **H*#
>
"
*K8 E *#
!#
$
* 1A`I+*, +**#
#
"
*:;C * #
#
$
#
"
*:;C5 * #
#
$
*------*.. ... #
"
*:;C * #
#
$
*+T 5G;------* #
$
Jak łatwo zauważyć, zapis formatowanych danych realizuje funkcja
. Gdy
pojawia się potrzeba zapisania położenia wskaźnika pliku i późniejszego jego przy-
wrócenia, można skorzystać z funkcji
i
. Pierwsza z nich odczy-
tuje bieżącą pozycję wskaźnika pliku:
T]Z&4( 4 #
242
Hack Wars. Na tropie hakerów
Funkcja
ustawia wskaźniki pliku na określonej pozycji:
T]Z&4 ( 4 #
Typ
)
zdefiniowany został w nagłówku stdio.h. Funkcje te są wygodniejsze
w użyciu niż
i
#
.
Z otwartym już strumieniem można skojarzyć nowy plik. Umożliwia to funkcja
:
T]Z&4 4 4T]Z&4#
Funkcja zamyka strumień istniejący i podejmuje próbę jego ponownego otwarcia przy
użyciu podanej nazwy pliku. Znajduje to zastosowanie przy przekierowywaniu stru-
mieni predefiniowanych
,
i
do pliku lub urządzenia. Przykłado-
wo, gdy pojawia się potrzeba przekierowania wszystkich danych wyjściowych kiero-
wanych do
na drukarkę, można użyć polecenia:
*Z9' *** #
Predefiniowane strumienie we-wy
Wstępnie zdefiniowane zostały trzy strumienie we-wy:
,
i
. Do-
myślnie
i
odpowiadają klawiaturze i monitorowi. Na wielu platformach,
w tym systemów DOS i UNIX, dostępna jest możliwość ich przekierowania. Stru-
mień
domyślnie powiązany jest z monitorem (wyświetlaczem). Praktyka jego
przekierowywania nie jest raczej stosowana. Jego podstawowym zadaniem jest za-
pewnienie możliwości wyświetlania komunikatów błędów, nawet w sytuacji gdy po-
wiązanie standardowego wyjścia (
) zostało zmienione:
*\ ;6* #
Funkcje
i
przekazują dane do strumienia
. Funkcje
i
pobierają dane ze strumienia
. Przekierowanie tych strumieni zmienia
sposób działania funkcji.
Jako przykład plikowych operacji we-wy na platformie PC, korzystających z możli-
wości przekierowania strumieni, przedstawimy prosty program przesyłający do stru-
mienia
zawartość określonego pliku, przedstawioną jako wartości szesnastko-
we. Polecenie w postaci:
( 20(5B2
pozwoli zmienić domyślne powiązanie strumienia
z monitorem.
< 20
< 20
< 20
< 20
4?@
"
#
?1!@#
#
#
#
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
243
>1
"
*+:cdX29;;S +* #
#
$
? @L([XLKZb#
"
*+:cdX2K8 E-+*? @#
#
$
*+MD,D['Lef9Z]\Y-++* ? @#
!#
"
34,; 43
!1!#
349 43
( . W#
34! < ;C43
#
34,V 43
*-!W-!I*#
H W#
34,V B 5 G 43
!# W#HH
*-!1* ?@#
34,V BDF7]] 5 G 43
!# W#HH
"
?@0A .. ?@ 1^
*-* ?@#
*2* #
$
34M_43
*+* #
$
34_43
!#
$
244
Hack Wars. Na tropie hakerów
Ci"gi
Język C należy do najlepiej wyposażonych w funkcje obsługi ciągów pośród uniwer-
salnych języków programowania. Ciąg to jednowymiarowa tablica znaków zakoń-
czona bajtem zerowym. Ciągi można inicjować dwoma sposobami. Pierwszym jest
nadanie im stałej wartości w kodzie programu:
"
4*F I*#
?@*9 *#
!#
$
Drugi to utworzenie ciągu w czasie wykonywania programu za pomocą funkcji
%
:
4 4 4"#
Funkcja
%
kopiuje ciąg źródłowy do lokalizacji docelowej, na przykład:
< 20
"
?I!@#
*FF *#
*+, BEC UU -*#
!#
$
Język C umożliwia bezpośredni dostęp do każdego bajtu ciągu:
< 20
"
?I!@#
*FF *#
*+, BEC UU -*#
34M C 5 CUU43
?!@UU#
*+, BEC UU -*#
!#
$
Niektóre kompilatory C wyposażone zostały w funkcje konwersji ciągów do wielkich
i małych liter, nie obejmuje ich jednak norma ANSI. W specyfikacji pojawiają się za
to funkcje
i
"
, zwracające pojedynczy znak ( w postaci wartości
)
zamieniony na literę wielką lub małą. Łatwo na tej podstawie utworzyć własne funk-
cje konwersji ciągów:
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
245
< 20
4
"
4#
#
4
"
40gN..4 11
4 4#
HH#
$
$
4
"
4#
#
4
"
40WI..4g!
4 4#
HH#
$
$
"
?I!@#
*FF *#
*+, BEC UU -*#
#
*+, BEC UU -*#
#
*+, BEC UU -*#
!#
$
' ; 2T 5 C ;
5C55 EVG;2\5 6
56 B A1 5 G865C
;2aB 5 65 8 C
C586 2, hFi
hAi2, 5 ; 6
; 2S
4
"
40gN..4 11
4 4#
HH#
246
Hack Wars. Na tropie hakerów
$
4
"
40WI..4g!
4 4#
HH#
$
X ;92:22
W przeciwieństwie do innych języków programowania C nie narzuca ograniczenia dłu-
gości ciągu. Jednak w przypadku niektórych procesorów (CPU) pojawia się ogranicze-
nie wielkości bloku pamięci. Oto prosty program odwracający kolejność znaków w ciągu:
< 20
< 20
4 4
"
34L5BEGC 5C543
34_KYZZ43
4#
4#
#
34Y VUU C 43
H #
34M VC C 43
#
34M43
0
"
4#
44#
4 #
#
HH#
$
#
$
"
? !!@#
4#
*'5 C*#
#
*+-*#
$
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
247
strtok()
Funkcja
#
jest istotną funkcją języka C, służącą do wyłączania fragmentów
ciągu. Stosuje się ją, gdy poszczególne podciągi rozdzielone są znanymi ograniczni-
kami, na przykład przecinkami:
< 20
< 20
"
?I!@#
4#
*7M&[,LKb9LJD[Dj7ML,bklc'bM]&ZLKbK]&:]&F\]*#
**#
"
#
KYZZ**#
$#
$
Program można oprzeć też na pętli
:
< 20
< 20
"
?I!@#
4#
*7M&[,LKb9LJD[Dj7ML,bklc'bM]&ZLKbK]&:]&F\]*#
**## KYZZ**
"
#
$#
$
W pierwszym wywołaniu funkcji
#
podajemy nazwę zmiennej ciągu oraz
ogranicznik. Funkcja zwraca wówczas wskaźnik do początku pierwszego podciągu
i zastępuje pierwszy ogranicznik zerem. Kolejne wywołania
#
wykonywane są
w pętli. Pierwszym parametrem jest wówczas
73..
, a funkcja zwraca kolejne podcią-
gi. Ponieważ dopuszczalne jest podanie listy ograniczników, funkcja
#
może
posłużyć do utworzenia prostego programu zliczającego słowa:
< 20
< 20
< 20
4?@
"
T]Z&4#
?1IW@#
248
Hack Wars. Na tropie hakerów
4#
#
>1
"
*+:cdX29;;S +* #
!#
$
34L G 43
? @**#
34FV ; 43
>
"
*+:cdX2K8 E VG;+* #
!#
$
34]5 543
!#
"
34L 5 43
1II#
34FV C; ;C <43
//
#
34M; 43
34F;G865 43
34+ +#S2>R543
*+ +#S2>R*#
"
HH#
KYZZ*+ +#S2>R*#
$
$
>..>#
34L _2:;CR43
"
*+:;C VG;+* #
#
!#
$
34L _<43
34, 6;G43
*+9--;G;+*? @#
#
$
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
249
Zamiana liczb na ci*gi i ci*gów na liczby
Wszystkie kompilatory C zapewniają możliwość konwertowania liczb na ciągi przy
użyciu takich funkcji jak
. Funkcja ta ma jednak wiele zastosowań, co po-
woduje, że jest rozbudowana i mało wydajna. Może ją zastępować funkcja
61B/
,
korzystająca z dwóch parametrów: liczby całkowitej ze znakiem i wskaźnika do ciągu
znakowego. Funkcja kopiuje liczbę do określonego wskaźnikiem miejsca w pamięci.
Podobnie jak
, funkcja
61B/
nie sprawdza, czy ciąg docelowy ma wystar-
czającą do przechowania wyniku konwersji długość. Oto przykładowa funkcja, która
kopiuje liczbę
do ciągu znakowego.
]'LF4
"
34M_6 C 6; CCG43
?g@" !!!!!!!! !!!!!!! !!!!!! !!!!! !!!! !!! !! ! $#
#
34FV43
!
"
4 HHUU#
34M_ BE 6C43
!#
$
!#g#HH
"
0 ?@
"
4 HHU!UH3 ?@#
- ?@#
$
$
4 U+!U#
_;_ ;
å 68 592:2
#
$
98;;5 5 5C6
å 2985 CC5692:2S
]'LF4
"
34M_6 C 6; CCG43
?g@" !!!!!!!! !!!!!!! !!!!!! !!!!! !!!! !!! !! ! $#
#
!#33 C C
34FV43
!
"
4 HHUU#
HH#
34M_ BE 6C43
!#
$
250
Hack Wars. Na tropie hakerów
!#g#HH
"
HH#
4 HHU!UH3 ?@#
- ?@#
$
4 U+!U#
#33G VC C
!#
4 UU336 C 5B5
HH#
4 U!U"335C
HH#
HH#
$
4 >U+!U"
4 4 #33C 5 8 C
HH#
$
4 U+!U#
#
$
Język C oferuje dwie funkcje do zamiany ciągów znakowych na liczby zmiennoprze-
cinkowe:
i
. Prototyp funkcji
ma postać:
4 #
a prototyp funkcji
:
4 44 #
Obie funkcje przeglądają ciąg i przeprowadzają konwersję aż do momentu natrafienia
na niezrozumiały znak. Różnica między nimi polega na tym, że
pobiera do-
datkowy parametr, wskaźnik
ustawiany na pierwszy znak ciągu, który nie został
objęty konwersją. Znacznie zwiększa to wygodę sprawdzania poprawności wykona-
nia operacji.
Aby zamienić ciąg na wartość całkowitą, można użyć funkcji
:
4 #
Należy pamiętać, że funkcja
nie zapewnia żadnej kontroli przepełnienia zmien-
nej. Nie jest zdefiniowana wartość zwracana w takiej sytuacji. W podobny sposób
działa funkcja
, zwracająca wartość
. Odpowiedniki z dodatkowym para-
metrem noszą nazwy
i
.
Obsługa tekstu
Człowiek zapisuje informacje jako pewien „tekst”, złożony ze słów, liczb i znaków
przestankowych. Słowa złożone są z liter wielkich i małych, odpowiednio do wyma-
gań gramatyki. Wszystko to sprawia, że komputerowe przetwarzanie tekstu nie jest
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
251
zadaniem prostym. Norma ANSI definiuje wiele funkcji przetwarzania ciągów zna-
kowych, które z natury rozpoznają wielkość liter. Oznacza to, że litera „A” rozpo-
znawana jest jako różna od „a”. Jest to pierwsze zagadnienie, którego rozwiązanie
musi znaleźć programista pracujący nad programem przetwarzającym tekst. Na szczę-
ście, zarówno kompilatory Borlanda, jak i Microsoftu wyposażone zostały w funkcje
obsługi ciągów, które nie rozpoznają wielkości liter.
Taką odmianą funkcji
jest
, a
—
. Gdy jed-
nak pojawia się kwestia przenośności kodu, niezbędna jest zgodność z ANSI C, co
pociąga za sobą napisanie własnych funkcji.
Poniżej przedstawiamy prostą implementację nierozróżniającej wielkości liter odmia-
ny funkcji
. Tworzy ona kopie ciągów, zamienia je na wielkie litery i wyko-
nuje standardową operację
. Pozwala to określić poszukiwaną wartość prze-
sunięcia i utworzyć wskaźnik do ciągu źródłowego.
4 4 41
"
? !!!@#
1? !!!@#
4#
#
11#
#
1#
1#
H #
KYZZ#
$
Kolejna funkcja przegląda ciąg
, wyszukując słowo podane jako
G
. Aby funkcja
zwróciła wartość
1230
, znalezione musi zostać odrębne słowo, a nie jedynie sekwen-
cja znaków. Wykorzystujemy przygotowaną wcześniej funkcję
.
(4 41
"
34 BEC5815 ; 43
4#
4m#
#
!#
m #
"
34Z 5 C5G1 43
m1#
"
252
Hack Wars. Na tropie hakerów
34M43
#
0
"
34FVC43
4 0UDU..4 UU
!#
$
34K 5C 43
H 1#
4
"
34FVC43
40UDU..4UU
!#
$
$
m#
$
..>#
#
$
Szerokie zastosowanie znajdzie kilka dalszych prostych funkcji znakowych.
obcina ciąg znakowy:
4
"
34L U UGC UU43
? @!#
$
usuwa końcowe znaki spacji (odstępu międzywyrazowego) w ciągu:
4
"
34 5_43
4#
. ? @#
4A1..0
4!#
$
zmienia długość ciągu:
4
"
34M; BEC ;C5C 5C43
0!
H H #
"
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
253
! #
H H #
$
$
umieszcza jeden ciąg w innym:
44m
"
34, CmC 43
m#
m m#
$
zastępuje wszystkie wystąpienia pewnego podciągu innym podciągiem:
44 41
"
34M 6 5 C C143
4#
#
"
!#
#
"
34Y _C43
! #
34, C43
1#
#
$
$
#
$
Data i godzina
Język C wyposażony jest w funkcję
, która odczytuje zegar systemowy kom-
putera i podaje informację o dacie i godzinie w postaci liczby sekund, która upłynęła
od północy 1 stycznia 1970 roku. Wartość ta może zostać zamieniona na czytelny dla
człowieka ciąg znaków za pomocą funkcji
:
< 20
< 20
"
34F 243
( #
349 66 43
KYZZ#
*:8C S-+* . #
$
254
Hack Wars. Na tropie hakerów
Na ciąg zwracany przez
składa się siedem pól:
J
J
J
J
dzień tygodnia,
J
J
J
J
miesiąc roku,
J
J
J
J
dzień miesiąca,
J
J
J
J
godzina,
J
J
J
J
minuty,
J
J
J
J
sekundy,
J
J
J
J
rok.
Uzupełnieniem jest znak nowego wiersza i końcowe 0. Ponieważ pola mają stałą sze-
rokość, ciąg zwracany przez
idealnie nadaje się do operacji wymagających
wyodrębnienia elementów daty lub godziny. W poniższym programie definiujemy
strukturę
!
oraz funkcję
!)!
, której zadaniem jest wypełnienie
struktury treścią pól ciągu
:
< 20
< 20
< 20
"
(#34J 43
(#34%43
(#34F 43
$#
( 4
"
( #
?1W@#
4 #
349 66 43
KYZZ#
349 66 C 43
. #
34L 5 43
? g@!#
. ? @#
349 5C 5 43
*-1S-1S-1*. 0(. 0(. 0(#
$
"
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
255
#
(. #
*+a -!1S-!1S-!1*. 2(. 2(. 2(#
$
Norma ANSI przewidziała również funkcję konwertującą wartość zwracaną przez
funkcję
do postaci struktury. Przedstawiony poniżej przykład zawiera deklara-
cję struktury
z nagłówka
+
:
< 20
< 20
"
( #
4 #
349 43
KYZZ#
34M_ BE 6 43
. #
*+a -!1S-!1S-!1* 0 ( 0 ( 0 (#
!#
$
Struktura
(zawarta w pliku
) ma następującą postać:
"
(#
(#
( #
(#
(#
(#
(#
(#
( #
$#
' 8 E6BC 5 5 5 8
å ;G2, 5 5 B ;C2
å J85C E 5 G;G6
å BE 6I 92:2
Liczniki czasu
Programy często korzystają z możliwości pobrania daty i czasu z nieulotnej pamięci
RAM komputera. Norma ANSI przewiduje kilka różnych funkcji, które mogą zostać
do tego celu wykorzystane. Pierwszą jest funkcja
, zwracająca liczbę sekund od
1 stycznia 1970 roku:
( ( 4#
256
Hack Wars. Na tropie hakerów
Funkcja wypełnia przekazaną jej jako parametr zmienną typu
)
jeśli nie jest to
73..
, zwracając tę samą wartość również jako wartość wyjściową. Można więc wywo-
ływać funkcję
z parametrem
73..
i korzystać z wartości zwracanej:
< 20
"
( #
KYZZ#
$
Funkcja
zamienia strukturę
na 26-znakowy ciąg (przedstawiony przy
opisie funkcji
):
4 4 #
Funkcja
zamienia wartość czasu (zwracaną przez
) na 26-znakowy ciąg:
< 20
< 20
< 20
"
( #
?A!@#
KYZZ#
. #
$
Kolejna funkcja,
, zwraca, liczoną w sekundach, różnicę między dwoma
wartościami typu
)
. Służy więc do wyznaczania ilości czasu, jaki upłynął mię-
dzy dwoma zdarzeniami, czasu wykonywania funkcji lub generowania przerw w pra-
cy programu, na przykład:
< 20
< 20
X&ZDb
"
( #
KYZZ#
KYZZH
#
$
"
*+[222I *#
X&ZDbI#
*+L_2*#
$
Rozdział 6.
¨
¨
¨
¨ Podstawy programowania dla hakerów
257
Funkcja
zamienia lokalną wartość czasu
)
na wartość GMT o postaci
struktury
. Działanie tej funkcji zależy od ustawienia globalnej zmiennej strefy cza-
sowej. Struktura
została wstępnie zdefiniowana w nagłówku
+
. Przedstawili-
śmy ją kilka stron wcześniej.
"
(#
(#
( #
(#
(#
(#
(#
(#
( #
$#
Element struktury
) %
przechowuje dzień miesiąca (od 1 do 31), a
)" %
— dzień
tygodnia (gdzie niedzieli odpowiada 0). Czas jest mierzony od 1900 roku. Wartość
)
to znacznik, który informuje o tym, czy stosowany jest czas letni. Stosowa-
ne nazwy struktury i jej elementów mogą różnić się w zależności od kompilatora,
jednak sama struktura zasadniczo pozostaje niezmieniona.
Funkcja
#
zamienia strukturę
na wartość
)
, uzupełniając wartości pól
)" %
i
)% %
:
( 4#
W kolejnym przykładzie umożliwiamy wprowadzanie daty i używamy funkcji
#
do ustalenia dnia tygodnia. Należy pamiętać, że funkcje związane z czasem rozpo-
znają wyłącznie daty późniejsze niż 1 stycznia 1970:
< 20
< 20
< 20
"
#
#
? !!@#
4#
4?@
"***;** **B** **C ** **
å gN!*$#
"
!#
*+,V 633*#
g #
*3*#
>KYZZ
2 ( #
#
258
Hack Wars. Na tropie hakerów
KYZZ*3*#
>KYZZ
2 ( #
#
KYZZ*3*#
>KYZZ
2 ( #
#
#
$
>#
2 ( !#
2 (!#
2 ( #
2 ( #
34' _ 43
.
2 (N#
*'_ -+*? 2 (@#
$
Funkcja
#
zapewnia również wprowadzenie odpowiednich poprawek dla warto-
ści przekraczających swój dopuszczalny zakres. Można to wykorzystać do ustalenia do-
kładnej daty, odległej o
dni:
< 20
< 20
< 20
"
4 #
( 5#
5 KYZZ#
.5#
0 (H !#
#
0 (0gg
0 (- !!#
5CB ;6 5
å ;C; g!!5 ; 8gg 92:2
*M6E 6-!13-!13-!1+* 0 ( 0 (H
å 0 (#
$