Jak uruchomić program bez systemu operacyjnego?

239

Jak samemu uruchomić program bez uruchomionego systemu operacyjnego? Czy można tworzyć programy asemblacyjne, które komputer może ładować i uruchamiać podczas uruchamiania, np. Uruchomić komputer z dysku flash i uruchamiać program na CPU?

użytkownik2320609
źródło
4
Na jakiej architekturze? x86? RAMIĘ?
Kissiel
1
Mówiłem ogólnie, ale najprawdopodobniej x86 lub x64
użytkownik2320609
2
tak, właśnie tak uruchamiają się procesory. nie musi być montażem, C jest często używane z odrobiną asma dla bootstrapu i być może innego wsparcia.
old_timer
24
Pomyśl o tym: gdyby nie było takiej możliwości, jak sam system operacyjny mógłby się uruchomić i uruchomić? :)
Seva Alekseyev

Odpowiedzi:

153

Jak samemu uruchomić program bez uruchomionego systemu operacyjnego?

Umieszczasz swój kod binarny w miejscu, w którym procesor szuka po ponownym uruchomieniu (np. Adres 0 na ARM).

Czy można tworzyć programy asemblacyjne, które komputer może ładować i uruchamiać podczas uruchamiania (np. Uruchomić komputer z dysku flash i uruchamia program znajdujący się na dysku)?

Ogólna odpowiedź na pytanie: można to zrobić. Często nazywane jest „programowaniem bez systemu metalowego”. Aby czytać z dysku flash, chcesz wiedzieć, co to jest USB i chcesz mieć sterownik do pracy z tym USB. Program na tym dysku musiałby również mieć określony format, na określonym systemie plików ... Jest to coś, co zwykle robią programy ładujące, ale twój program może zawierać własny program ładujący, więc jest samodzielny, jeśli oprogramowanie układowe będzie tylko załaduj mały blok kodu.

Wiele tablic ARM pozwala ci robić niektóre z tych rzeczy. Niektóre mają moduły ładujące, które pomagają w podstawowej konfiguracji.

Tutaj możesz znaleźć świetny samouczek na temat wykonywania podstawowego systemu operacyjnego na Raspberry Pi.

Edytuj: ten artykuł, a cała wiki.osdev.org odpowie na większość twoich pytań http://wiki.osdev.org/Introduction

Ponadto, jeśli nie chcesz eksperymentować bezpośrednio ze sprzętem, możesz uruchomić go jako maszynę wirtualną przy użyciu hiperwizorów, takich jak qemu. Zobacz, jak uruchomić „hello world” bezpośrednio na zwirtualizowanym sprzęcie ARM tutaj .

Kissiel
źródło
723

Przykłady możliwe do uruchomienia

Stwórzmy i uruchommy kilka maleńkich programów typu hello world, które działają bez systemu operacyjnego na:

Wypróbujemy je również na emulatorze QEMU w jak największym stopniu, ponieważ jest to bezpieczniejsze i wygodniejsze w rozwoju. Testy QEMU przeprowadzono na hoście Ubuntu 18.04 z wstępnie spakowanym pakietem QEMU 2.11.1.

Kod wszystkich przykładów x86 poniżej i więcej jest obecny w tym repozytorium GitHub .

Jak uruchomić przykłady na prawdziwym sprzęcie x86

Pamiętaj, że uruchamianie przykładów na prawdziwym sprzęcie może być niebezpieczne, np. Możesz przypadkowo wyczyścić dysk lub uszkodzić sprzęt: rób to tylko na starych komputerach, które nie zawierają krytycznych danych! Lub jeszcze lepiej, użyj tanich półprzezroczystych devboardów, takich jak Raspberry Pi, patrz przykład ARM poniżej.

W przypadku typowego laptopa x86 musisz zrobić coś takiego:

  1. Wypal obraz na pendrivie (zniszczy twoje dane!):

    sudo dd if=main.img of=/dev/sdX
    
  2. podłącz USB do komputera

  3. włącz to

  4. powiedz, żeby uruchomił się z USB.

    Oznacza to, że oprogramowanie układowe wybiera USB przed dyskiem twardym.

    Jeśli nie jest to domyślne zachowanie twojego komputera, po włączeniu naciskaj Enter, F12, ESC lub inne takie dziwne klawisze, aż pojawi się menu rozruchu, w którym możesz wybrać rozruch z USB.

    Często w tych menu można skonfigurować kolejność wyszukiwania.

