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

Вирус.

Из главы вы узнаете, как поместить вирус временно в память, как передать ему управление, как пытаться найти файл...

Мы будем заражать ТОЛЬКО *.COM-файлы! Если будет время и желание у вас, то можем написать вирус, заражающий и *.EXE-файлы.

  • Как вирус "прикрепляется" к файлу, не нарушая его работоспособности?
  • Что должен сделать вирус в первую очередь?
  • Как передать управление вирусу из чужой программы?

Как вы знаете, com-файл сразу начинается с кода, первый байт которого располагается в свободном сегменте по смещению 100h. Следовательно, нам нужно будет сохранить в теле нашего вируса первые три байта "файла-жертвы", записать наш вирус в "хвост" файла и вместо сохраненных трех байт установить команду jmp на начало нашего вируса (т.е. передать ему первому управление). После того, как вирус отработал свое, восстановить сохраненные три байта (записать их в памяти по адресу 100h) и передать им управление. На практике все будет понятней...

Получается примерно так:

Программа до заражения (следите за адресами):

1234:0100h mov ax,34 ; код программы до заражения, расположенный по адресу 100h
1234:0103h mov dx,15 ; здесь может быть все, что угодно... Но нам-то все равно...
1234:0106h add ax,bx
1234:0108h .... и т.д.
...
1234:0500h int 20h ; последний байт программы
1234:0502h --- здесь идет уже свободная память...

Программа после заражения:

1234:0100h jmp 0502h ;прыгаем на начало нашего вируса (заменили мы байты здесь)
1234:0103h mov dx,15 ;а это байты "файла-жертвы"
1234:0106h add ax,bx
...
1234:0500h int 20h
1234:0502h --- а здесь уже начинается код нашего вируса.
... --- здесь тело вируса. Делаем, что хотим...
1234:0700h mov word ptr cs:[0100h], First_bytes_1 ;восстановим первые два байта "файла-жертвы"
1234:0705h mov byte ptr cs:[0102h], First_bytes_2 ;восстановим третий байт "файла-жертвы"...
; Теперь по адресу 100h первые два байта не jmp 502h, а mov ax,34 (т.е. оригинальный код программы)
; Вспоминаем из прошлых глав о том, что в Ассемблере можно менять код программы "на лету"...
; Все это меняется только в памяти, а не на диске!
1234:0709h jmp 0100h ; ...и перейдем по адресу 100h, т.е. передадим управление программе

Сложновато? Сравните два куска кода: незараженной программы и зараженной. Должно быть все понятно...

Сейчас будем практиковаться...

Возьмите файл здесь.

Первая строка - .286 - указывает Ассемблеру, что будем использовать инструкции (команды, операторы) 286 процессора. Т.е. на 8086 компьютере наш вирус уже работать не будет!

С первого же байта перейдем на метку Init (инициализации нашего вируса).

Сразу же возникает проблема: при поиске файла функцией DOS мы затираем DTA "программы-жертвы".

И тут же возникает вторая проблема, точнее, вопрос: что такое DTA и для чего оно нужно? Это мы рассмотрим позже.

Как вы помните, все *.com-программы начинаются с адреса 100h (org 100h). Что же находится в памяти от 0 до 100h? Там расположен PSP (Program Segment Prefix - префикс программного сегмента). По адресу 80h находится по умолчанию DTA (Data Transfer Area - область переноса данных). В DTA записывается информация, когда функция поиска файла (4Eh и 4Fh) находит очередной файл. Все вроде бы и ничего, но проблема в том, что по этому адресу (80h) располагается изначально командная строка. Например:

format.com c:/s/u

По адресу CS:0080h будет находится: L_c:/s/u, где L - длина командной строки, а _ - символ пробела. Для того, чтобы удостовериться, запустите отладчик AFD так:

afd.exe format c:/s/u

Затем посмотрите, что будет находиться по адресу CS:0080h.

К чему все это? Да к тому, что когда мы попробуем искать первый файл, то, найдя его, мы затрем командную строку (L_c:/s/u). Получается, что "программа-жертва", к которой мы "подцепились" не сможет прочитать те параметры, которые ей передал пользователь. В данном случае - это L_/s/u.

