реклама
ЭКСПЕРТИЗА САЙТОВ НА СЛИВ ИНФОРМАЦИИ

ВОССТАНОВЛЕНИЕ ИНФОРМАЦИИ С РАЗЛИЧНЫХ НАКОПИТЕЛЕЙ В КИЕВЕ
уход за растениями - озеленение - фитодизайн
реклама

proxy  статьи  библиотека  softice  free_юр.консультация  hard  iptv
рекламодателям  фирмы/add  расшифровка штрих-кодов  links/add

http://kiev-security.org.ua

Содержание

Анализаторы кода в антивирусах

Содержание

 1.   Вступление
 2.   Технология анализа кода
 2.1. Инициализация анализатора кода
 2.2. Эвристический анализ
 2.3. Поиск вирусных сигнатур
 2.4. "Поиск" процедуры подсчета "delta value"
 2.5. Полная имитация выполнения инструкций
 2.6. Изменение параметров работы эмулятора
 3.   Заключение

 1. Вступление

  Данная статья посвящена технологии анализатора кода, которая используется в антивирусных программах.

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

  Все рассуждения и примеры будут приводиться на ассемблере, для исполняемых файлов формата Portable Executables.

 2. Технология анализа кода

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

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

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

 2.1. Инициализация анализатора кода

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

.data
;
; тип значений хранящихся в регистрах, в соотвествии с их номерами, например:
;  _regs+000 - eax
;  _regs+001 - ecx
;   ...
;  _regs+007 - edi
;
; типы: 2 - смещение расположенное в виртуальном стеке
;       1 - delta value или заготовка его последующего расчета
;       0 - все остальные значения не подходящие типам 1 и 2
;
;       подробнее о типах
;
 _regs                 db 8 dup (?)   
;
; переменные, необходимые анализатору для поиска процедур по расчету delta value (см. ниже)
;
; ca_ip_stage    - номер этапа, на котором находится эта функция кодо-анализатора
; ca_ip_instr    - количество инструкций проверенных после вызова "CALL"
; ca_ip_call_off - смещение полученной после вызова "CALL"
;
 ca_ip_stage            db ?
 ca_ip_instr            db ?
 ca_ip_call_off         dd ?
.code
;
 ca_init        proc
;                                                                              
                push   eax ecx edi
                mov    word ptr [ca_ip_stage],0 ; присвоение нулевого значения переменным "ca_ip_stage" и "ca_ip_instr"
 reg_analyze:   sub    eax,eax
                sub    ecx,ecx
                mov    cl,8
                mov    edi,offset _regs
 ra_init_loop:  stosb
                loop   ra_init_loop
                mov    byte ptr [_regs+4],2; регистр в любом случае esp будет смещение на данные внутри виртуального стека
                pop    edi ecx eax
                ret
 endp

 2.2. Эвристический анализ

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

  Участки кода, которые часто используются при создании различных типов вирусов, являются сигнатурами (масками), для эвристического анализатора, иногда их так же называют вирусными break point'ами.

  Использование эвристического анализатора является не обязательным, но очень удобным, потому что позволяет не только обнаруживать новые вирусы, но и значительно ускорить процесс проверки файлов.
  При наличии функций эвристического анализа, можно производить поиск известных вирусных сигнатур только в тех позициях, в которых "эвристик" встретит одну из масок. Потому что, проверка совпадения всех вирусных сигнатур, после каждой пройденной эмулятором инструкции займет достаточно много времени, особенно если:

  1. база содержит большое количество записей
  2. база небольшого размера, но сигнатуры проверяются полностью - для поиска модификаций известных вирусов
    • В этом случае сигнатура сравнивается полностью, и если совпало хотя бы 80% - возможно обнаружена модификация.

  Более подробно описывать технологию эвристического анализа я не буду, так как она заслуживает отдельной статьи.

 2.3. Поиск вирусных сигнатур

  Анализ участков кода и их сравнение с вирусными сигнатурами, записанными в базе. Для детектирования известного программе вируса достаточно именно обычного сравнения участка кода с конкретной (вирусной) сигнатурой.

  Приведем пример части анализатора, которая производит проверку всех вирусных сигнатур из базы. А так же пример формата вирусной базы.

;
 entry_size            equ e_type+e_name+e_slen+e_dlen+e_sdata+e_curem ; длина одной записи
