ZF

     Мартин Шварц, ФАЙЛОВАЯ СИСТЕМА ЛАОЛА

     "Структурированное хранилище"

     Двоичная структура OLE-документов

     (c) 1996, 1997 by martin schwartz@cs.tu-berlin.de

*****(Перевод: Constantin E. Climentieff aka DrMad, 1998)*************

     Пособие для хакинга

     A - Предварительные замечания
     B - Общие сведения
     C - Базовые понятия

     1. Блок заголовка
     2. Хранилище Больших Блоков
     3. Большие Блоки Данных
        3.1. Хранилище Малых Блоков
        3.2. Хранилище Набора Свойств
        3.3. Хранилище Свойств
        3.4. Где располагаются Малые Блоки Данных?
        3.5. Наборы Свойств
     4. Блоки Мусора

     Table 1: LAOLA Header
     Table 2: Property Storage

     Таблица 1: Заголовок ЛАОЛы
     Таблица 2: Хранилище Свойств

     Только для белых


                    A - Предварительные замечания

    Однажды я начал писать некую программу,  которая должна была  иметь
доступ к документам,  созданным при помощи Microsoft Wor for Windows 6.
Я хотел оставить ее совместимой,  а для этого не следовало использовать
методы,  специфичные  для операционных систем.  Таким образом,  я решил
научиться разбираться в двоичной структуре документов.
    Посмотрев на  двоичный дамп документа,  я слегка припух.  На первый
взгляд формат файла документа казался очень сильно  отличным  от  того,
который  создавался  в  Word 2.  Когда я посмотрел внимательней,  стало
ясно,  что некоторые очень похожие двоичные куски  находятся  и  внутри
новых  наборов  данных.  Фактически,  документ Word 6 оказался похож на
документ  формата  Word  2,  сохраненный  среди  прочих  дополнительных
данных.
    Насколько я  знаю,  не  существует  публично  доступных  источников
информации,  как  эта  файловая система (формат документа) работает.  В
общем надо бы,  чтобы  производители  были  обязаны  показывать,  какие
ингридиенты  есть в их продуктах.  В случае личных систем я думаю,  что
людям  следовало  бы  знать,  какого  рода  дополнительная   информация
находится в их (возможно публично доступных) документах. Иными словами,
содержат ли  документы  информацию  про  дату  создания,  информацию  о
принтерах,  структурах  данных и серийных номерах.  Или,  что несколько
хуже,  содержат ли документы или могут ли  содержать  другие  приватные
данные.
    Суммируя эту тему для нижеописанного формата файла,  можно сказать,
что  он  всегда сохраняет дату последней модификации данных в объектах.
Вследствие слабой реализации Windows  3.x  и  старых  версий  32-битных
Windows,  он  все еще содержит некие секции "мусора".  Эти секции могут
хранить нежелательные сведения об авторе.  Конечно,  в  зависимости  от
принадлежности  программ,  и  другие приватные данные могут сохраняться
незаметно.
    Этот текст *не* объясняет,  как структурирован файл Microsoft Word.
Этот текст объясняет как  работает  файловая  система,  которую  ранние
программы  от  Microsoft  типа Microsoft Word используют для сохранения
своих  документов.  Фактически,  это  следовало  бы  назвать   файловой
системой  OLE,  а  философию,  на  которой  это  основано - технологией
OLE/Com.  Но ввиду  отсутствия  какой-либо  технической  низкоуровневой
спецификации  касаемо  этой  темы,  мое  объяснение  может  в некоторых
случаях отличаться или быть неверным. В этих случаях я не могу уверенно
объяснить файловую систему OLE,  но будет по крайней мере похоже. Таким
образом, я решил дать своим разработком похожее имя. Имя - LAOLA.
    Копирование. Этот файл и исходные коды,  на которые имеются ссылки,
распространены в терминах GNU General Public License,  версия 2 от Июля
1991 г. Если у Вас нет копии, можете найти ее здесь.

                      B - Общие сведения

    Цифровые документы обычно состоят из более,  чем одного файла.  При