Na przykład na moim T430 widzę następujące.

Po włączeniu muszę nacisnąć klawisz Enter, aby wejść do menu rozruchu:

wprowadź opis zdjęcia tutaj

Następnie tutaj muszę nacisnąć F12, aby wybrać USB jako urządzenie rozruchowe:

wprowadź opis zdjęcia tutaj

Stamtąd mogę wybrać USB jako urządzenie rozruchowe w następujący sposób:

wprowadź opis zdjęcia tutaj

Alternatywnie, aby zmienić kolejność rozruchu i wybrać USB, który ma wyższy priorytet, więc nie muszę go wybierać ręcznie za każdym razem, uderzyłbym F1 na ekranie „Menu przerwania uruchamiania”, a następnie nawiguję do:

wprowadź opis zdjęcia tutaj

Sektor rozruchowy

Na x86 najprostszym i najniższym poziomem, co możesz zrobić, jest utworzenie głównego sektora rozruchowego (MBR) , który jest rodzajem sektora rozruchowego , a następnie zainstalowanie go na dysku.

Tutaj tworzymy jedną za pomocą jednego printfpołączenia:

printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img

Wynik:

wprowadź opis zdjęcia tutaj

Pamiętaj, że nawet bez robienia kilku znaków jest już wydrukowanych na ekranie. Są one drukowane przez oprogramowanie układowe i służą do identyfikacji systemu.

A na T430 dostajemy pusty ekran z migającym kursorem:

wprowadź opis zdjęcia tutaj

main.img zawiera następujące elementy:

  • \364in octal == 0xf4in hex: kodowanie hltinstrukcji, która każe CPU przestać działać.

    Dlatego nasz program nic nie zrobi: wystarczy uruchomić i zatrzymać.

    Używamy ósemki, ponieważ \xliczby szesnastkowe nie są określone przez POSIX.

    Możemy łatwo uzyskać to kodowanie za pomocą:

    echo hlt > a.S
    as -o a.o a.S
    objdump -S a.o
    

    które wyjścia:

    a.o:     file format elf64-x86-64
    
    
    Disassembly of section .text:
    
    0000000000000000 <.text>:
       0:   f4                      hlt
    

    ale jest to również oczywiście udokumentowane w podręczniku Intela.

  • %509swyprodukuj 509 miejsc. Konieczne jest wypełnienie pliku do bajtu 510.

  • \125\252ósemkowo ==, 0x55po której następuje 0xaa.

    Są to 2 wymagane bajty magiczne, które muszą być bajtami 511 i 512.

    BIOS przeszukuje wszystkie nasze dyski w poszukiwaniu dysków rozruchowych i bierze pod uwagę tylko te, które mają te dwa bajty.

    Jeśli nie jest obecny, sprzęt nie będzie traktował tego jako dysku rozruchowego.

Jeśli nie jesteś printfmistrzem, możesz potwierdzić zawartość za main.imgpomocą:

hd main.img

który pokazuje oczekiwane:

00000000  f4 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |.               |
00000010  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
*
000001f0  20 20 20 20 20 20 20 20  20 20 20 20 20 20 55 aa  |              U.|
00000200

gdzie 20jest spacja w ASCII.

Oprogramowanie układowe BIOS odczytuje te 512 bajtów z dysku, umieszcza je w pamięci i ustawia komputer na pierwszy bajt, aby rozpocząć ich wykonywanie.

Witaj, sektorze rozruchowym świata

Teraz, gdy stworzyliśmy minimalny program, przejdźmy do cześć świata.

