Problemas al colocar Hardware Breakpoints

4 views
Skip to first unread message

aguml

unread,
May 25, 2020, 8:53:28 AM5/25/20
to CyC++ Buenos Aires
Bueno amigos, esto avanza poquito a poquito. Ahora me encuentro con el tema de los Hardware BreakPoints los cuales al pasar el codigo a x64 no funcionan y ademas el programa depurado da una excepcion y se acaba cerrando.
Para ponerlos en x32 tenia estas constantes:

#define EXCEPTION_GUARD_PAGE_VIOLATION 0x80000001L

enum ModeBPM BMP_WRITEBMP_ACCESS,BMP_EXECUTION};
enum ModeHW HW_WRITEHW_ACCESSHW_EXECUTION};
enum LenHW HW_BYTEHW_WORDHW_DWORD};

//Constantes para los HW
#define HW_DR0        0x1
#define HW_DR1        0x2
#define HW_DR2        0x3
#define HW_DR3        0x4

#define DR0_LOCAL_EXACT_BPM_ENABLED    0x01
#define DR0_GLOBAL_EXACT_BPM_ENABLED   0x02

#define DR0_W      0x10000
#define DR0_IO      0x20000
#define DR0_RW          0x30000
#define DR0_EXECUTION    0x0

#define DR0_LEN1    0x0
#define DR0_LEN2    0x40000
#define DR0_LEN3    0x0C0000

#define DR1_LOCAL_EXACT_BPM_ENABLED  0x4
#define DR1_GLOBAL_EXACT_BPM_ENABLED 0x8

#define DR1_W      0x100000
#define DR1_IO      0x200000
#define DR1_RW      0x300000
#define DR1_EXECUTION          0x0

#define DR1_LEN1    0x0
#define DR1_LEN2    0x400000
#define DR1_LEN3    0x0C00000

#define DR2_LOCAL_EXACT_BPM_ENABLED  0x10
#define DR2_GLOBAL_EXACT_BPM_ENABLED 0x20

#define DR2_W      0x1000000
#define DR2_IO      0x2000000
#define DR2_RW      0x3000000
#define DR2_EXECUTION          0x0

#define DR2_LEN1    0x0
#define DR2_LEN2    0x4000000
#define DR2_LEN3    0x0C000000

#define DR3_LOCAL_EXACT_BPM_ENABLED   0x40
#define DR3_GLOBAL_EXACT_BPM_ENABLED  0x80

#define DR3_W      0x10000000
#define DR3_IO      0x20000000
#define DR3_RW      0x30000000
#define DR3_EXECUTION          0x0

#define DR3_LEN1    0x0
#define DR3_LEN2    0x40000000
#define DR3_LEN3    0x0C0000000

#define LOCAL_EXACT_BPM_ENABLED      0x100
#define LOCAL_EXACT_BPM_DISABLED    0x0
#define GLOBAL_EXACT_BPM_ENABLED    0x200
#define GLOBAL_EXACT_BPM_DISABLED   0x0
#define GENERAL_DETECT_ENABLED      0x2000
#define RESERVED_BIT10              0x400

#define CLEAR_DR0            0x0FFF0FFFC
#define CLEAR_DR1               0x0FF0FFFF3
#define CLEAR_DR2                   0x0F0FFFFCF
#define CLEAR_DR3                   0x0FFFFF3F 

Y esta funcion:

