Вирус на ассемблере

Вирус готов!

Несколько слов о вирусе (прочтите, прежде чем запускать вирус!):

  1. Вирус нерезидентный. Это значит, что заражать он будет программы только, если запустить зараженный com-файл. В памяти ничего не остается!
  2. Вирус не делает ничего, кроме как заражает файлы.
  3. Ни один антивирус не вылечит файл, зараженный нашим вирусом! Так что, не надейтесь ни на Dr. Web, ни на что-либо еще...
  4. Вирус заражает только *.com-файлы в текущем каталоге. Что это значит? Допустим, в каталоге c:\assm есть следующие файлы:
    1. test.com
    2. dn.com
    3. nc.exe
    причем, test.com заражен нашим вирусом. Запуская test.com на выполнение, он заражает dn.com. Запустив затем dn.com, вирус ничего не заразит, т.к. в данном каталоге больше нет незараженных нашим вирусом *.com-файлов.
  5. НО! Если у вас в path (в autoexec.bat) стоит путь к каталогу c:\assm, то, запустив test.com (test.asm) с текущего каталога, скажем, c:\nc, наш вирус заразит первый встречный com-файл в каталоге c:\nc. Будьте осторожны!!!

  6. Как вы думаете, зачем мы изучали работу отладчика в 15 главе? Дело в том, что если вы будете исследовать работу вируса в отладчике, то столкнетесь с одной проблемой. Записав вирус в файл, используя отладчик, вы с удивлением заметите, что зараженный файл работать не будет. Почему? Вспомните, что отладчик вставляет INT 3 (0CCh). Для чего мы проходили это? Думайте, вспоминайте...

В последующих главах мы напишем антивирус.

Теперь ближе к делу.

Вопрос: можно ли вместо RET и CALL использовать JMP? А вместо JMP - RET?

Конечно, можно! Вот простейшие примеры:

Команда
Альтернатива
Примечание
RET POP AX
JMP AX
Вытащим со стека адрес возврата и перейдем на него
CALL PROCED MOV BX,OFFSET LAB_RET
PUSH BX
JMP PROCED
LAB_RET:
Занесем в стек адрес возврата и "прыгнем" на метку процедуры
JMP LABEL1 PUSH OFFSET LABEL1
RET
Занесем в стек адрес метки и сымитируем выход из процедуры

В некоторых случаях бывает полезно пользоваться нестандартными командами.

К чему я это говорю? Обратите внимание, как мы теперь переходим на метку инициализации вируса (Init):

push offset Init ;3 байта
ret ;1 байт

Ну и зачем? Дело в том, что так будет нам удобней менять адрес метки Init в зараженных файлах. Читайте ниже - будет понятно.

Как происходит заражение файла?
Как выглядит "файл-жертва" после заражения?

Заражение файла происходит следующим образом (смотрите процедуру Infect_file). Опустим поиск файлов. Будем считать, что файл уже найден. Осталось только проверить его длину:

mov ax,cs:[1Ch] ;Получим второе слово длины заражаемого файла
or ax,ax ;Если оно не равно 0, то выходим...
jnz Error_infect ;...это значит, что размер файла больше 64Кб.
mov bp,cs:[1Ah] ;Получим младшее слово (т.е. размер файла)

Зачем мы проверяем длину com-файла, если известно, что com-файлы не могут быть больше 64Кб? Дело в том, что DOS определяет какой тип файла запускается не по расширению, а по его первым двум байтам содержимого файла. Если это "MZ" или "ZM", то файл - EXE, иначе - COM. Можете проверить... Таким образом, ничего не мешает любому пользователю переименовать файл, например, test.exe длиной 450Кб в test.com. DOS все равно определит, что это EXE-файл по первым двум байтам.

Примером может служить COMMAND.COM, который в DOS 7.0 занимает 95Кб. Расширение COM оставили для совместимости с программами, написанными в более старых версиях DOS.

Так что же получится, если мы попробуем заразить тот же COMMAND.COM, который по сути является COMMAND.EXE? Да ничего! Мы просто его испортим! Т.е. он перестанет работать вообще. Вот, собственно, для этого и следует проверить размер на то, что он меньше 64Кб.

Обратите внимание, как мы это делаем. После того, как функция 4Fh (4Eh) нашла файл, в DTA по смещению 1Ah заносится размер файла. Т.к. два байта могут хранить число до 65.535, то для определения размера файла используется два слова. Первое (смещение 1Ah) - младшая половинка, второе (смещение - 1Ch) - старшее. Вспоминаем, что данные в памяти хранятся наоборот ("задом наперед").

Что мы еще не сделали в вирусе, так это - не проверили первые два символа (MZ). Зачем? А что, если exe-файл имеет длину менее 64Кб, но расширение COM? Такое ведь тоже может быть... Ну, просто кто-то пошутил... Надеюсь, что Вы без труда это сделаете сами.

Следующие шаги:

открываем найденный файл на чтение / запись (3D02h);
читаем первые шесть байт его (3Fh);
проверяем, заражен ли этот файл нашим вирусом уже или еще нет.
Здесь остановимся ненадолго. В нашем примере мы используем т.н. сигнатуру 1122h для определения того, заражен ли найденный файл нашим вирусом или нет. Эта сигнатура должна быть расположена по смещению +4 от начала файла. Обратите внимание, что мы проверяем

cmp word ptr [bx+4],1122h

а в файле 1122h будут располагаться наоборот: 2211h. Вот вам еще одно подтверждение того, что данные в компьютере хранятся "задом наперед".

