• 简易调试器的实现(硬件断点)


    一、前言

      上篇链接  简易调试器的实现(一)

      先说一下上次对于软件断点CC还原的位置,int 3断点,属于陷阱类异常,恢复的地方应该是发生异常指令的下一条指令,但是我们在收到信息的时候FirstChance的时候是下一条,在第二次的时候却是断点发生的地方。最近看了下<软件调试>得到了解释

      首先写个小程序

    int _tmain(int argc,_TCHAR* argv[])
    {
      __asm int 3;
      printf("Hello INT 3");
      return 0l      
    }

      当调试的时候,进入反汇编窗口我们看到发生异常的地址为0x013813CE

      在查看寄存器窗口,发现EIP为也是0x013813CE

      在软件调试中得到的答案是

    二、硬件断点的实现

      DRx调试寄存器总共有8个,从DRx0到DRx7。每个寄存器的特性如下:
        DR0~DR3:调试地址寄存器,保存需要监视的地址,如设置硬件断点;
        DR4~DR5:保留,未公开具体作用;
        DR6:调试寄存器组状态寄存器,控制哪个寄存器被命中
        DR7:控制着哪个DRx设置的断点,局部或者全局,读写/执行/写断点类型,断点长度1/2/4/8,的信息

      其中DR6和DR7每位的意义如下:

    typedef struct _DBG_REG6
    {
        /*
        //     断点命中标志位,如果位于DR0~3的某个断点被命中,则进行异常处理前,对应
        // 的B0~3就会被置为1。
        */
        unsigned B0 : 1;  // Dr0断点触发置位
        unsigned B1 : 1;  // Dr1断点触发置位
        unsigned B2 : 1;  // Dr2断点触发置位
        unsigned B3 : 1;  // Dr3断点触发置位
        /*
        // 保留字段
        */
        unsigned Reserve1 : 9;
        /*
        // 其它状态字段
        */
        unsigned BD : 1;  // 调制寄存器本身触发断点后,此位被置为1
        unsigned BS : 1;  // 单步异常被触发,需要与寄存器EFLAGS的TF联合使用
        unsigned BT : 1;  // 此位与TSS的T标志联合使用,用于接收CPU任务切换异常
        /*
        // 保留字段
        */
        unsigned Reserve2 : 16;
    }DBG_REG6,*PDBG_REG6;
    
    typedef struct _DBG_REG7
    {
        /*
        // 局部断点(L0~3)与全局断点(G0~3)的标记位
        */
        unsigned L0 : 1;  // 对Dr0保存的地址启用 局部断点
        unsigned G0 : 1;  // 对Dr0保存的地址启用 全局断点
        unsigned L1 : 1;  // 对Dr1保存的地址启用 局部断点
        unsigned G1 : 1;  // 对Dr1保存的地址启用 全局断点
        unsigned L2 : 1;  // 对Dr2保存的地址启用 局部断点
        unsigned G2 : 1;  // 对Dr2保存的地址启用 全局断点
        unsigned L3 : 1;  // 对Dr3保存的地址启用 局部断点
        unsigned G3 : 1;  // 对Dr3保存的地址启用 全局断点
        /*
        // 【以弃用】用于降低CPU频率,以方便准确检测断点异常
        */
        unsigned LE : 1;
        unsigned GE : 1;
        /*
        // 保留字段
        */
        unsigned Reserve1 : 3;
        /*
        // 保护调试寄存器标志位,如果此位为1,则有指令修改条是寄存器时会触发异常
        */
        unsigned GD : 1;
        /*
        // 保留字段
        */
        unsigned Reserve2 : 2;
        /*
        // 保存Dr0~Dr3地址所指向位置的断点类型(RW0~3)与断点长度(LEN0~3),状态描述如下:
        */
        unsigned RW0 : 2;  // 设定Dr0指向地址的断点类型
        unsigned LEN0 : 2;  // 设定Dr0指向地址的断点长度
        unsigned RW1 : 2;  // 设定Dr1指向地址的断点类型
        unsigned LEN1 : 2;  // 设定Dr1指向地址的断点长度
        unsigned RW2 : 2;  // 设定Dr2指向地址的断点类型
        unsigned LEN2 : 2;  // 设定Dr2指向地址的断点长度
        unsigned RW3 : 2;  // 设定Dr3指向地址的断点类型
        unsigned LEN3 : 2;  // 设定Dr3指向地址的断点长度
    }DBG_REG7,*PDBG_REG7;

      知道了Dr7的各个位的意义,我们就能设置硬件断点了。

      首先通过GetThreadContext获得Dr7的值,在通过SetThreadContext来设置Dr7的值。

    HANDLE SetHardwareBreakpoint(HANDLE hThread,HWBRK_TYPE Type,HWBRK_SIZE Size,void* s)
    {    
    
        if(m_vecHard.size( ) > 3) 
            return FALSE;
    
        PointInfo bkpt;
        bkpt.lpPointAddr = (DWORD)s; //记录断点地址
        bkpt.ptType = DR_POINT; //记录断点类型
    
        int j = 0;
        int y = 0;
        CONTEXT ct = {0};
        int iReg = 0;
    
        j =SuspendThread(g_hThread);   //这里我遇到一个坎一直过不去
         //这里总是无效句柄,
        y = GetLastError();
    
        ct.ContextFlags = CONTEXT_DEBUG_REGISTERS|CONTEXT_FULL;
        if(!GetThreadContext(hThread,&ct))
        {
            y = GetLastError();
            MessageBox(NULL,L"Fail",L"1",1);
        }
        int FlagBit = 0;
    
        bool Dr0Busy = false;
        bool Dr1Busy = false;
        bool Dr2Busy = false;
        bool Dr3Busy = false;
        if (ct.Dr7 & 1) //0位  0 local
            Dr0Busy = true;
        if (ct.Dr7 & 4) //2位  1 local
            Dr1Busy = true;
        if (ct.Dr7 & 16)//4位  2 local
            Dr2Busy = true;
        if (ct.Dr7 & 64)//6位  3 local
            Dr3Busy = true;
        
        
            if (!Dr0Busy)
            {  bkpt.Number = 0;
                iReg = 0;
                ct.Dr0 = (DWORD_PTR)s;  //地址
                Dr0Busy = true; 
            }
            else
                if (!Dr1Busy)
                {
                     bkpt.Number = 1;
                    iReg = 1;
                    ct.Dr1 = (DWORD_PTR)s;
                    Dr1Busy = true;
                }
                else
                    if (!Dr2Busy)
                    { bkpt.Number= 2;
                        iReg = 2;
                        ct.Dr2 = (DWORD_PTR)s;
                        Dr2Busy = true;
                    }
                    else
                        if (!Dr3Busy)
                        {
                            bkpt.Number = 3;
                            iReg = 3;
                            ct.Dr3 = (DWORD_PTR)s;
                            Dr3Busy = true;
                        }
                        else
                        {
                            //h->SUCC = false;
                            j = ResumeThread(hThread);
                            y = GetLastError();
                            return 0;
                        }
        ct.Dr6 = 0;
        int st = 0;
        if (Type == HWBRK_TYPE_CODE)
                   st = 0;
        if (Type == HWBRK_TYPE_READWRITE)
            st = 3;
        if (Type == HWBRK_TYPE_WRITE)
            st = 1;
        int le = 0;
        if (Size == HWBRK_SIZE_1)
            le = 0;
        if (Size == HWBRK_SIZE_2)
            le = 1;
        if (Size == HWBRK_SIZE_4)
            le = 3;
        if (Size == HWBRK_SIZE_8)
            le = 2;
    
        SetBits(ct.Dr7, 16 + iReg*4, 2, st);
        SetBits(ct.Dr7, 18 + iReg*4, 2, le);
        SetBits(ct.Dr7, iReg*2,1,1);
        
    
    
    
        ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
        if(!SetThreadContext(hThread,&ct))
        {
            y = GetLastError();
            MessageBox(NULL,L"Fail",L"1",1);
        }
    
    
        ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
        if(!GetThreadContext(hThread,&ct))
        {
            y = GetLastError();
            MessageBox(NULL,L"Fail",L"1",1);
        }
    
        j = ResumeThread(hThread);
    
        y = GetLastError();
        
        m_vecHard.push_back(bkpt);
        return 0;
    }
    
    
    void SetBits(DWORD_PTR& dw, int lowBit, int bits, int newValue)
    {
        DWORD_PTR mask = (1 << bits) - 1; 
        dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
    }

      在GetThreadContext之前一定要挂起线程,SuspendThread,不然可能context在获得的时候线程的堆栈正发生变化,那么结构也不能准确。

  • 相关阅读:
    Windows下使用Nginx+tomcat配置负载均衡
    Windows系统搭建Mysql Cluster集群
    java.math.BigDecimal cannot be cast to java.lang.String解决方法
    Java从后台重定向(redirect)到另一个项目的方法
    Java代码中获取配置文件(config.properties)中内容的两种方法
    mybatis和redis整合 log4j打印sql语句
    Windows系统安装测试redis
    myeclipse 2014新建maven web 项目步骤
    myeclipse用maven搭建web项目后tomcat启动报找不到jar包解决办法
    《剑指offer》丑数
  • 原文地址:https://www.cnblogs.com/aliflycoris/p/5348572.html
Copyright © 2020-2023  润新知