ZF

                          АНАТОМИЯ DOC-ФАЙЛОВ
                               by DrMAD


    ВВЕДЕНИЕ
    1. О КАКИХ МАКРОВИРУСАХ ПОЙДЕТ РЕЧЬ
    2. КАК УСТРОЕН DОС-ФАЙЛ
    3. КАК УСТРОЕН WORD-ДОКУМЕНТ
    4. ПРО АНТИВИРУС
    5. ОПИСАНИЕ ПРИМЕРА
    ЛИТЕРАТУРА
    ПРИЛОЖЕНИЕ. ИСХОДНИК ДЕМОНСТРАЦИОННОЙ УТИЛИТЫ.

    ВВЕДЕНИЕ

    Эта небольшая   статья   адресована  тем,  кто  хочет  узнать,  где
конкретно живут макровирусы,  как на них посмотреть глазками,  и как их
пощупать  ручками.

    1. О КАКИХ МАКРОВИРУСАХ ПОЙДЕТ РЕЧЬ

    Только о макровирусах для MS Word.  Макровирусы для MS  Access,  MS
Excel и т.п.  здесь не рассматриваются.
    Кроме того,  будем иметь в  виду,  что  рассматриваются  в-основном
макровирусы, написанные на языке Visual Basic for Application (VBA) для
MS Word версий 97,  2000 и XP.  Макровирусы для  Word  6.0/7.0  уже  не
актуальны, а как дело обстоит в Word 2003 - пока не слишком понятно.

    2. КАК УСТРОЕН DОС-ФАЙЛ

    Подробности см. в /1,2/, а здесь вкратце.
    Это файл  с  расширением  .DOC  (или  .DOT)  со  сложной внутренней
структурой  под  названием  "структурированное  хранилище"  (structured
storage).   MS  Word  также  может  создавать  документы  в  "неродных"
форматах, например, в .RTF, но мы эту ситуцию рассматривать не будем.
    Правила structured storage предусматривают, что:
    - файл  разбит на множество секторов (размер сектора по умолчанию -
512 байтов);
    - сектора пронумерованы и связаны в цепочки;

                 +-----------------------+
                 |                       V
+---------+ +----+----+ +---------+ +---------+ +---------+  +---------+
|Заголовок| |    0    | |    1    | |    2    | |    3    |->|    4    |
+---------+ +---------+ +----+----+ +---------+ +---------+  +---------+
                             |                       ^
                             +-----------------------+

    - внутри файла есть заголовок, описывающий параметры хранилища;
    - внутри файла есть FAT-таблица,  описывающая,  какие сектора каким
цепочкам принадлежат;
    - внутри файла есть каталоги,  описывающие имя цепочки и  адрес  ее
первого сектора.
    Цепочки образуют  потоки  (streams),  в  которых  и  содержится вся
полезная информация.  (На самом деле,  есть еще lock bytes,  в  которых
может  храниться  "безымянная" информация,  о которой MS Word ничего не
знает).

    3. КАК УСТРОЕН WORD-ДОКУМЕНТ

    Подробности см. в /3,4/, а здесь вкратце.
    В общем случае документ занимает несколько потоков внутри файла (но
не обязательно все!).  Вот достаточно типичная конфигурация потоков для
здорового документа:

    1Table
    <1>CompObj
    ObjectPool
    WordDocument    <- Здесь текст документа
    <5>SummaryInformation
    <5>DocumentSummaryInformation

    А вот для документа, содержащего макросы (вирус):

    1Table
    Macros
     VBA
      dir
      __SRP_0       <-+
      __SRP_1         | Здесь exe-code макросов
      __SRP_2         |
      __SRP_3       <-+
      ThisDocument  <-  Здесь s-code и p-code макроса
      Tralivali     <-  И здесь
      Newmacros     <-  И здесь тоже
      _VBA_PROJECT  <-  Здесь описание различных частей проекта
     PROJECT
     PROJECTwm
    [1]CompObj
    ObjectPool
    WordDocument    <- Здесь текст документа
    [5]SummaryInformation
    [5]DocumentSummaryInformation

    Один и тот же макрос распределен по разным потокам:
    1) s-code - это его сжатый методом LZSS текстовый исходник;
    2) p-code - это перекодировка исходника в  промежуточный постфиксный
