Русский
Русский
English
Статистика
Реклама

Препарируем Compound File Binary format (CFB), или начинаем парсить DOC

Compound File это довольно сложный универсальный бинарный формат файлов, лежащий в основе форматов офисных документов до MS Office 2007 (doc, xls, ppt, msg, ), отчасти MS Office 2007+ (например vbaProject.bin внутри xlsm) и других.
Под катом краткое описание как Compound File устроен внутри, которое, надеюсь, будет полезно как ликбез и поможет читателю лучше понимать что делают утилиты или про что пишут в статьях про CFB файлы.


Устройтсво CFB файла сильно напоминает устройство диска с FAT. Логически CFB хранит древовидную структуру объектов, подобную файловой системе с директориями (тип объекта в CFB STORE) и файлами (в CFB STREAM). Каждый объект имеет имя.
Весь файл разбит на сектора размером 512 байт для v3 и 4096 байт для v4. Это, на мой взгляд, главная разница между v3 и v4. Дальше цифры для примеров я буду приводить для v3 (и в скобках для v4). В начальном секторе хранится заголовок файла и начало таблицы DIFAT (см. ниже). Значение содержимого других секторов определяется динамически по таблицам DIFAT и FAT (да, она прям так и называется, как в файловой системе), фиксированных значений сектор n содержит информацию типа t больше нет.
Всего максимум может быть 0xFFFFFFFA секторов. Номера секторов больше 0xFFFFFFFA являются маркерами и фактически ни на какой сектор не указывают. Например 0xFFFFFFFE (EndOfChain) означает, что текущий сектор последний в списке.

DIFAT


DIFAT Double Indirection File Access Table. Это массив 4х байтных номеров секторов, в которых находится FAT. Первые 109 записей хранятся в первом секторе файла (пять из них выделены жёлтым внизу КДПВ). Если файл больше 6.8 Мбайт (436 Мбайт для v4), то следующие сектора DIFAT хранятся как односвязный список. Номер первого сектора из этого списка хранится в заголовке. В кажом из этих секторов хранится 127 (1023) указателей на сектора с FAT, а в последних 4х байтах номер следующего сектора DIFAT.

FAT


FAT File Access Table. Это массив из 4х байтных значений. Работает так же, как в одноимённой файловой системе. Каждому сектору в файле (кроме заголовочного) соответствует 4х байтное значение в FAT.
Для чтения потока данных (STREAM) нужно знать длину потока (в байтах) и номер первого сектора этого потока. Номер следующего сектора всегда записан в элементе FAT, соответствующем текущему сектору. В элементе FAT, соответствующем последнему сектору в потоке, будет записано значение 0xFFFFFFFE (EndOfChain).
Пример цепочки в FAT
Если данные в потоке записаны в 3 сектора: 0, 1 и 4, то в FAT будут записаны такие значения: 1, 4, x, x, 0xFFFFFFFE (где x какое-то значение, к данному потоку отношения не имеющее).
Для чтения этого потока мы читает данные из сектора 0, расположенного сразу после заголовка файла (сам заголовочный сектор имеет номер -1), читаем номер следующего сектора из FAT[0] = 1, читаем данные из сектора 1 и номер следующего сектора из FAT[1] = 4, читаем данные из сектора 4 и в FAT[4] видим что это последний сектор этого потока (EndOfChain).


Directory


Это структура, которая хранит информацию обо всех остальных объектах в файле, аналог структуры каталогов. Сама она хранится в виде потока данных, длина и первый сектор которого записаны в заголовке файла. Представляет из себя массив 128 байтных записей. Каждая запись соответствует хранимому объекту. Объекты в directory имеют имя и бывают 4х видов:
  1. ROOT корень дерева каталогов. В directory хранится в элементе 0, имя всегда Root Entry. По сути является директорией, но хранит информацию о потоке данных с MiniStream (см. ниже).
  2. STORE директория. Логически является вместилищем других объектов типа STORE или STREAM. Физически дочерние объекты хранятся в виде списка (ещё точнее чёрно-красного дерева), а в STORE хранится указатель на один из дочерних объектов. Никакой поток данных к STORE не прицеплен, но есть ClSid GUID приложения, к которому относится содержимое этой директории.
  3. STREAM файл. Хранит информацию о потоке данных (первый сектор и длина); дочерних объектов и ClSid нет.
  4. UNKNOWN пустой объект. Запись, (почти) забитая 0, чисто технически нужна для дополнения списка Directory до длины, кратной длине сектора.

