Zirkon? Was ist das?
Im August 2016 wurden ohne offizielle Ankündigungen von Google die Quellen des neuen Betriebssystems entdeckt
Fuchsia. Dieses Betriebssystem basiert auf einem Mikrokernel namens Zircon, der wiederum auf LK (Little Kernel) basiert.
Fuchsia ist nicht Linux
Was wird im Artikel besprochen?
vDSO in Zircon ist das einzige Mittel, um auf Systemaufrufe (Syscalls) zuzugreifen .
Ist es wirklich unmöglich, die Prozessoranweisungen SYSENTER / SYSCALL direkt aus unserem Code aufzurufen? Nein, diese Prozessoranweisungen sind nicht Teil des System-ABI. Es ist dem Benutzercode untersagt, diese Anweisungen direkt zu befolgen.
Wer mehr über einen solchen architektonischen Schritt erfahren möchte, lade Sie zu Cat ein.

Zircon vDSO (virtuelles dynamisches gemeinsames Objekt)
Das Akronym vDSO steht für Virtual Dynamic S hared O bject:
- Dynamic Shared Object ist ein Begriff, der für gemeinsam genutzte Bibliotheken für das ELF-Format (.so-Dateien) verwendet wird.
- Dieses Objekt ist virtuell, da es nicht aus einer vorhandenen separaten Datei im Dateisystem geladen wird. Das vDSO-Image wird direkt vom Kernel bereitgestellt.
Kernel-Unterstützung
Die Unterstützung von vDSO als einzigem kontrollierten ABI für Anwendungen im Benutzermodus wird auf zwei Arten implementiert:
Projizieren eines virtuellen Speicherobjekts ( VMO, Virtual Memory Object ).
Wenn zx_vmar_map VMO für vDSO verarbeitet (und ZX_VM_PERM_EXECUTE
in den Argumenten angefordert wird), muss der Kernel den Offset und die Größe genau mit dem ausführbaren vDSO-Segment übereinstimmen. Dies (einschließlich) garantiert nur eine vDSO-Projektion in den Prozessspeicher. Nach der ersten erfolgreichen Projektion von vDSO in den Prozess kann es nicht mehr gelöscht werden. Ein Versuch, vDSO erneut in den Prozessspeicher zu projizieren, versucht, das projizierte VMO für vDSO oder ein Projekt mit dem falschen Offset und / oder der falschen Größe zu löschen, schlägt mit dem Fehler ZX_ERR_ACCESS_DENIED
.
Der Offset und die Größe des vDSO-Codes werden beim Kompilieren aus der ELF-Datei extrahiert und dann im Kernel-Code verwendet, um die obigen Überprüfungen durchzuführen. Nach der ersten erfolgreichen vDSO-Projektion merkt sich der Betriebssystemkern die Adresse für den Zielprozess, um die Überprüfungen zu beschleunigen.
Überprüfen Sie die Rücksprungadressen für Systemaufruffunktionen.
Wenn der Benutzermoduscode den Kernel aufruft, wird eine Systemanrufnummer auf niedriger Ebene im Register übertragen. Systemaufrufe auf niedriger Ebene sind die interne (private) Schnittstelle zwischen vDSO und dem Zirkonkern. Einige (die meisten) entsprechen direkt den Systemaufrufen des öffentlichen ABI, andere nicht.
Für jeden Systemaufruf auf niedriger Ebene im vDSO-Code gibt es einen festen Satz von Offsets im Code, die diesen Aufruf ausführen. Der Quellcode für vDSO definiert interne Zeichen, die jeden dieser Speicherorte identifizieren. Zur Kompilierungszeit werden diese Speicherorte aus der vDSO-Symboltabelle abgerufen und zum Generieren von Kernelcode verwendet, der die Vorhersage der Gültigkeit der Codeadresse für jeden Systemaufruf auf niedriger Ebene bestimmt. Mit diesen Prädikaten können Sie den aufrufenden Code angesichts des Versatzes vom Anfang des vDSO-Codesegments schnell auf Gültigkeit überprüfen.
Wenn das Prädikat feststellt, dass der aufrufende Code keinen Systemaufruf ausführen darf, wird eine synthetische Ausnahme ausgelöst, ähnlich wie wenn der aufrufende Code versucht hat, eine nicht vorhandene oder privilegierte Anweisung auszuführen.
vDSO beim Erstellen eines neuen Prozesses
Um die Ausführung des ersten Threads eines neu erstellten Prozesses zu starten, wird der Systemaufruf zx_process_start verwendet . Der letzte Parameter dieses Systemaufrufs (siehe arg2 in der Dokumentation) übergibt das Argument für den ersten Thread des erstellten Prozesses. Gemäß der akzeptierten Vereinbarung ordnet der Programmlader vDSO dem Adressraum des neuen Prozesses (einer vom System ausgewählten zufälligen Stelle) zu und überträgt die Basisadresse der Zuordnung mit dem Argument arg2 an den ersten Thread des erstellten Prozesses. Diese Adresse ist die Header-Adresse der ELF-Datei, unter der die erforderlichen benannten Funktionen für Systemaufrufe gefunden werden können.
Speicherkarte (Layout) vDSO
vDSO ist eine reguläre gemeinsam genutzte EFL-Bibliothek, die wie jede andere betrachtet werden kann. Bei vDSO wird jedoch absichtlich eine kleine Teilmenge des gesamten ELF-Formats ausgewählt. Dies hat mehrere Vorteile:
- Die Zuordnung eines solchen ELF zum Prozess ist einfach und enthält keine komplexen Grenzfälle, die zur vollständigen Unterstützung von ELF-Programmen erforderlich sind.
- Die Verwendung von vDSO erfordert keine voll funktionsfähige dynamische ELF-Bindung. Insbesondere hat vDSO keine dynamischen Umzüge. Das Projizieren von PT_LOAD-Segmenten einer ELF-Datei ist die einzige erforderliche Aktion.
- Der vDSO-Code ist zustandslos und wiedereintrittsfähig. Es funktioniert ausschließlich mit Prozessorregistern und dem Stack. Dies macht es für den Einsatz in einer Vielzahl von Kontexten mit minimalen Einschränkungen geeignet, was dem obligatorischen ABI-Betriebssystem entspricht. Es vereinfacht auch die Code-Analyse und -Verifizierung für Zuverlässigkeit und Sicherheit.
Der gesamte vDSO-Speicher wird durch zwei aufeinanderfolgende Segmente dargestellt, von denen jedes ausgerichtete ganze Seiten enthält:
- Das erste Segment ist schreibgeschützt und enthält ELF-Header sowie konstante Daten.
- Das zweite Segment ist ausführbar und enthält vDSO-Code.
Das gesamte vDSO-Image besteht nur aus den Seiten dieser beiden Segmente. Für die Anzeige des vDSO-Speichers sind nur zwei aus ELF-Headern extrahierte Werte erforderlich: die Anzahl der Seiten in jedem Segment.
Konstante Daten zur Startzeit des Betriebssystems
Einige Systemaufrufe geben einfach Werte zurück, die konstant sind (Werte müssen zur Laufzeit angefordert werden und können nicht in Benutzermoduscode kompiliert werden). Diese Werte werden entweder zur Kompilierungszeit im Kernel festgelegt oder beim Start vom Kernel festgelegt (Startparameter und Hardwareparameter). Zum Beispiel: zx_system_get_version () , zx_system_get_num_cpus () und zx_ticks_per_second () . Der Rückgabewert der letzten Funktion wird beispielsweise vom Kernel-Befehlszeilenparameter beeinflusst .
Warten Sie, ist die Anzahl der CPUs eine Konstante?Interessanterweise besagt die Beschreibung der Funktion zx_system_get_num_cpus () auch explizit, dass das Betriebssystem das Hot- Swapping der Anzahl der Prozessoren nicht unterstützt:
Diese Nummer kann sich während eines Systemlaufs nur beim Booten nicht ändern.
Dies zeigt zumindest indirekt an, dass das Betriebssystem nicht als Server positioniert ist.
Da diese Werte konstant sind, ist es nicht sinnvoll, für echte Systemaufrufe an den Betriebssystemkern zu zahlen. Stattdessen handelt es sich bei ihrer Implementierung um einfache C ++ - Funktionen, die aus dem vDSO-Konstantensegment gelesene Daten zurückgeben. Während der Kompilierung erfasste Werte (z. B. die Versionszeichenfolge des Systems) werden einfach in vDSO kompiliert.
Für Werte, die beim Booten ermittelt wurden, sollte der Kernel den Inhalt von vDSO ändern. Dies erfolgt mithilfe von früh ausführbarem Code, der das VMO-vDSO bildet, bevor der Kernel den ersten Benutzerprozess startet (und ihm den VMO-Deskriptor übergibt). Während der Kompilierung werden Offsets aus dem vDSO-Image ( vdso_constants ) aus der ELF-Datei extrahiert und dann in den Kernel eingebettet. Beim Booten zeigt der Kernel vorübergehend Seiten, die sich über vdso_constants erstrecken, in seinem eigenen Adressraum an, um die Struktur mit den richtigen Werten vorinitialisieren zu können (für den aktuellen Systemstart).
Warum all diese Kopfschmerzen ?
Einer der wichtigsten Gründe ist die Sicherheit. Das heißt, wenn es einem Angreifer gelingt, beliebigen (Shell-) Code auszuführen, muss er vDSO-Funktionen verwenden, um Systemfunktionen aufzurufen. Das erste Hindernis ist die oben erwähnte Randomisierung der vDSO-Startadresse für jeden erstellten Prozess. Und da der Betriebssystemkern für das VMO (Virtual Memory Object) von vDSO verantwortlich ist, kann er ein völlig anderes vDSO einem bestimmten Prozess zuordnen und so gefährliche (und für einen bestimmten Prozess nicht benötigte) Systemaufrufe verhindern. Beispiel: Sie können verhindern, dass Treiber untergeordnete Prozesse erzeugen oder MMIO-Bereiche projizieren. Dies ist ein großartiges Werkzeug, um die Angriffsfläche zu reduzieren.
Hinweis: Derzeit wird die Unterstützung für mehrere vDSOs aktiv weiterentwickelt. Es gibt bereits eine Proof-of-Concept-Implementierung und einfache Tests. Es sind jedoch weitere Arbeiten erforderlich, um die Zuverlässigkeit der Implementierung zu verbessern und festzustellen, welche Optionen verfügbar sind. Das aktuelle Konzept bietet vDSO-Image-Optionen, mit denen nur eine Teilmenge der vollständigen vDSO-Systemaufrufschnittstelle exportiert wird.