Oto „przywrócone” obrazy, dzięki dalszym badaniom tillberga:
Zgodnie z oczekiwaniami 5-bajtowy znacznik bloku co około 0x4020 bajtów. Format wygląda następująco:
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 */
};
Po odczytaniu znacznika następne marker.len
bajty tworzą blok, który jest częścią pliku. marker.notlen
jest zmienną kontrolną taką, że marker.len + marker.notlen == 0xffff
. Ostatni blok jest taki, że marker.tag == 1
.
Struktura jest prawdopodobnie następująca. Nadal istnieją nieznane wartości.
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? */
};
Nie zorientowałem się, co jest na końcu, ale ponieważ PNG akceptują padding, nie jest to zbyt dramatyczne. Jednak zakodowany rozmiar pliku wyraźnie wskazuje, że ostatnie 4 bajty należy zignorować ...
Ponieważ nie miałem dostępu do wszystkich znaczników bloków tuż przed początkiem pliku, napisałem ten dekoder, który zaczyna się na końcu i próbuje znaleźć znaczniki bloków. To wcale nie jest solidne, ale dobrze, zadziałało dla twoich zdjęć testowych:
#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;
}
Starsze badania
Oto, co dostajesz, usuwając bajt 0x4022
z drugiego obrazu, a następnie usuwając bajt 0x8092
:
Tak naprawdę nie „naprawia” obrazów; Zrobiłem to metodą prób i błędów. Mówi jednak, że co 16384 bajtów występują nieoczekiwane dane. Domyślam się, że obrazy są spakowane w jakąś strukturę systemu plików, a nieoczekiwane dane to po prostu znaczniki bloków , które należy usunąć podczas odczytywania danych.
Nie wiem, gdzie dokładnie są znaczniki bloku i ich rozmiar, ale sam rozmiar bloku z pewnością wynosi 2 ^ 14 bajtów.
Byłoby pomocne, gdybyś mógł również dostarczyć zrzut heksadecymalny (kilkadziesiąt bajtów) tego, co pojawia się tuż przed obrazem i zaraz po nim. Dałoby to wskazówki na temat tego, jakie informacje są przechowywane na początku lub na końcu bloków.
Oczywiście istnieje również możliwość, że w kodzie ekstrakcyjnym występuje błąd. Jeśli używasz bufora 16384 bajtów do operacji na plikach, najpierw bym tam sprawdził.