Wie führe ich ein Programm ohne Betriebssystem aus?

239

Wie kann man ein Programm alleine ausführen, ohne dass ein Betriebssystem ausgeführt wird? Können Sie Assembly-Programme erstellen, die der Computer beim Start laden und ausführen kann, z. B. den Computer von einem Flash-Laufwerk starten und das Programm ausführen, das sich auf der CPU befindet?

user2320609
quelle
4
Auf welcher Architektur? x86? ARM?
Kissiel
1
Ich sprach allgemein, aber höchstwahrscheinlich x86 oder x64
user2320609
2
Ja, genau so booten Prozessoren. muss nicht Assembly sein, C wird oft mit ein wenig Asm für einen Bootstrap und vielleicht eine andere Unterstützung verwendet.
old_timer
23
Denken Sie daran: Wenn es keine solche Funktion gäbe, wie würde das Betriebssystem selbst starten und ausgeführt? :)
Seva Alekseyev

Antworten:

153

Wie kann man ein Programm alleine ausführen, ohne dass ein Betriebssystem ausgeführt wird?

Sie platzieren Ihren Binärcode an einer Stelle, nach der der Prozessor nach dem Neustart sucht (z. B. Adresse 0 auf ARM).

Können Sie Assembly-Programme erstellen, die der Computer beim Start laden und ausführen kann (z. B. den Computer von einem Flash-Laufwerk starten und das auf dem Laufwerk befindliche Programm ausführen)?

Allgemeine Antwort auf die Frage: Es kann getan werden. Es wird oft als "Bare-Metal-Programmierung" bezeichnet. Um vom Flash-Laufwerk zu lesen, möchten Sie wissen, was USB ist, und Sie möchten einen Treiber haben, der mit diesem USB funktioniert. Das Programm auf diesem Laufwerk müsste auch in einem bestimmten Format vorliegen, in einem bestimmten Dateisystem ... Dies ist etwas, was Bootloader normalerweise tun, aber Ihr Programm könnte einen eigenen Bootloader enthalten, so dass es in sich geschlossen ist, wenn die Firmware dies nur tut Laden Sie einen kleinen Codeblock.

Auf vielen ARM-Boards können Sie einige dieser Dinge tun. Einige haben Bootloader, die Ihnen bei der Grundeinstellung helfen.

Hier finden Sie ein großartiges Tutorial zum Ausführen eines grundlegenden Betriebssystems auf einem Raspberry Pi.

Bearbeiten: Dieser Artikel und die gesamte wiki.osdev.org werden die meisten Ihrer Fragen beantworten http://wiki.osdev.org/Introduction

Wenn Sie nicht direkt mit Hardware experimentieren möchten, können Sie diese auch als virtuelle Maschine mit Hypervisoren wie qemu ausführen. Sehen Sie, wie „Hallo Welt“ direkt auf virtualisierten ARM Hardware laufen hier .

Kissiel
quelle
722

Ausführbare Beispiele

Lassen Sie uns einige winzige Bare-Metal-Hallo-Welt-Programme erstellen und ausführen, die ohne Betriebssystem ausgeführt werden:

Wir werden sie auch so oft wie möglich auf dem QEMU-Emulator ausprobieren, da dies sicherer und bequemer für die Entwicklung ist. Die QEMU-Tests wurden auf einem Ubuntu 18.04-Host mit der vorgefertigten QEMU 2.11.1 durchgeführt.

Der Code aller folgenden x86-Beispiele und mehr ist in diesem GitHub-Repo enthalten .

So führen Sie die Beispiele auf realer x86-Hardware aus

Denken Sie daran, dass das Ausführen von Beispielen auf realer Hardware gefährlich sein kann, z. B. wenn Sie versehentlich Ihre Festplatte löschen oder die Hardware blockieren: Tun Sie dies nur auf alten Computern, die keine kritischen Daten enthalten! Oder noch besser, verwenden Sie billige Einweg-Devboards wie den Raspberry Pi (siehe ARM-Beispiel unten).