сохранении и изменении таких документов есть  проблема  объединять  все
такие файлы вместе в соответствии с некими иерархическими соглашениями.
Формат файлов LAOLA позволяет сохранять файлы в иерархическом порядке в
одном  общем  файле.  Он  может содержать один или более каталогов,  из
которых каждый может содержать один или более файлов и каталогов.
    На самом  деле  это  можно успешно сделать использованием некоторых
популярных архивов типа "zip". Но Microsoft пошел своим путем. Я думаю,
не только вследствие своей маркетинговой философии, но также потому что
они,  наверное,  намеревались разработать файловую систему, управляемую
их   философией   "OLE",   что  требует  иметь  иерархическую  файловую
структуру.
    К сожалению,   Microsoft   не   содержит   механизмов   верификации
документов.  То-есть,  если документ LAOLA поврежден,  то он становится
недоступен. Если кто-то вмешается в структуру документа, этого никто не
заметит. Если документ содержит много неиспользуемого пространства, это
так  и  останется.  Microsoft  стратегически не предусматривает никакой
компенсации недостатков своей новой файловой системы.

                     C - Базовые понятия.

    Никаких гарантий! Этот текст описывает предположения, основанные на
спекуляциях  и  экспериментах,  и совсем немного на документах.  Тем не
менее,  даже если я считаю какой либо факт верным,  он может  содержать
ошибки!
    Что такое файл LAOLA?  Коротко,  файл LAOLA это архив.  Архив может
объединять  файлы  и  каталоги.  Каждая  точка входа в архив имеет блок
информации длиной 0х80 байт.  Чтобы  сохранять  файлы,  архив  содержит
список  больших  блоков  данных  и список малых блоков данных.  Файлы с
размером меньше,  чем 0x1000 байтов,  будут  сохранены  в  Малые  Блоки
данных, а остальные - в Большие Блоки данных.

    Типы данных. Файлы LAOLA имеют три основных типа данных:

1. 4-байтовые целые ("long") 0x12345678         ->0x78 0x56 0x34 0x12
2. 2-байтовые целые ("word") 0x1234 0x5678      ->0x34 0x12 0x78 0x56
3. 1-байтовые целые ("char") 0x12 0x34 0x56 0x78->0x12 0x34 0x56 0x78

    Целые сохраняются  в  режиме  "little endian" ("VAX",  "x86").  Это
означает,  что они сохраняются группами по 8  бит,  в  которых  младший
значащий бит - сначала.  Потоки символов сохраняются в очереди:  первый
вошел, первый вышел.
    Блоки. Во время первого шага каждый файл LAOLA разделяется на 0х200
-байтовые (512-байтовые) "большие блоки",  так что размер каждого файла
LAOLA   делится   без  остатка  на  0х200.  Каждый  блок  ссылается  на
перечисление,  начинающееся с номера - 1 для первых 0х200 байтов,  если
считать снизу вверх. Файл состоит из множества блоков:

    файл <=> объединение больших блоков {-1, 0, 1 .. $maxblock},

    ($maxblock = (sizeof(file)-1) / 0x200 -1), $maxblock e {1, 2, .. }

     Базовые части. Большие блоки делят файл на четыре базовые чати:

     1. Блок заголовка
     2. Хранилище Больших Блоков
     3. Большие Блоки данных
     4. Мусорные блоки данных

    Неиспользуемая, но не мусорная информация.  Блоки  LAOLA,  кажется,
содержит   некоторые  неструктурированные  данные.  Некоторые  из  них,
кажется,  (все еще?) абсолютно  не  нужны,  а  назначение  других  пока
непонятно.   Тем   не  менее,  для  создания  файлов  LAOLA  достаточно
скопировать  эти  значения.  В  таблицах  в   конце   этого   документа
неизменяемые значения маркированы точкой ".",  известные и изменяемые -
восклицательным знаком "!".

                          1. Блок Заголовка

    Заголовок построен  из  первого  блока  (блок  -1,  пробегающий  от
смещения  0х00  до  0х1FF).  Заголовок начинает с 8-байтовой 16 -ричной
строки {d0 cf  11  e0  a1  b1  1a  e1}.  Заголовок  содержит  некоторую
первичную  информацию  о  структуре.  Назначение будет объяснено ниже в
этом документе.

Пример:

    00000: d0 cf 11 e0  a1 b1 1a e1  00 00 00 00  00 00 00 00
    00010: 00 00 00 00  00 00 00 00  3b 00 03 00  fe ff 09 00
    00020: 06 00 00 00  00 00 00 00  00 00 00 00  01 00 00 00
    00030: 01 00 00 00  00 00 00 00  00 10 00 00  02 00 00 00
    00040: 01 00 00 00  fe ff ff ff  00 00 00 00  00 00 00 00
    00050: ff ff ff ff  ff ff ff ff  ff ff ff ff  ff ff ff ff *

00:  stream $laola_id        Идентификатор {d0 cf 11 e0 a1 b1 1a e1}
2c:  long $num_of_bbd_blocks Количество их в Хранилище Больших Блоков
30:  long $root_startblock   1-й Большой Блок в Корневом Каталоге
3c:  long $sbd_startblock    1-й Большой Блок Хранилища Малых Блоков
4c[]:long $bbd_list[i]       массив из $num_of_bbd_blocks номеров
                              Больших Блоков

(детальную информацию см. в Таблице 1)

2. Хранилище Больших Блоков

    Хранилище Больших Блоков управляет Большими Блоками.  Большие Блоки
имеют  размер  в  точности 0х200 (512) байтов.  Часто Хранилище Больших
Блоков будет состоять всего из одного Большого Блока.

  Хранилище Больших Блоков<=>Объединение Больших Блоков {bbd_list[i]},

     bbd_list состоит  из $num_of_bbd_blocks элементов,  сохраненных в
              позиции  <header:4c>.

Пример:

   00200: fd ff ff ff  05 00 00 00  fe ff ff ff  04 00 00 00
   00210: 06 00 00 00  fe ff ff ff  07 00 00 00  08 00 00 00
   00220: 09 00 00 00  0a 00 00 00  0b 00 00 00  fe ff ff ff
   00230: ff ff ff ff  ff ff ff ff  ff ff ff ff  ff ff ff ff *

    Хранилище Больших Блоков представляет собой массив  номеров блоков,
чей индекс начинается с нуля. Вхождение 0 в этом примере со значением 0
xfffffffd  (-3),  ссылается  на  блок  0.  Вхождение  1,  со  значением
0x00000005  ссылается  на  блок 1.  Вхождение 2 соответствует блоку 2 и
т.д. Каждое вхождение может иметь одно из следующих значений:

   0xfffffffd (-3) : это специальный блок
   0xfffffffe (-2) : конец цепочки
   0xffffffff (-1) : не используется
   0 .. $maxblock  : следующий элемент цепочки (номер Большого Блока)
   $maxblock+1 ..  : не определено

    В заголовке     переменная     $root_startblock     должна     быть
проинициальзирована,  в примере ему присвоено значение 1.  Это значение
указывает,  какой блок первый в цепочке блоков, принадлежащих корневому
каталогу. В примере это должно быть прочитано так:

    Как читать  цепочку  блоков.  Начнем  с  позиции  1.  Первый  блок,
принадлежащий корневому каталогу,  это блок  1.  Значение  вхождения  в
Хранилище Больших Блоков с позицией 1 есть: 0x00000005. Следующий блок,
принадлежащий корневому каталогу,  это блок  5.  Значение  вхождения  в
Хранилище Больших Блоков в позиции 5 это 0xfffffffe (-2). Это означает:
здесь конец цепочки.  Итак,  корневой каталог окончательно  состоит  из
блоков: {1,5}.

    В заголовке  также  известно  значение  переменной $sbd_startblock.
Попытайтесь найти это  значение,  затем  попытайтесь  получить  цепочку
принадлежащих  блоков!  (Если  вы хотите увидеть решение,  посмотрите в
конец этого документа).

    Замечание: во время чтения цепочки только значения в диапазоне от 0
до  $maxblock,  и -2,  являются корректными.  Если в цепочке попадаются
другие значения, то это ошибка.

    Замечание: Хранилище  Малых  Блоков  может  отсутствовать.  В  этом
случае $sbd_startblock имеет значение 0xfffffffe (-2).

    Выводы: с  помощью  блока  Заголовка  и  Хранилища  Больших  Блоков
становятся  известны  значения  списка  Больших  Блоков:  @root_list  и
@sbd_list.

     3. Большие Блоки Данных

     3.1. Хранилище Малых Блоков

    Хранилище Малых  Блоков  обслуживает  Малыми  Блоками.  Малые Блоки
имеют длину в точности 0х40 (64) байтов.  Часто Хранилище Малых  Блоков
будет состоять в точности из одного большого блока. Некоторые документы
не имеют Хранилища Малых Блоков вообще.

 Хранилище малых блоков <=> объединение больших блоков {sbd_list[i]},

    sbd_list состоит из number of chain_elements(sbd_list) элементов.
             Список считывается из Хранилища Больших Блоков,
             начало списков - это $sbd_startblock (-> Раздел 2)

Пример:

    00600: 01 00 00 00  fe ff ff ff  ff ff ff ff  ff ff ff ff
    00610: ff ff ff ff  ff ff ff ff  ff ff ff ff  ff ff ff ff

    Вхождения в  Хранилище  Малых  Блоков  *не* ссылаются на абсолютную
позицию в файле,  подобно как вхождения в Хранилище Больших Блоков. Они
ссылаются   на  позицию  в  (файл  состоит  из  больших  блоков)  @sbd,
соответствующем списку больших блоков.  Этот список  отмечен  отдельным
вхождением  в  корневом  каталоге  Хранилища  Свойств.  Таким  образом,
необходимо объяснить Хранилище Свойств.

3.2 Хранилище Набора Свойств

    После прочтения Раздела 2 стало известно содержимое  списка Больших
Блоков  @root_list.  Этии блоки содержат блоки Хранилища Набора Свойств
(ppss).

    блоки хранилища набора свойств <=> объединение больших блоков
                                       {root_list[i]}

    root_list состоит      из      number_of_chain_elements(root_list)
              элементов.  Список  считывается  из  хранилища   Больших
              Блоков, начало списков - это $root_startblock (-> Раздел
              2)


3.3 Хранилище Свойств

    Блоки типа  ppss  (->  раздел  3.2)  делятся на блоки размером 0х80
байт.  Эти 0х80-байтовые  блоки  составляют  Хранилище  Свойств  (pps).
Каждый элемент типа pps - это число,  начиная с 0.  Каждый pps сылается
на "файл" или представляет "каталог".

Пример:

00400: 52 00 6f 00 6f 00 74 00 20 00 45 00 6e 00 74 00 R o o t  E n t
00410: 72 00 79 00 00 00 00 00 00 00 00 00 00 00 00 00 r y
00420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00440: 16 00 05 00 ff ff ff ff ff ff ff ff 03 00 00 00
00450: 00 09 02 00 00 00 00 00 c0 00 00 00 00 00 00 46
00460: 00 00 00 00 00 00 00 00 00 00 00 00 86 29 f6 1f
00470: ad 57 bb 01 03 00 00 00 00 0f 00 00 00 00 00 00

40:  word $pps_sizeofname      размер структуры $pps_rawname
42:  byte $pps_type            тип pps-объекта (1=хранилище|2=поток|
                               5=корневой каталог)
44:  long $pps_prev            предыдущий pps
48:  long $pps_next            следующий pps
4c:  long $pps_dir             каталог pps
74:  long $pps_sb              стартовый блок свойств
78:  long $pps_size            размер свойств

(Для детальной информации см. Таблицу 2)

    Первые 0х40  байтов  зарезервированы  под  имя  pps.  Длина   имени
располагается в $pps_sizeofname. Имя может быть конвертировано в строку
ASCII $pps_name  только  удалением  каждого  четного  символа.  В  этом
примере   длина   имени  0х16,  а  $pps_name  -  это  "Root  Entry\00".
Завершающий \0 в стиле языка С должен быть удален. Если случится, что $
pps_sizeofname  это  0,  то  этот  блок размером 0х80 байтов - не pps и
должен игнорироваться.
    Каждый объект типа pps имеет префикс и окончание.  Каждый pps также
