Mit transaktionalem NTFS jeden Prozess zum Laufen bringen: Mein erster Schritt zum Erstellen einer Sandbox für Windows

TransactionMaster Es gibt ein Modul im Windows-Kernel, das die Gruppierung von Dateivorgängen in eine Entität unterstützt, die als Transaktion bezeichnet wird . Aktionen auf diese Entität sind isoliert und atomar: Sie können sie permanent machen oder rückgängig machen . Sehr praktisch bei der Installation von Programmen, zustimmen? Wir wechseln immer von einem vereinbarten Zustand in einen anderen, und wenn etwas schief geht, werden alle Änderungen rückgängig gemacht.


Da ich von der Unterstützung solcher Funktionen erfahren habe, wollte ich diese Transaktionen immer von innen betrachten. Und wissen Sie was? Ich habe eine einfache und wirklich wunderbare Methode gefunden, mit der jeder Prozess innerhalb einer Dateitransaktion funktioniert. aber die Ränder des Buches sind zu eng für ihn . In den meisten Fällen sind hierfür nicht einmal Administratorrechte erforderlich.


Lassen Sie uns herausfinden, wie es funktioniert, mit meinem Programm experimentieren und verstehen, worum es in den Sandkästen geht.


Repository


Für diejenigen, die es unbedingt versuchen möchten : TransactionMaster auf GitHub .


Theorie


Die Unterstützung für transaktionales NTFS oder TxF wurde in Windows Vista eingeführt und ermöglichte es, den Code, der für die Wiederherstellung nach Fehlern beim Aktualisieren der Software und des Betriebssystems verantwortlich ist, erheblich zu vereinfachen. Tatsächlich wurde die Wiederherstellungsaufgabe auf den Kernel des Betriebssystems übertragen, der damit begann, die vollständige ACID- Semantik auf Dateioperationen anzuwenden - fragen Sie einfach.


Zur Unterstützung dieser Technologie wurden neue API-Funktionen hinzugefügt, die die vorhandene Funktionalität duplizierten und einen neuen Parameter hinzufügten - eine Transaktion. Die Transaktion selbst ist neben Dateien, Prozessen und Synchronisationsobjekten zu einem der vielen Kernelobjekte im Betriebssystem geworden. Im einfachsten Fall besteht die Abfolge der Aktionen beim Arbeiten mit Transaktionen darin, ein Transaktionsobjekt durch Aufrufen von CreateTransaction , mit Dateien zu arbeiten (unter Verwendung von Funktionen wie CreateFileTransacted , MoveFileTransacted , DeleteFileTransacted und dergleichen) und die Transaktion mit CommitTransaction / RollbackTransaction anzuwenden / CommitTransaction .


Betrachten wir nun die Architektur dieser Funktionen. Wir wissen, dass die dokumentierte API-Schicht aus Bibliotheken wie kernel32.dll die Steuerung nicht direkt auf den Kernel des Betriebssystems überträgt, sondern auf die zugrunde liegende Abstraktionsschicht im Benutzermodus verweist - ntdll.dll , die bereits einen Systemaufruf ntdll.dll . Und hier erwartet uns eine Überraschung: Es gibt einfach keine Duplizierung von Funktionen für die Arbeit mit Dateien im Kontext von Transaktionen in ntdll , wie im Kernel .


API-Ebenen

Dennoch haben sich die Prototypen dieser Funktionen aus der Native API seit jeher nicht geändert, was bedeutet, dass sie im Kontext der Transaktion, die die Operation ausführen soll, von einer anderen Stelle lernen werden. Aber woher? Die Antwort ist, dass jeder Thread ein spezielles Feld hat, in dem das Handle der aktuellen Transaktion gespeichert ist. Der Speicherbereich, in dem er sich befindet, wird als TEB (Flow Environment Block) bezeichnet. Von bekannten Dingen werden dort auch der letzte Fehlercode und die letzte Stream-ID gespeichert.


