Clear Sky Science · de

Stack-basierte statische WebAssembly-Binärschnittbildung und Mutation zur Erzeugung gültiger Sub‑Binaries

· Zurück zur Übersicht

Warum das Zerteilen von Code in kleinere Stücke wichtig ist

Moderne Web‑Apps setzen zunehmend auf WebAssembly, eine kompakte, maschinennahe Sprache, die Programme in C, C++ oder Rust nahezu mit nativer Geschwindigkeit in Browsern, Clouds und sogar kleinen eingebetteten Geräten ausführen lässt. Da immer mehr Systeme WebAssembly ausführen, brauchen wir sichere und gründliche Methoden, um diese Ausführungsengines auf Fehler und Sicherheitslücken zu testen. Doch realistische WebAssembly‑Programme sind groß, komplex und schwer in ausreichender Vielfalt zu sammeln. Dieses Papier stellt eine neue Methode vor, bestehende WebAssembly‑Programme in viele kleinere, in sich geschlossene Mini‑Programme zu zerschneiden und diese anschließend gezielt zu verändern. Das Ergebnis ist ein reichhaltiger Vorrat an gültigen, handlichen Testfällen, die WebAssembly‑Engines weitaus effektiver belasten können als die heute verfügbaren Werkzeuge.

Figure 1
Figure 1.

Von einem großen Programm zu vielen kleinen

Die Autoren gehen von der Beobachtung aus, dass das reine Erzeugen zufälligen WebAssembly‑Codes praktisch nie funktioniert: Die Sprache ist stark geprüft, besonders bezüglich ihres Stacks, der temporäre Werte hält. Anstatt Code von Grund auf neu zu erzeugen, beginnen sie mit einem vorhandenen „Basis“‑WebAssembly‑Binärfile und verwenden eine Technik namens Programmslicing. Ein Slice ist eine reduzierte Version eines Programms, die nur die Codeabschnitte beibehält, die einen gewählten Interessenspunkt beeinflussen, etwa eine bestimmte Anweisung innerhalb einer Funktion. Indem die Datenflüsse, Speicherverwendung und die Kontrollflüsse rückwärts von diesem Punkt verfolgt werden, extrahiert die Methode ein kompaktes Fragment, das in Rechenbegriffen noch „Sinn“ ergibt und kein zufälliges Rauschen ist.

Den unsichtbaren Stack im Gleichgewicht halten

WebAssembly‑Programme beruhen auf einer strikten Stack‑Disziplin: Jede Anweisung poppt und pusht Werte, und strukturierte Blöcke und Verzweigungen müssen den Stack beim Verlassen in genau der erwarteten Form hinterlassen. Das naive Herauslösen von Programmateilen bricht diese Disziplin fast immer und macht das Resultat ungültig. Frühere Arbeiten reparierten Slices, indem sie sie mit festen Mustern von Platzhalter‑Anweisungen auffüllten; das hielt sie zwar ausführbar, schränkte aber ihre Vielfalt ein. Dieses Papier führt stattdessen einen dedizierten Algorithmus zur Korrektur der Stack‑Balance ein. Er analysiert den Kontrollfluss innerhalb jedes Slices, findet Stellen, an denen Verzweigungen oder Blockenden die erwartete Stack‑Form stören, und fügt maßgeschneiderte Befehlssequenzen ein, die überflüssige Werte entfernen und neue Werte des passenden Typs synthetisieren. Diese kleinen „Gadgets“ stellen die Korrektheit wieder her und erlauben zugleich weit reichendere Instruktionsmuster als einfaches Padding.

Ganze Mini‑Programme mit kontrollierter Komplexität bauen

Ein korrigiertes Slice ist immer noch nur ein Funktionskörper, und diese Funktion kann andere aufrufen oder von Modulebenenfeatures wie globalen Variablen und linearer Memory abhängen. Daher erweitern die Autoren ihre Methode, um vollständige, ausführbare Mini‑Module zusammenzusetzen. Ausgehend von einer zufällig gewählten Einstiegsfunktion schneiden und reparieren sie wiederholt alle aufgerufenen Funktionen bis zu einer vom Nutzer gewählten „Aufruftiefe“, die festlegt, wie viele Aufrufebenen erhalten bleiben. Über diese Tiefe hinaus werden Aufrufe durch kurze Stellvertreter ersetzt, die die richtige Anzahl von Argumenten und Ergebnissen nachahmen, ohne mehr Code hineinzuziehen. Das System rekonstruiert außerdem die umgebenden Sektionen eines WebAssembly‑Moduls — Typen, Tabellen, Memory usw. — sodass jedes erzeugte Binärfile selbstständig ist und direkt von Standardwerkzeugen und Laufzeiten ausgeführt werden kann.

