Forum AoA- Хакерство, Читерство, Взлом Онлайн Игр.Баги боты и читы в  la2-WoW-CS-RF-PW. Все о Безопасности в Сети...  
   

Вернуться   Forum AoA- Хакерство, Читерство, Взлом Онлайн Игр.Баги боты и читы в la2-WoW-CS-RF-PW. Все о Безопасности в Сети... > Underground > Хакерство и Безопасность > Вирусология

Вирусология Новости , эпидемии , способы лечения.
Разработка и применение
Темы про Pinch и Xinch тут.

Ответ
 
LinkBack Опции темы Опции просмотра
Старый 15.11.2007   #1 (permalink)
SystemError
© AoA Team
 
Регистрация: 14.11.2007
Сообщений: 166
Вы сказали Спасибо: 2
Поблагодарили 6 раз(а) в 5 сообщениях
Андрюха на пути к лучшему
По умолчанию Антивирусные технологии: эмуляция программного кода

Содержание

1. Введение
2. Экскурс в историю
3. Способы эмуляции
4. Пример использования технологии
4.1. Лирическое отступление
4.2. Имитация исполнения инструкций
4.2.1. Запуск инструкции в специальной среде
4.2.2. Полная имитация исполнения инструкции
4.2.3. Комбинация двух способов
4.3. Поворот не туда
4.3.1. Дизассемблер
4.3.2. Эмулятор
4.4. "Предел терпения" (Enough)
5. Пример использования технологии для детектирования вирусов
5.1. Кодо-анализатор
6. Заключение

1. Введение

Эта статья не является продолжением пособия по написанию антивирусных программ, это всего лишь теоретическое описание технологии используемой в «хороших» антивирусных программах. Причем технологии далеко не примитивной, а очень и очень сложной для понимания и реализации …

Это описание не является отличным или хорошим это всего лишь базис (основы) технологии, как я понимаю это, вполне возможно я понимаю ее неправильно, этого я не отрицаю.

В этой статье не будет информации о нахождении точки входа в запускаемых файлах, информации о различных терминах (полиморфик, сигнатура …), детектировании и лечении вирусов … если вы читаете эту статью, значит, все это вы уже должны знать.

Тем, кому не интересны мои размышления о возможной истории появления технологии, могут пропустить «Экскурс в историю» и перейти непосредственно к описанию алгоритма технологии.

2. Экскурс в историю

Непосредственное использование эмуляторов программного кода (в антивирусных программах) появилось в начале 90ых. Толчком стал выход первого полноценного полиморфного вируса, точнее сказать полиморфного движка.

Полиморфным движком – называется «универсальный» программный код (библиотека), подключив который в вирус (и не только) можно сделать его полиморфным.

Этот движок назывался просто - MuTation Engine (MTE). После его выхода антивирусные конторы стали хвататься за голову (особенно американские), некоторые чувствовали, что что-то подобное скоро выйдет, уже обдумывали универсальные методы по детектированию шифрованных вирусов.

Кстати автором MTE являлся программист из Болгарии, более известный под псевдонимом Dark Avenger. Он же автор культовых вирусов Eddie и если я не ошибаюсь Dir так же поделки этого человека. Именно этот человек двигал «прогресс» (в плане разработки новых вирусных технологий) в конце 80ых годов.

Полиморфикам предшествовали шифрованные (иногда их так же называют «само шифрующимися») вирусы, для детектирования которых антивирусы в качестве сигнатур использовали постоянные участки расшифровщиков вирусного кода. Если сигнатура совпадала, то из расшифровщика брались необходимые данные (например, алгоритм шифровки, если он менялся, ключ …) и использовались для расшифровки вирусного кода, затем вторая сигнатура проверяла расшифрованный код на наличие вируса. Однако это очень и очень неудобно, процесс детектирования одного такого вируса занимает много кода, и используя такой тип детектирования шифрованных вирусов для каждого вируса придется иметь две сигнатуры, специальную процедуру по расшифровке … но задумываться об универсальном способе детектирования шифрованных вирусов создатели не хотели, пока … не появился MTE.