может быть каталогом (или "хранилищем"). $pps_prev, $pps_next, $pps_dir
ссылаются  на  возрастающий  номер 0х80-байтовых блоков,  как объяснено
выше. В этом примере pps, который начинается с 0х400, содержит номер 0,
pps  c  0х480  содержит  1,  pps  с  500 содержит 2 и т.д.  Если читать
последовательно, упорядоченный список pps-ов окончится (см. функцию get
_pps_chain в "laola.pl").

     Типы свойств. Каждый pps может быть одного из трех типов:

     1. Хранилище, это каталог
     2. Поток, это файл
     3. Корневой каталог, это корневой каталог

    Если $pps_size  ненулевой,  то  $pps_sb ссылается на начальный блок
принадлежащего свойства.  Начальный блок ссылается на Хранилище Больших
Блоков,  если  $pps_size  больше  или равен 0x1000 (4096) байтов.  Если
размер свойства меньше, то $pps_sb ссылается на Хранилище Малых Блоков.
Есть  одно  исключение:  $pps_sb в корневом каталоге (это всегда pps 0)
всегда ссылается на Хранилище Больших Блоков.
    Теперь легко читать "файлы": список Больших или Малых Блоков должен
быть получен (как это делалось ранее при помощи root_list  и  sbd_list)
из  Хранилища  Больших или Малых блоков,  так что соответствующие блоки
должны быть прочитаны на следующем шагу, и размер должен быть усечен до
$pps_size.
    Если тип pps это корневой каталог или хранилище,  то,  как минимум,
переменные   $pps_ts2d   и   $pps_ts2s   инициализированы.  Вместе  эти
переменные составляют  64-битовое  целое  число,  которое  представляет
время и дату.  Эта переменная считает в 10 ^-7 c,  начиная с 01/01/1601
00:00.  Если тип - корневой каталог,  то $pps_sb  указывает  на  первый
большой блок в списке малых блоков @sb_list. См. ниже:

3.4 Где Малые Блоки данных?

    Корневой каталог - это 0х1000-байтовое исключение из правил раздела
3.3.  Размер в примере  равен  0хF00,  и  значит,  должен  принадлежать
Хранилищу  Малых  Блоков.  Фактически,  файл в корневом каталоге всегда
ссылается на таблицу Больших Блоков.  Стартовый блок  здесь  -  это  3.
Посмотрите:

    00200: fd ff ff ff  05 00 00 00  fe ff ff ff  04 00 00 00
    00210: 06 00 00 00  fe ff ff ff  07 00 00 00  08 00 00 00
    00220: 09 00 00 00  0a 00 00 00  0b 00 00 00  fe ff ff ff
    00230: ff ff ff ff  ff ff ff ff  ff ff ff ff  ff ff ff ff *

    Результирующая цепочка:  {3,  4, 6, 7, 8, 9, a, b}. Эта "урожайная"
цепочка  дает  блоки,  которые снабжают пространством для малых блоков.
Сылки на малые блоки соответствуют позициям в "файле малых блоков", что
в  данном  примере построено из цепочки больших блоков,  начинающейся с
блока 3.

    small data blocks <=> объединение больших блоков {sb_list[i]}

    sb_list состоит из  number  of  chain_elements(sb_list)  элементов.
Список читается из хранилища больших блоков, начало списка - это $root_
startblock -> хранилище свойств 0 -> pps_sb.

    Это все,  что требуется,  чтобы вытянуть какой-либо файл из  архива
LAOLA.  Вы  должны  проверить  это  при  помощи  "lls -s".  Но есть еще
кое-что.

     3.5. Наборы свойств

    В противоположность обычным  сохраненным  файлам  иногда  требуется
сохранять   специально   структурированные   файлы  "баз  данных".  Эти
структурированные  файлы  обращаются  к  наборам  свойств.   Существует
хорошая статья про то,  как это сделано у Microsoft, "OLE Property Sets
Exposed" от Charlie Kindel.  Там упоминается, что точная информация про
наборы свойств находится также в Win32 SDK.

   Выводы и дальнейшая информация - еще предстоит сделать! -


     4. Мусорные блоки

    Последние 4  раздела  посвящены  "мусорным  блокам   данных".   Эти