Daher setzen Funktionen mit dem Suffix *Transacted das Feld der aktuellen Transaktion, rufen eine ähnliche Funktion ohne Suffix auf und stellen dann den vorherigen Wert wieder her. Sie tun dies mit einem Paar von RtlGetCurrentTransaction / RtlSetCurrentTransaction von ntdll . Der Code der Funktionen selbst ist sehr einfach, mit Ausnahme des Falls mit WoW64 , auf den weiter unten eingegangen wird.


Was bedeutet das alles für uns? Durch Ändern einer Variablen im Prozessspeicher können wir steuern, in welchem ​​Kontext die Transaktion mit dem Dateisystem arbeitet. Sie müssen keine Traps setzen und keine Funktionsaufrufe abfangen, sondern nur den Transaktionsdeskriptor an den Zielprozess übergeben und für jeden der Threads einige Bytes im Speicher korrigieren. Das klingt elementar, lass es uns tun!


Fallstricke


Die ersten Experimente haben gezeigt, dass die Idee funktioniert: Far Manager , den ich anstelle von Windows Explorer verwende, übersteht die Substitution von Transaktionen im Handumdrehen perfekt und ermöglicht es Ihnen, die Welt in ihrem Kontext zu betrachten. Es gab aber auch Programme, die ständig neue Threads für Dateioperationen erstellen. Und im ursprünglichen Szenario ist dies eine Lücke, da es nicht sehr praktisch ist, die Erstellung von Threads in einem anderen Prozess zu verfolgen (ganz zu schweigen von der Tatsache, dass "Verspätung" hier entscheidend ist). Ein Beispiel für eine Anwendung aus der zweiten Klasse ist das kürzlich portierte WinFile .


Tracking-DLL


Glücklicherweise ist die synchrone Nachverfolgung der Thread-Erstellung und die anschließende Konfiguration von Transaktionen für diese innerhalb des Zielprozesses vollständig elementar. Es reicht aus, eine DLL einzubetten, und der Modul-Loader ruft bei jedem * Erstellen eines neuen Threads seinen Einstiegspunkt mit dem Parameter DLL_THREAD_ATTACH . Durch die Implementierung dieser Funktionalität habe ich die Kompatibilität mit einem Dutzend weiterer Programme behoben.


* Technisch funktioniert ein Aufruf nicht immer, und dieses Verhalten kann manchmal in der Oberfläche meines Programms beobachtet werden. Ausnahmen sind größtenteils Threads aus dem Arbeitspool des Modul-Loaders. Die Sache ist, dass DLL-Bibliotheken unter der Bootloader-Sperre benachrichtigt werden. Dies bedeutet, dass Sie derzeit keine neuen Module laden können. Und genau das tun die Loader-Threads, die den Zugriff auf das Dateisystem parallelisieren. In solchen Fällen gibt es eine Ausnahme: Wenn Sie THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH als Flag angeben, wenn Sie THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH aufrufen, können Sie vermeiden, vorhandenen DLLs bzw. Deadlocks einen neuen Thread hinzuzufügen. Darüber ist, was hier passiert.


Starten Sie den Explorer


Es bleibt die dritte, letzte Kategorie von Programmen, die immer noch abstürzen, wenn versucht wird, sie innerhalb einer Transaktion zum Laufen zu bringen. Eines dieser Programme ist Windows Explorer. Ich kann das Problem nicht genau diagnostizieren, aber die Anwendung ist kompliziert, und ein Hot-Switching innerhalb der Transaktion hat keinen großen Einfluss darauf. Vielleicht liegt es daran, dass es viele offene Dateideskriptoren gibt, von denen einige im neuen Kontext nicht mehr gültig sind. Oder vielleicht ist es etwas anderes. In solchen Situationen hilft ein Neustart des Prozesses, sodass er von Anfang an in der Transaktion funktioniert. Dann sollten keine Inkonsistenzen auftreten.


Aus diesem Grund habe ich dem Programm die Möglichkeit hinzugefügt, neue Prozesse zu starten, für die die Transaktion und Nachverfolgung neuer Flows bereits vor Erreichen des Einstiegspunkts konfiguriert wird, während der Prozess angehalten ist. Und weißt du was, es hat funktioniert! Richtig, da der Explorer COM-Objekte außerhalb des Prozesses aktiv verwendet, wird die Vorschau beim Verschieben von Dateien unterbrochen. Ansonsten ist alles stabil.


