Anti anti-debug
Desde hace años siempre me han interesado las tecnicas anti-debug ya que en muchos casos suelen ser bastante ingeniosas en si mismas, o bien tiene que serlo el metodo para saltarselas. A continuacion os comento una que vi hace algun tiempo, muy sencilla de implementar y tambien una solucion para evitar la misma, si acaso alguien necesita depurar algun malware que haga uso de ella 🙂
La tecnica anti-debug es muy sencilla (seguro que la mayoria ya la habeis visto alguna vez) y simplemente consiste en borrar los registros de depuracion dr0, dr1, dr2 y dr3, de modo que depuradores de modo nucleo como SoftIce vean afectado su funcionamiento. Para hacer esto, es necesario que el malware en cuestion se ejecute en ring0:
xor eax, eax
mov dr0, eax
mov dr1, eax
mov dr2, eax
mov dr3, eax
¿Como podriamos implementar una solucion para evitar este truco molesto? Lo que explico a continuacion es una idea que podria ser aplicada por alguien que este desarrollando su propio debugger de modo nucleo y quiera implementarle algunos trucos anti-antidebug. En su dia, yo lo implemente para que funcionara con el SoftIce, pero dado que tuve que utilizar algunos trucos muy guarretes y ya que lo importante es la idea (aparte de que el SoftIce va a desaparecer) solo pongo las partes basicas de lo que hice, modificando aquello que tenia que ver con el SoftIce. Hace falta un poquito mas de trabajo para que lo siguiente funcione perfectamente.
En la arquitectura IA32, el registro DR7 (Debug Control Register) se puede utilizar para controlar determinados aspectos de los breakpoint establecidos. En concreto, si activamos el flag 13 de dicho registro, saltara una excepcion de depuracion cada vez que una instruccion intente modificar alguno de los registros de depuracion. De hecho, la excepcion salta JUSTO ANTES de que se ejecute la instruccion en cuestion, dandonos el poder para evitar que la misma llegue a ejecutarse y saltandonos asi esta tecnica anti-debug.
Para poder identificar que la excepcion de depuracion fue causada por el acceso a un registro de depuracion, el procesador activara el flag BD (tambien es el 13) del registro DR6 antes de invocar al manejador de excepciones de depuracion. Sabiendo esto, los pasos que podemos tomar son los siguientes:
1) Hookear la interrupcion 1 para capturar las excepciones de depuracion:
SIDT baseidt
mov eax, dword ptr [baseidt + 2]
add eax, 8 ;entrada de la interrupcion 1
mov edi, dword ptr [eax + 4]
mov di, word ptr [eax] ;edi = manejador instalado actualmente para la int 1
mov [OldInt1], edi ;guardamos el manejador antiguo
mov edi, offset OurExceptionHandler
mov word ptr [eax], di
shr edi, 16
mov word ptr [eax + 6], di ;acabamos de instalar nuestro manejador de excepciones de depuracion
mov eax, dr7
or eax, 2000h ;activamos el flag #bd (13)
mov dr7, eax ;del registro dr7
2) Programar nuestro manejador de excepciones de depuracion:
OurExceptionHandler proc
pushad
mov ax, 30h
mov fs, ax
assume fs:nothing
mov ebx, dword ptr [eax + 28h] ;Estos tres pasos los copie de la funcion GetThreadContext
mov ebx, dword ptr [eax + 18h] ;del ntoskrnl.exe (Windows 2000). Obtendremos en eax un ;puntero a la estructura de contexto del thread que intento ;modificar un registro de depuracion
assume ebx : ptr CONTEXT
mov eax, dr6
and eax, 2000h
jz epilog ;Si no esta activado el flag 13, tenemos un falsa alarma
cmp [PermitedDRException], 1 ;Podemos utilizar una variable global para permitir a nuestro
jz epilogPermitedDRExceptoin ;depurador manipular los registros de depuracion
mov ecx, [ebx].regEip
add ecx, 3 ;las instruccion de tipo «mov drx, xxx» constan siempre de 3 bytes,
mov [ebx].regEip, ecx ;asi que sumandole 3 a eip, pasaremos por encima de dicha instruccion
epilogPermitedDRExceptoin:
mov [WantedException], 0
mov eax, dr6
and eax, 0FFFFDFFFh
mov dr6, eax ;limpiamos el flag 13 (bd) del registro DR6
mov eax, dr7
or eax, 2000h ;activamos el flag bd del dr7 ya que el procesador lo limpia antes
mov dr7, eax ;de invocar al manejador de excepciones. Si no fuese asi, al intentar
;acceder a un reg de depuracion, nuestro manejador comenzaria a
;invocarse recursivamente
epilog:
mov ax, 10h
mov fs, ax
popad
push [OldInt1] ;para terminar, le pasamos el control al manejador de excepciones
ret ;original
OurExceptionHandler endp
Bueno, espero que se haya entendido la idea, aunque el codigo en si podria estar mejor, repito que lo importante para mi era explicar la idea y recuperar algunas cosillas que de otro modo se ivan a pudrir en un disco duro o en un viejo cd que utilice para mis copias de seguridad (previo pago del canon a los inutiles parasitos sociales maleducados y malparidos de la SGAE) aunque en privado me gusta llamarles de otra manera, lastima que este sea un blog decente.