DWORD __fastcall THiloDebugger::OnSingleStepEventThread(EXCEPTION_DEBUG_INFO *info)
{
    
pBP BPaux;
    
int HWBySingleStep=0;

    
DWORD Aux;

     
/*-------------------------------------------------------------------------------------
    *
    * DEBUG REGISTER 7
    *
    * LEN3 R/W3 LEN2 R/W2 LEN1 R/W1 LEN0 R/W0 0 0 GD 0 0 1 GE LE G3 L3 G2 L2 G1 L1 G0 L0
    *
    *  00   00   00   00   00   00   00   00  0 0 0  0 0 0 0  1  0  1  0  1  0  1  0  1
    *
    *-------------------------------------------------------------------------------------*/

    
if(RepairHW == true){
        switch(
ItemHWRepair.DrW){

            case 
HW_DR0:
                
con.Dr0 ItemHWRepair.Address;
                break;

            case 
HW_DR1:
                
con.Dr1 ItemHWRepair.Address;
                break;

            case 
HW_DR2:
                
con.Dr2 ItemHWRepair.Address;
                break;

            case 
HW_DR3:
                
con.Dr3 ItemHWRepair.Address;
                break;

        }
        
RepairHW false;

        if(
UserStepIn == true){
            
UserStepIn false;

            
DisableBP(0); // Desactivamos todos los BPs para que si el usuario ve la memoria esta se muestre como debe
            
ClearBMP(true); // reponemos las propiedades de la memoria

            
Synchronize(SingleStepInSincronyze);
        }
    }
    else{

        
// Verificamos si la parada fue por un HW
        
if (con.Dr6 1) {
            
HWBySingleStep HW_DR0;
        }
        else if(
con.Dr6 2){
            
HWBySingleStep HW_DR1;
        }
        else if(
con.Dr6 4){
            
HWBySingleStep HW_DR2;
        }
        else if(
con.Dr6 8){
            
HWBySingleStep HW_DR3;
        }

        if (
HWBySingleStep != 0) {
            
UserStepIn false;
            
// el sigle step fue originado por un HW
            
DisableBP(0); // Desactivamos todos los BPs para que si el usuario ve la memoria esta se muestre como debe
            
ClearBMP(true); // reponemos las propiedades de la memoria

            
Aux con.Dr7;
            switch(
HWBySingleStep){

                case 
HW_DR0:
                        
HWAddress con.Dr0;
                        
Aux >>=  16;
                        break;
                case 
HW_DR1:
                        
HWAddress con.Dr1;
                        
Aux >>=  20;
                        break;
                case 
HW_DR2:
                        
HWAddress con.Dr2;
                        
Aux >>= 24;
                        break;
                case 
HW_DR3:
                        
HWAddress con.Dr3;
                        
Aux >>= 28;
                        break;

            }
            
Aux &= 3;
            if( 
Aux == 00){ // por ejecución
                
LastHWMode HW_EXECUTION;
            }
            else if(
Aux ==01){
                
LastHWMode HW_WRITE;
            }
            else{
                
LastHWMode HW_ACCESS;
            }

            
// llamar al usuario
            
Synchronize(HardwareBreakPointSincronyze);


            switch(
HWBySingleStep){

                case 
HW_DR0:

                    if(
con.Dr0 == LastExceptionAddress){ // por ejecución

                        
ItemHWRepair.Address LastExceptionAddress;
                        
ItemHWRepair.DrW HW_DR0;
                        
con.Dr0 0// hay que guardar el dato
                        
RepairHW=true;
                    }
                    break;
                case 
HW_DR1:
                    if(
con.Dr1 == LastExceptionAddress){ // por ejecución

                        
ItemHWRepair.Address LastExceptionAddress;
                        
ItemHWRepair.DrW HW_DR1;

                        
con.Dr1 0// hay que guardar el dato
                        
RepairHWtrue;

                    }
                    break;
                case 
HW_DR2:
                    if(
con.Dr2 == LastExceptionAddress){ // por ejecución

                        
ItemHWRepair.Address LastExceptionAddress;
                        
ItemHWRepair.DrW HW_DR2;

                        
con.Dr2 0// hay que guardar el dato
                        
RepairHWtrue;

                    }
                    break;
                case 
HW_DR3:
                    if(
con.Dr3 == LastExceptionAddress){ // por ejecución

                        
ItemHWRepair.Address LastExceptionAddress;
                        
ItemHWRepair.DrW HW_DR3;

                        
con.Dr3 0// hay que guardar el dato
                        
RepairHWtrue;

                    }
                    break;
            }
            if(
RepairHW== true){
                
SetSingleStep();
            }

        }
        else if( 
UserStepIn == true){
            
UserStepIn false;
            
DisableBP(0); // Desactivamos todos los BPs para que si el usuario ve la memoria esta se muestre como debe
            
ClearBMP(true); // reponemos las propiedades de la memoria

            
Synchronize(SingleStepInSincronyze);

        }

         if(
ItemBMP.Address != NULL){  // La excepción no fue por un BMP
            
SetBMP(ItemBMP.Address,ItemBMP.Size,ItemBMP.Mode);
        }

        
// Reponemos los BPs
        
for(int i=0ListaBPs->Count;i++){
            
BPaux = (TBP*)ListaBPs->Items[i];
            if(
BPaux->estado == false){
                
SetBP(BPaux->dir);
            }
        }
    }
    return 
DBG_CONTINUE// exception continuation



El caso es que no se si los valores a usar en x64 son diferentes a los que se usan en x32. ¿Pueden echarme una mano?

aguml

unread,
May 25, 2020, 4:42:04 PM5/25/20
to CyC++ Buenos Aires
El caso es que ese trozo de código lo hizo otro compañero y ya le perdí la pista y no puedo preguntarle nada porque me gustaría saber de dónde sacó esas constantes para intentar entenderlo.
He estado buscando la manera de solucionarlo y me he creado una pequeña tool que calcula el valor de Dr7 basándome en este código que encontré por la red:

void SETBITS(DWORD64 *dw, int lowBit, int bits, int newValue) {

int mask = (1 << bits) - 1;
*dw = (*dw & ~(mask << lowBit)) | (newValue << lowBit);

}

BOOL SetHardwareBP(HANDLE hThread, __int64 Address, DWORD Length, int Condition)
{

CONTEXT context = { CONTEXT_DEBUG_REGISTERS };
int i;
if (!GetThreadContext(hThread, &context)) return -1;

// find available hardware register

for (i = 0; i < 4; i++)
{
if ((context.Dr7 & (1 << (i * 2))) == 0)
{
*(&context.Dr0 + i) = Address;

SETBITS(&context.Dr7, 16 + i * 4, 2, Condition);
SETBITS(&context.Dr7, 18 + i * 4, 2, Length);
SETBITS(&context.Dr7, i * 2, 1, 1);

if (!SetThreadContext(hThread, &context))
return -1;

return i;
}
}

return -1;
}

Lo he probado comparando los valores que me devuelve con los que muestra x64dbg y coinciden con los de mi tool pero mi debuger da valores que no tiene nada que ver. Por ejemplo si no tengo ningún hbp y pongo uno onexecute en mi tool y en x64dbg obtengo 0x1 mientras que en mi debugger obtengo 0x501 😢. Probé a, cuando estoy parado ahí modificar el valor de Dr7 para que salga Dr7 con 1 pero se sigue cerrando la aplicación.
Ya no sé que hacer que no implique modificar todo el código de mi compañero y hacerlo de otra manera pero es una tarea muy dura y seguro que es alguna yo tenía que haya cambiado entre x32 y x64.

aguml

unread,
May 27, 2020, 6:08:00 AM5/27/20
to CyC++ Buenos Aires
Pongo un HBP en el EntryPoint y la siguiente excepción que tengo es EXCEPTION_DEBUG_EVENT justo en la siguiente dirección, o sea mi EntryPoint es 0x0000000000403280 y la excepción se produce en 0x0000000000403281 que es justo el siguiente byte del binario. El codigo de error que obtengo es 0x00000000c0000005 que es un EXCEPTION_ACCESS_VIOLATION la pregunta es ¿por que me da ese error justo después del EntryPoint si mi HBP lo tengo puesto en el inicio de MessageBoxW?
No lo entiendo
No para en EXCEPTION_SINGLE_STEP que es donde debería parar para el hbp sino que da la excepción de acceso de violación nada más salir del evento del EntryPoint.
Si no pongo el HBP no da esa excepción 😭
Por favor échenme una mano, si quieren el proyecto para revisarlo... sin problema, es para compartirlo. Está creado en Embarcadero Rad C++Builder 10.3.
Ya no se que más probar, estoy en un punto muerto que no voy ni para atrás .

RFOG

unread,
May 27, 2020, 6:18:25 AM5/27/20
to cp...@googlegroups.com
A ver, 0x00000000c0000005 es acceso a memoria sin inicializar (un puntero loco, vamos)... Si no recuerdo mal, y puede incluso que haya cambiado, para poder debuggear, necesitas instalar algún tipo de hook... pero no me hagas mucho caso, de esto fue hace muuuchos años y solo en plan teórico.

--
--
¿Eres miembro de "CyC++ Buenos Aires" verdad? Si no lo eres, has recibido este mesaje por error.
En caso de duda visita "http://groups.google.com/group/cppba"
---
Has recibido este mensaje porque estás suscrito al grupo "CyC++ Buenos Aires" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a cppba+un...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/cppba/db14f7b1-fff2-4b36-9bc6-98cb706a6eb4%40googlegroups.com.

RFOG

unread,
May 27, 2020, 6:20:09 AM5/27/20
to cp...@googlegroups.com
Hasta donde yo recuerdo, un breakpoint es reemplazar la instrucción correspondiente por un int3, que debe capturar el debugger. Creo que está explicado en algún libro de Sys Internals. 

aguml

unread,
May 27, 2020, 6:39:17 AM5/27/20
to CyC++ Buenos Aires
Un HBP o Hardware BreakPoint es un BreakPoint que no usa la instruccion int3 sino que usa la int1. Solo se pueden poner 4 HBPs porque el procesador tiene 8 registros para ese menester donde los 4 primeros (Dr0, Dr1, Dr2 y Dr3) almacenan la direccion del HBP y Dr7 tiene 6 bits para cada uno de los 4 registros anteriores donde 2 son para la activacion de ese HBP, 2 para su tipo (OnExecute, OnAccess, OnWrite) y 2 para su longitud (BYTE, WORD, DWORD, QWORD), los bites 8 y 9 no se usan desde el 486, el 10 siempre vale 1 y el resto ya no nos interesan para nada jejeje. En el caso de x64,. los 32 primeros bytes no se usan.
Hasta ahí se.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a cp...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages