| ESTUDIO COLECTIVO DE DESPROTECCIONES Última Actualizacion: 25/10/2001 |
Programa | [ SEH - Y LAS EXCEPCIONES DE DEPURACIÓN] | W95 / W98 / NT
Descripción | Structured Exception Handling (Manejo Estructurado de Excepciones)
Tipo |
Tipo de Tutorial | [X]Original, []Adaptación, []Aplicación, []Traducción
Url |
Protección |
Dificultad | 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas |
Objetivo | Cómo implementar puntos de ruptura por hardware
Cracker | Mr.Silver [WkT!]
Grupo | http://www.whiskeykontekila.org
Fecha | 9 de Septiembre de 2001
Cómo implementar puntos de ruptura por hardware
Mediante el uso de la técnica descrita anteriormente podriamos crear un sistema de puntos de ruptura sobre memória aunque ciertamente los inconvenientes de VirtualProtect hacen esta tarea algo engorrosa. Veamos ahora otra alternativa para implementar puntos de ruptura usando un manejador SEH. Hemos visto que tenemos una estructura con el contexto actual del thread, en su interior se encuentran los registros de depuración (solo a partir de Pentium o superior), podemos usar estos registros para establecer un punto de ruptura por hardware sobre cualquier dirección donde se ejecute una instrucción o donde se lea/escriba memória, esto es debido a que los registros de depuración son capazes de provocar excepciones (0x80000004L) de depuración que pueden ser controladas por un manejador SEH. Para ello necesitamos saber como funcionan los registros de depuración. A partir del procesador 386 los micros siguientes constan de 6 registros de depuración, DR0, DR1, DR2, DR3, DR6, DR7 los registros DR4 y DR5 estan reservados por Intel y no se utilizan.Los cuatro primeros registros contienen 4 posibles direcciones de memoria donde se establece el punto de ruptura, el registro DR7 (Control Debug Register) controla el tipo de punto de ruptura para cada uno de esos puntos de ruptura (DR0 a DR3) y el registro DR6 ( Debug Status Register) controla el tipo de punto de ruptura que se activó por ultima vez. Veamos El formato de los registros de depuración: Extraido del documento: Intel Architecture Software Developer’s Manual Volume 3: System Programming DR7: El registro de control de depuración (DR7) activa o desactiva puntos de ruptura y establece las condiciones que deberán cumplirse para que tenga lugar la excepción. El significado de cada bit para este registro es como sigue: L0 hasta L3 (activar punto de ruptura local) flags (bits 0, 2, 4, and 6) Activa (si estan establecidos) la condición de ruptura para el punto de ruptura asociado para la tarea actual. Cuando se detecta una condición y su flag Ln está activo, se genera una excepción de depuración. El procesador automáticamente borra estos flags en cada cambio de tarea para evitar rupturas no deseadas en las nuevas tareas. G0 hasta G3 (activar punto de ruptura global) flags (bits 1, 3, 5, and 7) Activa (si estan establecidos) la condición de ruptura para el punto de ruptura asociado para todas las tareas. Cuando una condición de ruptura es detectada y su flag Gn asociado está activo, se genera una excepción de depuración.El procesador no borra estos flags al cambiar de tarea, permitiendo que el punto de ruptura se active en otras tareas. LE y GE (activar punto de ruptura exacto local y global) flags (bits 8 y 9) (No soportado en la familia de procesadores P6.) Cuando estan activos, estos flags hacen que el procesador detecte la instrucción concreta que causó la condición de ruptura para datos. Para compatibilidad con anteriores y posteriores arquitecturas de procesador, Intel recomienda que se pongan estos flags a 1 si se requieren puntos de ruptura exactos. GD (activar detección general) flag (bit 13) Activa (cuando está establecido) la protección de los registros de depuración, la cual causa una excepción de depuración que se genera antes de la instrucción MOV que accedió al registro de depuración. Cuando se detectan este tipo de condiciones, el flag BD en el registro de estado DR6 se establece antes de generar la excepción.El procesador borra el flag GD una vez se ha entrado en el manejador de excepciones, para permitirle a este el acceso a los registros de depuración. R/W0 hasta R/W3 (lectura/escritura) campos (bits 16, 17, 20, 21, 24, 25, 28, y 29): Especifica la condición de ruptura para el correspondiente punto de ruptura. El flag DE (debug extensions) en el registro de control CR4 determina como son interpretados los bits en los campos R/Wn.Cuando el flag DE esta activo, el procesador interpreta estos bits de la siguiente manera. 00—Parar sólo si se ejecutan instrucciones. 01—Parar solo en accesos de escritura a datos. 10—Parar en accesos de escritura o lectura de E/S. 11—Parar en accesos de lectura o escritura a datos, pero no en ejecución. Cuando el flag DE esta borrado, el procesador interpreta los bits de R/Wn de la misma manera que para los procesadores Intel386™ y Intel486™, esto es de la siguiente manera: 00—Parar sólo si se ejecutan instrucciones. 01—Parar solo en accesos de escritura a datos. 10—Sin definir. 11—Parar en accesos de lectura o escritura a datos, pero no en ejecución. LEN0 hasta LEN3 (Longitud) campos (bits 18, 19, 22, 23, 26, 27, 30, y 31): Especifica el tamaño de la posición de memoria especificada en la dirección del correspondiente registro (DR0 hasta DR3) Los bits son como sigue: 00—1-byte de longitud 01—2-bytes de longitud 10—Sin definir 11—4-bytes de longitud. Si el campo correspondiente RWn en el registro DR7 es 00 (ejecución de instrucción), entonces el campo LENn deberia ser 0. El efecto de usar cualquier otra longitud es indefinido. DR6: El registro de estado de depuración DR6, informa sobre las condiciones que fueron muestreadas cuando tuvo lugar la última excepción de depuración, este registro sólo se actualiza cuando se genera una excepción. bits B0 hasta B3 (condición de ruptura detectada) Indican (cuando estan activos) que la condición de ruptura asociada se cumple cuando se generó la excepción de depuración. Estos flags se establecen si la condición descrita para cada uno de los puntos de ruptura por el campo LENn, y R/W del registro DR7 es cierta. Estos bits se establecen incluso si el punto de ruptura no se activó por los flags Ln y Gn del registro DR7. BD (acceso a los registros de depuración) flag (bit 13): Indica que la siguiente instrucción accederá a uno de los registros de depuración (DR0 a DR7). Este flags se activa cuando el flag GD (general detect) en el registro DR7 esta activo. BS (trazado paso a paso) flag (bit 14) Indica (cuando está activo) que la excepción de depuración fue activada por el modo de trazado paso a paso (activado mediante el flag TF en el registro EFLAGS). El trazado paso a paso es la excepción de depuración con mayor prioridad. Cuando el flag BS esta activo, cualquier otro bit del registro de estado puede ser establecido. BT (cambio de tarea) flag (bit 15) Indica (cuando está activo) que la excepción de depuración resultó de un cambio de tarea donde el flag T (debug trap flag) en el TSS (Task-State Segment) de la tarea objetivo fue establecido.(Para más info sobre el TSS referirse a la sección 6.2.1. del Intel Architecture Software Developer’s Manual Volume 3: System Programming). No hay ningún flag en el registro DR7 que permita activar o desactivar esta excepción; el flag T del TSS es sólo un flag de activación. Como podemos ver básicamente necesitamos el registro DR7 y los registros de direcciones DR0 a DR3, el uso del registro DR6 es opcional y solo se usa si desea tener un control mas preciso sobre los puntos de ruptura, en este tutorial sólo pretendo mostrar las capacidades básicas de los registros de depuración, para aquellos que deseen profundizar más les recomiendo que consulten el PDF Intel Architecture Software Developer’s Manual Volume 3: System Programming. Bien, como ya muchos sabrán el acceso directo a los registros de depuración es una tarea que solo esta permitida en Ring0, vamos a ver ahora un método para acceder a ellos desde Ring3 aunque no sea directamente el método nos resuelve la papeleta. Se basa en el uso de las APIs GetThreadContext y SetThreadContext: BOOL SetThreadContext( HANDLE hThread, // handle del thread CONST CONTEXT *lpContext // dirección donde se leera la nueva info del contexto ); BOOL GetThreadContext( HANDLE hThread, // handle del thread LPCONTEXT lpContext // dirección donde almacenar la info del contexto ); Si estamos bajo NT, el handle al thread deberá tener privilegios de acceso de tipo THREAD_SET_CONTEXT. Alternativamente podemos usar GetCurrentThreadContext o SetCurrentThreadContext si lo que pretendemos es alterar el contexto del thread actual. Como podemos apreciar los contextos son propios para cada thread, es decir cada thread tiene su contexto y Windows se encarga de modificar este contexto cada vez que se cambia de thread (llamemoslo tarea), por lo que un proceso puede tener multiples threads con un contexto por cada thread.La clave del asunto está en los bits del registro DR7, dependiendo de los bits que activemos podremos lograr un punto de ruptura que tendrá lugar sobre la dirección indicada en el registro de depuración DR0 a DR3. Un ejemplo, viendo el significado de los bits de DR7 podemos deducir el valor adecuado de DR7 para poner un breakpoint sobre la dirección en el registro DR0: Estas serian las mascaras correspondientes para cada uno de los registros (DR0 a DR3): DR0_LOCAL_EXACT_BPM_ENABLED equ 1b DR0_GLOBAL_EXACT_BPM_ENABLED equ 10b DR0_W equ 010000000000000000b DR0_IO equ 100000000000000000b DR0_RW equ 110000000000000000b DR0_EXECUTION equ 0b DR0_LEN1 equ 0b DR0_LEN2 equ 01000000000000000000b DR0_LEN4 equ 11000000000000000000b DR0_LENU equ 10000000000000000000b DR1_LOCAL_EXACT_BPM_ENABLED equ 100b DR1_GLOBAL_EXACT_BPM_ENABLED equ 1000b DR1_W equ 0100000000000000000000b DR1_IO equ 1000000000000000000000b DR1_RW equ 1100000000000000000000b DR1_EXECUTION equ 0b DR1_LEN1 equ 0b DR1_LEN2 equ 01000000000000000000000000b DR1_LEN4 equ 11000000000000000000000000b DR1_LENU equ 10000000000000000000000000b DR2_LOCAL_EXACT_BPM_ENABLED equ 10000b DR2_GLOBAL_EXACT_BPM_ENABLED equ 100000b DR2_W equ 01000000000000000000000000b DR2_IO equ 10000000000000000000000000b DR2_RW equ 11000000000000000000000000b DR2_EXECUTION equ 0b DR2_LEN1 equ 0b DR2_LEN2 equ 01000000000000000000000000000000b DR2_LEN4 equ 11000000000000000000000000000000b DR2_LENU equ 10000000000000000000000000000000b DR3_LOCAL_EXACT_BPM_ENABLED equ 1000000b DR3_GLOBAL_EXACT_BPM_ENABLED equ 10000000b DR3_W equ 010000000000000000000000000000b DR3_IO equ 100000000000000000000000000000b DR3_RW equ 110000000000000000000000000000b DR3_EXECUTION equ 0b DR3_LEN1 equ 0b DR3_LEN2 equ 01000000000000000000000000000000000000b DR3_LEN4 equ 11000000000000000000000000000000000000b DR3_LENU equ 10000000000000000000000000000000000000b // Bits generales LOCAL_EXACT_BPM_ENABLED equ 100000000b GLOBAL_EXACT_BPM_ENABLED equ 1000000000b LOCAL_EXACT_BPM_DISABLED equ 000000000b GLOBAL_EXACT_BPM_DISABLED equ 0000000000b GLOBAL_EXACT_BPM_ENABLED equ 1000000000b GENERAL_DETECT_ENABLED equ 10000000000000b RESERVED_BIT10 10000000000b // el típico 0x400 que nos indica SICE ;) Veamos ahora una rutina que podria servir para establecer un punto de ruptura de ejecución sobre DR0 // Pone un BPM sobre el Thread especificado en la dirección especificada usando el // registro DR0 como contenedor de la dirección, en DR7 se setean los bits para // activar el BPM sobre DR0 void PutBPM(HANDLE hThread,DWORD Address) { // Estructura para establecer el contexto del thread CONTEXT Regs; // obtenemos contexto del thread GetThreadContext(hThread,&Regs); // activamos los flags para los registros de depuración, que es lo unico que tocaremos del contexto Regs.ContextFlags=CONTEXT_DEBUG_REGISTERS; // ponemos DR7 para k active un BPM en DR0 Regs.Dr7=LOCAL_EXACT_BPM_ENABLED|DR0_EXECUTION|DR0_LOCAL_EXACT_BPM_ENABLED|DR0_LEN1; // sobre Address Regs.Dr0=Address; // y establecemos de nuevo el contexto del Thread SetThreadContext(hThread,&Regs); } La rutina es bastante sencilla y se podría modificar para que permitiera una mayor versatilidad, como elegir el DRx y el tipo de punto de ruptura, pero eso se deja como práctica para el lector :), antes de llamar a esta rutina se deberia establecer el manejador de excepciones con SetUnhandledExceptionFilter, el manejador podría ser algo así: // Manejador de excepciones para cuando el BPM surta efecto static LONG WINAPI MyBPMHandler(LPEXCEPTION_POINTERS pExceptStruct) { PEXCEPTION_RECORD ExcepRecord; PCONTEXT Context; // cogemos puntero al contexto y al registro de la excepción Context=pExceptStruct->ContextRecord; ExcepRecord=pExceptStruct->ExceptionRecord; // Es una excepción de trazado paso a paso (0x80000004L) if (ExcepRecord->ExceptionCode==STATUS_SINGLE_STEP) { // puede k sea la nuestra // aquí hacemos lo que nos plazca, como mostrar un mensaje o cualquier otra cosa ........................................................ ........................................................ // borramos el BPM Context->Dr0=0; // el bit 10 del registro DR7 esta reservado, por lo que hay que dejarlo a 1 Context->Dr7=GLOBAL_EXACT_BPM_DISABLED|LOCAL_EXACT_BPM_DISABLED|RESERVED_BIT10; // modificamos el contexto completo (es necesario reestablecer el contexto ya que si no la excepción entrara en un bucle infinito Context->ContextFlags=CONTEXT_FULL|CONTEXT_DEBUG_REGISTERS; // y seguimos return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; } Este sería el esqueleto básico del manejador, por supuesto que la cosa puede mejorarse para conseguir algo más decente pero la base y el funcionamiento básico no cambiarian, aquí teneis un proyecto de Visual C que muestra el uso de esta técnica poniendo un punto de ruptura sobre el API MessageBox, el manejador se encarga de mostrar la dirección del API y la dirección de retorno donde se volverá tras ejecutar el MessageBox. Con esto doy por terminado este tutorial aunque no descarto una nueva serie con aspectos más avanzados. [ SEH - AIRBAG PARA TU CÓDIGO ]
[ Entrada | Documentos Genéricos | WkT! Web Site ]
[ Todo el ECD | x Tipo de Protección | x Fecha de Publicación | x Orden Alfabético ]
(c) Whiskey Kon Tekila [WkT!] - The Original Spanish Reversers. Si necesitas contactar con nosotros , lee esto antes e infórmate de cómo puedes ayudarnos