язык, например

    ; a = 1234

    00A4 Let
    1234 1234
    0027 ->
    0220 номер в списке имен переменных

    3) exe-code - это его код, скомпонованный для исполнения виртуальной
VBA-машиной, с командами типа:

    Push Аргумент1
    Push Аргумент2
    [Стек] := [Стек] + [Стек+2]

    S-code и соответствующий ему p-code лежат вместе в одном потоке.
    Все такие потоки начинаются  с сигнтуры 01 16 01, по этому признаку их 
    легко отличить.

    4. ПРО АНТИВИРУС

    Вообще-то, в  95%   случаев   исходного   текста   макроса   вполне
достаточно,  чтобы  распознать  конкретный  вирус или заподозрить в нем
заразу. О наличии заразы, как правило, говорит присутствие методов:

    1) OrganizerCopy;
    2) .Import/.Export;
    3) .InsertLine, .AddFile, и т.п.

    Остальные 5% приходятся на случаи:

    1) 1%  (на самом деле,  еще реже) - когда запакованный исходник  из
файла удален (например, каким-нибудь кривым антивирусом);
    2) 4% - когда вирус полиморфный.

    Можно попробовать  так:  не  декомпилировать  p-code  и  тем  более
ex-code   из   недокументированных  Microsoft-овских  кодов  (хотя  так
поступают  в  "настоящих"  антивирусах),  а  наоборот,  загнать  хорошо
известную  VBA-грамматику  в  какой-нибудь  Yacc  и получить транслятор
s-code  в  свой  собственный  внутренний  код.  А  уж  по  этому   коду
(очищенному  от  пробелов,  комментариев,  "скленных"  строк  и т.  п.)
распознавать вирей и даже попытаться проэмулировать некоторые их куски.

    5. ОПИСАНИЕ ПРИМЕРА

    Устройство DOC-файла  можно  изучить  при  помощи  плагина  DocFile
Browser   к   FAR   Manager-у:   увидеть  дерево  объектов,  посмотреть
внутренности  потока  в  виде  16-ричного  дампа...  Но  FAR  не  умеет
распаковывать и показывать исходный текст скрывающихся внутри макросов,
а мы умеем /3/.  В качестве  примера  -  консольная  утилитка,  которая
обходит в doc-файле потоки, находит в них сигнатуру 01 16 01, находит в
этих потоках запакованный  исходник,  распаковывает  его,  ищет  в  нем
подстроки '.Import' и '.Export' и ругается,  если вдруг нашла.  Утилита
использует библиотеку OLE2.DLL и ее методы  для  работы  со  structured
storage /5/.  Кроме того, под Windows 95/ 98 утилитка работать не будет
и тихо завершится,  потому что в этих операционках присутствует  только
огрызок   библиотеки   NTDLL.DLL.   Решения  проблемы:  1)  выдрать  из
"хорошей"    библиотеки   код   процедуры    RtlDecompressBuffer;    2)
самостоятельно распаковывать исходники макросов, это достаточно просто.

    ЛИТЕРАТУРА

    1. Martin Schwartz. Laola file system.
    2. DrMAD. Внутрений формат документов MS Word.
    3. DrMAD. Прямой доступ к макросам в документах MS Word.
    4. DrMAD. Как Пымать Выря За Хвост. Глава 6. Борьба с макровирусами.
    5. Артем Каев. ActiveX по шагам.

    ПРИЛОЖЕНИЕ. ИСХОДНИК ДЕМОНСТРАЦИОННОЙ УТИЛИТЫ.

//*********************************************************************
// Демонстрация поиска неполиморфных вирей в документах
// Компилятор Borland C/C++ v5.02
// (c) Климентьев К. aka DrMAD, Самара 2003
//*********************************************************************
#include "windows.h"
#include "ole2.h"
#include "iostream.h"

#define MAXBUF 0xFFFF
#define NAMELEN 256

char Buf[MAXBUF];  // Буфер под поток
char SBuf[MAXBUF]; // Буфер под распакованный текст

