Bufory

https://chacker.pl/

Termin bufor odnosi się do miejsca przechowywania używanego do odbierania i przechowywania danych do czasu, aż będą mogły zostać obsłużone przez proces. Ponieważ każdy proces może mieć swój własny zestaw buforów, niezwykle ważne jest, aby były one proste; odbywa się to poprzez alokację pamięci w sekcji .data lub .bss pamięci procesu. Pamiętaj, że raz przydzielony bufor ma stałą długość. Bufor może przechowywać dowolny, predefiniowany typ danych; jednak dla naszych celów skupimy się na buforach opartych na ciągach znaków, które służą do przechowywania danych wejściowych użytkownika i zmiennych tekstowych.

Sekcja Środowisko/Argumenty

https://chacker.pl/

Sekcja środowisko/argumenty służy do przechowywania kopii zmiennych na poziomie systemu, które mogą być wymagane przez proces w czasie wykonywania. Na przykład uruchomionemu procesowi udostępniana jest między innymi ścieżka, nazwa powłoki i nazwa hosta. Ta sekcja jest zapisywalna, co pozwala na jej wykorzystanie w exploitach związanych z przepełnieniem ciągu formatującego i bufora. Dodatkowo w tym obszarze przechowywane są argumenty wiersza poleceń. Sekcje pamięci znajdują się w przedstawionej kolejności. Przestrzeń pamięci procesu wygląda następująco:

Programy w pamięci

https://chacker.pl/

Kiedy procesy są ładowane do pamięci, są zasadniczo podzielone na wiele małych sekcji. Zajmujemy się tylko sześcioma głównymi sekcjami, które omówimy.

Sekcja .text

Sekcja .text, znana również jako segment kodu, zasadniczo odpowiada części .text binarnego pliku wykonywalnego. Zawiera instrukcje maszynowe umożliwiające wykonanie zadania. Ta sekcja jest oznaczona jako czytelna i wykonywalna, a próba zapisu spowoduje naruszenie zasad dostępu. Rozmiar jest ustalany w czasie wykonywania, kiedy proces jest ładowany po raz pierwszy.

Sekcja .data

Sekcja .data służy do przechowywania inicjowanych zmiennych globalnych, takich jak

int a = 0;

Rozmiar tej sekcji jest ustalany w czasie wykonywania. Należy go jedynie oznaczyć jako czytelny.

Sekcja .bss

Poniższa sekcja stosu (.bss) służy do przechowywania niektórych typów niezainicjowanych zmiennych globalnych, takich jak int a; Rozmiar tej sekcji jest ustalany w czasie wykonywania. Segment ten musi być czytelny i zapisywalny, ale nie powinien być wykonywalny.

Sekcja sterty

Sekcja sterty służy do przechowywania dynamicznie przydzielanych zmiennych i rośnie od pamięci o niższym adresie do pamięci o wyższym adresie. Alokacją pamięci steruje się za pomocą funkcji malloc(), realloc() i free(). Na przykład, aby zadeklarować liczbę całkowitą i przydzielić pamięć w czasie wykonywania, możesz użyć czegoś takiego:

Sekcja sterty powinna być czytelna i zapisywalna, ale nie powinna być wykonywalna, ponieważ osoba atakująca, która uzyska kontrolę nad procesem, może z łatwością wykonać wykonanie kodu powłoki w obszarach takich jak stos i sterta.

Sekcja stosu

Sekcja stosu służy do śledzenia wywołań funkcji (rekurencyjnie) i w większości systemów rośnie od pamięci o wyższym adresie do pamięci o niższym adresie. Jeśli proces jest wielowątkowy, każdy wątek będzie miał unikalny stos. Jak zobaczysz, fakt, że stos rośnie od dużej pamięci do małej pamięci, pozwala na istnienie tematu przepełnienia bufora. Zmienne lokalne istnieją w sekcji stosu.

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).