Figure 2
Figure 2.

Vielfalt hinzufügen durch gezielte Mutation

Selbst mit Slicing ähneln viele erzeugte Binaries noch ihrem Ursprung. Um die Abdeckung zu erweitern, führen die Autoren eine Mutationsphase ein, die rückwärts durch jede Funktion arbeitet. Für Anweisungen, die Eingaben konsumieren, wählt der Algorithmus zufällig einen neuen Ergebnis‑Typ, findet Ersatzanweisungen, die diesen Typ produzieren, und mutiert dann rekursiv die früheren Anweisungen, die deren Eingaben liefern, sodass die gesamte Kette wieder zusammenpasst. Dadurch bleibt die Stack‑Korrektheit erhalten, während die Berechnung umgestaltet wird. Verglichen mit einer Basismenge realer WebAssembly‑Programme enthalten die mutierten Binaries deutlich mehr unterschiedliche Instruktionsmuster und nutzen viel stärker selten gesehene Features wie Vektoroperationen, die wichtig sind, um die vollständige Implementierung von WebAssembly‑Engines zu prüfen.

Was die Experimente zeigen

Zur Validierung ihrer Vorgehensweise implementierten die Autoren rund 17.000 Zeilen Python, die nahezu den vollen WebAssembly‑2.0‑Standard abdecken. Mithilfe eines großen öffentlichen Datensatzes von tausenden realen Binaries zeigten sie, dass ihre Technik Programme routinemäßig auf einen Bruchteil der ursprünglichen Instruktionsanzahl verkleinern kann — oft unter 40 % der größten Basisgrößen — und dabei bei aktivierter Mutation in über 99 % der Fälle gültig bleibt. Die Methode läuft schnell, typischerweise deutlich unter einer Sekunde pro erzeugtem Binary mittlerer Größe, und erhöht sowohl die Vielfalt der Instruktionstypen als auch die Anzahl einzigartiger Instruktionssequenzen deutlich. Praktisch bedeutet das, dass Tester und Sicherheitsforscher einen bescheidenen Korpus von WebAssembly‑Programmen in eine große, vielfältige Suite kleiner, gültiger Binaries verwandeln können, die ideal zum Fuzzing, Differential‑Testing und Regressionstesten von WebAssembly‑Runtimes sind.

Wie das hilft, WebAssembly sicher zu halten

Alltagswirklich beschreibt das Papier einen intelligenten „Code‑Veredler“ für WebAssembly: Er zerschneidet große Programme in viele kleinere, sinnvolle Stücke, repariert sie automatisch so, dass sie weiterhin die strikten Sprachregeln einhalten, und formt sie anschließend um, um ungewöhnliche Bereiche des Instruktionssatzes zu erkunden. Weil diese Mini‑Programme realistisch und zugleich kompakt bleiben, lassen sie sich schnell und in großer Zahl ausführen, wodurch die Wahrscheinlichkeit steigt, subtile Fehler oder Sicherheitsprobleme in der Software, die WebAssembly ausführt, aufzudecken. Während sich WebAssembly von Browsern in Clouds und Geräte ausbreitet, bietet eine solche systematische Erzeugung gültiger, vielfältiger Test‑Binaries ein wichtiges Werkzeug, um dieses Ökosystem robust und sicher zu halten.

Zitation: Choi, G., Jeon, S. Stack-based static WebAssembly binary slicing and mutation for generating valid sub-binaries. Sci Rep 16, 10910 (2026). https://doi.org/10.1038/s41598-026-45837-y

Schlüsselwörter: WebAssembly‑Tests, Programmslicing, Binärmutation, Software‑Sicherheit, Laufzeitverifikation