мусорные  блоки  -  это  блоки,  сохраненные в документе без каких-либо
ссылок на них со стороны системы LAOLA. Они имеются не всегда. Наиболее
известный  пример  этого  -  опция  Microsoft Word "быстрое сохранение"
(отключите ее,  если этого  не  сделали).  Сохраненные  с  этой  опцией
документы  обычно  наполовину  состоят из мусора.  Другой пример - Star
Writer 3.1, который принципиально делает 2 больших блока мусора.
    Некоторые блоки только частично состоят из мусора, их можно было бы
назвать "вонючими" блоками. Это происходит потому, что размер данных не
всегда  точно  соответствует  0х200  (или  0х40 байт для малых блоков).
Тогда остаток последнего блока в цепочке всегда будет  содержать мусор.
    Как и  любой  мусор,  мусорные данные - нежелательны.  В простейшем
случае они просто раздувают размер файлов.  Также в  любом  случае  они
противоречат  требованиям  безопасности данных.  Поскольку вы не можете
знать,  что внутри,  вы теряете контроль над своими данными. В Usenet-е
даже  сообщалось,  что таким образом с Word-файлом "уплывали" секретные
пароли и приватные письма.
    Но хорошо,  что в отличие от радиоактивных отходов, мусорные данные
удаляемы.  Просто посмотрите на демонстрационную программу  "lclean"  в
секции  исходников.  Что  касается мусорных данных,  то,  как я слышал,
Microsoft знает об этой проблеме в OLE и пофиксили ее в Win32.  Тем  не
менее,  если вы используете Windows 3.1, вам, вероятно, поможет lclean.

Ну и как?

Martin

                                - The End -

Таблицы

Таблица 1: Блок 0 (заголовок laola)

смещение тип значения      константа функция
00:     stream $laola_id        !  identifier {d0 cf 11 e0 a1 b1 1a e1}
08:     long 0                  .  ?
0c:     long 0                  .  ?
10:     long 0                  .  ?
14:     long 0                  .  ?
18:     word 3b                 .  ? revision ?
1a:     word 3                  .  ? version  ?
1c:     word -2                 .  ?
1e:     byte 9                  .  ?
1f:     byte 0                  .  ?
20:     long 6                  .  ?
24:     long 0                  .  ?
28:     long 0                  .  ?
2c:     long $num_of_bbd_blocks !  Number of big block depot blocks
30:     long $root_startblock   !  Root chain 1st block
34:     long 0                  .  ?
38:     long 1000               .  ?
3c:     long $sbd_startblock    !  small block depot 1st block
40:     long 1                  .  ?
44:     long -2                 .  ?
48:     long 0                  .  ?
4c[]:   long $bbd_list[i]       !  array of $num_of_bbd_blocks big block
                                   numbers
The rest of block 0 should be:
        long -1                 .

####

Таблица 2: Хранилище свойств

смещение тип значения      константа функция
00:     stream $pps_rawname       !  name of the pps
40:     word $pps_sizeofname      !  size of $pps_rawname
42:     byte $pps_type            !  type of pps (1=storage|2=stream|5=root)
43:     byte $pps_uk0             !  ?
44:     long $pps_prev            !  previous pps
48:     long $pps_next            !  next pps
4c:     long $pps_dir             !  directory pps
50:     stream 00 09 02 00        .  ?
54:     long 0                    .  ?
58:     long c0                   .  ?
5c:     stream 00 00 00 46        .  ?
60:     long 0                    .  ?
64:     long $pps_ts1s            !  timestamp 1 : "seconds"
68:     long $pps_ts1d            !  timestamp 1 : "days"
6c:     long $pps_ts2s            !  timestamp 2 : "seconds"
70:     long $pps_ts2d            !  timestamp 2 : "days"
74:     long $pps_sb              !  starting block of property
78:     long $pps_size            !  size of property
7c:     long                      .  ?

  ---------------------------------------------------------------------

Решение:

Упражнение 1:
    Найдите цепочку блоков, принадлежащих переменной $sbd_startblock

Решение:
    Значение находится в позиции 0х3С заголовка.  Это 0х00000002 ==  2.
Значение  вхождения  в  Хранилище  Больших  Блоков  в  позиции  2 - это
0хFFFFFFFE,  что означает:  конец цепочки.  Таким образом,  sbd состоит
только из большого блока 2.

*****(Перевод: Constantin E. Climentieff aka DrMad, 1998)*************


(C) NF, 1998-2004