Was ist los mit WoW64?


Dieses Subsystem zum Starten von 32-Bit-Programmen auf 64-Bit-Systemen ist ein äußerst praktisches Tool, aber die Notwendigkeit, seine Funktionen zu berücksichtigen, erschwert häufig die Systemprogrammierung. Ich habe oben erwähnt, dass das Verhalten von Rtl[Get/Set]CurrentTransaction bei ähnlichen Prozessen deutlich anders ist. Der Grund dafür liegt in der Tatsache, dass Threads in WoW64-Prozessen bis zu zwei Umgebungsblöcke haben. Sie haben unterschiedliche Größen des Zeigers, und es ist wünschenswert, sie in einem konsistenten Zustand zu halten, obwohl im Fall von Transaktionen der 64-Bit-TEB Vorrang hat. Wenn wir Transaktionen aus der Ferne einrichten, müssen wir das Verhalten dieser Funktionen reproduzieren. Es ist nicht schwer, aber Sie sollten es nicht vergessen, und Details finden Sie hier . Schließlich benötigen WoW64-Prozesse eine zusätzliche 32-Bit-Kopie unserer Tracking-DLL.


Ungelöste Probleme


Zum Trauern gezwungen - das allererste Szenario, das mir in den Sinn kommt, nämlich das Starten von Programminstallationsprogrammen in diesem Modus - ist noch nicht einsatzbereit. Erstens ist es nicht so konfiguriert, dass untergeordnete Prozesse in derselben Transaktion erfasst werden. Hier gibt es mehrere Lösungen, ich arbeite daran. Wenn die Anwendung jedoch mehrere Prozesse erstellt, ist es noch zu früh, sie in Kombination mit Transaktionen zu verwenden.


Zweitens verdient der Fall mit ausführbaren Dateien, die nicht außerhalb der Transaktion existieren, besondere Aufmerksamkeit. Ich erinnere mich, dass es eine Art Virus gab, der naive Antivirenprogramme auf diese Weise ausgetrickst hat: Es wurde in eine Transaktion entpackt, selbst gestartet und dann die Transaktion zurückgesetzt. Es gibt einen Prozess, aber keine ausführbare Datei. Antivirus kann entscheiden, dass nichts gescannt werden muss, und die Bedrohung ignorieren. Wir müssen auch an kreativen Lösungen arbeiten, da NtCreateUserProcess (und entsprechend CreateProcess ) aus irgendeinem Grund die aktuelle Transaktion ignoriert. Natürlich bleibt NtCreateProcessEx immer erhalten, aber es wird viel NtCreateProcessEx erwartet, um Kompatibilitätsprobleme zu beheben. Mir fällt etwas ein.


Und wo sind die Sandkästen?


Schauen Sie sich das Bild an. Hier zeigen drei verschiedene Programme den Inhalt desselben Ordners aus drei verschiedenen Transaktionen an. Cool, richtig?


Ein Blick aus der Transaktion

Und doch ist mein Programm keineswegs eine Sandbox, es fehlt ein wichtiges Detail - die Sicherheitsgrenze . Natürlich hindert dies einige Unternehmen nicht daran, ähnliche Kunsthandwerke unter dem Deckmantel vollwertiger Sandkästen zu verkaufen, was ich sagen kann, eine Schande für sie. Und trotz der Tatsache, dass dies völlig unmöglich erscheint, wie können wir verhindern, dass ein Programm eine Variable in unserem Speicher ändert, auch wenn wir selbst ein Debugger sind? - Ich habe einen wunderbaren Trick im Angebot, der es mir ermöglicht, das, was ich gestartet habe, zu vervollständigen und die erste mir bekannte Sandbox zu erstellen, für die kein Treiber erforderlich ist, die aber das Dateisystem virtualisiert. Warten Sie bis dahin auf Updates, verwenden Sie Sandboxie und experimentieren Sie mit der AppContainer- Technologie. Vielen Dank für Ihre Aufmerksamkeit.


Projekt-Repository auf GitHub: TransactionMaster .
Derselbe Artikel auf Englisch .

Source: https://habr.com/ru/post/de485784/


All Articles