AIREITER

Native umschreiben oder Browser-Bridge dulden? Eine Drei-Stufen-Leiter für reverse-engineerten Code

Zuletzt aktualisiert: 2026-07-31 07:16:04

Nach dem ersten Teil der Reverse Engineering-Arbeit — mechanisches Zerlegen, Einordnen der Algorithmusfamilie, differenzielles Verifizieren — steht meist die Erkenntnis: „Ich weiß, was der Code berechnet.“ Auslieferbar ist das noch nicht. Die Logik hängt weiterhin an ihrer ursprünglichen Laufzeitumgebung, und nun entscheidet sich, ob daraus eine reine Funktion für die CI wird oder ein externer Prozess, den jemand dauerhaft betreuen muss. Wer hier die falsche Form wählt, zahlt die zuvor gesparte Zeit im Betrieb mit Zinsen zurück.

Der Überblick über die vier Stufen fasst diesen Schritt in einem Satz zusammen: „Stufe 4: über die Transportebene degradieren.“ Hier geht es ins Detail. Die Kernaussage lautet: Mit jeder Stufe nach unten wachsen Abhängigkeitsfläche, mögliche Fehlerbilder und Deployment-Aufwand um eine Größenordnung — standardmäßig kämpft man sich also nach oben.

Drei Zielzustände — und eine feste Reihenfolge

Für reverse-engineerte Logik gibt es genau drei sinnvolle Zielformen. Ihre Reihenfolge sollte in euren Konventionen stehen, statt jedes Mal nach dem Motto „Hauptsache, es läuft zuerst“ neu entschieden zu werden:

  1. Nativer Rewrite. Die Logik wird in der Zielsprache nachgebaut, von der ursprünglichen Runtime entkoppelt und benötigt nur die Standardbibliothek. Voraussetzung ist, dass die Algorithmusfamilie korrekt erkannt wurde. Wenn der Fingerprinting-Schritt bestanden ist, stammen 90 % aus einer öffentlichen Implementierung; die verbleibenden Abweichungen werden gezielt behandelt. Das Ergebnis landet als reine Funktion.

  2. Lokale JS-Engine mit Minimalfragment. Manche Logik lässt sich kurzfristig nicht wirtschaftlich bereinigen. Dann bleibt ein kleiner Ausschnitt des ursprünglichen JavaScript erhalten, und nur die benötigten paar Dutzend Zeilen laufen auf einem lokalen Node/V8 — nicht die komplette Seite.

  3. Passive Browser-Bridge. Manche Zustände existieren ausschließlich in einer echten, angemeldeten Seiten-Runtime: etwa eine zur Laufzeit gelieferte Signatur oder eine sitzungsgebundene dynamische Kennung. Statische Rekonstruktion kann sie nicht reproduzieren; vorerst lassen sie sich nur im Browser auslesen. Das ist eine Übergangslösung, muss in den Schnittstellennotizen klar markiert sein und darf niemals zum Standardweg werden.

Warum jede Stufe deutlich teurer wird

Die Reihenfolge ist nicht willkürlich: Die Kosten der drei Stufen steigen nicht linear. Jede Stufe kostet ungefähr eine Größenordnung mehr als die vorherige.

Stufe

Abhängigkeitsfläche

Fehlerbild

CI-tauglich?

Nativer Rewrite

Standardbibliothek, keine externen Prozesse

Abweichendes Ergebnis, auffindbar mit einem assert

Ja — es ist eine reine Funktion

Lokale JS-Engine

Eine zusätzliche Node-Runtime; der V8-Kontext ist nicht threadsicher, Parallelität braucht einen Lock

Engine-Version passt nicht, oder dem Fragment fehlt eine globale Abhängigkeit

Nur eingeschränkt — die Engine muss installiert werden

Passive Browser-Bridge

Echtes Chrome + Erweiterung + eine von Menschen gepflegte Sitzung + lokaler Loopback-Kanal

Seite nicht geöffnet, Sitzung abgelaufen, Struktur verändert, Tab geschlossen

Nein — sie braucht einen echten Menschen

Ein Fehler auf der ersten Stufe fällt im Unit-Test auf; auf der dritten heißt der Fehler: „Der Nutzer hat diesen Tab heute geschlossen.“ Wer etwas, das als reine Funktion laufen könnte, zur Lösung der dritten Stufe macht, bindet bei jedem Aufruf einen Menschen ein. Als Richtwert: Ist die Algorithmusfamilie erkannt, kann ein verschleiertes Signatur-SDK als eigenständige Implementierung mit unter 600 Zeilen landen, die nur das eingebaute crypto nutzt und auf der ersten Stufe läuft. Was vermeintlich zwingend über die Browser-Bridge gehen muss, ist oft nur ein Algorithmus, der noch nicht vollständig identifiziert wurde.

