Visualiseren van binaire bestanden

Verbeteringen aan de hex-editor bine

In mijn hex-editor, bine, is alles in feite zwart-wit. Een tijd geleden kreeg de xxd-functie van Vim de mogelijkheid om de output te kleuren (deze functie is niet opgenomen in Vim Classic, die ik gebruik).

Ik wilde deze functionaliteit ook in bine implementeren. Een vroege versie hiervan was een verbetering, omdat het hierdoor makkelijker werd om bereiken met voornamelijk ASCII-tekst te herkennen. Toch was het niet optimaal; er waren meer kleuren nodig en het zou waarschijnlijk beter zijn als de hex-kolommen direct naast elkaar stonden, in plaats van gescheiden door een spatie.

Mijn TUI-framework, movwin, maakt gebruik van ncurses (dit is een bewuste keuze vanwege de stabiliteit van de functies van ncurses). Het framework zelf is geschreven in Python, terwijl ncurses een C-bibliotheek is. Ik merkte dat het aanroepen van ncurses vanuit Python erg kostbaar kan zijn.

Zonder kleuren schrijft bine een volledige hex-regel met één enkele curses-aanroep. Met kleuren zouden er echter veel aanroepen per regel nodig zijn, wat te traag zou zijn. Bovendien is het framework zelf niet optimaal ingericht om snel veel verschillende kleuren te gebruiken; het heeft een hiërarchisch paletsysteem dat veel rekenkracht kost. Daarnaast zou ik minstens twee versies van het kleurenpalet moeten onderhouden, omdat movwin standaard zowel een donker als een licht thema ondersteunt.

De oplossing: Visuele tekens

Uiteindelijk kwam ik tot de volgende oplossing: in plaats van de hex-kolom te kleuren, gebruikt bine speciale tekens in de ASCII-kolom — tekens die normaal gesproken niet voorkomen, zodat er geen conflicten ontstaan.

  • NUL-bytes worden weergegeven als U+2593 (▓).
  • Bytes buiten het bereik 0x20 <= byte <= 0x7E worden weergegeven als U+2593 (░).
  • De overige bytes zijn printbare ASCII-tekens en worden zoals ze zijn getoond.

Hoewel dit niet alle scenario's dekt die xxd ondersteunt, is het voldoende. Je krijgt een snel overzicht van wat "platte tekst" is en wat niet, en NUL-bytes vallen direct op. De implementatie was eenvoudig en de impact op de runtime-prestaties is minimaal.

Binaire bestanden visualiseren als afbeeldingen

Daarnaast wilde ik een vergelijkbare visualisatiefunctie voor grote bestanden. Het kleine ASCII-paneel in bine kan immers maar een beperkte hoeveelheid informatie tonen. Ik zocht naar een oplossing voor bestanden van enkele megabytes of zelfs gigabytes.

Mijn idee voor visualisatie is in feite het creëren van een afbeelding. Waarom de computer niet dwingen om een bestaand binair bestand direct als afbeelding te interpreteren? Zonder enige voorverwerking worden alle bytes in het bereik van 0x00 tot 0xFF simpelweg gekoppeld aan pixels.

Een Portable Graymap (PGM) is precies dat. Hoewel de voorbeelden op Wikipedia vaak de ASCII-versie van dit formaat tonen, bestaat er ook een binaire versie. De implementatie hiervan is zeer eenvoudig:

printf 'P5\n%s %s 255\n' "$width" "$height"
cat "$infile"

De enige echte "logica" die ik heb geïmplementeerd, is het bepalen van een geschikte breedte en hoogte. Het resultaat kan worden geopend met elke image viewer (op Linux en BSD).

Analyse van visualisaties

Wanneer we kijken naar de visualisatie van een EXE-bestand van GORILLAS.BAS (het volledige spel), vallen enkele secties op:

  • De bovenkant: Een bijna "willekeurig" gebied met zeer donkere, zeer lichte en grijze pixels. Dit is de executable code.
  • Het midden: Een zeer regelmatig patroon. Hoewel dit lijkt op een Relocation Table, bevindt het zich op de verkeerde positie. Aangezien dit een EXEPACK-bestand is, compliceert dit de analyse.
  • Het einde: Een langere sectie van "donkergrijs" die relatief uniform is. Dit is ASCII-tekst; de high bit is hier nooit gezet, waardoor deze waarden duidelijk kleiner zijn dan de code-bytes. Programma's slaan hier vaak hun string-literalen op.

Het is interessant dat er geen voorverwerking nodig is; de visualisatie exploiteert simpelweg de eigenschappen van de data.

Andere voorbeelden

  • Tarballs: In een tarball met een tekstbestand en een afbeelding is het verschil duidelijk zichtbaar. In het tekstgedeelte is vaak een patroon te zien dat lijkt op een betegend behang; dit komt door repeterende tekst.
  • Ruff binary: In een visualisatie van ruff (25 MB) zijn stroken van voornamelijk NUL-bytes te zien, wat duidt op netjes uitgelijnde data en tabellen.
  • ISO-bestanden: Een .iso-bestand van 690 MB kan in 0,2 seconden worden omgezet naar een PGM-bestand. Het verkleinen van de afbeelding met ImageMagick duurt echter ongeveer 6 seconden.

Beperkingen en conclusie

Bij zeer grote bestanden loopt deze aanpak tegen grenzen aan. Bij een bestand van 4 GB verbruikte ImageMagick te veel RAM. In theorie zou dit opgelost kunnen worden door de afbeelding iteratief te verkleinen (slechts enkele rijen tegelijk in het geheugen te laden) en daarna de kleinere versie te comprimeren, maar ik heb hier nog geen tool voor geschreven.

Toch is dit een nuttig hulpmiddel, afhankelijk van het type data waarnaar gezocht wordt. Hoewel ik zelden bestanden van meerdere gigabytes hoef te inspecteren, kan ik deze indien nodig simpelweg opsplitsen in kleinere delen. Zelfs bij een oud schijmafbeelding van 8 GB biedt deze methode interessante inzichten.

Voor mijn huidige behoeften is de tool in zijn huidige vorm voldoende.