BreackPoints by Nox

  • marzo 11, 2019

Punto de quiebre

Los breakpoints o puntos de ruptura, tienen la habilidad de generar una interrupción que parará el proceso mientras este está siendo depurado.
Parado el proceso se puede inspeccionar la pila, los registros, regiones de memoria, es decir toda la información que tenga que ver con el proceso depurado.

 

Existen tres principales tipos de breakpoint: los BreakPoint por software (los de toda la vida, también conocidos como BP o BPX), Memory BreakPoint (abreviado como BPM) y los Hardware BreakPoint (abreviado como HBP).
Software BreakPoint

El breakpoint por software es el más común y usado a la vez. Para establecer un  breakpoint por software, se cambia el primer byte de la instrucción, remplazandolo por el código de operación de la instrucción INT3.

Ejemplo:
mov eax, ebx

La instrucción es simple, veamos los opcodes:
8BC3

La dirección donde se encuentra la instrucción, es está:
0B4DC0DE 8BC3 mov eax, ebx

Estableciendo un breakpoint:
0B4DC0DE 8BC3 mov eax, ebx

Después de establecer el breakpoint:
0B4DC0DE CCC3 mov eax, ebx

Un debugger establece en la dirección requerida un breakpoint cambiado el primer byte del opcode de la instrucción, por el opcode 0xCC, que no es nada menos que la instrucción INT3. Cuando la CPU llega a la instrucción donde hemos cambiado el primer byte del opcode a 0xCC, se dispara el evento INT3 de esa manera se generaría un punto de interrupción (se produce una excepción, misma que es manejada por el depurador) y del debugger pausaría la depuración.

El debugger no desarma la instrucción al establecer un breakpoint, que sería lo lógico, ya que no equivaldría los opcodes a la instrucción real, por cuestiones de estética la gran mayoría conserva la misma instrucción, después de establecer un breakpoint.

Los debuggers tienen internamente un breakpoint list, cuando se quita un breakpoint, el debugger busca la dirección en su lista, cuando encuentra la dirección, restaura el byte modificado, dejando integra la instrucción. Si nosotros queremos tracear por encima de un breakpoint que ha sido establecido en una instrucción válida, el debugger reconstruirá la instrucción original restaurando el primer byte del opcode que cambió, todo esto es transparente para el usuario.

Existen tres “tipos” de breakpoint por software.

  • One-Shot BreakPoint.
  • BreakPoints persistentes o continuos.
  • BreakPoints Condicionales y/o Logueo.

Los One-Shot Breakpoint, genera un único punto de interrupción, para luego ser removidos y restaurar el byte modificado cuando ha cumplido su cometido.

En los Breakpoint persistentes, la única manera de ser removidos es de manera “manual” (entiéndase “manual” a la opción que da el debugger de remover el breakpoint).

Los Breakpoint condicionales y/o Logueo, permiten establecer una condición de pausa; espera un valor específico de acuerdo a la condición establecida en el breakpoint. Cuando el CPU llega hacia el breakpoint condicional establecido, el debugger compara el valor obtenido según la condición y que esta se cumpla con todas las condiciones, si no es el valor esperado el debugger continua la ejecución, pero si el valor es el esperado, pausa la depuración.

Otra característica, es permitir el logueo de datos previamente fijados, sin necesidad de que el debugger pause la depuración ya que el único objetivo es obtener información concreta.

También podemos usar estas dos características juntas, que loguee información y a la vez esperar una condicional (muchas veces en relación a la información que se loguea) para pausar la depuración.

Si el debugger lo permite, se puede definir el número de veces que el CPU puede pasar por el breakpoint y se cumpla la condición (es importante que la condicional se cumpla sólo así será contabilizada como una pasada válida), para pausar la depuración una vez llegado al límite de pasadas.

NOTA:Se debe recordar que los breakpoint condicionales es una característica implementado por el debugger y cada debugger tiene una propia sintaxis para establecer dichos breakpoints. Los debuggers que conozco, que permiten breakpoint condicionales son, OllyDBG, WinDBG e IDA.

Fuente: http://web.archive.org/web/20130218103240/http://www.noxsoft.net/2013/01/breakpoint-parte-1/

Parte2:

BreakPoints
Memory BreakPoint

Una de las ventajas de este tipo de breakpoint es que puedes establecer un Memory Breakpoint a un rango de direcciones, gracias a eso, podemos saber que gran porción de bytes están siendo escritos, leídos, accedidos y/o ejecutados.

A diferencia del Breakpoint por software, un Memory Breakpoint no cambia un byte de alguna instrucción de una determinada dirección, sino que cambian los permisos de una página o páginas de memoria.

Cuando uno reserva (allocate) una página o región de memoria, se le asigna los permisos correspondientes según se necesite. De la misma manera se cambian los permisos a las páginas de memoria para efectos de establecer un Memory BreakPoint, y pueden ser los siguientes:

  • PAGE_EXECUTION: Permite la ejecución, pero puede causar una excepción de access violation si se intenta leer o escribir en la página o región de memoria.
  • PAGE_READ: Permite solo la lectura en la página o región de memoria, pero puede causar una excepción de access violation si se intenta ejecutar o escribir.
  • PAGE_WRITE: Permite solamente escribir en la página o región de memoria.
  • PAGE_GUARD: Las páginas en la región se convierten en guard page.

El uso satisfactorio de los Memory Breakpoint, es la combinación de los permisos sobre las regiones de memoria.

Por ejemplo, si tú quieres que pare por escritura y de acceso, se le establece el flag PAGE_READ, es decir, se le da permisos de solo lectura, y cuando quiere ejecutar o escribir se producirá una excepción tipo access violation, es aquí donde entra a tallar el flag PAGE_GUARD.

La excepción de access violation puede terminar la ejecución de la aplicación, pero al usar PAGE_GUARD, por así decirlo, “protege” la página, cuando se accede a la región de memoria este manda una notificación al debugger, OllyDBG lo controla nativamente, cosa muy contraria a la de IDA que no tiene esta característica nativa.

Recordemos que al cambiar los permisos en memoria, estamos cambiando como mínimo el tamaño de una página de memoria (4026 bytes o 0×1000 bytes o 4KB), dicho esto, ¿qué pasaría si deseo sólo establecer un Memory Breakpoint menor a 4026 bytes?, el debugger es el encargado de gestionar ese tipo de inconveniente.

Podemos interpretarlo con un ejemplo para que sea más entendible.

Establezco un Memory BreakPoint on Execution desde la dirección 0×1000 hasta la 0×1100,  pero lo que en realidad está haciendo, es cambiar los permisos de una página completa, desde 0×1000 hasta 0×2000. Luego de eso corremos el debugger y en determinado momento el CPU ejecuta la instrucción de la dirección 0×1101, ¿qué pasaría?, como dije antes, el debugger se encarga de gestionar este inconveniente y lo hace totalmente transparente al usuario. El debugger comprueba si la dirección que se está ejecutando está dentro del rango especificado por el usuario, si no lo está, restaura los permisos originales para seguir con la ejecución, vuelve a poner el Memory Breakpoint establecido por el usuario y verifica en la siguiente pausa, si la instrucción a ejecutar está dentro del rango establecido (esta pausa y verificación mencionadas, son transparentes al usuario), si lo está, pausa la ejecución.

Existe una herramienta que fue concebida para establecer Memory BreakPoints cuando un proceso está siendo depurado y el debugger no puede establecer alguno de manera nativa, la creo un gran amigo de la lista, Guan de Dio, y la pueden encontrar
http://web.archive.org/web/20130218103415/http://guandedio.no-ip.org/herramientas/descarga/92-bpm.html

así que si desean poner algún Memory BreakPoint, ver como cambia los permisos de las páginas de memoria, los invito a usarla 🙂
Nota 2019: aquí la versión x64
http://www.ricardonarvaja.info/WEB/CONCURSOS%202011/CONCURSO%209/Concurso%209%202011%2C%20PESPING%2064%20by%20GUAN%20DE%20DIO.rar

Fuente:http://web.archive.org/web/20130218103415/www.noxsoft.net/2013/01/breakpoints-parte-ii-de-iii/

Parte3:

BreakPoints
Hardware BreakPoint

Los Hardware Breakpoints son útiles cuando queremos usar pequeños puntos de ruptura, y no queremos que el código del software que está siendo depurado sea modificado.

Si el software tiene algún tipo de comprobación de integridad, CRC, checksum, etc, los Hardware Breakpoints no cambiaran el resultado final pasando desapercibidos por estos algoritmos, diferente a los Software Breakpoints y los Memory Breakpoint que si pueden ser detectados.

Este tipo de breakpoint se establece a nivel del CPU, en los registros especiales llamados registros de depuración (debug registers).
Registros de Depuración (debug registers)

A partir del procesador 386 fueron añadidos en la arquitectura 8 registros de depuración, DR0, DR1, DR2, DR3, DR4, DR5, DR6, DR7.

Estos registros se pueden leer y escribir usando la instrucción “move destino/fuente”. Es decir que puede ser la fuente o destino de algunas instrucciones.

Los registros de depuración son de recursos privilegiados. Es decir una instrucción MOV que accede a estos registros, se puede ejecutar en modo real (también llamado modo de dirección real en los manuales de intel).

La principal función de los registros de depuración es la de establecer y controlar 4 puntos de ruptura desde los registros DR0 hasta DR3.

Para cada breakpoint, se puede especificar la siguiente información:

  • La dirección lineal donde el breakpoint va a ocurrir.
  • La longitud de la ubicación del breakpoint (1, 2 o 4 bytes).
  • La operación que debe realizarse en la dirección de la excepción de depuración que será generado.
  • Si el breakpoint está habilitado.
  • Si la condición del breakpoint estaba presente cuando la excepción de depuración fue generada.

Debug Register
Registros de Depuración de Direcciones (DR0 – DR3)

Cada uno de los registros de depuración son de 32bits (Figura Debug Register), estos cuatro primeros registros (DR0 – DR3) contienen cuatro posibles direcciones de memoria donde se establece el breakpoint. El contenido del registro de depuración DR7 (Control Debug Register), especifica las condición del punto de ruptura.
Registros de Depuración DR4 y DR5

Los registros de depuración DR4 y DR5 son reservados, cuando las extensiones de depuración están habilitadas (cuando la bandera DE (Debug Extensions) en el registro de control CR4 está activado) y al tratar de hacer referencia los registros DR4 y DR5 causan una excepción de tipo invalid-opcode.

Cuando no está habilitado (es decir el flag DE está limpio), estos registros son para depurar los registros DR6 y DR7.

Nota:
Aunque los registros DR4 y DR5 están documentados como reservados, generaciones previas de procesadores “hacen referencia” a estos registros para depurar a los registros DR6 y DR7, respectivamente.
Registro de Depuración de Estado (DR6)

El registro de depuración (DR6), registra las condiciones de depuración en el momento que se generó la última excepción. Las actualizaciones de este registro sólo se produce cuando se genera una excepción .

Flags (bits 0 al 3) B0 al B3 (condición del breakpoint detectado), indican cuando son establecidos, la condición de cada breakpoint asociado, la longitud del breakpoint (1, 2 o 4 bytes) y si es de lectura, escritura o ejecución. Si el registro de depuración DR7 está establecido.

Flag (bit 13) BD (accesos detectados de los registros de depuración),   Indica que la instrucción siguiente en la secuencia de la instrucción accede a uno de los registros de depuración.

Flag (bit 14) BS (single step), indica (cuando se establece), que la excepción de depuración fue provocada por el modo de ejecución single-step (cuando es activado el flag T -Trap Flag- del registro EFLAGS).