Есть два способа обойти это.

1. Сохранить PSP программы перед поиском файла. А затем, как наш вирус отработал, восстановить его.

2. Установить DTA на другую область памяти, а затем восстановить его. Это позволяет сделать функция 1Ah прерывания 21h:

Вход:
AH=1Ah
DS:DX - адрес DTA для установки
Выход: ничего

Мы выберем второй путь.

Далее возникает еще одна проблема: мы теряемся в адресах. Т.е. мы занесем наш сассемблированный код в конец программы, при этом смещения все поменяются.

Например:

mov dx,offset String

Ассемблер занесет в DX смещение строки String в памяти. Фактически - после ассемблирования - это будет выглядеть так:

mov dx,125h --- какое именно число - не важно. Главное, что в DX будет находиться адрес (смещение) строки в памяти, отсчитывающееся от 0. Но мы-то запишем код нашего вируса в конец программы, включая все строки и прочие области данных! Получается, что строка в памяти будет находится по одному адресу, а в регистры будет загружаться совсем другой адрес!

Вот пример:

1234:0100h mov dx,400h --- в неассемблированном варианте это выглядит, как mov dx,offset String. Т.е. Ассемблер заменит offset String на адрес (смещение) этой строки в памяти, начиная от нуля.
...
1234:0400h 'Строка' --- а вот и строка, которая расположиться по такому адресу. Она может расположиться и по любому другому (надеюсь, вы понимаете почему?), но суть не в этом.

Теперь, представим, что "файл-жертва" занимает 100h байт. Мы записываем наш код в конец файла. Получается, что строка будет находиться по такому адресу: 1234:0400h + 100h = 1234:0500h! Хорошо бы было, если бы все файлы имели одну длину. Но один файл может быть 100 байт, а другой 23000 байт! В итоге, обращаясь к строке в зараженной программе, мы получаем:

1234:0200h mov dx,400h --- 200h потому, что 100h байт занимает "файл-жертва", а мы в "хвосте" у него...
...
1234:0400h --- все, что угодно, но только не наша строка.
...
1234:0500h 'Строка' --- вот, где она будет!

Можно, конечно, перед заражением получить длину "файла-жертвы" и затем заменить mov dx,400h на mov dx,500h. Но что делать, если таких ссылок много? Представляете, до каких размеров разрастется наш вирус?!

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

До перемещения:

1234:0200h mov dx,400h --- мы в хвосте программы
....
1234:0500h 'Строка'

После перемещения:

5678:0100h mov dx,400h --- перебросили себя в сегмент 5678h, по смещению 0100h
....
5678:0400h 'Строка' --- строка стала на свое место (смещение)!!!

Еще вопрос: где гарантия того, что в данном сегменте никого нет, и мы не затрем код какой-нибудь программы?

Я предлагаю временно (т.е. на тот момент, пока работает вирус) переслать наш вирус в адрес 8-ой страницы дисплея. Видеокарта имеет достаточно памяти для размещения восьми страниц. Эти страницы, кроме первой или нулевой, если отсчет вести с нуля), почти никогда не используются программами. Более того известны точные сегменты этих страниц. Вот они:

0B800 - нулевая
0B900 - первая
0BA00 - вторая
0BB00 - третья
0BC00 - четвертая
0BD00 - пятая
0BE00 - шестая
0BF00 - седьмая

Давайте посчитаем размер одной страницы. Хватит ли нам места для того, чтобы разместить на ней код вируса? Наш вирус будет занимать не более 300-400 байт.

Возьмем известный вам режим 3: в одной строке 80 символов, строк на экране 25. Один символ занимает два байта (атрибут / смещение). Получаем: 80 x 25 x 2 = 4000 байт. Хватит ли нам этого? Конечно хватит! Даже, если бы не хватало, ты мы могли бы использовать две, три, четыре страницы.