/* Поиск чунка внутри потока */
ULONG search_chunk( char *Buf, ULONG streamlen)
{
 ULONG ch_pos; // Позиция чунка внутри потока

 if ((Buf[0]==1)&&(Buf[1]==0x16)&&(Buf[2]==1))
  {
   ch_pos = streamlen/2;
   while ((ch_pos<(streamlen-3)) && ((Buf[ch_pos]!=1)||
         ((Buf[ch_pos+2]&0xF0)!=0xB0))) ch_pos++;
   if (ch_pos < (streamlen-3)) return ch_pos;
  }
  return 0;
}

/* Поиск в исходнике .Import/.Export */
LookAtVirus() {
 int i=0;
 while (SBuf[i]) {
  if ((SBuf[i]=='.')   &&
      (SBuf[i+3]=='p') &&
      (SBuf[i+4]=='o') &&
      (SBuf[i+5]=='r') &&
      (SBuf[i+6]=='t')) cout << endl << "*** Maybe virus! ***" << endl;
  i++;
 }
}

typedef UINT (WINAPI* RTLD)(ULONG, PVOID, ULONG, PVOID, ULONG, PULONG);

/* Распаковка исходника */
view_src( char *StreamName, char *Buf, ULONG ch_pos)
{
 HMODULE h;                // Хэндл библиотеки NTDLL.DLL
 RTLD RtlDecompressBuffer; // Указатель на функцию
 ULONG ulen;               // Длина распакованного фрагмента
 int i=0;

 LoadLibrary("ntdll.dll");
 h = GetModuleHandle("ntdll.dll");
 RtlDecompressBuffer = (RTLD) GetProcAddress(h, "RtlDecompressBuffer");
 if (RtlDecompressBuffer)
  {
   RtlDecompressBuffer(0x2, SBuf, MAXBUF, &(Buf[ch_pos+1]), MAXBUF-ch_pos, &ulen);
   cout << " *** " << StreamName << " *** " << endl;
   while (SBuf[i]) cout << SBuf[i++];
   LookAtVirus();
  }
}

/* Рекурсивный обход потоков */
walk(char *s, LPSTORAGE ls)
{
 OLECHAR FileName[NAMELEN];	// Unicode-имя для structured storage
 char StreamName[NAMELEN];	// ASCII-имя потока
 LPENUMSTATSTG lpEnum=NULL;	// Интерфейс перечислителя
 LPSTORAGE pIStorage=NULL;	// Интерфейс структурированного хранилища
 LPSTORAGE pIStorage2=NULL;	// Интерфейс хранилища нижнего уровня
 LPSTREAM pIStream=NULL;	// Интерфейс потока
 STATSTG stat;		// Очередная запись в каталоге
 ULONG uCount;		// Счетчик перечисления
 ULONG streamlen;	// Реальная длина потока
 ULONG ch_pos;		// Позиция чунка внутри потока

 if (!ls) // Первый вызов
  {
   mbstowcs(FileName, s, 256);
   StgOpenStorage(FileName,NULL,STGM_READ|STGM_SHARE_EXCLUSIVE,
                  NULL,0,&pIStorage);
   walk("", pIStorage);
  }
 else // Повторный рекурсивный вызов
 {
  ls->EnumElements(0,NULL,0,&lpEnum);
  if (lpEnum)
  while (lpEnum->Next(1,&stat,&uCount)==S_OK)
   {
    if (stat.type==STGTY_STORAGE) // Это хранилище
     {
      ls->OpenStorage(stat.pwcsName,NULL,STGM_READ|STGM_SHARE_EXCLUSIVE,
                      NULL,0,&pIStorage2);
      walk("", pIStorage2);
     }
    else // Это поток
     {
      ls->OpenStream(stat.pwcsName,NULL,STGM_READ|STGM_SHARE_EXCLUSIVE,
                     0,&pIStream);
      pIStream->Read(Buf, MAXBUF, &streamlen);
      wcstombs(StreamName, stat.pwcsName, 256);
      ch_pos = search_chunk(Buf, streamlen);
      if (ch_pos) view_src(StreamName, Buf, ch_pos);
     }
    };
   ls->Release();
  }
}

int main(int argc, char* argv[]) {
 if (argc>1) walk(argv[1],NULL);
 else cout << "Usage: SVIR docfile" << endl;
}


(C) NF, 1998-2004