Oczywiste pytanie brzmi: jak zrobić IO? Kilka opcji:

  • poprosić oprogramowanie wewnętrzne, np. BIOS lub UEFI, aby zrobiło to za nas

  • VGA: specjalny region pamięci, który zostanie wydrukowany na ekranie, jeśli zostanie zapisany. Może być używany w trybie chronionym.

  • napisz sterownik i porozmawiaj bezpośrednio ze sprzętem wyświetlającym. Jest to „właściwy” sposób na zrobienie tego: mocniejszy, ale bardziej złożony.

  • port szeregowy . Jest to bardzo prosty znormalizowany protokół, który wysyła i odbiera znaki z terminala hosta.

    Na komputerach wygląda to tak:

    wprowadź opis zdjęcia tutaj

    Źródło .

    Niestety nie jest narażony na większość współczesnych laptopów, ale jest powszechną drogą do tworzenia płyt deweloperskich, patrz przykłady ARM poniżej.

    To naprawdę szkoda, ponieważ takie interfejsy są naprawdę przydatne na przykład do debugowania jądra Linuksa .

  • użyj funkcji debugowania układów. ARM na przykład nazywa ich semihosting . Na prawdziwym sprzęcie wymaga dodatkowej obsługi sprzętu i oprogramowania, ale na emulatorach może być bezpłatną wygodną opcją. Przykład .

Tutaj zrobimy przykład systemu BIOS, ponieważ jest on prostszy na x86. Pamiętaj jednak, że nie jest to najbardziej niezawodna metoda.

sieć elektryczna

.code16
    mov $msg, %si
    mov $0x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int $0x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"

GitHub w górę .

link.ld

SECTIONS
{
    /* The BIOS loads the code from the disk to this location.
     * We must tell that to the linker so that it can properly
     * calculate the addresses of symbols we might jump to.
     */
    . = 0x7c00;
    .text :
    {
        __start = .;
        *(.text)
        /* Place the magic boot bytes at the end of the first 512 sector. */
        . = 0x1FE;
        SHORT(0xAA55)
    }
}

Złóż i połącz z:

as -g -o main.o main.S
ld --oformat binary -o main.img -T link.ld main.o
qemu-system-x86_64 -hda main.img

Wynik:

wprowadź opis zdjęcia tutaj

A na T430:

wprowadź opis zdjęcia tutaj

Testowane na: Lenovo Thinkpad T430, UEFI BIOS 1.16. Dysk wygenerowany na hoście Ubuntu 18.04.

Oprócz standardowych instrukcji montażu dla użytkownika, mamy:

  • .code16: informuje GAS, aby wyprowadził 16-bitowy kod

  • cli: wyłącz przerwania programowe. Mogą one spowodować, że procesor zacznie ponownie działać pohlt

  • int $0x10: wykonuje połączenie BIOS. To właśnie drukuje znaki jeden po drugim.

Ważne flagi linków to:

  • --oformat binary: wypisuje nieprzetworzony kod binarnego zestawu, nie zawijaj go w pliku ELF, jak ma to miejsce w przypadku zwykłych plików wykonywalnych dla użytkownika.

Aby lepiej zrozumieć część skryptu linkera, zapoznaj się z krokiem relokacji linkowania: Co robią linkery?

Chłodniejsze programy x86 bez systemu metalowego

Oto kilka bardziej skomplikowanych ustawień bez systemu metalowego:

Użyj C zamiast montażu

Podsumowanie: użyj GRUB multiboot, który rozwiąże wiele irytujących problemów, o których nigdy nie pomyślałeś. Sekcja poniżej.

Główną trudnością na x86 jest to, że BIOS ładuje tylko 512 bajtów z dysku do pamięci, i możesz wysadzić te 512 bajtów podczas używania C!

Aby rozwiązać ten problem, możemy użyć dwustopniowego programu ładującego . Powoduje to kolejne wywołania systemu BIOS, które ładują więcej bajtów z dysku do pamięci. Oto minimalny przykład montażu etapu 2 od zera przy użyciu wywołań int 0x13 BIOS :

Alternatywnie:

  • jeśli potrzebujesz go tylko do pracy w QEMU, ale nie na prawdziwym sprzęcie, skorzystaj z -kernelopcji, która ładuje cały plik ELF do pamięci. Oto przykład ARM, który utworzyłem tą metodą .
  • w przypadku Raspberry Pi domyślne oprogramowanie układowe zajmuje się ładowaniem obrazu z pliku o nazwie ELF kernel7.img, podobnie jak -kernelrobi to QEMU .

Tylko do celów edukacyjnych, oto przykładowy minimalny przykład C w jednym etapie :

main.c