;
 e_type                equ 1                     ; тип записи для файлов:
  etype_dos_com        equ 0                     ;  - com (dos)
  etype_dos_exe        equ 1                     ;  - exe (dos)
  etype_pe             equ 2                     ;  - PE  (win32)
;
 e_name                equ 25                    ; длина названия записи (в ASCII символах)
 e_slen                equ 1                     ; байт содержит "реальную" длину сигнатуры
 e_dlen                equ 2                     ; обязательная длина исполняемого кода (размер декриптора)
 e_sdata               equ 30                    ; полная длина сигнатуры
 e_curem               equ 4                     ; метод лечения или смещение процедуры для лечения
   cmethod_unk         equ 0                     ;  - вылечить немогу
   cmethod_del         equ 1                     ;  - необходимо удалить
;                       
.data
;                                                  вирусная база
 vir_no                 dd 2                     ; количество записей в базе
@001_type               db etype_pe
@001_name               db 'Win32/HLLM Mydoom'   ; штамм B (32.768 байт)
                        db 008 dup (0)
@001_sig_len            db 020
@001_dec_len            dw 000                   ; декриптор отсуствует
@001_sig_data           db 055h,08Bh,0ECh,081h, '?',005d,056h,057h,0E8h, '?'
                        db 004d,08Dh,085h, '?',004d,050h,06Ah,002h,0FFh,015h
                        db 010 dup (?)
@001_cure_loc           dd cmethod_del
;-----------------------
@002_type               db etype_pe
@002_name               db 'Win32/Parite'        ; штамм B
                        db 013 dup (0)
@002_sig_len            db 020
@002_dec_len            dw 049+1                 ; размер декриптора постоянен - 49 байт
@002_sig_data           db 055h,08Bh,0ECh,081h,0C4h,0C0h,0FEh,0FFh,0FFh,08Bh
                        db 0C5h,083h,0C0h,004h, '?',003h,056h,057h,033h,0DBh
                        db 010 dup (?)
@002_cure_loc           dd offset parite_b_pe
;
.code
;                                                                              
; detect - сравнение сигнатур из базы "bloc" с участком кода из "cloc"
;
; on start :      bloc - расположение базы сигнатур
;                 cloc - расположение кода для сравнения
;
; on exit  :  eax == 0 - ни одна из сигнатур не совпала
;             eax != 0 - содержит номер совпавшей сигнатуры в базе
;
 detect         proc   cloc:dword, bloc:dword
;                                                                              
                push    ecx edx esi
                sub     edx,edx                ; edx - регистр под номер записи
                inc     edx                    ; edx=1 - рассмотрим 1 запись
                mov     esi,bloc
                mov     ecx,dword ptr [esi]    ; ecx - кол-во записей в базе
                add     esi,4                  ; esi - данные первой записи
 det_main_loop: sub     eax,eax
                mov     al,byte ptr [esi+e_type+e_name]
                push    eax
                add     esi,e_type+e_name+e_slen+e_dlen
                push    esi                    ; расположение сигнатуры в записи
                push    cloc                   ; расположение кода для сравнения
                call    detect_sig             ; совпадает сигнатура ?
                or      eax,eax
                jnz     det_sig_found          ; если сигнатура совпала
;               
 det_main_next: inc     edx                    ; если сигнатура не совпала,
                add     esi,e_sdata+e_curem    ; проверим следующую запись
                loop    det_main_loop          ;
                sub     eax,eax                ; если ни одна из сигнатур не совпала - eax=0
 det_main_ret:  pop     esi edx ecx            
                ret
;               
 det_sig_found: xchg    eax,edx                ; eax=edx = номер совпавшей сигнатуры
                jmp     det_main_ret
 endp
;                                                                              
; проверка участка на совпадение с сигнатурой
;
; on start : slen - длина сигнатуры
;            sloc - расположение сигнатуры
;            cloc - расположение кода для сверки
; on exit  : eax = 0 - не совпадают, иначе сигнатура совпала
;
 detect_sig     proc   cloc:dword, sloc: dword, slen: dword
;                                                                              
                push    ecx edx esi edi
                                         ; установим параметры сигнатуры:
                mov     ecx,slen         ; ecx - размер
                mov     esi,sloc         ; esi - расположение
                mov     edi,cloc         ; edi - код для сравнения с сигнатурой
 detect_loop:   lodsb
                cmp     al,'?'
                jnz     det_compare
