niedziela, 24 maja 2009

ASM: pisanie własnego MBR cz.1

Pisanie programów komputerowych w ASM nie jest aktualnie zbyt popularne. Trzeba jednak przyznać, że w rękach sprawnego programisty język ten powinien dać najlepsze możliwości. No i oczywiście kod będzie najwydajniejszy. Assembler sprawdza się wszędzie tam, gdzie zasoby są ograniczone. Bez wątpienia pisanie Bootloadera bez ASM nie ma za wiele sensu.

Ten artykuł to dopiero wstęp do tego zajęcia. Zabawa z ASM i samym MBR to niestety zajęcie na wiele godzin, zważywszy na fakt, że materiały są ograniczone. Ponadto pojawiają się trudności z kompilatorami, które tak trywialnego kodu widać kompilować nie chcą.

Po kiego grzyba pisać własny Bootloader? Albo raczej cokolwiek na siłę samodzielnie wpisywać w MBR? Jak pamiętamy swego czasu dość popularne były (może nawet jeszcze są) wirusy infekujące właśnie ten obszar dysku. Powiedzmy, że zamarzyło mi się sprawdzić się w takim kodowaniu. Ponadto chciałem sprawdzić jakie są aktualnie możliwości takich infekcji przy najnowszych systemach z rodziny Windows.

Systemy Windows z rodziny NT bootują bezpośrednio. W prawdzie istnieją takie pliki jak autoexec.bat, czy config.sys, ale nie są wykorzystywane. Pojęcia nie mam czy cokolwiek robią w systemie nowszym niż Windows Me. Z poziomu systemu windows uruchomienie aplikacji piszącej do sektora na dysku (dowolnego) powoduje monit o zagrożeniu. Ostrzeżenie systemu można zignorować, jednak zapis do dysku nie specjalnie się uda. MBR jest przecież pod specjalną ochroną OS. W takim razie wydaje się, że nie bardzo istnieje możliwość zautomatycznego uszkodzenia MBR. Istnieją jednak całe 3 możliwości:
  • stworzenie aplikacji podobnej do scandisk
  • zarejestrowana usługa systemowa w Windows (mamy dostęp do wszystkiego w systemie)
  • napisanie bootsectora i edycja pliku boot.ini
Pierwszy punkt brzmi tak mało profesjonalnie w opisie. Wiele się na tej opcji w swoich poszukiwaniach nie skupiałem. Chodzi coś o natywne usługi. Są to twory, które mają dostęp do wszelkich zasobów komputera na najwyższym poziomie, do tego uruchamiają się zanim system cokolwiek zablokuje. Absolutnie nie są to aplikacje DOSowe, jak się niekiedy można doczytać w różnych miejscach.

Drugi punkt wymaga od nas zainstalowania programu jako usługi. Na własnym komputerze nie ma problemu, ale co z infekcją? Wyrzucimy na ekran monit: "czy chcesz zainstalować naszego wirusa?"?. No nie wygląda to zbyt fajnie.

Trzecia opcja polega na oszukaniu mechanizmu bootowania systemów z rodziny NT. Proces bootowania Windows z rodziny NT jest dość ciekawy i co ważne pozwala na wybór wśród wielu systemów. Bootloader od Redmont potrafi bootować nawet systemy z rodziny *nix (podobno, bo nie sprawdzałem nigdy). Proces jego działania polega jednak na wczytaniu opcji z pliku boot.ini. Tutaj mamy pole do popisu. Plik ten zawiera informacje o tym gdzie szukać bootloaderów dla konkretnych systemów zainstalowanych na maszynie. Posiada też opcje konfiguracyjne, jak domyślny system, albo czas na wybór. Daleko temu do Lilo, czy GRUB'a, które mogą mieć dowolne tło, ale zawsze lepszy rydz niż nic. Słabość tego systemu polega na tym, że plik ten można po prostu zedytować i dodając 2 linijki i zmieniając jedną uprzykrzyć użytkownikowi życie. Można też korzystając z materiałów w sieci stworzyć własny bootloader dorównujący projektom opensource i podmienić go dla Windows. Wiedząc jak powinien być zbudowany MBR możemy to zrobić poprawnie i na dodatek oszukać windows, aby myślał, że to jego własny twór.

Plik boot.ini pozwala na dodanie opcji bootowania dla systemu z rodziny "dosowatych", dla przykładu Win98. Aby to uczynić dodajemy linijkę:
c:\="Windows 98"