void main(void) {
    int i;
    char s[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
    for (i = 0; i < sizeof(s); ++i) {
        __asm__ (
            "int $0x10" : : "a" ((0x0e << 8) | s[i])
        );
    }
    while (1) {
        __asm__ ("hlt");
    };
}

wejście S.

.code16
.text
.global mystart
mystart:
    ljmp $0, $.setcs
.setcs:
    xor %ax, %ax
    mov %ax, %ds
    mov %ax, %es
    mov %ax, %ss
    mov $__stack_top, %esp
    cld
    call main

linker.ld

ENTRY(mystart)
SECTIONS
{
  . = 0x7c00;
  .text : {
    entry.o(.text)
    *(.text)
    *(.data)
    *(.rodata)
    __bss_start = .;
    /* COMMON vs BSS: /programming/16835716/bss-vs-common-what-goes-where */
    *(.bss)
    *(COMMON)
    __bss_end = .;
  }
  /* /programming/53584666/why-does-gnu-ld-include-a-section-that-does-not-appear-in-the-linker-script */
  .sig : AT(ADDR(.text) + 512 - 2)
  {
      SHORT(0xaa55);
  }
  /DISCARD/ : {
    *(.eh_frame)
  }
  __stack_bottom = .;
  . = . + 0x1000;
  __stack_top = .;
}

biegać

set -eux
as -ggdb3 --32 -o entry.o entry.S
gcc -c -ggdb3 -m16 -ffreestanding -fno-PIE -nostartfiles -nostdlib -o main.o -std=c99 main.c
ld -m elf_i386 -o main.elf -T linker.ld entry.o main.o
objcopy -O binary main.elf main.img
qemu-system-x86_64 -drive file=main.img,format=raw

Biblioteka standardowa C.

Sprawa staje się jeszcze przyjemniejsza, jeśli chcesz również korzystać ze standardowej biblioteki C, ponieważ nie mamy jądra Linux, które implementuje większość standardowych funkcji biblioteki C za pośrednictwem POSIX .

Kilka możliwości, bez przechodzenia na w pełni funkcjonalny system operacyjny, taki jak Linux, to:

  • Napisz swoje własne. To tylko garść nagłówków i plików C, prawda? Dobrze??

  • Newlib

    Szczegółowy przykład na stronie : /electronics/223929/c-standard-libraries-on-bare-metal/223931

    Newlib realizuje wszystkie rzeczy nudne non-OS specyficzne dla ciebie, na przykład memcmp, memcpyitd

    Następnie udostępnia kilka kodów pośredniczących umożliwiających wdrożenie potrzebnych połączeń systemowych.

    Na przykład możemy zaimplementować exit()na ARM poprzez semihosting z:

    void _exit(int status) {
        __asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456");
    }
    

    jak pokazano w tym przykładzie .

    Na przykład, można przekierować printfdo układów UART lub ARM, lub realizować exit()z semihosting .

  • wbudowane systemy operacyjne, takie jak FreeRTOS i Zephyr .

    Takie systemy operacyjne zazwyczaj umożliwiają wyłączenie planowania wyprzedzającego, co daje pełną kontrolę nad czasem działania programu.

    Można je postrzegać jako rodzaj wstępnie wdrożonego Newlib.

GNU GRUB Multiboot

Sektory rozruchowe są proste, ale nie są zbyt wygodne:

  • możesz mieć tylko jeden system operacyjny na dysk
  • kod ładowania musi być naprawdę mały i zmieścić się w 512 bajtach
  • musisz sam dużo uruchomić, na przykład przejść do trybu chronionego

Z tych powodów GNU GRUB stworzył wygodniejszy format pliku o nazwie multiboot.

Minimalny przykład działania: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

Używam go również w moim repozytorium przykładów GitHub, aby móc z łatwością uruchamiać wszystkie przykłady na prawdziwym sprzęcie bez milionowego spalania USB.

Wynik QEMU:

wprowadź opis zdjęcia tutaj

T430:

wprowadź opis zdjęcia tutaj

Jeśli przygotujesz system operacyjny jako plik z wieloma uruchomieniami, GRUB będzie mógł go znaleźć w zwykłym systemie plików.

To właśnie robi większość dystrybucji, umieszczając obrazy systemu operacyjnego /boot.

Pliki Multiboot są w zasadzie plikiem ELF ze specjalnym nagłówkiem. Są one określone przez GRUB pod adresem : https://www.gnu.org/software/grub/manual/multiboot/multiboot.html

Możesz zmienić plik z wieloma uruchomieniami na dysk rozruchowy za pomocą grub-mkrescue.

Oprogramowanie układowe

W rzeczywistości sektor rozruchowy nie jest pierwszym oprogramowaniem działającym na procesorze systemu.

Najpierw uruchamia się tak zwane oprogramowanie układowe , które jest oprogramowaniem:

  • wykonane przez producentów sprzętu
  • zwykle zamknięte źródło, ale prawdopodobnie oparte na C.
  • przechowywane w pamięci tylko do odczytu, a zatem trudniejsze / niemożliwe do modyfikacji bez zgody dostawcy.

Dobrze znane oprogramowanie wewnętrzne obejmuje:

  • BIOS : stare, wszechstronne oprogramowanie x86. SeaBIOS jest domyślną implementacją open source używaną przez QEMU.
  • UEFI : następca systemu BIOS, lepiej ustandaryzowany, ale bardziej wydajny i niesamowicie rozdęty.
  • Coreboot : szlachetna próba otwartego oprogramowania typu cross arch

Oprogramowanie wewnętrzne wykonuje następujące czynności:

  • zapętlaj każdy dysk twardy, USB, sieć itp., aż znajdziesz coś rozruchowego.

    Kiedy uruchamiamy QEMU, -hdamówi, że main.imgjest to dysk twardy podłączony do sprzętu i hdajest pierwszym, który zostanie wypróbowany i jest używany.

  • załaduj pierwsze 512 bajtów na adres pamięci RAM 0x7c00, umieść tam RIP procesora i pozwól mu działać

  • pokaż na ekranie takie menu, jak menu uruchamiania lub wywołania drukowania systemu BIOS

Oprogramowanie układowe oferuje funkcje podobne do systemu operacyjnego, od których zależy większość systemów operacyjnych. Np. Podzestaw Python został przeniesiony do działania w systemie BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM

Można argumentować, że oprogramowanie układowe jest nierozróżnialne od systemów operacyjnych i że oprogramowanie układowe jest jedynym „prawdziwym” programowaniem bez systemu operacyjnego.

Jak to ujął to deweloper CoreOS :

Trudna część

Po włączeniu komputera układy scalone (mostek północny, mostek południowy i SuperIO) nie są jeszcze poprawnie zainicjowane. Chociaż pamięć ROM systemu BIOS jest tak daleko od procesora, jak to tylko możliwe, jest ona dostępna dla procesora, ponieważ musi być, w przeciwnym razie procesor nie miałby instrukcji do wykonania. Nie oznacza to, że BIOS ROM jest całkowicie zmapowany, zwykle nie. Ale tylko tyle jest zmapowane, aby rozpocząć proces rozruchu. Wszelkie inne urządzenia, po prostu zapomnij.

Po uruchomieniu Coreboot w QEMU możesz eksperymentować z wyższymi warstwami Coreboot i ładunkami, ale QEMU oferuje niewiele możliwości eksperymentowania z kodem startowym niskiego poziomu. Po pierwsze, pamięć RAM działa od samego początku.

Stan początkowy po BIOS

Podobnie jak wiele rzeczy w sprzęcie, standaryzacja jest słaba, a jedną z rzeczy, na których nie należy polegać, jest stan początkowy rejestrów, gdy kod zaczyna działać po BIOS.

Więc zrób sobie przysługę i użyj kodu inicjalizacji, takiego jak: https://stackoverflow.com/a/32509555/895245

Rejestruje się %dsi %esma ważne skutki uboczne, więc powinieneś je wyzerować, nawet jeśli nie używasz ich wyraźnie.

Zauważ, że niektóre emulatory są ładniejsze niż prawdziwy sprzęt i zapewniają dobry stan początkowy. Potem, gdy idziesz na prawdziwym sprzęcie, wszystko się psuje.

El Torito

Format, który można wypalić na płytach CD: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29

Możliwe jest również wytworzenie obrazu hybrydowego działającego na ISO lub USB. Można to zrobić za pomocą grub-mkrescue( przykład ), a także przy make isoimageużyciu jądra Linux isohybrid.

RAMIĘ

W ARM ogólne pomysły są takie same.

Nie ma powszechnie dostępnego, częściowo znormalizowanego, wstępnie zainstalowanego oprogramowania układowego, takiego jak BIOS, do użycia dla IO, więc dwa najprostsze typy IO, które możemy wykonać to:

  • serial, który jest szeroko dostępny na devboardach
  • migać diodę LED

Przesłałem:

Niektóre różnice w stosunku do x86 obejmują:

  • IO odbywa się poprzez bezpośrednie pisanie na magiczne adresy, nie ma instrukcji ini outinstrukcji.

    Nazywa się to We / Wy mapowanym na pamięć .

  • w przypadku niektórych prawdziwych urządzeń, takich jak Raspberry Pi, możesz samodzielnie dodać oprogramowanie układowe (BIOS) do obrazu dysku.

    To dobrze, ponieważ sprawia, że ​​aktualizacja tego oprogramowania jest bardziej przejrzysta.

Zasoby

Ciro Santilli
źródło
3
Unikernels to alternatywa dla osób, które nie mogą / nie chcą zejść na tak niskim poziomie i nadal chcą czerpać korzyści z ich bardzo małej powierzchni.
AndreLDM
1
@AndreLDM Chciałem dodać te unikernelowe wiadomości oparte na Linuksie, ale czułem się zbyt nerwowy: next.redhat.com/2018/11/14/ukl-a-unikernel-based-on-linux
Ciro Santilli 郝海东 冠状 病26 事件 法轮功
14
Naprawdę szczegółowa odpowiedź, ale „program działający bez systemu operacyjnego jest systemem operacyjnym” nie jest prawdą. Możesz napisać program, który po prostu miga diodą ON / OFF, ale to nie czyni go systemem operacyjnym. Niektóre kody oprogramowania układowego, które uruchamiają mikrokontroler na dysku flash, nie powodują, że jest to system operacyjny. System operacyjny jest co najmniej warstwą abstrakcji, aby łatwiej pisać inne oprogramowanie. W dzisiejszych czasach to minimum. Powiedziałbym, że jeśli nie ma harmonogramu, to prawdopodobnie nie jest to system operacyjny.
Vitali
4
Dobra odpowiedź, z wyjątkiem absolutnego nonsensu, że każdy program, który nie działa w systemie operacyjnym, jest systemem operacyjnym.
curiousdannii
3
@MichaelPetch hej, żeby zapisać zero w sektorze rozruchowym :-) Prawdopodobnie nie warto.
Ciro Santilli 4 冠状 病 六四 事件 法轮功 法轮功
3