Für einen typischen x86-Laptop müssen Sie Folgendes tun:

  1. Brennen Sie das Bild auf einen USB-Stick (zerstört Ihre Daten!):

    sudo dd if=main.img of=/dev/sdX
    
  2. Schließen Sie den USB an einen Computer an

  3. Mach es an

  4. Sagen Sie ihm, er soll vom USB booten.

    Dies bedeutet, dass die Firmware vor der Festplatte USB auswählt.

    Wenn dies nicht das Standardverhalten Ihres Computers ist, drücken Sie nach dem Einschalten die Eingabetaste, F12, ESC oder andere seltsame Tasten, bis Sie ein Startmenü erhalten, in dem Sie auswählen können, ob Sie vom USB-Gerät booten möchten.

    In diesen Menüs ist es häufig möglich, die Suchreihenfolge zu konfigurieren.

Auf meinem T430 sehe ich beispielsweise Folgendes.

Nach dem Einschalten muss ich die Eingabetaste drücken, um das Startmenü aufzurufen:

Geben Sie hier die Bildbeschreibung ein

Dann muss ich hier F12 drücken, um den USB als Startgerät auszuwählen:

Geben Sie hier die Bildbeschreibung ein

Von dort aus kann ich den USB wie folgt als Startgerät auswählen:

Geben Sie hier die Bildbeschreibung ein

Um alternativ die Startreihenfolge zu ändern und den USB-Anschluss mit höherer Priorität auszuwählen, damit ich ihn nicht jedes Mal manuell auswählen muss, drücke ich im Bildschirm "Startup Interrupt Menu" auf F1 und navigiere dann zu:

Geben Sie hier die Bildbeschreibung ein

Bootsektor

Unter x86 können Sie am einfachsten und niedrigsten Ebene einen Master Boot Sector (MBR) erstellen , bei dem es sich um eine Art Bootsektor handelt , und ihn dann auf einer Festplatte installieren.

Hier erstellen wir eine mit einem einzigen printfAufruf:

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

Ergebnis:

Geben Sie hier die Bildbeschreibung ein

Beachten Sie, dass auch ohne etwas zu tun bereits einige Zeichen auf dem Bildschirm gedruckt sind. Diese werden von der Firmware gedruckt und dienen zur Identifizierung des Systems.

Und auf dem T430 erhalten wir nur einen leeren Bildschirm mit einem blinkenden Cursor:

Geben Sie hier die Bildbeschreibung ein

main.img enthält Folgendes:

  • \364in octal == 0xf4in hex: die Codierung für eine hltAnweisung, die die CPU anweist, nicht mehr zu arbeiten.

    Daher wird unser Programm nichts tun: nur starten und stoppen.

    Wir verwenden Oktal, da \xHex-Zahlen von POSIX nicht angegeben werden.

    Wir könnten diese Codierung leicht erhalten mit:

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

    welche Ausgänge:

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

    Aber es ist natürlich auch im Intel-Handbuch dokumentiert.

  • %509s509 Räume produzieren. Muss die Datei bis Byte 510 ausfüllen.

  • \125\252in oktal == 0x55gefolgt von 0xaa.

    Dies sind 2 erforderliche magische Bytes, die die Bytes 511 und 512 sein müssen.

    Das BIOS durchsucht alle unsere Datenträger nach bootfähigen Datenträgern und berücksichtigt nur bootfähige Datenträger mit diesen beiden magischen Bytes.

    Wenn nicht vorhanden, behandelt die Hardware dies nicht als bootfähige Festplatte.

Wenn Sie kein printfMeister sind, können Sie den Inhalt von main.imgmit bestätigen:

hd main.img

welches das erwartete zeigt:

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

Wo 20ist ein Leerzeichen in ASCII.

Die BIOS-Firmware liest diese 512 Bytes von der Festplatte, speichert sie im Speicher und setzt den PC auf das erste Byte, um sie auszuführen.

Hallo Welt Boot Sektor

Nachdem wir ein minimales Programm erstellt haben, gehen wir zu einer Hallo-Welt.