Конечно, очень многие полиморфики можно детектировать не эмуляцией кода расшифровщика, а различными алгоритмическими процедурами. Но опять же это очень сложные процедуры, причем они редко дают сто процентный результат определения зараженности файла вирусом, особенно если полиморфность расшифровщика достаточно высока (т.е. расшифровщик имеет очень мало сигнатур или не имеет их вообще). В итоге из 100 файлов зараженных вирусом использующим «слабенький» полиморфный алгоритм, антивирусы обнаруживали только 60 или немного больше (меньше).

Несмотря на геморрой, связанный с таким детектированием многие антивирусные компании решили выбрать этот сложный путь (например, взять ту же McAfee), то время, пока авторы вирусов только учились новой технологии полиморфизма, еще можно было использовать. Но в 1994 году, в Англии (уже не в Болгарии), появляется очень серьезный полиморфный движок SMEG (Simulated Metamorphic Encryption Generator). Определять «старым способом» декрипторы созданные по алгоритмам использовавшимся в этом движке практически невозможно, а кроме того нужно еще и расшифровывать вирусное тело, что бы излечить инфицированный файл! Ходили слухи, что английская полиция летом 1995 года, арестовала автора известного под кличкой Black Baron, за создание опасных вирусов Pathogen и Quueg и трех версий полиморфных движков SMEG. Но к этому моменту было уже достаточно умных программистов, способных писать гораздо более продвинутые полиморфики. Примерно в это же время, в Словакии, появляется Explosion’s Mutation Machine (от «культовой» личности – Vyvojar, он же автор знаменитого вируса OneHalf). Немного позже в России (Санкт-Петербурге) появляется Zhengxi, от одноименного автора. Но наилучшей разработкой под DOS (по моему мнению) стал Red Team Polymorphy от человека под псевдонимом SoulManager (который был выпущен в 1997 году, с тех пор так и остался «неконкурентоспособным» ;-) ).

Конечно, каждой вирусной технологии можно противопоставить обратную (антивирусную), но теперь факт заключался лишь в том, что хороший полиморфик написать гораздо проще, чем его обнаруживать. Так полиморфики и полиморфные движки стали появляться как грибы после дождя, в результате (к сожалению многих сторонников oldschool) пришлось завязать с алгоритмическим детектированием. Хотя был такой русский антивирус, который просто игнорировал появление полиморфных вирусов. Но это длилось до поры, до времени, пока в Россию не пришел вирус OneHalf, который стал распространяться с бешеной скоростью и этот антивирус оказался бессильным против OH. Позже проект был закрыт, автор антивируса так и не смог (а может, просто не хотел) включить в свое творение эмулятор программного кода.

Затем появился русский Dr. Web. Насколько я знаю, он изначально создавался именно для детектирования полиморфных вирусов, одной из первых процедур в нем был заложен эмулятор кода.

3. Способы эмуляции

Каждый уважающий себя антивирус должен содержать в себе эмулятор. Если мы исключим известные антивирусы и возьмем «кустарные», то я знаю только несколько программ, которые содержали реализацию эмулятора (конечно кривые, убогие, но они работали и достаточно сносно). Это MultiScan (конечно, кустарным, его можно назвать с натяжкой), Lecar (содержал некоторое подобие) и мое творение the_Sweeper. Я смотрел исходные тексты некоторых иностранных программ, но там все детектировалось по плавающим сигнатурам или алгоритмами, иногда (но очень редко) использовали обычную трассировку.

И так преступим к описанию технологии. Эмуляция программного кода означает разбор программного кода на инструкции и имитация их исполнения. Все это может быть выполнено двумя способами.

Первый способ подразумевает собой обычную трассировку программы, т.е. ее загрузку в память и исполнение путем использования отладочного прерывания (int 1). Таким методом пользуется большинство отладчиков, но вся проблема в том, что даже безрукий человек может обломать процесс трассировки. В крайнем случае, есть множество статей на тему облома и TD и SoftIce. А если в полиморфном расшифровщике вставлены антиотладочные трюки, которые в случае обнаружения трассировки запускают какую-нибудь деструкцию. В результате антивирус сам запустит деструкцию в процессе эмуляции и только навредит. Кстати такой способ для детектирования вирусов семейства SMEG, использовал в своем антивирусе американец StormBringer.

Второй способ это непосредственно эмуляция. Кусок кода читается в буфер антивируса, разбирается на инструкции и эмулируется их исполнение с помощью различных трюков. Но что бы правильно имитировать исполнение всех компьютерных заморочек, может понадобиться очень-очень много сил и времени. Однако это более безопасный способ, его обычно и используют в антивирусах, а вполне возможно (на самом деле это горькая правда), что кто-то использует комбинацию этих двух способов.

4. Пример использования технологии

В своем антивирусе “the_Sweeper” я использовал некоторую комбинацию этих двух способов, в результате получилось что-то совсем простенькое и кривое. Но антивирус мог ловить OneHalf, TMC, Pieck Примитивную реализацию алгоритма схожего с тем, что я использовал в антивирусе, представляю на Ваш суд.



Программа пример не работает с файлами, всю необходимую информацию она содержит в себе. Самое главное она обладает некоторыми особенными функциями, которые, взаимодействуя «позволяют» эмулировать код.

Программа состоит из:
Процедуры (рас)шифровки данных, которая шифрует и расшифровывает текстовую строку.
Эмулятора программного кода, возможности которого позволяют имитировать выполнение процедуры (рас)шифровки. Эмулятор для своей работы использует некоторое подобие дизассемблера команд, который знает все инструкции используемые в (рас)шифровщике, умеет определять длину, тип и некоторые параметры этих инструкций.

Программа работает по алгоритму:
Процедура (рас)шифровки вместе с текстом, который она должна шифровать (или расшифровывать) переносится в буфер.
Эмулятор запускает код, который был перенесен в этот буфер на «псевдоисполнение». В результате текстовая строка, которая также содержалась в этом буфере за процедурой (рас)шифровки будет зашифрована.
Далее используется процедура (рас)шифровки, которая содержится в программе и расшифровывается текстовую строку из буфера (которуа, ранее, была зашифрована эмулятором).
На экран выводится содержимое текстовой строки,содержащейся в буфере.

Если эмулятор неправильно выполнил свою работу, то текстовая строка окажется зашифрованной неправильно, следовательно оригинальная (которая содержится в буфере) процедура (рас)шифровки не сможет вернуть строку в исходное состояние. Если на экран выведется мусор, все пропало.

[emul.asm]
PHP код:
name   "code emulation" example
author andy [Most Needful Things]
home   http://amethyst.nm.ru
;
;  
Пример программы умеющей выполнять "псевдо-эмуляцию" программного кода.
;
компилировать
tasm32 -ml -m5 emul.asm
tlink32 -Tpe --x emul.obj ,,, import32
;
.386P
.model flatstdcall
 jumps
;
 
extrn                  lstrlenA:near            список needful апи
 extrn                 _wsprintfA
:near
 extrn                  GetStdHandle
:near
 extrn                  WriteConsoleA
:near
 extrn                  ExitProcess
:near
;                      
.
data
_stack                 db 10000 dup 
(?)          ; буфер под стэк
 temp                   dd 
?
 
instr_buf:            db 30 dup (?)              ; буфер для запуска инструкции в "карантине"
 
buffer                 db 10000 dup (?)          ; буфер под код
 flags                   dd 
?                              ; значение флагового регистра EFLAGS эмулируемого кода
 regs                    dd 08 dup 
(?)                ; значения eax/ecx/edx/ebx/esp/ebp/esi/edi эмулируемого кода
;                                                                              
.
code
 start
:      mov     esi,offset decryptor            смещ. (рас)шифрощика и текста
               mov     ecx
,(emulate-decryptor)/размер (рас)шифровщика и текста
               mov     edi
,offset buffer                 перенесем данные в буфер
                rep      movsd
;               
                
push    offset decryptor         старое значение ip (рас)шифровщика
                push    
(emulate-decryptor)   ; размер кода для эмуляции
                push    offset buffer               
расположение кода
                call      emulate                      
эмулируемт.езашифровываем
                                                               
текст идущий после (рас)шифровщика
;               
                
mov     edi,offset buffer         esi смещбуфера
                add       edi
,(message-decryptor)
                
push     edi
                call      decryptor_sze            
расшифруем текст из буфера
                pop      esi
;
;  
В данный момент ESI содержит смещение расшифрованного текстатеперь осталось вывести
текст на экранПоддержку консоли я убрал из исходных текстовпредставленных в статье  (полные
доступны в архиве).
;
                

;               
 exit:        
push    0
                call      ExitProcess
;               
decryptor простейший алгоритм (раз)шифроки текста идущего после ...
;
;  
Эмулятор "не понимает" инструкцию nopкоторая следует после цикла
; (рас)шифровкипо этому когда он дойдет до инструкциито выйдет с "ошибкой".
Но самое главноечто данные будут (за/рас)шифрованны ... в противном случае
эмулятор бы просто завис в вечном цикле. ;-)
;
decryptor:mov    edi,offset message
                pushfd
                popfd
                call      aaa
                jmp     decryptor_sze
                           db 125 dup 
(?)
 
aaa:         sub     edi,0ffffffh
                add     edi
,0ffffffh-004h
                scasd
                ret
 decryptor_sze
:
                
mov    ecx,0
                add     cl
,( (emulate-message) / )
 
decrypt_loop
                xor      
dword ptr [edi],0ffffffh
                scasd
                loop    decrypt_loop
                nop
                ret 
 message             db 
'Hey you, ugly face!',13,10
                           db 
'Give me the bottle of tequilla!',13,10
                           db 
'Hurry up, don''t make me mad!',13,10,0
;                                                                              
 include                
incemulator.inc         эмуляция кода
 
include                incdisasm.inc            дизассемблер
;                                                                              
                
end      start
[incemulator.inc]
.
code
;                                                                              
emulate эмуляция участка кода
;
on start :       ip оригинальный ip блока
;            codesize размер блока для эмуляции
;             codeloc смещение на код для эмуляции
on exit  :  - - - - -
;
 
emulate        proc   codelocdwordcodesizedwordipdword
;                                                                              
процесс инициализации эмулятораперед работой с участком кода
;  асохранение значения регистров
;  бустановка параметров переданных в процедуру (ip/codesize/codeloc)
;  
вустановка значения стекадля эмуляции кода
;
 
emul_init:
               
push    eax ecx edx ebx ebp esi edi
               mov     esi
,offset _stack+10000    буфер под стекдля эмуляции
               mov     dword ptr 
[regs+016],esi  установим значение
                mov     esi
,codeloc                       смещение на начало кода
                mov     ebx
,esi
                add      ebx
,codesize                    смещение на конец кода
;                                                
 
emulate_loop:  
                
push    esi                        включаем кодо-анализатор
                call     disasm                   
помощью дизассемблера
                cmp     eax
,-1                   если инструкция известна
                jnz      copy_instr             
дизассемблерупродолжим разбор
 emulate_error

                 
jmp     emulate_ret         иначе (!) тихо выйдем
;                                                
перенесем в буфер "instr_buf" инструкции для эмуляции стека и
разобранную дизассемблером инструкцию размера ecx
;
в результате получим в "instr_buf" примерно:
;
;               
mov     dword ptr [res_stack+1],esp
;               mov     esp,dword ptr [regs+016]
;           ... 
разобранная инструкция ...
;               
mov     dword ptr [regs+016],esp
res_stack:    mov     esp, ?
;               
ret
;
 
copy_instr:    
                
push     eax ecx                 запомним данные о инструкциикоторую разбирал дизассемблер
                mov     edi
,offset instr_buf
                mov     ax
,2589h
                stosw                                
перенесем "mov 4 ptr [?],esp"
;               
                
mov     eax,offset instr_buf    рассчитаем смещение
                add      eax
,ecx                       "res_stack+1" (смвышеи
                add       eax
,6+6+6+1             перенесем остаток инструкции
                stosd
;               
                
mov     ax,258Bh                   перенесем инструкцию
                stosw                                      
"mov esp,4 ptr [regs+16]"
                
mov     eax,offset regs+016
                stosd
;               
                
rep     movsb                          перенесем разобраннуюдизассемблером инструкцию
;               
                
mov     ax,2589h                   перенесем инструкцию
                stosw                                     
"mov 4 ptr [regs+16],esp"
                
mov     eax,offset regs+016
                stosd
;               
                
mov     al,0bch                     перенесем инструкцию
                stosb                                     
"mov esp,?"
                
stosd 
                mov     al
,0c3h                     "поставим последнюю точку" перенесем "ret"
                
stosb                                     

;
                
pop      ecx eax                    вспомним данные инструкции
;                                                                              
работа с инструкциямикоторые требуют специальной имитации исполнения
первый этап определение типа инструкции
;
                
push    esi
                pop     edi                            
edi смещение инструкции
                sub      edi
,ecx                     ;  которую разбирали
;                          инструкции размером 1 байт
 
@one_byte:     
                
cmp     cl,1
                jnz    
@two_byte
                cmp     byte ptr 
[edi],0c3h   ;  
                
jz     @found_ret             найден "ret" ?
;                          
инструкции размером 2 байта
 
@two_byte:     
                
cmp  cl,2
                jc      run_instr                  
если размер меньше или не равен
                jnz    
@three_byte
 
@check_xor:    cmp     byte ptr [edi],031h   ;  
                
jz     @found_xor             ; (де)шифрующая инструкция xor?
 @
check_loop:   cmp     byte ptr [edi],0e2h   ;  
                
jz     @found_loop            найден "loop"?
                
jmp     run_instr
;                         инструкции размером 3 байта
 
@three_byte:   
                
cmp   cl,3
                jc       run_instr                  
если размер меньше или не равен
                jnz    
@five_byte
 
@check_xor2:
               
cmp     byte ptr [edi],030h   найдена разновидность
                jz     
@found_xor               ; (де)шифрующей инструкции xor?
                
jmp     run_instr
;                         инструкции размером 5 байт
 
@five_byte:    
                
cmp  cl,5
                jc      run_instr                    
если размер меньше или не равен
                jnz    
@six_byte                 5 байтам запустим в "карантине"
                
cmp     byte ptr [edi],0e8h  ;  
                
jz     @found_call               найден "call" ?
                
cmp     byte ptr [edi],0e9h  ;  
                
jz     @found_jmp               найден "jmp" ?
                
jmp     run_instr
;                          инструкции размером 6 байт
 
@six_byte:     
               
cmp     cl,6
                jc        run_instr                      
если размер меньше или не равен
                jnz       run_instr                     
6 байтам запустим в "карантине"
                
cmp     byte ptr [edi],081h      "add/sub/xor [ereg], dword" ?
                
jnz       run_instr                     нетзапустим в "карантине"
                
cmp     byte ptr [edi+1],037h нам нужен только "xor"а
                jna    
@found_xor                  остальные можно просто запустить
                jmp       run_instr                    
на исполнение в "карантине"
;                                                                              
работа с инструкциямикоторые требуют специальной имитации исполнения
второй этап имитация исполнения инструкции
;
;                             
инструкции размером 1 байт
имитировать исполнение инструкции "ret" очень просто:
;  
анеобходимо взять двойное слово из стека эмулируемого кода
;     смещение на это слово содержится в "regs+16"
;  бэто слово будет новым указателем на смещение разбираемой эмулятором
;     инструкции (значение регистра esi)
;  
вубрать из стека эмулируемого кодавзятое значение
;     add dword ptr [regs+16],4
;
 @
found_ret:    
                
mov     esi,dword ptr [regs+016]
                
lodsd
                add     dword ptr 
[regs+016],4
                xchg    esi
,eax
                jmp     emulate_check
;                            инструкции размером 2 байта
Первая часть процесса имитации выполнения дешифрующей инструкции
;
сейчас буфер "dregs" содержит примерно следующие данные:
;  
dregs 000 : ????? (количество регистров использующихся в инструкции)
;  
dregs 004 номер регистра 1
;  dregs 008 номер регистра 2
;  dregs +  n  номер регистра n 4
;
;  
Нам необходимо определить сумму значений регистров (в данном случае двух)
использующихся в инструкции и передать ее во вторую часть процессаВ нашем
случае это будет выглядеть примерно так:
;
;   
= [dregs+004] * 4
;   = [regs+a]
;   
= [dregs+008] * 4
;   + [regs+a]
;   
и так далее ...
;
где x является той самой суммойкоторую необходимо узнать
;
 @
found_xor:    
                
push    ebx esi
                sub      edx
,edx
                mov     dl
,4
                sub      ebx
,ebx
                mov     esi
,offset dregs
                lodsd
                xchg    eax
,ecx
 
@fxor_load_lp
                
push     edx
                lodsd
                mul      edx
                add      ebx
,dword ptr [regs+eax]
                
pop      edx
                loop   
@fxor_load_lp
                xchg     ebx
,ecx
                pop      esi ebx
;
Вторая часть процесса имитации выполнения …
;
допустим (только допустим!), что максимальный размер расшифровщика
может составлять 200 байт
значит (!) если сумма значений регистров (xнаходится в промежутке
от ip до ip+200,
то менять значение не обязательновполне возможно что:
;  - 
декриптор содержит в себе процедуру определения ip
;  - значение регистра уже менялось раньшет.еинструкция выполняется
;    не первый раз
если одно из вышеуказанных утверждений правда (true), то просто запускаем
инструкцию на выполнение в специальных условиях
;
 @
found_xor_ip
                
mov     edx,ip
                cmp     ecx
,edx
                jc         run_instr
                add      edx
,200
                cmp     ecx
,edx
                ja         run_instr
;
Третья часть процесса имитации выполнения …
;
;  
если значение x выпадает из промежутка
;  
тоaнеобходимо рассчитать разницу между старым ip и x (суммой
;         значений регистров)
;      
бдобавить эту разницу к смещению начала буфера (codeloc), в который
;         мы копировали эмулируемый код
;      вполученное смещение поместить в значение регистра "dreg+004"
;      гзначение остальных регистров использующихся в инструкции обнулить
;
                
sub     ecx,ip
                add     ecx
,codeloc
;
новое смещение зашифрованного кодаполученное действиямиописанными
в пунктах "а" и "б" (смвыше), положив в регистр номер "dreg+004"
;
                
mov     eax,dword ptr [dregs+004]
                
sub       edx,edx
                mov     dl
,4
                push     edx
                mul      edx
                pop      edx
                mov     dword ptr 
[regs+eax],ecx
;
циклдля обнуления значений всех регистров начиная с "dreg+008"
;
                
push    esi
                mov     esi
,offset dregs
                lodsd
                cmp     al
,02
                jc     
@fxor_emu_ret
                xchg    eax
,ecx
                dec     ecx
                lodsd
 
@fxor_setz_lp
                
push    edx
                lodsd
                mul      edx
                mov     dword ptr 
[regs+eax],0
                pop      edx
                loop   
@fxor_setz_lp
 
@fxor_emu_ret:
                
pop     esi
                jmp     run_instr    
можно запустить инструкцию на выполнение
;               
имитируя исполнение инструкции "loop"мы должны уменьшить значение ecx
эмулируемого блока на 1если он не будет равен 0то перейти к "началу цикла"
;
 @
found_loop:
               
dec      dword ptr [regs+004]     ; уменьшим значение регистра
                cmp     dword ptr 
[regs+004],"ecx" эмулируемого кода
                jz        emulate_check               
если равно 0выйдем из цикла
                sub      esi
,ecx                           иначе рассчитаем смещение
                sub       esi
,eax                           начала цикла
                jmp      emulate_check               
перейдем к следующей инструкии
;                            инструкции размером 5 байт
если перед нами инструкции "call" и "jmp" то необходимо изменить смещение
; (значение esiинструкциикоторая будет следующей исполняться эмулятором
НО в случае если мы разбираем "call"нужно сохранить в стеке эмулируемого
участка смещение команды следующей прямо за callомчто бы потом по встрече
"ret" вернуться "на путь истинный"
;
 @
found_call:&nbs