Вот «восстановленные» изображения, благодаря дальнейшим исследованиям Тильберга:
Как и ожидалось, 5-байтовый маркер блока примерно каждые 0x4020 байт. Формат выглядит следующим образом:
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 */
};
После считывания маркера следующие marker.len
байты образуют блок, который является частью файла. marker.notlen
контрольная переменная такая, что marker.len + marker.notlen == 0xffff
. Последний блок таков, что marker.tag == 1
.
Структура, вероятно, следующая. Есть еще неизвестные значения.
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? */
};
Я не понял, что в конце, но, так как PNG допускают заполнение, это не слишком драматично. Однако размер закодированного файла ясно указывает на то, что последние 4 байта следует игнорировать ...
Поскольку у меня не было доступа ко всем маркерам блоков непосредственно перед началом файла, я написал этот декодер, который запускается в конце и пытается найти маркеры блоков. Это совсем не надежно, но хорошо, это работает для ваших тестовых изображений:
#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;
}
Старые исследования
Вот что вы получаете, удаляя байт 0x4022
из второго изображения, затем удаляя байт 0x8092
:
Это действительно не «восстанавливает» изображения; Я сделал это методом проб и ошибок. Однако, это говорит о том, что каждые 16384 байта появляются непредвиденные данные. Я предполагаю, что изображения упакованы в некую структуру файловой системы, а неожиданные данные - это просто маркеры блоков, которые вы должны удалить при чтении данных.
Я не знаю, где именно находятся маркеры блоков и их размер, но сам размер блока, безусловно, составляет 2 ^ 14 байт.
Было бы полезно, если бы вы также могли предоставить шестнадцатеричный дамп (несколько десятков байт) того, что появляется прямо перед изображением и сразу после него. Это даст подсказки о том, какая информация хранится в начале или конце блоков.
Конечно, есть вероятность того, что в вашем коде извлечения есть ошибка. Если вы используете буфер 16384 байта для файловых операций, я сначала проверил бы там.