Flag (bit 15) BT (cambio de tarea), indica (cuando esta establecido) que la excepción de depuración fue fruto de un cambio de tarea en la que el flag T (debug trap flag) en el TSS (segmento de estado de tarea) de el objetivo fue establecido.
Registro de Depuración de Control (DR7)

El registro de depuración de control (DR7), activa o desactiva los puntos de interrupción y establece las condiciones de los breakpoints. Los flags y campos en el registro de depuración de control, son los siguientes:

Flags (bits 0, 2, 4, y 6) L0 al L3 (habilitación de punto de ruptura local), se activa (cuando se establece) la condición de punto de interrupción para el breakpoint asociado para la tarea actual. Cuando una condición de punto de interrupción se detecta y su flag Ln está asociada,  se genera una excepción de depuración. El procesador borra automáticamente estas banderas/indicadores en cada cambio de tarea para evitar condiciones indeseables en los puntos de interrupción de la nueva tarea.

Flags (bits 1, 3, 5, y 7) G0 al G3 (habilitación de punto de ruptura global), se activa (cuando se establece) la condición del punto de interrupción para el punto de interrupción asociado a todas las tareas. Cuando una condición de punto de interrupción es detectado y el flag Gn asociado está activado, se genera una exception de depuracion

Flags (bits 8 y 9) LE y GE (habilitación de punto de ruptura exacto local y global), esta característica no se admite en los procesadores de la familia P6, procesadores posteriores IA-32 y los procesadores Intel 64. Cuando está activa, estas flags causan que el procesador detecte la instrucción exacta que causó una condición de punto de interrupción de datos.

Flag (bit 13) GD (habilitación general de detección), activa (cuando se establece) la protección en los registros de depuración, lo que causa que se genera una excepción de depuración ante cualquier instrucción MOV que accede a un registro de depuración.

Campos (bits 16, 17, 20, 21, 24, 25, 28, y 29) R/W0 al R/W3 (lectura/escritura), especifican la condición del punto de interrupción correspondiente para el breakpoint.  Cuando el flag DE  (debug extension) se establece, el procesador interpreta los bits de la siguiente manera:

  • 00 – Interrumpe (break) solamente cuando se ejecuta una instrucción .
  • 01 – Interrumpe (break) solamente cuando se escribe datos.
  • 10 – Interrumpe (break) cuando se lee o escribe en I/O.
  • 11 – Interrumpe (break) cuando lee o escribe datos, pero no encuentra la instrucción.

Cuando el flag DE está limpio, el procesador interpreta los bits R/Wn lo mismo para los procesadores  Intel386TM y Intel486TM de la siguiente manera:

  • 00 – Interrumpe (break) solamente cuando se ejecuta una instrucción.
  • 01 – Interrumpe (break) solamente cuando se escribe datos.
  • 10 – Sin definir.
  • 11 – Interrumpe (break) cuando lee o escribe datos, pero no encuentra la instrucción.

Campos (bits 18, 19, 22, 23, 26, 27, 30, y 31) EN0 al LEN3 (longitud), especifican el tamaño de la posición de memoria en la dirección indicada en el punto de interrupción correspondiente registros de depuración de direcciones (DR0 al DR3). Estos campos se interpretan de la siguiente manera:

  • 00 – 1byte de longitud.
  • 01 – 2bytes de longitud.
  • 10 – Sin definir (8bytes de longitud).
  • 11 – 4bytes de longitud.

Estableciendo Hardware Breakpoints

Para establecer los Hardware Breakpoints desde user-land necesitamos establecer un nuevo contexto al hilo correspondiente usando la estructura Context
http://web.archive.org/web/20130224012722/http://msdn.microsoft.com/en-us/library/windows/desktop/ms679284(v=vs.85).aspx

/*CONTEXT _X86_*/