;
; если встретился символ "?", значит необходимо пропустить количество
; (указанное в следующем байте сигнатуры) байт в исследуемом коде
;
                sub     eax,eax
                lodsb
                dec     ecx
                add     edi,eax
                jmp     detect_cont+1
 det_compare:   cmp     byte ptr [edi],al        ; сравним байты сигнатуры и кода
                jz      detect_cont
                sub     eax,eax
                jmp     detect_ret
 detect_cont:   inc     edi
                loop    detect_loop              ; если цикл окончен и сигнатура
                mov     al,1                     ; совпала полностью EAX = 1
 detect_ret:    pop     edi esi edx ecx
                ret
 endp

 2.4. «Поиск» процедуры подсчета "delta value"

  Delta value - это число, которое является разницей между начальным положением кода (тем, которое он имел после компиляции) в памяти и текущим положением. Прибавляя это значение к смещениям различных "ресурсов", мы всегда получим их текущее местоположение в памяти.

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

  Процедура подсчета delta value, может выглядеть примерно так:

 
 delta:  call get_ip              или    delta:  call get_ip
 get_ip: pop  ebp                        get_ip: mov  ebp,dword ptr [esp]
         sub  ebp,offset get_ip                  add  esp,4
                                                 sub  ebp,offset get_ip

  Первый вариант является самым простым и распространенным, именно по этому он и несколько других, являются дополнительной эвристической сигнатурой у многих антивирусов. Естественно при написании вирусов, стараются использовать видоизмененные варианты этих процедур.

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

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

  Самый удобный метод определения процедур, это поиск по сигнатурам. Но здесь тот случай, когда явную сигнатуру выделить никак нельзя. В обеих процедурах постоянной остается одна инструкция – "CALL", точнее сказать только один ее байт (первый) - 0E8h. В этом случае попытаемся воспользоваться поиском сигнатур другого типа:

  Приведем пример простейшей реализации части анализатора кода, которая предназначена для выявления процедур подсчета delta value и регистра, содержащего это значение:

;
; ca_ip_stage    - номер этапа, на котором находится эта функция кодо-анализатора
; ca_ip_instr    - количество инструкций проверенных после вызова "CALL"
; ca_ip_call_off - смещение полученной после вызова "CALL"
;
.data
 ca_ip_stage            db ?
 ca_ip_instr            db ?
 ca_ip_call_off         dd ?
.code
;                                                                              
; начальные параметры : esi - смещение разбираемой инструкции
;
 ca_ip_srch     proc
;                                                                              
                push    eax ecx edx ebp edi esi
;               
; Работа процедуры делится на два этапа.
; После успешного завершения второго этапа, функция прекращает свою работу для текущего файла.
;
                cmp     byte ptr [ca_ip_stage],2
                jz      ca_ip_srch_ret
                cmp     byte ptr [ca_ip_stage],1
                jz      ca_ip_stage_2
;               
; Первый этап
;
; - Поиск инструкции CALL (0E8h)
; - Сохранение в "ca_ip_call_off" смещения, полученного в результате работы найденной инструкции "CALL".
; - ca_ip_stage = 1
;
 ca_ip_stage_1: lodsb
                cmp     al,0e8h
                jnz     ca_ip_srch_ret
                lodsd
                inc     byte ptr [ca_ip_stage]
                mov     dword ptr [ca_ip_call_off],esi
                jmp     ca_ip_srch_ret
;               
; Второй этап: первая часть
;
;  Проверка значений всех регистров (которые храняться в буфере "regs"), на наличие в них значения совпадающего
; с "ca_ip_call_off".
;  Ведется счетчик инструкций. Если, в течении разбора эмулятором четырех инструкций, ни один из регистров не содержит
; значения - заготовки для подсчета delta value. То начинаем с первого этапа - обнуляем "ca_ip_percent".
;
 ca_ip_stage_2: mov     esi,offset [regs]
                sub     ecx,ecx
                mov     cl,7+1
 ca_ip_srch_lp: lodsd
                cmp     dword ptr [ca_ip_call_off],eax
                jz      ca_ip_stage_3            ; перейдем ко второй части этапа
                loop    ca_ip_srch_lp
                inc     byte ptr [ca_ip_instr]
                cmp     byte ptr [ca_ip_instr],4
                jnz     ca_ip_srch_ret
                mov     word ptr [ca_ip_stage],0
                mov     dword ptr [ca_ip_call_off],0
                jmp     ca_ip_srch_ret
;               
; Второй этап: вторая часть
;
; - Рассчитаем номер регистра, который содержит delta_value и укажем это в таблице информации о регистрах ("_regs").
; - Присвоим значение 2 переменной "ca_ip_percent", это означает, что работа этой части кодо-анализатора прекращена.
;
 ca_ip_stage_3: inc     byte ptr [ca_ip_percent]
                sub     eax,eax
                mov     al,7+1
                sub     al,cl
                mov     byte ptr [eax+_regs],1   ; в eax номер регистра содержащего "заготовку" для delta value
;               
 ca_ip_srch_ret:pop     esi edi ebp edx ecx eax
                ret
 endp

  Получается, что анализатор – "заведует" информацией о типе значений, которые хранятся в каждом из регистров. В примере, для этого использовался буфер "_regs". Нормальная работа эмулятора без информации такого рода просто невозможна. Ниже я расскажу о том, как эмулятор использует эту информацию.

  В процессе работы, эмулятор «предоставляет» разбираемой программе "виртуальный стек". То есть программа будет использовать в своей работе стек расположенный в специальном буфере. Следовательно, если в ходе эмуляции инструкции будут обращаться по смещениям находящимся в стеке, каких либо изменений со стороны эмулятора не требуется. В данном случае при инициализации кодо-анализатора в буфере "_regs", для регистра esp ("regs+4") должна делаться специальная пометка типа регистра (см. выше).

 2.5. Полная имитация выполнения инструкций

  Допустим в цикле работы эмулятора, дизассемблер встретил инструкцию типа:

xor dword ptr [erega+eregb],eregc или xor dword ptr [erega+eregb],_dword_

  Рассмотрим процесс разбора параметров инструкции:

  Номера регистров:

       AX или AL - 000  =  0
       CX или CL - 001  =  1
       DX или DL - 010  =  2
       BX или BL - 011  =  3
       SP или AH - 100  =  4
       BP или CH - 101  =  5
       SI или DH - 110  =  6
       DI или BH - 111  =  7

  Для хранения количества регистров использующихся в указании смещения и номеров этих регистров отведем специальный буфер "dregs". Процесс сохранения параметров необходимых для полной имитации выполнения будет выглядеть примерно так:

  1. mov dword ptr [dregs+000],2 - количество регистров
  2. mov dword ptr [dregs+004],erega - номер регистра erega
  3. mov dword ptr [dregs+008],eregb - номер регистра eregb</</li>

  Рассмотрим участок кода эмулятора, который производит вычисление смещения использующегося в инструкции и последующую иммитацию выполнения:

.data
;
; dregs+000 - кол-во регистров использующихся в инструкции для указания смещения
; dregs+1*4 - номер первого регистра использующегося для ...
; dregs+2*4 - номер второго регистра ...
; dregs+n*4 - немер n-ого регистра ...
;
 dregs                 dd 010 dup (?)
  regs                 dd 008 dup (?)
.code
 @found_spec:   push    ebp
;
; настроим регистры:
;
;  ecx = dregs+000 - количество регистров использующихся в указании смещения 
;  ebx = по окончанию цикла, будет содержать смещение, которое содержалось в регистрах
;  ebp = 0 - если в инструкции не использовались регистры содержащие delta value или смещение в стеке
;  esi = смещение в буфере dregs
;
                sub     ebp,ebp
                sub     ebx,ebx
                mov     esi,offset dregs
                lodsd
                xchg    eax,ecx
 count_sum_lp:  lodsd
                cmp     byte ptr [eax+_regs],0 ; тип регистра не = 0, значит содержит вычисленное delta value
                jz      count_sum              ; или смещение в области стека, ...
                inc     ebp                    ; ... ebp = ebp + 1
 count_sum:     push    ecx edx
                sub     edx,edx
                mov     dl,4
                mul     edx
                pop     edx ecx
;
; буфер regs содержит значения регистров эмулируемой программы, добавим значение использующегося регистра в смещение.
;                
                add     ebx,dword ptr [regs+eax]
                loop    count_sum_lp
;
;  Далее необходимо рассчитать разницу между старым местоположением в файле и тем по которому инструкция
; расположена в антивирусном буфере. Но если в указании смещения участвовал регистр содержащий delta value или
; смещение в области "виртуального стека", подобные изменения будут лишними (смотри пункт 2.4).
; Если значение регистра EBP > 0, то изменений не требуется.
;
                or      ebp,ebp
                pop     ebp
                jnz     @spec_emul_01
                sub     ebx,старое местоположение кода в файле
                add     ebx,местоположение кода в антивирусном буфере
 @spec_emul_01: ...

  Далее следует:

  1. Проверка полученного смещения: смещение должно быть расположенно исключительно внутри антивирусного буфера или "виртуального стека", ко всем остальным "данным" исследуемая программа обращаться не может.
  2. Разбор третьего параметра инструкции mov eax,номер регистра eregc или mov eax,_dword_
    • Имитация исполнения инструкции: xor dword ptr [ebx],eax

 2.6. Изменение параметров работы эмулятора

  При запуске эмулятора, антивирус передает ему некоторые параметры, например:

  Рассмотрим часть анализатора кода, которая выполняется перед запуском эмулятора и изменяет размер участка данных, который необходимо выполнить (параметр номер 3). Зачем это нужно? Как я уже говорил, эмулятор антивируса может предназначаться только для имитации работы расшифровщиков (декрипторов). В этом случае основная и единственная его задача - расшифровать вирусное тело и сравнить с сигнатурами известных вирусов (информация о которых присутствует в базе). Как правило длина таких расшифровщиков несколько сотен байт, однако существуют различные утилиты, которые позволяют упаковать или зашифровать код вирусных дропперов или программ зараженных вирусами. Такими утилитами, часто пользуются, когда распространяют вирусы (особенно те, в которых отсутствуют технологии полиморфизма или шифровки - черви или троянские кони), в надежде на то, что антивирусы не смогут обнаружить вирусы в таких файлах. Подобные утилиты прикрепляют к "обработанным" файлам достаточно большие и комплексные декрипторы. Если эмулятор антивируса, способен имитировать работу декрипторов создаваемых, той или иной утилитой, то при их «встрече», размер эмулируемого кода должен равняться максимальному размеру декрипторов создаваемых этой утилитой. Очень часто, декрипторы содержат множество сигнатур, которые и могут являться критерием встречи с "продуктом" какой либо утилиты.

  Пример процедуры входящей в состав кодо-анализатора, назначением которой является определение в коде наличие декриптора, создаваемого утилитой UPX:

;                                                                              
; структура записей полностью аналогична структуре вирусной базы, для поиска сигнатуры используется
; функция описанная (выше)
;
.data
 pck_no                 dd 1                             ; количество записей в базе
_001_type               db etype_pe                      ; запись для PE-файлов
_001_name               db 'UPX'                         ; название утилиты - UPX
                        db 022 dup (0)
_001_sig_len            db 006                           ; длина сигнатуры 6 байт
_001_dec_len            dw 1000                          ; максимальная длина декриптора ;)
_001_sig_data           db 060h,0BEh, '?',004d,08Dh,0BEh ; сигнатура
                        db 024 dup (0)
_001_cure_loc           dd 0                             ; это поле не используется, возможно любое значение
.code
;                                                                              
; det_pack - проверка на упаковщики и шифровщики
;
; on exit  :  ebx - длина кода, которую необходимо разобрать для "распаковки" файла
;
 det_pack       proc
;                                                                              
                push    eax ecx edx ebp edi esi
;               
                push    offset pck_no
                push    смещение точки входа в код
                call    detect
                or      eax,eax
                jz      det_pack_ret
;               
                dec     eax
                mov     edx,( (_001_cure_loc+1) - _001_type )
                mul     edx
                add     eax,offset _001_name             ; eax - смещение на имя

                выведем информацию о том, что файл упакован (зашифрован) утилитой ...

                sub     ebx,ebx
                mov     bx,word ptr [_001_type+e_type+e_name+e_slen]
 det_pack_ret:  pop     esi edi ebp edx ecx eax
                ret
 endp                                                                              



2004

Содержание

HOME


Если у вас есть сайт или домашняя страничка - поддержите пожайлуста наш ресурс, поставьте себе кнопочку, скопировав этот код:

<a href="http://kiev-security.org.ua" title="Самый большой объем в сети онлайн инф-ции по безопасности на rus" target="_blank"><img src="http://kiev-security.org.ua/88x31.gif" width="88" height="31" border="0" alt="security,безопасность,библиотека"></a>

Идея проекта(C)Anton Morozov, Kiev, Ukraine, 1999-2022,