Segmentacja pamięci

https://chacker.pl/

Temat segmentacji z łatwością mógłby zająć cały rozdział. Jednak podstawowa koncepcja jest prosta. Każdy proces (w dużym uproszczeniu jako program wykonawczy) musi mieć dostęp do własnych obszarów pamięci. Przecież nie chciałbyś, żeby jeden proces nadpisywał dane innego procesu. Dlatego pamięć jest dzielona na małe segmenty i przekazywana procesom w miarę potrzeb. Rejestry, omówione w dalszej części rozdziału, służą do przechowywania i śledzenia bieżących segmentów utrzymywanych przez proces. Rejestry offsetowe są do tego przyzwyczajone  śledź, gdzie w segmencie przechowywane są krytyczne fragmenty danych. Segmentacja opisuje także układ pamięci w wirtualnej przestrzeni adresowej procesu. Segmenty takie jak segment kodu, segment danych i segment stosu są celowo przydzielane w różnych obszarach wirtualnej przestrzeni adresowej w ramach procesu, aby zapobiec kolizjom i umożliwić odpowiednie ustawienie uprawnień. Każdy działający proces otrzymuje własną wirtualną przestrzeń adresową, a ilość miejsca zależy od architektury (np. 32-bitowej lub 64-bitowej), ustawień systemu i systemu operacyjnego. Podstawowy 32-bitowy proces systemu Windows domyślnie otrzymuje 4 GB, z czego 2 GB jest przypisane do strony procesu w trybie użytkownika, a 2 GB jest przypisane do strony procesu w trybie jądra. Tylko niewielka część tej wirtualnej przestrzeni w każdym procesie jest mapowana na pamięć fizyczną i w zależności od architektury istnieją różne sposoby mapowania pamięci wirtualnej na fizyczną poprzez wykorzystanie stronicowania i translacji adresów.

Endian

https://chacker.pl/

W Internet Experiment Note (IEN) 137, „On Holy Wars and a Plea for Peace” z 1980 r., Danny Cohen podsumował Podróże Guliwera Swifta częściowo w następujący sposób, omawiając kolejność bajtów: Guliwer dowiaduje się, że istnieje prawo , ogłoszony przez dziadka obecnego władcy, nakazujący wszystkim obywatelom Lilliputu rozbijanie jajek tylko na małych końcach. Oczywiście wszyscy obywatele, którzy rozbijali jajka na końcu, byli oburzeni proklamacją. Wybuchła wojna domowa pomiędzy Little-Endianami i Big-Endianami, w wyniku której Big-Endianowie schronili się na pobliskiej wyspie, królestwie Blefuscu. Celem artykułu Cohena było opisanie dwóch szkół myślenia podczas zapisywania danych w pamięci. Niektórzy uważają, że bajty o niższym numerze powinny być zapisywane jako pierwsze (nazywane przez Cohena „Little-Endianami”), inni natomiast uważają, że bajty o większym znaczeniu powinny być zapisywane jako pierwsze (tzw. „Big-Endiany”). Różnica naprawdę zależy od używanego sprzętu. Na przykład procesory oparte na procesorach Intel korzystają z metody Little-Endian, podczas gdy procesory oparte na Motoroli korzystają z metody Big-Endian.

Pamięć o dostępie swobodnym

https://chacker.pl/

W pamięci RAM dowolną część przechowywanych danych można odzyskać w dowolnym momencie – stąd termin dostęp swobodny. Pamięć RAM jest jednak ulotna, co oznacza, że wyłączenie komputera powoduje utratę wszystkich danych z pamięci RAM. Kiedy mówimy o nowoczesnych produktach opartych na procesorach Intel i AMD (x86 i x64), pamięć jest adresowalna odpowiednio 32-bitowo lub 48-bitowo, co oznacza, że szyna adresowa używana przez procesor do wyboru określonego adresu pamięci to 32 lub 48 bitów Szerokość 48 bitów. Dlatego największa pamięć, jaką można zaadresować w procesorze x86, to 4 294 967 295 bajtów lub 281 474 976 710 655 bajtów (256 terabajtów). W przypadku 64-bitowego procesora x64 adresowanie można w przyszłości rozszerzyć, dodając więcej tranzystorów, ale 248 w zupełności wystarczy w obecnych systemach.