typedef struct _CONTEXT {
DWORD ContextFlags;

DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;

FLOATING_SAVE_AREA FloatSave;

DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;

DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;

BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;

Usando los campos Dr0 al Dr3 y Dr7, podremos establecer sin ningún problemas breakpoints por hardware. En los registros de depuración de direcciones (DR0 al DR3) establecemos las direcciones dónde se comprobará la condición – estableciendo el registro DR7 con los valores correspondientes – para generar el punto de ruptura.

Podríamos mencionar un pequeño ejemplo, estamos creando un Loader Debugger y necesitamos usar los Hardware Breakpoints, queremos que se genere un punto de interrupción cuando se está intentando escribir en la dirección 0x0040C004 – perteneciente a la sección de datos -. Primero debemos pedir el contexto actual del hilo usando la API
GetThreadContext , para así preservar los registros y modificar sólo los indicados.
http://web.archive.org/web/20130224012722/http://msdn.microsoft.com/en-us/library/windows/desktop/ms679362(v=vs.85).aspx

Deseamos poner un sólo Hardware Breakpoint On Write, y elegimos el registro de depuración de dirección DR0, en el campo de la estructura CONTEXT establecemos la dirección y la condición OnWrite usando como base la explicación el subapartado Registro de Depuración de Control (DR7), y la información de la imagen Debug Register, estableciendo los siguientes valores:

Dirección: 0x40C004
Size: 4Bytes.
Condición: OnWrite.
Valor: 0xD0501

Si hacemos la conversión a binario del valor del registro de control DR7 (0xD0501), comprobaremos los flags que estamos usando para establecer la condición.

Posición:
----------|19|18|17|16|15|14|13|12|11|10|09|08|07|06|05|04|03|02|01|00|
----------|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|-
Valor:    | 1| 1| 0| 1| 0| 0| 0| 0| 0| 1| 0| 1| 0| 0| 0| 0| 0| 0| 0| 1|

  • Posición 0: Habilita el puntero de ruptura local del registro de depuración DR0.
  • Posición 8: Habilita el puntero de ruptura exacto local.
  • Posición 10: Reservado, siempre está activado.
  • Posición 17 y 16: Pausará (break) solamente cuando se escribe datos.
  • Posición 19 y 18: Longitud de 4 Bytes.

Después de establecer los datos correspondientes a la estructura CONTEXT, se estable el contexto del hilo usando la API SetThreadContext
[url=http://web.archive.org/web/20130224012722/http://msdn.microsoft.com/en-us/library/windows/desktop/ms680632(v=vs.85).aspx].

Finalmente debo mencionar la herramienta de un amigo de CracksLatinoS, Drx Calculator
http://code.google.com/p/drx-calculator/ programada por NCR/CRC! [ReVeRsEr]
https://twitter.com/crackinglandia , la cual tiene la función de calcular el valor del registro de depuración de control DR7 teniendo en cuenta el tipo de condición, la longitud y la cantidad de breakpoints que deseas establecer (el máximo es 4), te recomiendo que la uses y te pongas a jugar un rato con ella, es más, si planeas crear un Loader Debugger y deseas usar los Hardware Breakpoints, te serán de gran ayuda.

Nota:
Si desean más información acerca de los hardware breakpoint, les recomiendo la documentación de Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3A: System Programming Guide, Part 1, la página 675 en adelante, donde explican más a fondo este tipo de breakpoint, y es de ahí donde fueron tomados los detalles técnicos para esta entrada.

Agradecimientos, Longinos, un gran reverser me sorprende la manera en como avanza en conocimiento en unos cuantos meses yo seré quién pregunte seguro 🙂 , y a Apo mi gran amigo, gran persona en todas sus letras, a los dos por leer mi escrito o parte de el, y darme sus opiniones antes de publicarlo 🙂

Autor: Nox

Apuromafo.net

E-mail : apuromafo@apuromafo.net

    Soy una persona normal, con ánimos de aprender como ustedes, espero se animen a ver mis cursos

    One Comment

    Envía un comentario