А что, если в каком-то незараженном файле по смещению +4 от начала уже есть такие строки? Получается, что мы посчитаем, что файл уже заражен? Да. Но как вы думаете, какова вероятность того, что эта сигнатура будет находиться в файле по такому смещению? Возможно, даже еще и файла-то такого нет... Но если вы все равно сомневаетесь, то можете сделать сигнатуру более длинной. Например, 6 байт. Это уже дело времени...

Итак, проверили... Затем устанавливаем указатель файла на конец файла. Будем дописывать в хвост файла наш вирус.

Как вы уже заметили, мы используем функцию 4202h для перемещения указателя на конец файла. Вот, рассмотрим эту функцию:

Функция 4202h прерывания 21h: установка указателя на конец файла.

Вход:
AX=4202h
BX=номер файла
CX, DX = количество байт, которые необходимо отсчитать от конца файла и установить на них указатель.
Выход:
JC - ошибка

Если в регистрах CX и DX мы укажем 0, то указатель будет установлен сразу за последним байтом файла. Пока нас это только интересует. Указатель - только для DOS. Он служит для указания ОС из какого места читать или в какое место писать байт(ы). Если указатель не переместить на конец файла, то, записав тело вируса, мы затрем файл, начиная со смещения 7.

Запомните, что:

  • при открытии файла указатель устанавливается на начало файла автоматически. Если указатель установлен где-то на середину файла, то, закрыв файл, а затем обратно открыв его, то указатель установится на начало файла.
  • при чтении файла указатель перемещается на количество прочитанных байт. Т.е. если файл имеет длину 3.000 байт, то прочитав 1.500 байт, указатель переместится на середину файла.

    Все просто!

В нашем примере мы устанавливаем на конец файла указатель, чтобы записать наш вирус в "хвост" "файла-жертвы".

Итак, это сделали. Теперь можем и записывать вирус в "хвост", используя известную уже вам функцию 40h. Интересно, а вы сможете разобраться с этой строкой:

mov cx,offset Finish-100h-F_bytes

Сколько байт мы будем записывать и почему именно столько?

Записали. Теперь можно сказать, что файл уже заражен. Его размер увеличился на длину нашего файла. Чего не хватает?

Если мы запустим зараженный файл, то наш вирус не получит управления. Почему? А вы не поняли? Мало ли, какой чуши записано в конце файла. Эта "чушь" ведь не получает управление! А как сделать так, чтобы наш вирус первый получил управление? Нужно первые байты "файла-жертвы" (начинающиеся с адреса 100h) заменить на переход на наш вирус, а затем, как вирус отработает, восстановить эти байты в памяти и "прыгнуть" по адресу 100h. Вы поняли, о чем речь идет?

Следующий шаг: записываем после нашего вируса первые шесть байт, которые мы меняем:

mov ah,40h ;После тела вируса дописываем первые настоящие шесть байт
mov cx,F_bytes ;"файла-жертвы"...
mov dx,offset Finish
int 21h

Теперь нам нужно сделать так, чтобы наш вирус первым получил управление. Т.е. прежде, чем получит управление "файл-жертва", наш вирус должен получить управление, отработать, а затем сделать так, чтобы "файл-жертва" заработала, не подозревая о том, что кто-то прежде получил управление.

Для этого необходимо вместо первых шести байт, которые мы записали в "хвост" файла после тела вируса, записать переход на на метку Init. Т.о. при запуске файла наш вирус первый получит управление, отработает, восстановит в памяти первые шеть байт и передаст управление "файлу-жертве".

Заменяем первые шесть байт на диске. А как? Но мы ведь получили размер файла. Его храним в регистре BP (см. примечания в файле). Сделаем таким образом:

push адрес перехода на метку Init
ret

Если файл 3.000 байт, то после PUSH будет один адрес, а если файл 5.000 байт, то переход будет на другой адрес. Более подробней смотрите описание в файле.

Все! Теперь файл заражен корректно. При его запуске первым получит управление наш вирус. Осталось восстановить в памяти первые шесть байт "файла-жертвы" (мы-то их сохранили после тела вируса!) и передать управление по адресу 100h.

Итак, общая схема:

  1. ищем com-файл;
  2. читаем первые шесть байт найденного файла;
  3. файл больше 64Кб - см. пункт 1;
  4. по смещению +4 от начала файла находится 1122h? Если да - см. пункт 1;
  5. пишем в "хвост" найденного файла тело нашего вируса;
  6. пишем после тела вируса первые шесть настоящих байт "файла-жертвы";
  7. закрываем файл;
  8. создаем искусственный переход на метку Inint, путем вычисления размера файла;
  9. открываем файл (указатель на начале файла);
  10. пишем первые шесть байт перехода на на тело вируса + 1122h для опознавания того, что файл уже заражен нашим вирусом;
  11. закрываем файл;
  12. восстановим первые шесть реальных байт "файла-жертвы" в памяти (они-то у нас записаны в "хвосте" "файла-жертвы" (см. пункт 6.);
  13. передаем управление нашему "файлу-жертве";
  14. ВСЕ!!!

    Просто? Да проще не бывает! Внимательно изучите файл-приложение, комментарии к нему.

Еще раз подчеркну: будьте осторожны при экспериментах. Создайте отдельный каталог, куда перепишите com-файлы, над которыми можно будет производить эксперименты.

СКАЧАТЬ ИСХОДНИКИ ВИРУСОВ


Из книги Калашникова.
Сайт управляется системой uCoz