Pamięć komputera

https://chacker.pl/

Mówiąc najprościej, pamięć komputera to elektroniczny mechanizm, który ma zdolność przechowywania i odtwarzania danych. Najmniejsza ilość danych, jaką można zapisać, to 1 bit, który może być reprezentowany w pamięci przez 1 lub 0. Kiedy połączysz 4 bity, nazywa się to półbajtem i może reprezentować wartości od 0000 do –1111. Istnieje dokładnie 16 wartości binarnych, od 0 do 15, w formacie dziesiętnym. Kiedy połączysz razem dwa półbajty, czyli 8 bitów, otrzymasz bajt, który może reprezentować wartości od 0 do (28 – 1) lub od 0 do 255 w systemie dziesiętnym. Kiedy połączysz 2 bajty, otrzymasz słowo, które może reprezentować wartości od 0 do (216 – 1) lub od 0 do 65 535 w systemie dziesiętnym. Kontynuując łączenie danych, jeśli połączysz dwa słowa, otrzymasz słowo podwójne, czyli DWORD, które może reprezentować wartości od 0 do (232 – 1) lub od 0 do 4 294 967 295 w systemie dziesiętnym. Dwa DWORD razem to poczwórne słowo, czyli QWORD, które może reprezentować wartości od 0 do (264 – 1) lub od 0 do 18 446 744 073 709 551 615 w formacie dziesiętnym. Jeśli chodzi o adresowanie pamięci w 64-bitowych procesorach AMD i Intel, wykorzystywane jest tylko dolne 48 bitów, co daje 256 terabajtów adresowalnej pamięci. Jest to dobrze udokumentowane w niezliczonych zasobach internetowych. Istnieje wiele rodzajów pamięci komputerowej; skupimy się na pamięci o dostępie swobodnym (RAM) i rejestrach. Rejestry to specjalne formy pamięci wbudowane w procesory, które zostaną omówione w dalszej części.

Kompilacja za pomocą gcc

https://chacker.pl/

Kompilacja to proces przekształcania kodu źródłowego czytelnego dla człowieka w pliki binarne nadające się do odczytu maszynowego, które mogą zostać przetworzone przez komputer i wykonane. Mówiąc dokładniej, kompilator pobiera kod źródłowy i tłumaczy go na pośredni zestaw plików zwany kodem wynikowym. Pliki te są prawie gotowe do wykonania, ale mogą zawierać nierozwiązane odniesienia do symboli i funkcji, które nie są zawarte w oryginalnym pliku kodu źródłowego. Te symbole i odniesienia są rozpoznawane w procesie zwanym łączeniem, ponieważ każdy plik obiektowy jest łączony w wykonywalny plik binarny. Uprościliśmy dla Ciebie ten proces, ale oto główne kroki. Podczas programowania w języku C w systemach Unix większość programistów woli używać kompilatora GNU C (gcc). gcc oferuje wiele opcji podczas kompilacji. Najczęściej używane flagi wymieniono i opisano w tabeli

meet.c

https://chacker.pl/

A teraz coś nieco bardziej złożonego. Ten program pobierze dane wejściowe, zapisze je, a następnie wydrukuje:

Program ten pobiera dwa argumenty wiersza poleceń (1) i wywołuje funkcję greeting() (2), która wypisuje „Hello” i podaną nazwę, po której następuje powrót karetki (3). Kiedy funkcja greeting() zakończy się, sterowanie wraca do funkcji main(), która wypisuje „Bye” i podaną nazwę (4). Na koniec program kończy pracę (5).