Tyle już wystarczy. Mamy opcję wyboru dla systemu Windows 98, którego na maszynie z resztą nie ma. To oczywiście nieistotne. Gdy opcja ta zostaje wybrana do pamięci zostaje załadowany Bootsector dla tego systemu i przekazane do niego sterowanie. Bootsector jest ładowany z pliku Bootsect.dos i tam właśnie musi znaleźć się nasz własny bootloader. Nie trudno się domyśleć, że zamiast starać się na siłę pisać do MBR wystarczy nam napisać do tego pliku kod.

Jako mroczni i źli haxiorzy chcemy, aby ten kod został w najbliższej możliwej chwili uruchomiony, więc tą opcję trzeba ustawić jako domyślną, a czas oczekiwania na decyzję zmniejszyć na tyle, aby użytkownik się nie połapał. Zainteresowani znajdą jak to zrobić w konfiguracji systemu Windows. Nie wydaje się, aby była potrzeba to opisywać. Ten art ma posłużyć edukacji, a nie szkoleniu lepiej rozgarniętych dzieciaczków pragnących zniszczyć świat, albo przynajmniej komputer tatusia.

Na koniec wstępu warto podać jakie są założenia i ograniczenia. Kod, który chcę stworzyć ma być wirusem, który nie specjalnie ma cokolwiek niszczyć. Poza MBR w którym się zadomowi nie uszkodzi nic, a jego głównym celem po uruchomieniu komputera może być na przykład zrobienie matrixa na ekranie przez wyświetlenie zawartości pamięci, która jak wiemy pusta nie jest. Ograniczenia przy pisaniu takiego kodu wyglądają następująco:
  • mały rozmiar: tylko 510B do dyspozycji, 2 ostatnie Bajty zarezerwowane, razem 512B
  • tylko przerwania BIOS - w końcu nie ma OS z którego można skorzystać
  • kod jest umieszczany pod adresem 0000:7C00, wszystkie wskaźniki muszą tam wskazywać aby się nie pogubić
Może warto jeszcze wspomnieć o pewnej drobnostce. Kod, który piszę, ale też te którymi się posiłkuję, musi zostać skompilowany i sprawdzony. W tym celu potrzebne nam:
  • kompilator ASM - NASM
  • maszyna do testów - Maszyna VirtualBox
  • System z rodziny WinNT - Windows XP Professional (dokładnie to XPLite, ale to bez znaczenia)
Ta konfiguracja pozwala na testy niezależnie od tego czy na co dzień pracujemy pod Win, czy *nix. Bardzo ważne jest, aby użyć NASM. Osobiście zawsze preferowałem TASM'a, jednak przy tym projekcie natknąłem się na kilka problemów, które mocno mnie zniechęciły. Nawet próbowałem przez chwilę korzystać z MASM, ale ten też się nie sprawdził. Nawet kod z samym NOP z góry na dół sypie Errorami. W dalszej części, albo pewnie na początku następnego arta od razu wyjaśnię co zdyskwalifikowało TASM'a. Przyda się jednak do tego przykład kodu, którego tutaj nawet nie chce mi się jeszcze przytaczać.

Jak widać jest to dość ekstremalne programowanie. Jeśli pomyśleć, że ASCII Art rozmiaru 80x25 ma 2000B, to niestety musimy o tym zapomnieć (chyba, że zapiszemy go na dysku w znanym sobie sektorze i wczytamy go gdzieś i wyświetlimy).

Tyle tytułem wstępu i tytułem wyjaśnień. W kolejnych (kolejnej?) częściach pojawi się już konkretny kod i odnośniki do źródeł, gdzie można znaleźć potrzebne informacje. Proszę o cierpliwość. Zadanie na prawdę proste nie jest, szczególnie że jestem zadeklarowanym programistą wysokopoziomowym.

Obraz pochodzi z Pointer Rulez.

Zobacz też:

2 komentarze:

  1. a musisz miec pelne ascii? no bo jezeli zdecydujesz sie tylko na gwiazdki i spacje to masz 10 bajtow*25, czyli 250 ;)
    pozdrawiam

    OdpowiedzUsuń
  2. Tutaj nie wspomniałem w ogóle o koncepcji upakowania danych w jakiś sposób przy ASCII arcie. Oczywiście, że jest to możliwe na różne sposoby. Choćby tak jak wspomniałeś przez użycie jednego bitu do kodowania znaku.

    OdpowiedzUsuń