Die passive Bridge: genau ein Snapshot, niemals aktiv eingreifen

Eine passive Bridge gerät immer auf dieselbe Weise außer Kontrolle: Sie beginnt zu „helfen“ — aktualisiert automatisch, meldet sich automatisch an, wartet automatisch auf Ladezustände. Mit jedem „automatisch“ rückt sie vom Weiterleiter zum Crawler. Deshalb muss ihre Grenze extrem eng gezogen sein. Diese Regeln sind aus Erfahrung entstanden:

Genau einmal als Snapshot abfragen, die Seite niemals verändern. Cookies, Sitzungszustand und Seiten-Runtime einer bereits geöffneten Seite werden jeweils genau einmal abgefragt. Keine Tabs erzeugen, nicht aktualisieren, nicht navigieren, nicht fokussieren, nicht pollen und warten. Fehlt die Seite, dann fehlt sie — öffnet sie nicht stellvertretend für den Nutzer.

Fehlende Voraussetzungen sofort mit einem klaren Fehler melden. Gibt es keinen passenden Tab, kommt tab_unavailable; ist die Seite offen, aber nicht angemeldet, folgt not_logged_in; besteht eine Anmeldung, ist die Runtime aber noch nicht bereit, lautet der Fehler runtime_unavailable. Jeder der drei Codes steht für einen realen Zustand und eine nächste Aktion: auf die Seite warten, anmelden oder das Ziel wechseln. Statt eines pauschalen „fehlgeschlagen“, dessen Bedeutung der Aufrufer erraten muss.

Sensible Zustände verlassen den Browser nie. Die Erweiterung fordert keine Berechtigungen für cookies oder webRequest an. Sie sucht lediglich nach einem bereits geöffneten passenden Tab, führt die Anfrage im Kontext dieser Seite aus und bereinigt Felder vor der Rückgabe. Cookies und Signaturzustand der Seite verlassen Chrome zu keinem Zeitpunkt; der Kanal verbindet sich standardmäßig mit einer lokalen Loopback-Adresse. Die Bridge transportiert Ergebnisse, nicht Zugangsdaten.

Warum 15 Scopes nur wenige Plattformen abdecken

Die passive Bridge leitet ausschließlich freigegebene Anfragen weiter. Pfad, Parameter und Referer werden dabei jeweils durch einen Adapter eingeschränkt. Die überraschende Stelle ist die Granularität: Für nur wenige Plattformen gibt es insgesamt 15 Scopes, denn Scopes werden nach „Seitenkontext“ geschnitten, nicht nach „Plattform“. TikTok allein besteht aus Creative Center, Top Ads, Creator-Plattform, Influencer-Bibliothek und Ads Manager — fünf eigenständigen Scopes mit fünf unabhängigen Sitzungszuständen und fünf unabhängigen Seiten-Runtimes. Wer im Ads-Backend angemeldet ist, erhält damit nicht die Runtime der Creator-Plattform. Ein Scope pro Plattform klingt zunächst einfacher, scheitert aber beim ersten Fall „In Subsite A angemeldet, Subsite B kann dennoch nicht bedient werden“. Bei Xiaohongshu sind Hauptseite, app-äquivalente Pfade und Creator-Marktplatz entsprechend drei getrennte Scopes.

Dieselbe Granularität gilt für die Planung: Gesperrt wird nur auf Ebene der Plattformfamilie. Anfragen innerhalb einer Familie — etwa der Douyin-Familie — laufen seriell, weil sie denselben echten Tab verwenden. Gleichzeitige Aufrufe in einem Seitenkontext treten sich gegenseitig auf die Füße. Verschiedene Familien wie Douyin und Xiaohongshu können dagegen parallel laufen: Sie nutzen zwei unabhängige Tabs. Innerhalb einer Familie kommt zusätzlich ein Mindestabstand zwischen Anfragen hinzu. Zu grob geschnitten, wird parallelisierbare Arbeit unnötig serialisiert; zu fein geschnitten, kollidieren Anfragen im selben Tab. Die Plattformfamilie ist genau die natürliche Grenze dafür, was dieselbe Seiten-Runtime teilt.

Anmeldung nur per expliziter Übergabe