Т.о. пересылаем себя в область седьмой (если считать с нуля) видеостраницы, что мы и делаем сразу же за меткой Init. Код нашего вируса на экране не будет отображаться, т.к. обычно текущей стоит 0-ая страница. Хотя, если хотите, можете проверить это...

Думаю, что вам не составит труда разобраться в новом операторе movs. Принцип его работы полностью соответствует команде stos. Вот его описание:

Название
Перевод
Применение
Процессор
movs MOVe String - переместить строку (скопировать) Пересылка (копирование) строки (массива) 8086

При этом DS:SI указывает на то, откуда брать данные, ES:DI куда их копировать, а CX - количество пересылаемых байт / слов.

Вот примеры:

...
mov cx,10 ;количество пересылаемых байт
mov si,offset Str1 ; откуда будем брать
mov di,offset Str2 ; куда копировать
rep movsb ; Пересылаем побайтно, т.к. movsB. Теперь Str1 = Str2
...
Str1 db '0123456789'
Str2 db '9876543210'

Можно так:

...
mov cx,5 ;количество пересылаемых слов (два байта)
mov si,offset Str1 ; откуда будем брать
mov di,offset Str2 ; куда копировать
rep movsw ; Пересылаем пословно (по два байта), т.к. movsW. Теперь Str1 = Str2
...
Str1 db '0123456789'
Str2 db '9876543210'

Итак, теперь наш вирус есть в двух местах в памяти: 1. сразу за "программой-жертвой"; 2. в области 7-ой страницы (0BF00:0100h).

Нам осталось прыгнуть на адрес 0BF00:IP. Как известно, CS:IP всегда показывают текущее операцию (адрес текущей операции). Обратите внимание, как мы прыгаем:

jmp dword ptr cs:[Off_move]

Посмотрите, что содержит переменная Off_move, а также посмотрите в отладчике, что будет происходить с регистрами CS:IP. Все станет на свои места... Подсказка: начиная с метки Lab_jmp мы работаем в области 7-ой видеостраницы...

Затем мы устанавливаем DTA в область 7-ой видеостраницы со смещения 0. Туда будет записываться информация о найденных файлах для заражения.

Теперь можно попробовать найти первый *.com-файл в текущем каталоге. Для этого используется функция 4Eh прерывания 21h:

Вход:
AH=4Eh
CX - атрибуты для поиска файла: у нас - 0 (обычные, не read-only и hidden!)
DS:DX - маска для поиска. Например, *.*, *.exe, comm???.c??, c:\*.asm и т.д.
Выход: CF = 1 - нет такого файла
CF = 0: DTA заполняется информацией о найденном файле

CF - это флаг переноса. Если написано CF=1, то это значит, что флаг переноса установлен (равен 1), а если CF=0, то сброшен (равен 0). Флаг переноса используется DOS для индикации ошибки функции или для других целей.

В данном случае, если функция 4Eh установила флаг переноса, то это значит, что файлов, удовлетворяющих условию (маске поиска), не было найдено.

Если флаг переноса сброшен (равен нулю), то в DTA заносится информация о файле. Ее мы рассмотрим в последующих главах.

Если CF = 0 (сброшен флаг переноса), то можно что-нибудь сделать с найденным файлом.

Для того, чтобы найти следующий файл, удовлетворяющий нашему условию (маске поиска), необходимо воспользоваться функцией 4Fh прерывания 21h:

Вход:
AH=4Fh
CX - атрибуты для поиска файла: у нас - 0 (обычные, не read-only и hidden!)
DS:DX - маска для поиска (как у функции 4Eh).
Выход: CF = 1 - нет больше файлов, удовлетворяющих условию (маске поиска)
CF = 0: DTA заполняется информацией о найденном файле

Все также, как у функции 4Eh.

Хочу заметить, что вирус у нас пока неработоспособный. Т.е. он ничего не заражает вообще! Можете спокойно его ассемблировать и запускать. Только мало что увидите на экране. Лучше это дело смотреть под отладчиком.

Дальше все просто! Я думаю, что описаний в программе достаточно для того, чтобы понять принцип работы программы.

Следующий вирус

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