Przykładowe programy

https://chacker.pl/

Możesz teraz przejrzeć swój pierwszy program.

 hello.c

Zaczniemy od pokazania programu z // komentarzami, a następnie omówimy program.

Ten bardzo prosty program wypisuje komunikat „Hello haxor!” na ekran za pomocą funkcji printf, zawartej w bibliotece stdio.h. Spróbuj go skompilować, skoro już wiesz, jak to zrobić, i uruchom go!

if/else

https://chacker.pl/

Konstrukcja if/else służy do wykonania serii instrukcji, jeśli spełniony zostanie określony warunek; w przeciwnym razie wykonywany jest opcjonalny blok instrukcji else. Jeśli nie ma bloku instrukcji else, działanie programu będzie kontynuowane po zakończeniu nawiasu zamykającego blok if (}). Poniżej znajduje się przykład konstrukcji if/else zagnieżdżonej w pętli for:

W tym przykładzie używamy pętli while(1) do przeglądania instrukcji if/else. Zanim wejdziemy w pętlę, ustawiamy zmienną x na 0. Ponieważ x jest równe 0, spełniamy warunek z instrukcji if (2). Następnie wywołujemy funkcję printf, zwiększamy x o 1 i kontynuujemy. Ponieważ x wynosi teraz 1, nie spełniamy warunku instrukcji if podczas drugiej iteracji w pętli. Dlatego przechodzimy do instrukcji else (3), która wywołuje funkcję printf, a następnie przerywa (4) pętlę. W przypadku pojedynczych instrukcji można pominąć nawiasy klamrowe.

Uwagi

Aby ułatwić czytelność i udostępnianie kodu źródłowego, programiści umieszczają w kodzie komentarze. Możesz użyć jednego z dwóch sposobów umieszczania komentarzy w kodzie: // lub /* i */. Typ komentarza // wskazuje, że wszelkie znaki znajdujące się w pozostałej części wiersza mają być traktowane jako komentarze i komputer nie może na nie reagować podczas wykonywania programu. Para /* i */ rozpoczyna i zatrzymuje blok komentarzy, który może obejmować wiele wierszy. W tym przypadku /* służy do rozpoczęcia komentarza, a */ służy do wskazania końca bloku komentarza.

Pętle

https://chacker.pl/

Pętle są używane w językach programowania do wielokrotnego wykonywania serii poleceń. Dwa popularne typy to pętle for i while. for pętle rozpocznij zliczanie od wartości początkowej, sprawdź wartość pod kątem jakiegoś warunku, wykonaj instrukcję i zwiększ wartość w następnej iteracji. Format jest następujący:

for(<wartość początkowa>; <wartość testowa>; <wartość zmiany>){

<instrukcja>;

}

Dlatego pętla for jak

for(i=0; i<10; i++){

printf(„%d”, i);

}

wypisze cyfry od 0 do 9 w tej samej linii (ponieważ \n nie jest używane), w ten sposób:

0123456789.

W przypadku pętli for warunek jest sprawdzany przed iteracją instrukcji w pętli, więc może się zdarzyć, że nawet pierwsza iteracja nie zostanie wykonana. Jeżeli warunek nie jest spełniony, wykonywanie programu po pętli jest kontynuowane.

UWAGA: Ważne jest, aby zwrócić uwagę na użycie operatora mniejszego niż (<) zamiast operatora mniejszego lub równego (<=), co pozwala na wykonanie pętli jeszcze raz, aż do i=10 . Jest to ważna koncepcja, która może prowadzić do błędów typu off-by-one. Zauważ też, że licznik zaczynał się od 0. Jest to powszechne w C i warto się do tego przyzwyczaić. Pętla while służy do iteracji po serii instrukcji, aż do spełnienia warunku. Podstawowy przykład jest następujący:

Pętle mogą być także zagnieżdżane jedna w drugiej.