Ось «відновлені» зображення, завдяки подальшим дослідженням Донберга:
Як і очікувалося, є 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 байтів для ваших файлових операцій, я спершу там перевірю.