Die offensichtliche Frage ist: Wie mache ich IO? Einige Optionen:

  • Bitten Sie die Firmware, zB BIOS oder UEFI, dies für uns zu tun

  • VGA: Spezieller Speicherbereich, der beim Schreiben auf den Bildschirm gedruckt wird. Kann im geschützten Modus verwendet werden.

  • Schreiben Sie einen Treiber und sprechen Sie direkt mit der Display-Hardware. Dies ist der "richtige" Weg, dies zu tun: leistungsfähiger, aber komplexer.

  • serielle Schnittstelle . Dies ist ein sehr einfaches standardisiertes Protokoll, das Zeichen von einem Host-Terminal sendet und empfängt.

    Auf Desktops sieht es so aus:

    Geben Sie hier die Bildbeschreibung ein

    Quelle .

    Es ist leider auf den meisten modernen Laptops nicht verfügbar, aber es ist der übliche Weg für Entwicklungsboards, siehe die ARM-Beispiele unten.

    Dies ist wirklich eine Schande, da solche Schnittstellen zum Debuggen des Linux-Kernels zum Beispiel sehr nützlich sind .

  • Verwenden Sie die Debug-Funktionen von Chips. ARM nennt zum Beispiel ihr Semihosting . Auf echter Hardware ist zusätzliche Hardware- und Softwareunterstützung erforderlich, auf Emulatoren kann dies jedoch eine kostenlose und bequeme Option sein. Beispiel .

Hier machen wir ein BIOS-Beispiel, da es auf x86 einfacher ist. Beachten Sie jedoch, dass dies nicht die robusteste Methode ist.

Netz

.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 stromaufwärts .

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

Zusammenbauen und verknüpfen mit:

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

Ergebnis:

Geben Sie hier die Bildbeschreibung ein

Und auf dem T430:

Geben Sie hier die Bildbeschreibung ein

Getestet auf: Lenovo Thinkpad T430, UEFI BIOS 1.16. Auf einem Ubuntu 18.04-Host generierte Festplatte.

Neben den Standard-Montageanleitungen für Userland haben wir:

  • .code16: Weist GAS an, 16-Bit-Code auszugeben

  • cli: Software-Interrupts deaktivieren. Dadurch könnte der Prozessor nach dem erneut gestartet werdenhlt

  • int $0x10: führt einen BIOS-Aufruf durch. Dies ist es, was die Zeichen einzeln druckt.

Die wichtigen Link-Flags sind:

  • --oformat binary: Geben Sie rohen binären Assembly-Code aus und wickeln Sie ihn nicht in eine ELF-Datei ein, wie dies bei regulären ausführbaren Benutzerlanddateien der Fall ist.

Machen Sie sich mit dem Verschiebungsschritt des Verlinkens vertraut, um den Linker-Skript-Teil besser zu verstehen: Was machen Linker?

Cooler x86 Bare-Metal-Programme

Hier sind einige komplexere Bare-Metal-Setups, die ich erreicht habe:

Verwenden Sie C anstelle von Assembly

Zusammenfassung: Verwenden Sie GRUB Multiboot, um viele lästige Probleme zu lösen, an die Sie nie gedacht haben. Siehe den folgenden Abschnitt.

Die Hauptschwierigkeit bei x86 besteht darin, dass das BIOS nur 512 Bytes von der Festplatte in den Speicher lädt und Sie diese 512 Bytes wahrscheinlich in die Luft jagen, wenn Sie C verwenden!

Um dies zu lösen, können wir einen zweistufigen Bootloader verwenden . Dadurch werden weitere BIOS-Aufrufe ausgeführt, die mehr Bytes von der Festplatte in den Speicher laden. Hier ist ein minimales Beispiel für eine Assembly der Stufe 2 von Grund auf mit den BIOS-Aufrufen von int 0x13 :

Alternative:

  • Wenn Sie es nur benötigen, um in QEMU zu arbeiten, aber keine echte Hardware, verwenden Sie die -kernelOption, mit der eine gesamte ELF-Datei in den Speicher geladen wird. Hier ist ein ARM-Beispiel, das ich mit dieser Methode erstellt habe .
  • Für den Raspberry Pi übernimmt die Standard-Firmware das Laden von Bildern aus einer ELF-Datei mit dem Namen kernel7.img, ähnlich wie bei QEMU -kernel.

Hier ist nur zu Bildungszwecken ein einstufiges minimales C-Beispiel :

Haupt 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");
    };
}

Eintrag.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 = .;
}

Lauf

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

C Standardbibliothek

Es macht mehr Spaß, wenn Sie jedoch auch die C-Standardbibliothek verwenden möchten, da wir nicht über den Linux-Kernel verfügen, der einen Großteil der Funktionen der C-Standardbibliothek über POSIX implementiert .

Einige Möglichkeiten, ohne auf ein vollwertiges Betriebssystem wie Linux zuzugreifen, sind:

  • Schreibe dein Eigenes. Am Ende sind es nur ein paar Header und C-Dateien, oder? Richtig??

  • Newlib

    Detailliertes Beispiel unter: /electronics/223929/c-standard-libraries-on-bare-metal/223931

    Newlib implementiert alle langweiligen Nicht-OS - spezifische Dinge für Sie, zum Beispiel memcmp, memcpyusw.

    Anschließend erhalten Sie einige Stubs, mit denen Sie die Syscalls implementieren können, die Sie selbst benötigen.

    Zum Beispiel können wir exit()ARM durch Semihosting implementieren mit:

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

    wie in diesem Beispiel gezeigt .

    Sie können beispielsweise printfzu den UART- oder ARM-Systemen umleiten oder exit()mit Semihosting implementieren .

  • eingebettete Betriebssysteme wie FreeRTOS und Zephyr .

    Mit solchen Betriebssystemen können Sie in der Regel die vorbeugende Planung deaktivieren, sodass Sie die volle Kontrolle über die Laufzeit des Programms haben.

    Sie können als eine Art vorimplementierte Newlib angesehen werden.

GNU GRUB Multiboot

Bootsektoren sind einfach, aber nicht sehr praktisch:

  • Sie können nur ein Betriebssystem pro Festplatte haben
  • Der Ladecode muss sehr klein sein und in 512 Bytes passen
  • Sie müssen viel selbst starten, z. B. in den geschützten Modus wechseln

Aus diesen Gründen hat GNU GRUB ein bequemeres Dateiformat namens Multiboot erstellt.

Minimales Arbeitsbeispiel: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

Ich verwende es auch in meinem GitHub-Beispiel-Repo , um alle Beispiele problemlos auf realer Hardware ausführen zu können, ohne den USB millionenfach zu brennen.

QEMU-Ergebnis:

Geben Sie hier die Bildbeschreibung ein

T430:

Geben Sie hier die Bildbeschreibung ein

Wenn Sie Ihr Betriebssystem als Multiboot-Datei vorbereiten, kann GRUB es in einem regulären Dateisystem finden.

Dies ist, was die meisten Distributionen tun, indem sie OS-Images unterlegen /boot.

Multiboot-Dateien sind im Grunde eine ELF-Datei mit einem speziellen Header. Sie werden von GRUB unter folgender Adresse angegeben: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html

Sie können eine Multiboot-Datei mit in eine bootfähige Festplatte verwandeln grub-mkrescue.

Firmware

In Wahrheit ist Ihr Bootsektor nicht die erste Software, die auf der CPU des Systems ausgeführt wird.

Was tatsächlich zuerst läuft, ist die sogenannte Firmware , bei der es sich um eine Software handelt:

  • hergestellt von den Hardwareherstellern
  • normalerweise geschlossene Quelle, aber wahrscheinlich C-basiert
  • im Nur-Lese-Speicher gespeichert und daher ohne Zustimmung des Anbieters schwieriger / unmöglich zu ändern.

Bekannte Firmwares sind:

  • BIOS : alte allgegenwärtige x86-Firmware. SeaBIOS ist die Standard-Open-Source-Implementierung, die von QEMU verwendet wird.
  • UEFI : BIOS-Nachfolger, besser standardisiert, aber leistungsfähiger und unglaublich aufgebläht.
  • Coreboot : Der edle Cross-Source-Open-Source-Versuch

Die Firmware macht Dinge wie:

  • Durchlaufen Sie jede Festplatte, jeden USB-Stick, jedes Netzwerk usw., bis Sie etwas Bootfähiges finden.

    Wenn wir QEMU ausführen, -hdaheißt es, dass main.imges sich um eine an die Hardware angeschlossene Festplatte hdahandelt, die als erste ausprobiert wird und verwendet wird.

  • Laden Sie die ersten 512 Bytes in die RAM-Speicheradresse 0x7c00, legen Sie den RIP der CPU dort ab und lassen Sie ihn laufen

  • Zeigen Sie Dinge wie das Startmenü oder BIOS-Druckaufrufe auf dem Display an

Die Firmware bietet betriebssystemähnliche Funktionen, von denen die meisten Betriebssysteme abhängen. Beispielsweise wurde eine Python-Teilmenge für die Ausführung unter BIOS / UEFI portiert: https://www.youtube.com/watch?v=bYQ_lq5dcvM

Es kann argumentiert werden, dass Firmwares nicht von Betriebssystemen zu unterscheiden sind und dass Firmware die einzige "echte" Bare-Metal-Programmierung ist, die man machen kann.

Wie dieser CoreOS-Entwickler es ausdrückt :

Der schwierige Teil

Wenn Sie einen PC einschalten, werden die Chips, aus denen der Chipsatz besteht (Northbridge, Southbridge und SuperIO), noch nicht ordnungsgemäß initialisiert. Obwohl das BIOS-ROM so weit wie möglich von der CPU entfernt ist, kann die CPU darauf zugreifen, da dies erforderlich ist, da die CPU sonst keine Anweisungen zum Ausführen hätte. Dies bedeutet nicht, dass das BIOS-ROM vollständig zugeordnet ist, normalerweise nicht. Es wird jedoch gerade genug zugeordnet, um den Startvorgang in Gang zu setzen. Alle anderen Geräte, vergiss es einfach.

Wenn Sie Coreboot unter QEMU ausführen, können Sie mit den höheren Schichten von Coreboot und mit Nutzdaten experimentieren, aber QEMU bietet wenig Gelegenheit, mit dem Startcode auf niedriger Ebene zu experimentieren. Zum einen funktioniert RAM von Anfang an.

Post BIOS-Anfangszustand

Wie viele Dinge in der Hardware ist die Standardisierung schwach, und eines der Dinge, auf die Sie sich nicht verlassen sollten, ist der Anfangszustand der Register, wenn Ihr Code nach dem BIOS ausgeführt wird.

Tun Sie sich selbst einen Gefallen und verwenden Sie einen Initialisierungscode wie den folgenden: https://stackoverflow.com/a/32509555/895245

Register mögen %dsund %eshaben wichtige Nebenwirkungen, daher sollten Sie sie auf Null setzen, auch wenn Sie sie nicht explizit verwenden.

Beachten Sie, dass einige Emulatoren besser als echte Hardware sind und Ihnen einen guten Ausgangszustand bieten. Wenn Sie dann auf echter Hardware laufen, bricht alles zusammen.

El Torito

Format, das auf CDs gebrannt werden kann: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29

Es ist auch möglich, ein Hybrid-Image zu erstellen, das entweder auf ISO oder USB funktioniert. Dies kann mit grub-mkrescue( Beispiel ) erfolgen und wird auch vom Linux-Kernel bei make isoimageVerwendung durchgeführt isohybrid.

ARM

In ARM sind die allgemeinen Ideen dieselben.

Es gibt keine allgemein verfügbare halbstandardisierte vorinstallierte Firmware wie das BIOS, die wir für die E / A verwenden können. Die zwei einfachsten Arten von E / A, die wir ausführen können, sind:

  • Seriennummer, die auf Devboards weit verbreitet ist
  • Blink die LED

Ich habe hochgeladen:

Einige Unterschiede zu x86 sind:

  • IO erfolgt durch direktes Schreiben an magische Adressen, es gibt keine inund outAnweisungen.

    Dies wird als speicherabgebildete E / A bezeichnet .

  • Für echte Hardware wie den Raspberry Pi können Sie die Firmware (BIOS) selbst zum Disk-Image hinzufügen.

    Das ist gut so, denn dadurch wird die Aktualisierung dieser Firmware transparenter.

Ressourcen

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
3
Unikernels sind eine Alternative für Menschen, die nicht so niedrig sein können / wollen und trotzdem von ihrem sehr geringen Platzbedarf profitieren möchten.
AndreLDM
1
@AndreLDM Ich war kurz davor, diese Linux-basierten Unikernel-Nachrichten hinzuzufügen, fühlte mich aber noch zu nervös: next.redhat.com/2018/11/14/ukl-a-unikernel-based-on-linux
Ciro Santilli 郝海东 冠状 病
14
Wirklich detaillierte Antwort, aber "ein Programm, das ohne Betriebssystem läuft, ist ein Betriebssystem" ist nicht wahr. Sie können ein Programm schreiben, das nur eine LED ein- / ausschaltet, aber kein Betriebssystem daraus macht. Einige Firmware-Codes, mit denen der Mikrocontroller auf Ihrem Flash-Laufwerk ausgeführt wird, machen ihn nicht zu einem Betriebssystem. Ein Betriebssystem ist mindestens eine Abstraktionsschicht, um andere Software einfacher schreiben zu können. Zumindest würde ich heutzutage sagen, wenn es keinen Scheduler gibt, ist es wahrscheinlich kein Betriebssystem.
Vitali
4
Gute Antwort, bis auf den absoluten Unsinn, dass jedes Programm, das nicht in einem Betriebssystem ausgeführt wird, ein Betriebssystem ist.
neugierigdannii
3
@MichaelPetch hey, nur um die Null im Bootsektor zu speichern :-) Wahrscheinlich nicht wert.
Ciro Santilli 4 冠状 病. 事件 法轮功
3

Betriebssystem als Inspiration

Das Betriebssystem ist auch ein Programm , sodass wir auch unser eigenes Programm erstellen können, indem wir die Funktionen eines der kleinen Betriebssysteme von Grund auf neu erstellen oder ändern (einschränken oder hinzufügen) und es dann während des Startvorgangs ausführen (unter Verwendung eines ISO-Images ). .

Diese Seite kann beispielsweise als Ausgangspunkt verwendet werden:

So schreiben Sie ein einfaches Betriebssystem

Hier passt das gesamte Betriebssystem vollständig in einen 512-Byte-Bootsektor ( MBR )!

Ein solches oder ein ähnliches einfaches Betriebssystem kann verwendet werden, um ein einfaches Framework zu erstellen, das uns Folgendes ermöglicht:

Lassen Sie den Bootloader nachfolgende Sektoren auf der Festplatte in den Arbeitsspeicher laden und springen Sie zu diesem Punkt, um die Ausführung fortzusetzen . Oder Sie lesen FAT12, das auf Diskettenlaufwerken verwendete Dateisystem, und implementieren es .

Es gibt jedoch viele Möglichkeiten. Um beispielsweise ein größeres Betriebssystem für die x86-Assemblersprache zu sehen, können wir das Betriebssystem MykeOS , x86 kennenlernen, das ein Lernwerkzeug ist , um die einfache Arbeit mit 16-Bit-Betriebssystemen im Real-Modus mit gut kommentiertem Code und umfassender Dokumentation zu demonstrieren .

Bootloader als Inspiration

Andere gängige Arten von Programmen, die ohne das Betriebssystem ausgeführt werden, sind auch Bootloader . Wir können ein Programm erstellen, das von einem solchen Konzept inspiriert ist, beispielsweise über diese Website:

So entwickeln Sie Ihren eigenen Bootloader

Der obige Artikel stellt auch die grundlegende Architektur eines solchen Programms vor :

  1. Korrigieren Sie das Laden in den Speicher um die Adresse 0000: 7C00.
  2. Aufruf der BootMain-Funktion , die in der Hochsprache entwickelt wurde.
  3. Zeigen Sie auf dem Display die Meldung „Hallo, Welt…“ von niedriger Ebene an.

