Init. Commit
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
<h1 align="center">У него есть прошлое</h1>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/category-Stego-blueviolet" alt="Stego"/>
|
||||
</p>
|
||||
|
||||
На старте есть изображение `kot2_proshloe.png`, в котором записан образ диска с CatFS версии 2. Образ содержит снимок каталога, журнал транзакций и сектора данных. Нужно восстановить последнюю зафиксированную версию `nose_note.txt` перед её удалением.
|
||||
|
||||
## Решение
|
||||
|
||||
### Извлечение CatFS
|
||||
|
||||
В PNG находим `iTXt`-запись `CatFS.boot`. Она описывает геометрию носителя: плитки 32 × 32 от `(0, 0)`, обход по строкам, один бит в чётности синего канала (`B & 1`). Байты собираются старшим битом вперёд.
|
||||
|
||||
В плитке `32 × 32 = 1024` бита, то есть один сектор на 128 байт. Для изображения 1254 × 1254 получаем `39 × 39 = 1521` полный сектор.
|
||||
|
||||
Сектор `0` начинается с `CATF`, версия — `2`. Сверяем CRC32 первых 124 байт со значением по смещению `124` и читаем карту областей:
|
||||
|
||||
| Область | Сектора |
|
||||
|---|---|
|
||||
| Суперблок | `0` |
|
||||
| Снимок каталога | `1–2` |
|
||||
| Журнал транзакций | `3–7` |
|
||||
| Данные | С сектора `8` |
|
||||
|
||||
В суперблоке также указано `sectors_used=107` — это число занятых секторов всей файловой системы, включая служебные области.
|
||||
|
||||
У сектора данных есть `sector_id`, `next_sector`, `used_length` и `payload_crc`. Заголовок сектора занимает 16 байт, а CRC покрывает всё следующее 112-байтное поле payload, включая неиспользованную добивку. Следуем по `next_sector` до `0xffffffff`, полезную длину учитываем при сборке файла.
|
||||
|
||||
Запись каталога занимает 32 байта: `name[16]`, `inode u32`, `generation u16`, `reserved u8`, `flags u8`, `size u32`, `first_sector u32`. По снимку читаем живой `format.txt` — там правила журнала и точная цель восстановления.
|
||||
|
||||
### Снимок записки
|
||||
|
||||
В снимке `nose_note.txt` имеет `inode=1006`, `generation=1`, `first_sector=100`. У записи выставлен бит сжатия `flags & 2`. Собираем полезные байты цепочки секторов с номерами от `100` до `106`, затем распаковываем zlib. Получаем 1504 байта текста, но флаг обрывается на `caplag{zhurn4l_p0m`. Хвост появился позже, в журнале.
|
||||
|
||||
Записи журнала имеют формат `[type u8][reserved u8][length u16][payload][crc32]`. Многобайтные числа читаем в little-endian. CRC32 покрывает четырёхбайтный заголовок записи и её payload. Читаем до `type=0`, сверяем CRC и группируем операции между `TXN_BEGIN` и соответствующим `TXN_COMMIT`.
|
||||
|
||||
### Применение журнала
|
||||
|
||||
Сначала загружаем живые объекты снимка, сохраняя для каждого inode поколение и логические байты файла. Затем читаем транзакции в порядке журнала и применяем только завершённые COMMIT. Незавершённую транзакцию отбрасываем при следующем BEGIN или конце журнала.
|
||||
|
||||
| Транзакция | Действие | Что делаем |
|
||||
|---|---|---|
|
||||
| `t2`, есть COMMIT | `OP_WRITE` к `(1006, gen 1)`, offset `1504`, replace_len `0` | Распаковываем zlib-патч, дописываем его байты и получаем gen 2 |
|
||||
| `t3`, нет COMMIT | `OP_WRITE` к `(1006, gen 2)`, offset `1486`, replace_len `31` | Отбрасываем всю транзакцию |
|
||||
| `t4`, есть COMMIT | `OP_DELETE` к `(1006, gen 2)` | Сохраняем последнюю версию и фиксируем удаление |
|
||||
| `t5`, есть COMMIT | `OP_WRITE` к `(1006, gen 0)` | Создаём новую серию inode для `draft.txt` |
|
||||
|
||||
У `t3` правильные CRC отдельных записей, но это не делает транзакцию зафиксированной. Если применить её без COMMIT, настоящий текст заменится ложным.
|
||||
|
||||
Одного номера inode недостаточно, поскольку операции относятся к паре `(inode, generation)`, а создание с `generation=0` после удаления начинает новую серию. Поэтому `draft.txt` из `t5` не заменяет искомую историю записки.
|
||||
|
||||
Патч `t2` дописывает недостающий хвост. Последняя зафиксированная версия перед `t4` — gen 2 длиной 1568 байт. В её конце читаем полный флаг.
|
||||
|
||||
[Солвер](solve/solve.py).
|
||||
|
||||
## Флаг
|
||||
|
||||
`caplag{zhurn4l_p0mn1t_k0t4_n0s}`
|
||||
Reference in New Issue
Block a user