Ausführbare Codeabschnitte für AArch64-Systembinärdateien sind standardmäßig als „Nur ausführbar“ (nicht lesbar) gekennzeichnet, um eine Absicherung gegen Just-in-Time-Angriffe zur Wiederverwendung von Code zu gewährleisten. Code, der Daten und Code miteinander vermischt, und Code, der diese Abschnitte gezielt überprüft (ohne die Speichersegmente zunächst als lesbar zuzuordnen) funktioniert nicht mehr. Apps mit einem Ziel-SDK von 10 (API-Level 29 oder höher) sind betroffen, wenn die App versucht, Codeabschnitte von XOM-fähigen Systembibliotheken (Execute-Only Memory) im Speicher zu lesen, ohne den Abschnitt zuvor als lesbar zu markieren.
Um von dieser Schadensbegrenzung vollständig profitieren zu können, ist sowohl Hardware- als auch Kernel-Unterstützung erforderlich. Ohne diese Unterstützung könnten die Abhilfemaßnahmen möglicherweise nur teilweise durchgesetzt werden. Der gemeinsame Android 4.9-Kernel enthält die entsprechenden Patches, um dies auf ARMv8.2-Geräten vollständig zu unterstützen.
Implementierung
Vom Compiler generierte AArch64-Binärdateien gehen davon aus, dass Code und Daten nicht vermischt sind. Die Aktivierung dieser Funktion wirkt sich nicht negativ auf die Leistung des Geräts aus.
Für Code, der eine absichtliche Speicherintrospektion seiner ausführbaren Segmente durchführen muss, ist es ratsam, mprotect
für die Codesegmente aufzurufen, die überprüft werden müssen, damit sie lesbar sind, und dann die Lesbarkeit zu entfernen, wenn die Überprüfung abgeschlossen ist.
Diese Implementierung führt dazu, dass Lesevorgänge in Speichersegmenten, die als Nur-Ausführen markiert sind, zu einem Segmentierungsfehler ( SEGFAULT
) führen. Dies kann auf einen Fehler, eine Sicherheitslücke, mit Code vermischte Daten (literales Pooling) oder eine absichtliche Selbstprüfung des Speichers zurückzuführen sein.
Geräteunterstützung und Auswirkungen
Geräte mit früherer Hardware oder früherem Kernel (niedriger als 4.9) ohne die erforderlichen Patches unterstützen diese Funktion möglicherweise nicht vollständig oder profitieren nicht davon. Geräte ohne Kernel-Unterstützung erzwingen möglicherweise keinen Benutzerzugriff auf den Nur-Ausführungs-Speicher. Kernel-Code, der explizit prüft, ob eine Seite lesbar ist, kann diese Eigenschaft jedoch dennoch erzwingen, z. B. process_vm_readv()
.
Das Kernel-Flag CONFIG_ARM64_UAO
muss im Kernel gesetzt sein, um sicherzustellen, dass der Kernel Userland-Seiten berücksichtigt, die als „Execute-Only“ gekennzeichnet sind. Frühere ARMv8-Geräte oder ARMv8.2-Geräte mit deaktiviertem User Access Override (UAO) profitieren möglicherweise nicht vollständig davon und können möglicherweise immer noch Ausführungsseiten mithilfe von Systemaufrufen lesen.
Vorhandenen Code umgestalten
Code, der von AArch32 portiert wurde, enthält möglicherweise gemischte Daten und Code, was zu Problemen führen kann. In vielen Fällen ist die Behebung dieser Probleme so einfach wie das Verschieben der Konstanten in einen .data
Abschnitt in der Assemblydatei.
Handgeschriebene Assemblys müssen möglicherweise umgestaltet werden, um lokal gepoolte Konstanten zu trennen.
Beispiele:
Vom Clang-Compiler generierte Binärdateien sollten keine Probleme mit der Vermischung von Daten im Code haben. Wenn von der GNU-Compiler-Sammlung (GCC) generierter Code enthalten ist (aus einer statischen Bibliothek), überprüfen Sie die Ausgabebinärdatei, um sicherzustellen, dass Konstanten nicht in Codeabschnitten zusammengefasst wurden.
Wenn eine Code-Introspektion für ausführbare Codeabschnitte erforderlich ist, rufen Sie zunächst mprotect
auf, um den Code als lesbar zu markieren. Nachdem der Vorgang abgeschlossen ist, rufen Sie mprotect
erneut auf, um ihn als unlesbar zu markieren.
Aktivieren
„Nur ausführen“ ist standardmäßig für alle 64-Bit-Binärdateien im Build-System aktiviert.
Deaktivieren
Sie können „Nur ausführen“ auf Modulebene, für einen gesamten Unterverzeichnisbaum oder global für einen gesamten Build deaktivieren.
XOM kann für einzelne Module deaktiviert werden, die nicht umgestaltet werden können oder deren ausführbaren Code gelesen werden muss, indem die Variablen LOCAL_XOM
und xom
auf false
gesetzt werden.
// Android.mk LOCAL_XOM := false // Android.bp cc_binary { // or other module types ... xom: false, }
Wenn der Nur-Ausführungs-Speicher in einer statischen Bibliothek deaktiviert ist, wendet das Build-System dies auf alle abhängigen Module dieser statischen Bibliothek an. Sie können dies überschreiben, indem Sie xom: true,
verwenden.
Um den Nur-Ausführungs-Speicher in einem bestimmten Unterverzeichnis (z. B. foo/bar/) zu deaktivieren, übergeben Sie den Wert an XOM_EXCLUDE_PATHS
.
make -j XOM_EXCLUDE_PATHS=foo/bar
Alternativ können Sie die Variable PRODUCT_XOM_EXCLUDE_PATHS
in Ihrer Produktkonfiguration festlegen.
Sie können schreibgeschützte Binärdateien global deaktivieren, indem Sie ENABLE_XOM=false
an Ihren make
Befehl übergeben.
make -j ENABLE_XOM=false
Validierung
Für Nur-Ausführen-Speicher sind keine CTS- oder Verifizierungstests verfügbar. Sie können Binärdateien manuell überprüfen, indem Sie readelf
verwenden und die Segmentflags überprüfen.