System operacyjny jako inspiracja

System operacyjny jest także programem , więc możemy również stworzyć własny program, tworząc od podstaw lub zmieniając (ograniczając lub dodając) funkcje jednego z małych systemów operacyjnych , a następnie uruchamiając go podczas procesu rozruchu (przy użyciu obrazu ISO ) .

Na przykład tę stronę można wykorzystać jako punkt wyjścia:

Jak napisać prosty system operacyjny

Tutaj cały system operacyjny mieści się całkowicie w 512-bajtowym sektorze rozruchowym ( MBR )!

Taki lub podobny prosty system operacyjny może być wykorzystany do stworzenia prostej struktury, która pozwoli nam:

spraw, by bootloader ładował kolejne sektory z dysku do pamięci RAM i przeskoczył do tego punktu, aby kontynuować wykonywanie . Lub możesz przeczytać na FAT12, systemie plików używanym na dyskietkach i zaimplementować to .

Istnieje jednak wiele możliwości. Na przykład, aby zobaczyć większy system operacyjny w języku asemblera x86 , możemy zapoznać się z systemem operacyjnym MykeOS , x86, który jest narzędziem do nauki pokazującym proste 16-bitowe działanie systemów operacyjnych w trybie rzeczywistym, z dobrze skomentowanym kodem i obszerną dokumentacją .

