Преподобная мать должна сочетать
соблазнительные хитрости куртизанки с
неприступностью и величием девственной
богини, сохраняя эти качества столь
долго, сколько позволяет ее юность.
А когда ее молодость и красота исчезнут,
их место займут коварство и находчивость.
В
последние несколько лет, компьютерное
сообщество переживает бум макровирусов. Начиная
с WinWord-а они с каждым днем все больше и больше
"расползаются" по другим платформам. Уже
известны вирусы, поражающие скрипты PhotoShop, Corel Draw,
Visual Studio... Практически любая уважающая себя среда
разработки сегодня имеет развитый макроязык, и
тем самым уязвима перед вторжением.
В отличие от вирусов, поражающих исполняемые файлы, макровирусы замечательно функционируют даже на защищенных операционных системах, например, Windows NT. Если блокировать доступ к исполняемым файлам средствами операционной системы сравнительно нетрудно (т.е. только на исполнение), то файлы документов, с которым приходится активно работать пользователю, в том числе и на запись, просто невозможно надежно защитить от модификации/разрушения, поскольку с точки зрения приложения макрокоманды они не отличимы от команд пользователя.
Известный макро-вирус под оболочку FAR просто имитирует нажатие на F8 с последующим подтверждением удаления... И нет никакого способа этому воспрепятствовать. Невозможен макроязык, свободный от разрушающих программных воздействий.
Не обошла стороной эта проблема и дизассемблер IDA. Однако, сообщение о первом замеченном под эту платформу вирусе, вызвало недоверие специалистов. При близком рассмотрении, выясняется, что IDA не имеет никаких средств поиска файла-жертвы. Функции FindFirstFile / FindNextFile в нем не реализованы.
Поэтому вирусописателям пришлось выйти за пределы виртуальной машины IDA и находить способы передачи управления непосредственно на бинарный код. А таких способов оказалось более чем достаточно.
Первой идеей было использовать низкоуровневый ввод/вывод в файлы, и записывать вирусный код в заведомо существующий исполняемый файл. Например 'C:\command.com', 'C:\WINDOWS\win.com' ну или 'idax.exe' ('idaw.exe'). При последующем запуске модуль производил все задуманные автором манипуляции (поиск и внедрение себя в *.idc скрипты), а затем восстанавливал пораженный файл.
Любопытной особенностью одного из работающих по этой технологии вирусов было то, что файл носитель всегда восстанавливался если были успешно заражены две новые жертвы.
С точки зрения операционной системы это не было макро-вирусом, поскольку требовало для своей жизнедеятельности поражения исполняемых файлов и мало чем отличалось от обычных файловых вирусов. При соответствующем образом настроенных привилегиях пользователя, в защищенных операционных системах такое внедрение невозможно и ida-вирусы стало быть не функциональны.
Однако, очень интересен тот факт, что AVP, DrWeb и другие известные антивирусы обнаруживали вирус только в бинарных файлах, но не в скриптах IDA, что позволяло вирусу хоть и медленно (скрипты-то копируют редко), но все же уверенно распространяться от машины к машине.
Обнаружить "творения" этого типа очень просто, достаточно лишь просканировать скрипты на предмет наличия в них имен посторонних исполняемых файлов. Впрочем, далеко не очевидно, какие файлы будут исполняемым, а какие нет, например, можно поразить dn.prg или один из хранителей экрана, или вовсе не держать в памяти строку, а вычислить ее на основе какой-нибудь функции.
Например:
auto a,b,c,d; a="ida."; b=0xCA; c=b/2; b=b>>1; d=b + (b>>5) | (1<<4); a=a+c+d+b; Message("%s \n",a);
В этом примере c+d+b=='exe', однако это не очевидно ни при беглом просмотре скрипта, ни тем более при автоматическом сканировании (разве что писать полный эмулятор IDA-Си).
Сигнатурный поиск в исходных текстах совершенно не эффективен. Технически существует множество способов до неузнаваемости изменить вид исходного текста очень простыми приемами, например использовать #define и другие директивы. При этом антивирус должен содержать в себе полнофункциональный препроцессор, или его эмулятор...
Впрочем, оставим решение этой проблемы разработчикам антивирусов, а сами вернемся к происходящим событиям. Вскоре появляется вирус, читающий физическую память компьютера (командой _peek) в надежде обнаружить в ней буфер каталога с именами и путями файлов, доступными для заражения. Однако, это оказывается способным существовать только на DOS-версии IDA, ибо остальные операционные системы не предоставляют свободного чтения принадлежащих им фрагментов памяти, а генерируют при этом исключение защиты.
Сей вирус оказался нежизнеспособным и практически не вышел за пределы коллекций вирусописателей. Никакой опасности он не представлял, ввиду того, что большая часть пользователей IDA использовала именно его win32 версию, в которой вирус отказывался работать. Впрочем, он успел-таки поразить файл ida.idc (который автоматически загружается при запуске дизассемблера) на пиратских копиях, выложенных на всеобщее обозрение на некоторых ftp-серверах. Разумеется, при этом IDA отказывался запускаться и происходило все что угодно вплоть до повисания системы на win98, которая вместо генерации предупреждения о закрытии приложения впадала в дурной цикл. Пользователи же это списывали на "кривой" взлом приложения пиратами.
Все вышеперечисленные поделки не представляли серьезной опасности, и ничем, кроме как плохой репутацией пиратским серверам не грозили. Наконец, в конце июля 1999 года группой 'Visual SEX Ltd' был создан тщательно продуманный и довольно корректно написанный вирус, поражающий в своем числе и скрипты IDC. При этом для передачи управления на свой код использовался очень оригинальный алгоритм.
Вирус читал таблицу векторов прерываний, запоминал значение обработчика Int 0x16, после чего изменял его таким образом (с помощью команды _poke), что оно указывало на бинарный код модуля вируса. При первом же нажатии на клавишу, этот код получал управление, а затем восстанавливал старое значение обработчика.
Однако, прежде нужно было найти регион памяти, в который не рискуя ущемить собственностей какого-либо приложения было бы можно разместить свой код. Обычно вирусописатели используют для этого старшие адреса первых 640-килобайт памяти, которые как правило заняты транзитной частью command.com и безболезненно могут быть использованы под свои нужды. Конечно, обычно это еще далеко не всегда. И в зависимости от ситуации и конфигурации компьютера карта памяти может очень сильно отличаться от стандартной и по конкретным физическим адресам может быть расположено все что угодно от драйвера устройства, до тела резидентного вируса.
Гораздо надежнее просканировать цепочку MCB и разместить вирус в любом свободном блоке памяти. Для этого потребовалось бы вызвать функцию 0x21-прерывания, чтобы определить адрес самого первого блока в цепочке. IDA, разумеется, лишает вирус такой возможности, и последнему ничего не остается, кроме как заняться таким поиском самостоятельно.
Это может быть не столь очевидно, но технически реализовать очень просто. Сканируем память с младших адресов, пока не встретим сигнатуру MCB-блока 'M'. Сканируем предполагаемую цепочку на предмет поиска заключительного блока, отмеченного 'Z'. Если таковой не обнаружится, значит возвращаемся назад и ищем следующего претендента на роль первого MCB блока.
Любопытно, что отважившись на реализацию такого алгоритма, вирусописатели допустили по крайне мере две ошибки. Первая в общем-то не критичная, заключается в сканировании памяти по байтам, тогда как MCB блок может начинаться только с кратных 16 адресов и явно за таблицей прерываний и блоком параметров BIOS. Впрочем, ничем большим, кроме как субъективно незаметном уменьшении скорости работы скрипта, не грозит. А вот вторая ошибка может вызвать зависание системы.
Вирус не контролирует границы адресов при переходе на следующий блок в цепочке. Предположим, что первый найденный претендент на MCB блок, имеет в поле "размера" значение 0xFFFF, тогда последует переход на область памяти за границами первого мегабайта памяти с последующим нарушением границ доступа. Таким образом, вирус все же вызывал нестабильность работы ida, что и позволило его обнаружить.
Любопытно, что многие использовали схожие алгоритмы для интеграции IDA с внешними программами, например трассировщиками. Поэтому в конце статьи приведен простой скрипт, который осуществляет вышеописанную передачу управления бинарному коду. Он представляет не более чем исторический интерес, поскольку в настоящее время гораздо удобнее использовать плагины, подгружаемые в виде самостоятельной DLL и недокументированную функцию _call, осуществляющую передачу управления по физическому адресу.
В первых числах июля 1999 года появился и первый троянский скрипт, разрушающий жесткий диск владельца, причем корректно работающий как в win32, так и в dos версиях IDA. Устроен он был очень тривиально - просто передавал управление на соответствующую процедуру BIOS. Поскольку в адресном пространстве win32 приложений BIOS находится по тем же самым адресам, то троян ничуть ни хуже работал и на этой платформе. (Кстати, он легко обнаруживался по простому присутствию строк 'Hard disk is XAHA! KAJUK' даже при беглом просмотре короткого (~3 кб) файла скрипта).
Похоже, что тучи над IDA начинают сгущаться и в ближайшее время можно ждать нашествия вирусов, функционирующих на этой платформе. В предверии этого пришлось использовать макрос, который перед загрузкой скриптов сканировал бы их на предмет наличия "вирусо-опасных" функций _peek/_poke/_call. Казалось бы что этого на первое время будет достаточно.
Как бы не так! Тут же был обнаружен первый шифрованный самомодифицирующийся скрипт, основанный на присвоении зашифрованного кода строковой переменной, последующей его расшифровке с записью во временный файл и компиляции последнего функцией 'Compile'.
Это дает возможность писать сложные полиморфные расшифровщики и скрывать все "вирусопасные" вызовы, текстовые строки и даже менять логику работы.
Ниже приведен пример скрипта, расшифровывающего процедуру, выводящую строку "Hello, IDA!".
static main() { auto a,b,temp,s0; b=0; s0="0x040x030x160x030x1E0x140x570x1A0x160x1E0x19 0x5F0x5E0x570x0C0x3A0x120x040x040x160x100x12 0x5F0x550x3F0x120x1B0x1B0x180x5B0x570x3e0x33 0x360x560x570x2B0x190x550x5E0x4C0x0A0x7A0x7D 0x00\n"; a=fopen("temp.idc","wb"); while(1) { temp=substr(s0,b,b+4); b=b+4; temp=xtol(temp); if (temp==0) break; temp=temp ^ 0x77; fputc(temp,a); } fclose(b); Compile("temp.idc"); }
С точки зрения IDA, как среды для существования вирусов, такой вирус будет резидентным, ибо откомпилированная функция остается на всем протяжении сеанса работы с дизассемблером.
В Microsoft Office существует механизм, позволяющий устанавливать обработчики различных событий, таких например, как открытие файла, запуск или свершение сеанса работы и т.д. Фактически так и работают все макро-вирусы.
IDA предоставляет аналогичную возможность. Файл "ida.idc" исполяется при запуске дизассемблера, 'OnLoad.idc' при загрузке исследуемого файла и вирусу ничего не стоит внедрить свой код в эти файлы. Разумеется, этой возможностью уже не преминули воспользоваться, получив в свое распоряжение превосходный механизм для поиска файлов-жертв.
Близкий к этому способ был использован в одном вирусе, который внедрял свой код при сохранении результата работы в exe файл и удалял его при загрузке в дизассемблер, т.е. говоря иными словами реализовал "стелс" маскировку и вероятно был первым "Стелс"-вирусом для IDA.
Противодействия дизассемблированию встречаются и у других вирусов. Так например, один из них добавляет в 'OnLoad' проверку открываемого файла на наличие своей копии и при необходимости блокирует загрузку, выводя диалоговое окно с сообщением о неправильном формате файла.
В противном случае производится поиск оригинального файла и в него внедряется вирус. Поскольку после загрузки в базу, IDA уже никогда не обращается к оригинальному файлу эти изменения остаются незамеченными в дизассемблере.
Вирус, перехвативший 'OnLoad' может самостоятельно загружать инфицированные файлы, причем не только своей но и даже чужой копией. Любое другое событие можно перехватить только вставив клавиатурный макрос в раздел 'Macro definitions' файла 'idatui.cfg'. Все известные мне вирусы используют только один механизм для вызова своих модулей. Вызывают консоль, эмулируя нажатие <Shift-F2>, а там имя функции, уже загруженной при запуске IDA или непосредственная компиляция любого скрипта "налету" с помощью функции 'Compile'.
Что бы обнаружить такие вирусы, достаточно взглянуть в файлы 'idatui.cfg', 'ida.cfg' и 'idauser.cfg', а так же обязательно во все файлы, вызываемые из вышеперечисленных директивой '#include'.
Таким образом убедиться в отсутствии вирусов в полученном из сомнительных источников экземпляре IDA сложнее, чем приобрести лицензионную. Особенно для неподготовленных пользователей еще не освоивших встроенный язык IDA и не знающих всех тонкостей его работы. Несмотря на то что по крайней мере 5 из известных мне 8 пиратских версий IDA в той или иной степени поражены вредоносными скриптами, антивирусная индустрия отвечает на это гробовым молчанием. По крайней мере последние версии AVP и DrWeb не распознают ни одного из существующих на платформе IDA вирусов и неизвестно введут ли такую поддержку в дальнейшем.
В результате пользователи вынуждены эксплуатировать нестабильно работающие версии дизассемблера, не в силах выявить источник ошибок. Обычно пытаются переустановить операционную систему, убрать те или иные "ускорения" из BIOS. Нестабильная ошибка в вирусе - что может быть хуже - кажется вот уже проблема решена и IDA работает бессбойно, но надолго ли?
Про опасность подхватить вируса из различных коллекций скриптов, думаю не следует распространяться. Так скажем техническая возможность для этого есть. А все остальное зависит только от надежности источника и совести разработчика.
Как правило человек, занимающийся написанием скрипов, достаточно хорошо разбирается в последних что бы "проморгать" на своем компьютере вирус, но все же такой возможности полностью исключать нельзя.
Приложение:
Передача управления бинарному коду из языка IDA.
static MyGetByte(a) { return (_peek(a) & 0xFF); } static MyGetWord(a) { return MyGetByte(a)+(MyGetByte(a+1)*0x100); } static GetNextMCB(a) { a=a+ (MyGetWord(a+3) <<4) +0x10; return a; } static GoToMCB(a) { if (_peek(a)!='M') return 0; while(1) { a=GetNextMCB(a); if (_peek(a)=='Z') return 1; if (_peek(a)!='M') return 0; } } static FindFirstMCB() { auto a; a=0x0; while(1) if (GoToMCB(++a)) break; return a; } static FindFreeMCB() { auto a; a=FindFirstMCB(); while(MyGetWord(a+1)) { a=GetNextMCB(a); if (MyGetByte(a)=='Z') return 0; } return a; } static SaveInt0x16(a) { auto temp; for (temp=0;temp<4;temp++) _poke(a+temp,MyGetByte(0x16*4+temp)); } static SetNewInt0x16(a) { _poke(0x16*4,a); a=a>>4; a=a & 0xFFF0; _poke(0x16*4+1,0); _poke(0x16*4+2,a); a=a>>8; _poke(0x16*4+3,a); } static CopyEject(a) { _poke(a,0xCF); } static SetOldInt0x16(a) { auto temp; for (temp=0;temp<4;temp++) _poke(0x16*4+temp,MyGetByte(a+temp)); } static main() { auto a; a=FindFreeMCB(); if (!a) return; Message("Найден свободный блок по адресу %x \n",a); if (!AskYN("NO","Сейчас будет инфицирован Ваш компутер \n Вы в самом деле хотите это сделать? Подтвердите!")) return; if (!AskYN("NO","Подтвердите необходимость инфицирования еще раз!")) return; SaveInt0x16(a); CopyEject(a+4); SetNewInt0x16(a+4); AskYN("X","Поздравляем Вас с успешной активацией вируса!"); SetOldInt0x16(a); }
Полезные ссылки:
http://www.datarescue.com/ida.htm - Официальный сайт IDA Pro.
http://dore.on.ru/kpnc/pages/idabook.htm - Главы из книги автора этой статьи - "Образ мышления IDA"
http://www.rosprombank.ru/~ig/ - Страничка Ильфака Гульфанова, автора IDA.
http://www.csee.uq.edu.au/~csmweb/decompilation/ - О декомпиляции вообще.
http://www.eccentrica.org/Mammon/disasm.html - О IDA, скриптах и других дизассемблерах.
http://www.faizal.com/singapura-mirrors/fravia/quine_51.htm - Extending the IDA scripting language.