Ecco le immagini “restaurate”, grazie alle ulteriori ricerche di tillberg:
Come previsto, è presente un marker di blocco a 5 byte ogni circa 0x4020 byte. Il formato sembra essere il seguente:
struct marker {
uint8_t tag; /* 1 if this is the last marker in the file, 0 otherwise */
uint16_t len; /* size of the following block (little-endian) */
uint16_t notlen; /* 0xffff - len */
};
Dopo aver letto l'indicatore, i marker.len
byte successivi formano un blocco che fa parte del file. marker.notlen
è una variabile di controllo tale che marker.len + marker.notlen == 0xffff
. L'ultimo blocco è tale marker.tag == 1
.
La struttura è probabilmente la seguente. Ci sono ancora valori sconosciuti.
struct file {
uint8_t name_len; /* number of bytes in the filename */
/* (not sure whether it's uint8_t or uint16_t) */
char name[name_len]; /* filename */
uint32_t file_len; /* size of the file (little endian) */
/* eg. "40 25 01 00" is 0x12540 bytes */
uint16_t unknown; /* maybe a checksum? */
marker marker1; /* first block marker (tag == 0) */
uint8_t data1[marker1.len]; /* data of the first block */
marker marker2; /* second block marker (tag == 0) */
uint8_t data2[marker2.len]; /* data of the second block */
/* ... */
marker lastmarker; /* last block marker (tag == 1) */
uint8_t lastdata[lastmarker.len]; /* data of the last block */
uint32_t unknown2; /* end data? another checksum? */
};
Non ho capito cosa ci sia alla fine, ma poiché i PNG accettano il padding, non è troppo drammatico. Tuttavia, la dimensione del file codificato indica chiaramente che gli ultimi 4 byte devono essere ignorati ...
Dato che non avevo accesso a tutti i marcatori di blocchi appena prima dell'inizio del file, ho scritto questo decodificatore che inizia alla fine e tenta di trovare i marcatori di blocco. Non è affatto robusto ma bene, ha funzionato per le immagini di prova:
#include <stdio.h>
#include <string.h>
#define MAX_SIZE (1024 * 1024)
unsigned char buf[MAX_SIZE];
/* Usage: program infile.png outfile.png */
int main(int argc, char *argv[])
{
size_t i, len, lastcheck;
FILE *f = fopen(argv[1], "rb");
len = fread(buf, 1, MAX_SIZE, f);
fclose(f);
/* Start from the end and check validity */
lastcheck = len;
for (i = len - 5; i-- > 0; )
{
size_t off = buf[i + 2] * 256 + buf[i + 1];
size_t notoff = buf[i + 4] * 256 + buf[i + 3];
if (buf[i] >= 2 || off + notoff != 0xffff)
continue;
else if (buf[i] == 1 && lastcheck != len)
continue;
else if (buf[i] == 0 && i + off + 5 != lastcheck)
continue;
lastcheck = i;
memmove(buf + i, buf + i + 5, len - i - 5);
len -= 5;
i -= 5;
}
f = fopen(argv[2], "wb+");
fwrite(buf, 1, len, f);
fclose(f);
return 0;
}
Ricerche precedenti
Questo è ciò che ottieni quando rimuovi byte 0x4022
dalla seconda immagine, quindi rimuovendo byte 0x8092
:
In realtà non "ripara" le immagini; L'ho fatto per tentativi ed errori. Tuttavia, ciò che dice è che ci sono dati imprevisti ogni 16384 byte. La mia ipotesi è che le immagini siano racchiuse in una sorta di struttura del filesystem e che i dati imprevisti siano semplicemente dei marker di blocco che dovresti rimuovere durante la lettura dei dati.
Non so dove siano esattamente i marker di blocco e la loro dimensione, ma la dimensione del blocco stesso è sicuramente 2 ^ 14 byte.
Sarebbe utile se si potesse anche fornire un dump esadecimale (alcune dozzine di byte) di ciò che appare subito prima dell'immagine e subito dopo. Ciò darebbe suggerimenti su quale tipo di informazione è memorizzata all'inizio o alla fine dei blocchi.
Naturalmente c'è anche la possibilità che ci sia un bug nel tuo codice di estrazione. Se stai usando un buffer di 16384 byte per le tue operazioni sui file, per prima cosa controllerei lì.