Abschnitte mit ausführbarem Code für AArch64-Systembinärdateien sind standardmäßig als „Nur ausführen“ (nicht lesbar) gekennzeichnet, um Just-in-Time-Code-Wiederverwendungsangriffe zu erschweren. Code, in dem Daten und Code vermischt werden, und Code, der diese Abschnitte absichtlich prüft (ohne die Speichersegmente zuerst als lesbar neu zuzuordnen), funktionieren nicht mehr. Apps mit dem Ziel-SDK 10 (API-Level 29 oder höher) sind betroffen, wenn die App versucht, Codeabschnitte von XOM-fähigen (Execute-Only Memory) Systembibliotheken im Arbeitsspeicher zu lesen, ohne den Abschnitt zuerst als lesbar zu kennzeichnen.
Damit Sie diese Maßnahme optimal nutzen können, sind sowohl Hardware- als auch Kernel-Unterstützung erforderlich. Ohne diese Unterstützung wird die Risikominderung möglicherweise nur teilweise erzwungen. Der gemeinsame Kernel von Android 4.9 enthält die entsprechenden Patches, um dies auf ARMv8.2-Geräten vollständig zu unterstützen.
Implementierung
AArch64-Binärdateien, die vom Compiler generiert werden, gehen davon aus, dass Code und Daten nicht vermischt sind. Das Aktivieren dieser Funktion wirkt sich nicht negativ auf die Leistung des Geräts aus.
Bei Code, bei dem eine beabsichtigte Speicherinspecktion in den ausführbaren Segmenten durchgeführt werden muss, wird empfohlen, mprotect
für die Codesegmente aufzurufen, die geprüft werden müssen, damit sie lesbar sind, und dann die Lesbarkeit zu entfernen, wenn die Prüfung abgeschlossen ist.
Diese Implementierung führt dazu, dass Lesevorgänge in Arbeitsspeichersegmente, die als „nur zur Ausführung“ gekennzeichnet sind, zu einem Segmentierungsfehler (SEGFAULT
) führen. Dies kann aufgrund eines Bugs, einer Sicherheitslücke, von Daten, die mit Code vermischt sind (Literal-Pooling), oder einer beabsichtigten Speicherprüfung auftreten.
Geräteunterstützung und Auswirkungen
Geräte mit älterer Hardware oder älteren Kerneln (unter 4.9) ohne die erforderlichen Patches unterstützen diese Funktion möglicherweise nicht vollständig oder profitieren nicht in vollem Umfang davon. Auf Geräten ohne Kernelunterstützung werden Nutzerzugriffe auf Nur-Ausführungsspeicher möglicherweise nicht erzwungen. Kernelcode, der explizit prüft, ob eine Seite lesbar ist, kann diese Eigenschaft jedoch weiterhin erzwingen, z. B. process_vm_readv()
.
Das Kernel-Flag CONFIG_ARM64_UAO
muss im Kernel festgelegt sein, damit der Kernel Userland-Seiten, die als „nur ausführen“ gekennzeichnet sind, respektiert. Ältere ARMv8-Geräte oder ARMv8.2-Geräte mit deaktivierter User Access Override (UAO) können davon möglicherweise nicht in vollem Umfang profitieren und können weiterhin nur-ausführbare Seiten mithilfe von Systemaufrufen lesen.
Vorhandenen Code überarbeiten
Code, der von AArch32 portiert wurde, kann vermischte Daten und Code enthalten, was zu Problemen führen kann. In vielen Fällen lässt sich das Problem ganz einfach beheben, indem Sie die Konstanten in einen .data
-Abschnitt in der Assemblydatei verschieben.
Handgeschriebene Assembler-Code muss möglicherweise umgeschrieben werden, um lokal verwaltete Konstanten zu trennen.
Beispiele:
Bei Binärdateien, die vom Clang-Compiler generiert werden, sollten keine Probleme auftreten, wenn Daten in Code eingefügt werden. Wenn generierter Code der GNU Compiler Collection (GCC) (aus einer statischen Bibliothek) enthalten ist, prüfen Sie das Ausgabe-Binärprogramm, um sicherzustellen, dass Konstanten nicht in Codeabschnitten zusammengefasst wurden.
Wenn eine Code-Selbstprüfung für ausführbare Codeabschnitte erforderlich ist, rufen Sie zuerst mprotect
auf, um den Code lesbar zu markieren. Rufen Sie nach Abschluss des Vorgangs noch einmal mprotect
auf, um den Text unlesbar zu machen.
XOM aktivieren
„Nur ausführen“ ist standardmäßig für alle 64‑Bit-Binärdateien im Buildsystem aktiviert.
XOM deaktivieren
Sie können die Option „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 umgeschrieben werden können oder deren ausführbarer Code gelesen werden muss. Dazu müssen die Variablen LOCAL_XOM
und xom
auf false
festgelegt werden.
// Android.mk LOCAL_XOM := false // Android.bp cc_binary { // or other module types ... xom: false, }
Wenn der Nur-Ausführungsspeicher in einer statischen Bibliothek deaktiviert ist, wendet das Buildsystem dies auf alle abhängigen Module dieser statischen Bibliothek an. Sie können dies mit xom: true,
überschreiben.
Wenn Sie den Nur-Ausführungsspeicher in einem bestimmten Unterverzeichnis (z. B. foo/bar/) deaktivieren möchten, ü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 ausführbare Binärdateien global deaktivieren, indem Sie ENABLE_XOM=false
an den Befehl make
übergeben.
make -j ENABLE_XOM=false
Zertifizierungsstufe
Für Nur-Ausführungsspeicher sind keine CTS- oder Bestätigungstests verfügbar. Sie können Binärdateien manuell mit readelf
und den Segmentflags prüfen.