Boot Loader jako inspiracja

Inne popularne typy programów, które działają bez systemu operacyjnego, to także programy ładujące rozruch . Możemy stworzyć program inspirowany taką koncepcją, na przykład korzystając z tej strony:

Jak opracować własny moduł ładujący

Powyższy artykuł przedstawia także podstawową architekturę takich programów :

  1. Prawidłowe ładowanie do pamięci o 0000: 7C00.
  2. Wywołanie funkcji BootMain opracowanej w języku wysokiego poziomu.
  3. Pokaż komunikat „” Witaj, świecie… ”z niskiego poziomu” na wyświetlaczu.

Jak widzimy, ta architektura jest bardzo elastyczna i pozwala nam na implementację dowolnego programu , niekoniecznie programu ładującego.

W szczególności pokazuje, jak stosować technikę „kodu mieszanego”, dzięki której można łączyć konstrukcje wysokiego poziomu (z C lub C ++ ) z poleceniami niskiego poziomu (z asemblera ). Jest to bardzo przydatna metoda, ale musimy pamiętać, że:

aby zbudować program i uzyskać plik wykonywalny , będziesz potrzebował kompilatora i linkera Assemblera dla trybu 16-bitowego . W przypadku C / C ++ potrzebujesz tylko kompilatora, który może tworzyć pliki obiektowe dla trybu 16-bitowego .