Die passive Bridge bedient keine Seite selbst, aber Sitzungen laufen ab. Die Lösung besteht darin, den menschlichen Eingriff auf eine einzige explizite Aktion zu reduzieren: Ein interaktiver Befehl startet eine Übergabe, das Programm öffnet über das Betriebssystem die passende Geschäftsseite, wartet darauf, dass ihr euch manuell anmeldet und die Seite bereit ist, und spielt anschließend die ursprüngliche Anfrage erneut ab. Die Erweiterung klickt dabei keine Buttons, füllt keine Formulare aus und exportiert keine Cookies. Die Anmeldung findet in einem echten Browser durch euch statt; das Programm nimmt die Anfrage erst wieder auf, wenn ihr fertig seid.

Die entscheidende Vorgabe: Das darf niemals implizit ausgelöst werden. Ein nicht interaktiver Befehl — etwa CI oder ein geplanter Task — öffnet nie einen Browser. Er gibt sauber einen Sitzungsfehler zurück und überlässt der übergeordneten Schicht die Entscheidung. Die Übergabe muss außerdem Fehlinterpretationen verhindern: Nach erfolgreicher Anmeldung erhält die Runtime höchstens eine kurze zusätzliche Wartezeit — in der Implementierung 20 Sekunden — bevor ein Ergebnis feststehen muss. Hat die Geschäftsseite bereits auf eine Kontoseite umgeleitet, der der Kontext der Zielschnittstelle fehlt, endet die aktuelle Stufe sofort. Andernfalls würde ein eindeutiges „dorthin kommt man nicht“ fälschlich als „lädt noch“ gelten und sinnlos weitergewartet. Ein Ablauf, der Degradierung zulässt, vermerkt diese Stufe als unavailable und fährt fort, statt den gesamten Prozess scheitern zu lassen.

Stufenstatus: erledigt, bewusst übersprungen oder Sitzung erforderlich

Die gemeinsame Grundlage all dieser Regeln: Keine Stufe darf nur „Erfolg“ oder „Fehler“ zurückgeben. In einer Orchestrierungspipeline hat das Ergebnis jeder Stufe sechs mögliche Formen: completed (erledigt), empty (ausgeführt, aber ohne Daten), ready (vorbereitet, wartet auf Übermittlung), skipped (durch Regel bewusst übersprungen), unavailable (derzeit nicht verfügbar, meist weil eine Sitzung erforderlich ist) und blocked (eine Voraussetzung fehlt).

„Bewusst übersprungen“, „Sitzung erforderlich“ und „tatsächlich leer“ sind drei völlig unterschiedliche Signale. Liefert das System nur ein undurchsichtiges Ergebnis zurück, lässt sich nicht unterscheiden, ob leer wirklich leer sein soll oder ob die Sitzung unbemerkt abgelaufen ist. So lässt sich eine Pipeline nicht betreiben. Modelliert den Stufenstatus als endliches Enum; dann kann die Orchestrierungsebene — ein Skript oder ein Modell — anhand dessen entscheiden, ob sie degradiert, erneut authentifiziert oder abbricht. Es ist dasselbe Prinzip wie bei den drei Fehlercodes der passiven Bridge, nur auf die Ebene des gesamten Ablaufs gehoben.

Mit dem Modell entscheiden, auf welcher Stufe eine Fähigkeit landet

Erst an dieser Stelle kommt das Modell zum Einsatz, und zwar bewusst zurückhaltend: Es bereinigt die Logik nicht für euch, sondern hilft zu beurteilen, ob und wie weit sie bereinigt werden sollte. Das ist eine Architekturentscheidung, kein Signatur-Crack. Im Zentrum steht eine Frage: Lässt sich der benötigte Zustand statisch rekonstruieren, oder existiert er nur zur Laufzeit? Darauf aufbauend wägt ihr den Bereinigungsaufwand gegen die Änderungsfrequenz ab. Für die einzelnen Schritte braucht das Modell unterschiedliche Stärken:

Schritt

Benötigte Fähigkeit

Auswahl

model id

Gesamtes Modul lesen, um die Abhängigkeitsfläche zu erfassen

Langer Kontext, liest den Call Graph in einem Durchgang

Kimi K3

kimi-k3

Für und gegen eine Stufe argumentieren, „Hauptsache, es läuft“ widersprechen

Starkes Reasoning; argumentiert dafür, einen zusätzlichen Tag in die Bereinigung zu investieren

Claude Opus 5

claude-opus-5

Dutzende bis Hunderte Fähigkeiten in einem ersten Durchlauf vorsortieren

Günstig, hohe Parallelität

Claude Sonnet 5

claude-sonnet-5

Ursachenzuordnung nach einer Degradierung

Mittleres Reasoning; erklärt anhand eines Fehlerlogs

GPT-5.6 Sol

gpt-5.6-sol

Am wichtigsten ist die zweite Zeile. Der häufigste Fehler bei der Stufenzuordnung: Das Modell übernimmt euren Tonfall „Hauptsache, es läuft“ und antwortet „Die Browser-Bridge ist am einfachsten“. Es spiegelt euch, statt die langfristigen Kosten einzupreisen. Ein Modell mit starkem Reasoning hält dagegen: „Dieses Segment ist ein Standard-Hash plus eine konstante Abweichung; es lohnt sich, einen Tag zu investieren und es als reine Funktion auszuliefern. Es gehört nicht auf die Bridge.“ Verlasst euch nicht auf mein Wort — testet es selbst. Nehmt drei Logikstücke, darunter mindestens eines, dessen korrekte Zuordnung ihr bereits kennt, gebt claude-opus-5 und gpt-5.6-sol denselben Prompt — „Stufe zuordnen, begründen und einem vorschnellen Gang zur Bridge widersprechen“ — und schaut auf genau eine Sache: Versucht das Modell, euch eine Stufe nach oben zu bringen, oder fällt es bequem auf die dritte zurück?

Der eigentliche Hinderungsgrund: Wechselkosten

Vier Stufen von drei Anbietern bedeuten drei SDKs, drei Authentifizierungsschemata und drei Fehlerformate. Den Client umzubauen, nur um Modelle zwischen den Schritten wechseln zu können, lohnt sich nicht. Deshalb verwenden die meisten am Ende ein Modell für alles — und gerade bei der Zuordnungsentscheidung, die am stärksten von einer Reasoning-Stufe profitiert, ein Modell, das nur spiegelt.

AIReiter glättet diese Ebene: ein Schlüssel, eine OpenAI-kompatible Schnittstelle, alle vier Stufen dahinter. Für den Wechsel genügt es, das Feld model im Request-Body zu ändern.

# Argumentation zur Stufenzuordnung: die Reasoning-Stufe
curl https://aireiter.com/api/v1/chat/completions \
  -H "Authorization: Bearer $AIREITER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "messages": [{"role": "user", "content": "<placement prompt + the reversed logic fragment + dependency list>"}]
  }'

# Erster Durchlauf in großer Menge: ein Feld ändern
#   "model": "claude-sonnet-5"
# Ursachenzuordnung bei Degradierung:
#   "model": "gpt-5.6-sol"

Nutzt ihr bereits das OpenAI SDK? Dann setzt base_url auf https://aireiter.com/api/v1. Beim Anthropic SDK ruft ihr mit demselben Schlüssel POST /api/v1/messages auf. Preislich laufen Claude-Modelle mit 30 % Rabatt auf den Listenpreis, GPT-Modelle zum halben Preis. Die Kosten dieses Workflows konzentrieren sich auf zwei Bereiche: die erste Massen-Vorsortierung von Dutzenden bis Hunderten Fähigkeiten — Sonnet bei hohem Anfragevolumen — sowie das Lesen eines ganzen Moduls zur Analyse seiner Abhängigkeitsfläche — Kimi mit vielen Tokens pro Anfrage. Die Massen-Vorsortierung läuft auf einem Claude-Modell, sodass der Rabatt gerade beim dichtesten Schritt greift; auch die Reasoning-basierte Argumentation zur Stufenzuordnung nutzt ein Claude-Modell mit 30 % Rabatt. Die Long-Context-Stufe von Kimi K3 steht über denselben Schlüssel zur Verfügung.

Fazit

Reverse-engineerte Logik auszuliefern ist kein rein technisches, sondern ein Kostenproblem. Die Drei-Stufen-Leiter — nativer Rewrite > lokale JS-Engine > passive Browser-Bridge — lässt sich nicht umdrehen. Mit jeder Stufe nach unten wird aus einer reinen Funktion ein Prozess mit externen Abhängigkeiten, menschlichem Eingriff und einem offenen Tab. Die passive Bridge ist nicht verboten, aber sie ist eine temporäre Komponente mit strengen Grenzen: genau ein Snapshot, niemals aktiv eingreifen, bei fehlender Seite sofort einen klaren Fehler liefern, sensible Zustände nie aus dem Browser herausgeben, Anmeldung nur durch explizite Übergabe und stets lesbare Stufenstatus. Haltet ihr diese Regeln ein, ist sie ein verlässlicher Übergangsweg. Fehlt auch nur eine davon, wird sie zur Black Box, die niemand mehr anfassen möchte. Das Modell hilft bei der Entscheidung, auf welcher Stufe eine Fähigkeit landen soll, und setzt der Trägheit von „Hauptsache, es läuft“ etwas entgegen. Ob jeder Rewrite fachlich korrekt ist, entscheidet dagegen differenzielles Testen — nicht das Modell.