Wie wir sehen können, ist diese Architektur sehr flexibel und ermöglicht es uns, jedes Programm zu implementieren , nicht unbedingt einen Bootloader.

Insbesondere wird gezeigt, wie die "Mixed Code" -Technik verwendet wird, mit der Konstruktionen auf hoher Ebene (aus C oder C ++ ) mit Befehlen auf niedriger Ebene (aus Assembler ) kombiniert werden können . Dies ist eine sehr nützliche Methode, aber wir müssen uns daran erinnern:

Um das Programm zu erstellen und eine ausführbare Datei zu erhalten , benötigen Sie den Compiler und Linker von Assembler für den 16-Bit-Modus . Für C / C ++ benötigen Sie nur den Compiler, der Objektdateien für den 16-Bit-Modus erstellen kann .

Der Artikel zeigt auch, wie Sie das erstellte Programm in Aktion sehen und wie Sie es testen und debuggen.

UEFI-Anwendungen als Inspiration

In den obigen Beispielen wurde die Tatsache verwendet, dass der Sektor-MBR auf das Datenmedium geladen wurde. Wir können jedoch tiefer in die Tiefe vordringen, indem wir beispielsweise die UEFI- Anwendungen verwenden :

Über das Laden eines Betriebssystems hinaus kann UEFI UEFI-Anwendungen ausführen, die sich als Dateien auf der EFI-Systempartition befinden. Sie können über die UEFI-Befehlsshell, den Boot-Manager der Firmware oder andere UEFI-Anwendungen ausgeführt werden. UEFI-Anwendungen können unabhängig vom Systemhersteller entwickelt und installiert werden.

Eine Art von UEFI-Anwendung ist ein Betriebssystemlader wie GRUB, rEFInd, Gummiboot und Windows Boot Manager. Dadurch wird eine Betriebssystemdatei in den Speicher geladen und ausgeführt. Ein OS Loader kann auch eine Benutzeroberfläche bereitstellen, über die die Auswahl einer anderen UEFI-Anwendung ausgeführt werden kann. Dienstprogramme wie die UEFI-Shell sind ebenfalls UEFI-Anwendungen.

Wenn wir mit der Erstellung solcher Programme beginnen möchten , können wir beispielsweise mit diesen Websites beginnen:

Programmierung für EFI: Erstellen eines "Hello, World" -Programms / UEFI-Programmierung - Erste Schritte

Erkundung von Sicherheitsproblemen als Inspiration

Es ist bekannt, dass eine ganze Gruppe schädlicher Software (Programme) ausgeführt wird, bevor das Betriebssystem gestartet wird .

Eine große Gruppe von ihnen arbeitet im MBR-Sektor oder in UEFI-Anwendungen, genau wie die oben genannten Lösungen, aber es gibt auch solche, die einen anderen Einstiegspunkt verwenden, z. B. den Volume Boot Record (VBR) oder das BIOS :

Es sind mindestens vier BIOS-Angriffsviren bekannt , von denen zwei zu Demonstrationszwecken dienten.

oder vielleicht auch eine andere.

Angriffe vor dem Systemstart

Bootkits haben sich von der Proof-of-Concept-Entwicklung zur Massenverteilung entwickelt und sind nun effektiv zu Open-Source-Software geworden .

Verschiedene Möglichkeiten zum Booten

Ich denke auch, dass es in diesem Zusammenhang auch erwähnenswert ist, dass es verschiedene Formen des Bootens des Betriebssystems (oder des dafür vorgesehenen ausführbaren Programms) gibt . Es gibt viele, aber ich würde zu achten wie aus dem Netz zu laden , den Code mit Netzwerk - Boot - Option ( PXE ), die uns das Programm auf dem Computer ausgeführt werden können , unabhängig von dessen Betriebssystem und auch unabhängig von einem Speichermedium , das ist direkt mit dem Computer verbunden:

Was ist Network Booting (PXE) und wie können Sie es verwenden?

simhumileco
quelle