Artykuł pokazuje także, jak zobaczyć stworzony program w akcji oraz jak przeprowadzić jego testowanie i debugowanie.

Aplikacje UEFI jako inspiracja

W powyższych przykładach wykorzystano fakt ładowania MBR sektora na nośnik danych. Możemy jednak zagłębić się w otchłań , grając na przykład w aplikacjach UEFI :

Oprócz ładowania systemu operacyjnego UEFI może uruchamiać aplikacje UEFI, które znajdują się jako pliki na partycji systemowej EFI. Można je wykonać z powłoki poleceń UEFI, menedżera rozruchowego oprogramowania układowego lub innych aplikacji UEFI. Aplikacje UEFI można opracowywać i instalować niezależnie od producenta systemu.

Typem aplikacji UEFI jest moduł ładujący system operacyjny, taki jak GRUB, rEFInd, Gummiboot i Windows Boot Manager; który ładuje plik systemu operacyjnego do pamięci i wykonuje go. Ponadto moduł ładujący system operacyjny może zapewnić interfejs użytkownika, który umożliwia wybór innej aplikacji UEFI do uruchomienia. Narzędzia takie jak powłoka UEFI są również aplikacjami UEFI.

Jeśli chcielibyśmy zacząć tworzyć takie programy , możemy na przykład zacząć od tych stron internetowych:

Programowanie dla EFI: tworzenie programu „Hello, World” / Programowanie UEFI - pierwsze kroki

Inspiracją jest zgłębianie zagadnień bezpieczeństwa

Powszechnie wiadomo, że istnieje cała grupa złośliwego oprogramowania przed uruchomieniem systemu operacyjnego działa (które są programami) .

Ogromna grupa z nich działa w sektorze MBR lub aplikacjach UEFI, podobnie jak wszystkie powyższe rozwiązania, ale są też takie, które wykorzystują inny punkt wejścia, taki jak Volume Boot Record (VBR) lub BIOS :

Istnieją co najmniej cztery znane wirusy atakujące BIOS , z których dwa były przeznaczone do celów demonstracyjnych.

a może jeszcze jeden.

Ataki przed uruchomieniem systemu

Bootkity ewoluowały od rozwoju Proof-of-Concept do dystrybucji masowej i teraz skutecznie stają się oprogramowaniem typu open source .

Różne sposoby uruchamiania

Myślę również, że w tym kontekście warto również wspomnieć, że istnieją różne formy uruchamiania systemu operacyjnego (lub przeznaczonego do tego programu wykonywalnego) . Istnieje wiele, ale chciałbym zwrócić uwagę na ładowanie kodu z sieci za pomocą opcji rozruchu sieciowego ( PXE ), która pozwala nam uruchamiać program na komputerze niezależnie od systemu operacyjnego, a nawet niezależnie od dowolnego nośnika pamięci, który jest bezpośrednio podłączony do komputera:

Co to jest ładowanie sieciowe (PXE) i jak z niego korzystać?

simhumileco
źródło