Het artikel behandelt twee methoden voor het analyseren van binaire bestanden:
- Optimalisatie van de hex-editor bine: De auteur wilde kleuren toevoegen aan de editor, maar stuitte op prestatieproblemen door de overhead van Python en
ncurses. Als oplossing is gekozen voor het gebruik van specifieke Unicode-tekens (▓ en ░) in de ASCII-kolom om NUL-bytes en niet-printbare tekens snel visueel te onderscheiden zonder de runtime-prestaties te beïnvloeden.
- Visualisatie via afbeeldingen: Om grote bestanden beter te kunnen overzien, heeft de auteur een methode geïmplementeerd om binaire data direct om te zetten naar een Portable Graymap (PGM) afbeelding. Hierbij wordt elke byte behandeld als een pixel.
Bevindingen:
- Executable code ziet er willekeurig uit.
- ASCII-tekst vormt uniforme, donkergrijze secties.
- Tarballs en uitgelijnde data (zoals in de
ruff binary) vertonen herkenbare patronen.
De auteur merkt op dat deze methode bij zeer grote bestanden (meerdere GB's) kan leiden tot geheugenproblemen bij het verwerken van de resulterende afbeeldingen met tools zoals ImageMagick.
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.
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.