У каждого объекта есть временные метки: время создания и время модификации, правда часто они просто заполнены 0.
Пример дерева директорий небольшого doc файла:
Root_storage:Root Entry 06090200-0000-0000-c000-000000000046
- Stream:\x01CompObj[106]
- Stream:\x01Ole[20]
- Stream:1Table[4640]
- Stream:Data[374]
- Stream:\x05SummaryInformation[172]
- Stream:WordDocument[198510]
- Storage:ObjectPool 00000000-0000-0000-0000-000000000000
- Stream:\x05DocumentSummaryInformation[116]


MiniStream и MiniFAT


В принципе описанного выше достаточно для хранения данных, но в CFB предусмотрена ещё оптимизация для хранения коротких потоков данных меньше 4096 байт.
При хранении потока данных, длина которого не кратна длине сектора, в последнем секторе остаётся некоторое количество неиспользуемых байт. Чем больше сектор тем больше таких байт в среднем остаётся. Для борьбы с этой проблемой все небольшие (меньше 4096 байт) потоки данных хранятся не в обычных секторах по 512 (4096) байт и FAT, а в отдельном потоке, разбитом на сектора по 64 байта. Этот поток называется MiniStream, а информация для сцепления его секторов в потоки хранится в MiniFAT. Работает это всё совершенно аналогично основной FAT.
И MiniFAT, и MiniStream хранятся как обычные (не Mini) потоки данных. Номер первого сектора и длина MiniFAT хранятся в заголовке, а информация о MiniStream в объекте Root Entry (логической причины хранить именно там не вижу).
Получается такая матрёшка: файл разделён на 512 (4096) байтные сектора, а один из потоков, состоящих из этих секторов, рассматривается как последовательность 64 байтных секторов, и в нём уже хранятся мини потоки данных.
Откуда читать поток данных: из большой FAT или из MiniStream, определяется только по его размеру (меньше 4096 из MiniStream, иначе как обычный поток).

Где же текст/картинка/таблица/макрос?


CFB используется как хранилище, позволяющее сохранить данные разных приложений в одном файле. Если вы хотите докопаться до человекочитаемых данных, то надо парсить потоки данных, извлечённые из CFB, в соответствии с форматом данных приложения. Эта тема выходит за пределы данной статьи (но см. ссылки).

Выводы


По идее такая структура файла была разработана для ускорения внесения отдельных изменений в файл без его полной перезаписи. Не думаю, что это свойство ценно сейчас для обычных офисных документов размером несколько мегабайт.
В общем структура файла CFB просто кладезь для стеганографии, основанной на формате файла. Т. е. сделать совершенно корректный файл, который при открытии в ворде покажет Hello world, а внутри будет ещё много, которые правильный парсер будет считать просто мусором вообще не проблема.
С другой стороны огромный простор для ошибок, путаницы и т.п., огромное количество избыточных записей. Например, можно зациклить цепочку секторов, образующую поток. Наивный парсер при чтении такого файла зависнет.
P.S. Файлы MS Office 2007 и старше (docx, xlsx, pptx, docm,) имеют совершенно другой формат. Внутри там тоже дерево директорий, только вместо CFB там используется ZIP (да, их можно прям раззиповать). Однако макросы, например, хранятся внутри документа в файле vbaProject.bin, который является Compound файлом.

Ссылки


  • Сам CFB очень хорошо (на мой взгляд) документирован: [MS-CFB]: Compound File Binary File Format
  • [MS-DOC]: Word (.doc) Binary File Format для тех, кто хочет полноценно разобрать doc.
  • [MS-OVBA]: Office VBA File Format Structure о том, как хранятся макросы, в том числе в Office 2007+ (docm, xlsm, ...)
  • Ещё пара статей на Хабре про парсинг DOC от Rembish:
    Текст любой ценой: WCBFF и DOC и Текст любой ценой: Miette
  • Для практического анализа файлов MS Office (как до 2007 Офиса, так и после), в том числе подозрительных, рекомендую набор утилит OleTools. Их можно использовать и как отдельные программы, и как модули Python.
  • Ещё полезный набор утилит OleDump
  • CFB extractor, появившийся в процессе написания этой статьи. Из полезного умеет доставать из CFB файла все потоки данных (включая потоки, не имеющие соответствующей записи в директории).
Источник: habr.com
К списку статей
Опубликовано: 11.01.2021 10:24:00
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Информационная безопасность

Ctf

Information security

Infosec

Cfb

Compound file binary format

Compound files

Doc

Xls

Msg

Ms office

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru