The case of GrfBuilder

Everade trug mir gerade ein recht skuriles Problem vor: Mit Harmony verschlüsselte Dateien ließen sich nicht mit GrfBuilder packen. Die Dateien würden einfach übersprungen. Die originalen Dateien hingegen würden wunderbar funktionieren.
Erste Reaktion: Wie soll das gehen? GrfBuilder sollte die Dateien schließlich neutral behandeln und unabhängig von Inhalt packen. Allerdings ließ sich das Problem mit den besagten Dateien tatsächlich nachbilden. Seltsam..

Wie man sehen kann fehlen die größeren Dateien. Könnte es sein, dass er die Dateien aus irgendeinem Grund wirklich gar nicht erst liest? Kein Problem – einfach mal einen auf Mark Russinovich machen und Process Monitor anschmeißen:

Das sieht schonmal interessant aus! Man kann erkennen, dass alle Dateien geöffnet (CreateFile) und auch vollständig gelesen (ReadFile) werden. Einen wichtigen Unterschied gibt es jedoch: Nachdem die beiden funktionierenden Dateien gelesen sind wird in die test.grf geschrieben (markierte WriteFile Einträge). Offenbar werden Änderungen sofort in die GRF geschrieben. Bei der GND Datei fehlt aber dieser Schritt. Der Fehler liegt also irgendwo zwischen dem Einlesen der Daten und dem Schreiben in die GRF.
GrfBuilder ist ein speziell für RO entwickeltes Tool. Könnte es also sein, dass die Dateiformate überprüft werden, bevor eine Datei in die GRF gepackt wird? Durch die Verschlüsselung würden hier zwangsläufig Fehler entstehen. Theoretisch sollte das nicht so sein, aber wer weiß. Also folgende 2 Testläufe:

  • Die Dateien umbenennen (.bin, .txt), den Inhalt beibehalten
  • Die Formatheader einer gültigen GAT/GND Datei an den Anfang der verschlüsselten Dateien kopieren, Inhalt und Name beibehalten

Ergebnis: Der erste Testlauf brachte keine Ergebnisse, die Dateien wurden weiterhin ignoriert. Bei der zweiten Methode wurde nun jedoch eine Datei akzeptiert – die GND mit GAT Header. Interessant hierbei ist, dass ein Ändern des Inhaltes zum Erfolg geführt hat, obwohl der Dateityp mit dem verwendeten Inhalt eigentlich nichts zu tun hat. … Könnte es sich um Kompressionsproblem handeln?
Mit der höchsten Komprimierungsstufe wurde ein andere Datei (GAT mit GND Header) erfolgreich hinzugefügt. Und an dem Punkt kam mir der entscheidende Verdacht: Dateien innerhalb der GRF sind komprimiert. Kompression funktioniert umso besser, je mehr wiederkehrende Strukturen vorhanden sind. Durch Verschlüsselung werden diese Strukturen jedoch zerstört, was eine Kompression schwierig bis unmöglich macht. Nun könnte es sein, dass der zur Kompression verfügbare Speicher zu klein ist, weil eine zu hohe Kompressionsrate erwartet wird, die Dateigröße sich durch die zerstörte Struktur jedoch nicht verringert, sondern vergrößert. Ein Blick in den Source Code der verwendeten GRF Library bestätigte diese Vermutung:

1
2
3
4
// 1. Compress file, to have its size
ptr_comp = malloc(size+100);
if (ptr_comp == NULL) return NULL; /* out of memory? */
comp_size = zlib_buffer_deflate(ptr_comp, size + 100, ptr, size, handler->compression_level);

Der Entwickler hat wohl angenommen, dass unter keinen Umständen die Datei sich durch die Kompression um mehr als 100 Bytes vergrößert. Das könnte hier jedoch der Fall sein. Der entsprechende ASM Block war auch schnell gefunden:

Wenn man hier eine größere Zahl addiert sollte man den Bug zumindest umgehen können. Da man mit dem verwendeten Befehl maximal 127 addieren kann, habe ich den Codefluss einfach auf dir davor liegende Funktion (Datei umbenennen) umgeleitet, dort addiert und einen Rücksprung platziert.
Eine bessere Lösung wäre es übrigens, nicht einfach wild drauf los zu schätzen, sondern compressBound zu verwenden:

    The compressBound() shall return a value representing the upper bound of an array to allocate to hold the compressed data in a single call to compress() or compress2(). This function may return a conservative value that may be larger than sourceLen.

Im Fall von Everade genügte aber vorerst mein “Hackfix”. Mit der modifizierten grf.dll kann man nun zwar keine Dateien mehr umbenennen, aber dafür alle Dateien problemlos komprimieren. :)

2 Responses to “The case of GrfBuilder”

  1. chez says:

    *staun*

  2. Nae says:

    cranker shit :3

Leave a Reply