调试篇——断点与单步

博客 分享
0 210
优雅殿下
优雅殿下 2022-03-03 17:56:02
悬赏:0 积分 收藏

调试篇——断点与单步

调试篇之断点与单步,介绍断点和单步相关的基础知识和简单的实现。

写在前面

??此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

??看此教程之前,问几个问题,基础知识储备好了吗?保护模式篇学会了吗?练习做完了吗?没有的话就不要继续了。

?


?? 华丽的分割线 ??


?

概述

??在软件调试的过程中,最常用的就是断点和单步了。有过调试经验的人知道,断点有软件断点、硬件断点、内存断点;单步有单步步入和单步步过。下面我们来介绍它们的实现原理。

软件断点

??软件断点是实现的本质就是在下断点的地方插入int3,也被称之为CC断点。下面我们来做个实验:
??我们打开一个调试器调试一个进程,然后在某条汇编下断点,于此同时通过CE来查看当前指令的字节变化情况,如下是操作示例:

??也就是说,调试器帮我们屏蔽掉了0xCC的显示,本质上还是int3,就凭这些我们就可以写一个接管软件断点的简单的调试器了:

#include "stdafx.h"#include <windows.h>#include <stdlib.h>BOOL SysInt3 = TRUE;void WaitUserInput();BOOL BreakPointHandler(EXCEPTION_DEBUG_INFO* info);const UCHAR Int3 = 0xCC;UCHAR bInt3;PROCESS_INFORMATION pi ;#define DBGBREAKPOINTint main(int argc, char* argv[]){    char filename[]= "C:\\WINDOWS\\NOTEPAD.EXE";    STARTUPINFO si ={sizeof(STARTUPINFO)};    DEBUG_EVENT dbgEvent;    BOOL isContinue = TRUE;    DWORD buffer;            BOOL ret =CreateProcess(NULL,filename,NULL,NULL,FALSE,DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi);    if (ret)    {        while (isContinue)        {            ret = WaitForDebugEvent(&dbgEvent,INFINITE);            if (!ret)            {                printf("WaitForDebugEvent 出错:%d",GetLastError());                break;            }            EXCEPTION_DEBUG_INFO info = dbgEvent.u.Exception;            switch (dbgEvent.dwDebugEventCode)            {            case EXCEPTION_DEBUG_EVENT:                puts("EXCEPTION_DEBUG_EVENT");                switch (info.ExceptionRecord.ExceptionCode)                {                case EXCEPTION_BREAKPOINT:                    isContinue = BreakPointHandler(&info);                    break;                }                break;            case CREATE_THREAD_DEBUG_EVENT:                puts("CREATE_THREAD_DEBUG_EVENT");                break;            case CREATE_PROCESS_DEBUG_EVENT:                puts("CREATE_PROCESS_DEBUG_EVENT"); #ifdef DBGBREAKPOINT                puts("请输入要下断点的位置:");                scanf("%x",&buffer);                if (!ReadProcessMemory(pi.hProcess,(void*)buffer,&bInt3,1,NULL))                {                    puts("软件断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                if (!WriteProcessMemory(pi.hProcess,(void*)buffer,(void*)&Int3,1,NULL))                {                    puts("软件断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }#endif                break;            case EXIT_THREAD_DEBUG_EVENT:                puts("EXIT_THREAD_DEBUG_EVENT");                break;            case EXIT_PROCESS_DEBUG_EVENT:                puts("EXIT_PROCESS_DEBUG_EVENT");                break;            case LOAD_DLL_DEBUG_EVENT:                puts("LOAD_DLL_DEBUG_EVENT");                break;                 case UNLOAD_DLL_DEBUG_EVENT:                puts("UNLOAD_DLL_DEBUG_EVENT");                break;            case OUTPUT_DEBUG_STRING_EVENT:                puts("OUTPUT_DEBUG_STRING_EVENT");                break;            default:                break;            }            isContinue = ContinueDebugEvent(dbgEvent.dwProcessId,dbgEvent.dwThreadId,DBG_CONTINUE);           }        CloseHandle(pi.hProcess);        CloseHandle(pi.hThread);    }    else    {        printf("创建进程失败:%d\n",GetLastError());    }    system("pause");    return 0;}BOOL BreakPointHandler(EXCEPTION_DEBUG_INFO* info){    if (SysInt3)    {        SysInt3 = FALSE;        return TRUE;    }            EXCEPTION_RECORD record = info->ExceptionRecord;    WriteProcessMemory(pi.hProcess,record.ExceptionAddress,&bInt3,1,NULL);    printf(">> BreakPointHandler - Addr : 0x%X\n",record.ExceptionAddress);            WaitUserInput();            CONTEXT context;    context.ContextFlags = CONTEXT_FULL |CONTEXT_DEBUG_REGISTERS;    GetThreadContext(pi.hThread,&context);    context.Eip--;    SetThreadContext(pi.hThread,&context);    return TRUE;}void WaitUserInput(){    bool p = true;    char cmd = 0;    while(p)    {        printf("COMMAND>> ");                /*************清空缓冲区**************/        scanf("%*[^\n]");        scanf("%*c");        /*************************************/        cmd = getchar();        switch (cmd)        {        case 'g':            p = false;            break;        default:            break;        }    }}

??我定义DBGBREAKPOINT这个宏是为了方便之后实验只需定义宏来控制单个功能的测试。WaitUserInput函数里面清空缓冲区的代码不清楚的话可以阅读 羽夏闲谈—— C 的 scanf 的高级用法 ,如下是我的实验结果:

??当然我们只是简单的实现,一般的调试器都会保留这个断点,具体实现我就不实现了。

内存断点

??内存断点是通过修改页属性来制造异常出现接管的,既然是修改其他程序的,就需要调用下面的函数:

BOOL VirtualProtectEx(  HANDLE hProcess,        // handle to process  LPVOID lpAddress,       // region of committed pages  SIZE_T dwSize,          // size of region  DWORD flNewProtect,     // desired access protection  PDWORD lpflOldProtect   // old protection);

??内存断点有被分为内存访问断点和内存写入断点,它们分别对应的第四个参数为PAGE_NOACCESSPAGE_EXECUTE_READPAGE_NOACCESS属性会把对应的页属性的P位改为0,而PAGE_EXECUTE_READ对应的页属性的P位虽然是1,但R/W0。下面我们来实现一个简单的内存访问断点:

#include "stdafx.h"#include <windows.h>#include <stdlib.h>BOOL SysInt3 = TRUE;void WaitUserInput();BOOL BreakPointHandler(EXCEPTION_DEBUG_INFO* info);BOOL MemBreakPointHandler(EXCEPTION_DEBUG_INFO* info);const UCHAR Int3 = 0xCC;UCHAR bInt3;PROCESS_INFORMATION pi ;DWORD orignExcuteAccess;DWORD buffer;//#define DBGBREAKPOINT#define MemBREAKPOINTint main(int argc, char* argv[]){    char filename[]= "C:\\WINDOWS\\NOTEPAD.EXE";    STARTUPINFO si ={sizeof(STARTUPINFO)};    DEBUG_EVENT dbgEvent;    BOOL isContinue = TRUE;    BOOL ret =CreateProcess(NULL,filename,NULL,NULL,FALSE,DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi);    if (ret)    {        while (isContinue)        {            ret = WaitForDebugEvent(&dbgEvent,INFINITE);            if (!ret)            {                printf("WaitForDebugEvent 出错:%d",GetLastError());                break;            }            EXCEPTION_DEBUG_INFO info = dbgEvent.u.Exception;            switch (dbgEvent.dwDebugEventCode)            {            case EXCEPTION_DEBUG_EVENT:                puts("EXCEPTION_DEBUG_EVENT");                switch (info.ExceptionRecord.ExceptionCode)                {                case EXCEPTION_BREAKPOINT:                    ret = BreakPointHandler(&info);                    break;                case EXCEPTION_ACCESS_VIOLATION:                    ret = MemBreakPointHandler(&info);                    break;                }                if (!ret)                {                    printf("出错:%d\n",GetLastError());                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                isContinue = ret;                break;            case CREATE_THREAD_DEBUG_EVENT:                puts("CREATE_THREAD_DEBUG_EVENT");                break;            case CREATE_PROCESS_DEBUG_EVENT:                puts("CREATE_PROCESS_DEBUG_EVENT");  #ifdef DBGBREAKPOINT                puts("请输入要下断点的位置:");                scanf("%x",&buffer);                if (!ReadProcessMemory(pi.hProcess,(void*)buffer,&bInt3,1,NULL))                {                    puts("软件断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                if (!WriteProcessMemory(pi.hProcess,(void*)buffer,(void*)&Int3,1,NULL))                {                    puts("软件断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }#endif    #ifdef MemBREAKPOINT                buffer = (DWORD)dbgEvent.u.CreateProcessInfo.lpStartAddress;                   if (!VirtualProtectEx(pi.hProcess,(void*)buffer,1,PAGE_NOACCESS,&orignExcuteAccess))                {                    puts("内存执行断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                printf("内存访问断点的位置:0x%X\n",buffer);#endif                break;            case EXIT_THREAD_DEBUG_EVENT:                puts("EXIT_THREAD_DEBUG_EVENT");                break;            case EXIT_PROCESS_DEBUG_EVENT:                puts("EXIT_PROCESS_DEBUG_EVENT");                    break;            case LOAD_DLL_DEBUG_EVENT:                puts("LOAD_DLL_DEBUG_EVENT");                break;                 case UNLOAD_DLL_DEBUG_EVENT:                puts("UNLOAD_DLL_DEBUG_EVENT");                break;            case OUTPUT_DEBUG_STRING_EVENT:                puts("OUTPUT_DEBUG_STRING_EVENT");                break;            default:                break;            }            if (!isContinue)            {                break;            }            isContinue = ContinueDebugEvent(dbgEvent.dwProcessId,dbgEvent.dwThreadId,ret?DBG_CONTINUE:DBG_EXCEPTION_NOT_HANDLED);           }        CloseHandle(pi.hProcess);        CloseHandle(pi.hThread);    }    else    {        printf("创建进程失败:%d\n",GetLastError());    }    system("pause");    return 0;}BOOL BreakPointHandler(EXCEPTION_DEBUG_INFO* info){    if (SysInt3)    {        SysInt3 = FALSE;        return TRUE;    }            EXCEPTION_RECORD record = info->ExceptionRecord;    WriteProcessMemory(pi.hProcess,record.ExceptionAddress,&bInt3,1,NULL);    printf(">> BreakPointHandler - Addr : 0x%X\n",record.ExceptionAddress);            WaitUserInput();            CONTEXT context;    context.ContextFlags = CONTEXT_FULL |CONTEXT_DEBUG_REGISTERS;    GetThreadContext(pi.hThread,&context);    context.Eip--;    SetThreadContext(pi.hThread,&context);    return TRUE;}BOOL MemBreakPointHandler(EXCEPTION_DEBUG_INFO* info){    DWORD ExceptionADDR = info->ExceptionRecord.ExceptionInformation[1];    DWORD ExceptionAccess = info->ExceptionRecord.ExceptionInformation[0];            if (ExceptionADDR>>12 == buffer>>12)    //这里的判断是不对的,但为了做示例足够了    {        printf("内存执行断点:%x %x\n",ExceptionADDR,   ExceptionAccess);        WaitUserInput();        if (!VirtualProtectEx(pi.hProcess,(void*)buffer,1,  orignExcuteAccess,&orignExcuteAccess))        {            printf("内存断点修复失败:%d\n",GetLastError());        }        return TRUE;    }    return FALSE;}void WaitUserInput(){    bool p = true;    char cmd = 0;    while(p)    {                /*************清空缓冲区**************/        scanf("%*[^\n]");        scanf("%*c");        /*************************************/        printf("COMMAND>> ");        cmd = getchar();        switch (cmd)        {        case 'g':            p = false;            break;        default:            break;        }    }}

??可以看出,该代码是基于软件断点实验基础上写的。在WaitUserInput有一个小Bug,就是没有输入的话至少需要随意输入字符来继续清空缓冲区操作,不过对于示例来说无伤大雅,如下是测试效果图:

??注意,我是简单是实现内存断点,为什么这么说呢?是因为这个函数一旦影响就是一个物理页,所以你得做判断。如果对数据下访问断点,如果多于1个字节,你还得考虑在多个物理页的情况,还得实现内存断点的记录功能。这些都是一个基本能用的调试器所具备的功能。

硬件断点

概述

??硬件断点和上面的不同,它是基于硬件的,不依赖调试程序,有自己的优势,如果通过CRC校验是不会被检测到的。如下是与硬件断点相关的寄存器结构:

??Dr0 ~ Dr3用于设置硬件断点,Dr4Dr5被保留了。由于只有4个断点寄存器,所以最多只能设置4个硬件调试断点。Dr7是最重要的寄存器,它比较复杂,我们来看看它的结构:

L0/G0 ~ L3/G3

??控制Dr0 ~ Dr3是否有效,局部还是全局。每次异常后,Lx都被清零,Gx不清零。

LEN0 ~ LEN3

??表示硬件断点的长度。如果是0表示1个字节;是1表示2个字节;是3表示4个字节。

R/W0 ~ R/W3

??指示断点类型。如果是0表示执行断点;是1表示写入断点;是3表示访问断点。

处理

??硬件调试断点产生的异常是STATUS_SINGLE_STEP,即单步异常。触发异常后,B0 ~ B3对应的位会被置1,以此可以区分单步步入产生的单步异常,后续会详细讲解。

实验

??如下是实验代码:

#include "stdafx.h"#include <windows.h>#include <stdlib.h>BOOL SysInt3 = TRUE;void WaitUserInput();BOOL BreakPointHandler(EXCEPTION_DEBUG_INFO* info);BOOL MemBreakPointHandler(EXCEPTION_DEBUG_INFO* info);BOOL SingleStepHandler(EXCEPTION_DEBUG_INFO* info);const UCHAR Int3 = 0xCC;UCHAR bInt3;PROCESS_INFORMATION pi ;DWORD orignExcuteAccess;DWORD buffer;//#define DBGBREAKPOINT//#define MemBREAKPOINT#define HardwareBREAKPOINTint main(int argc, char* argv[]){    char filename[]= "C:\\WINDOWS\\NOTEPAD.EXE";    STARTUPINFO si ={sizeof(STARTUPINFO)};    DEBUG_EVENT dbgEvent;    BOOL isContinue = TRUE;            BOOL ret =CreateProcess(NULL,filename,NULL,NULL,FALSE,DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi);    if (ret)    {        while (isContinue)        {            ret = WaitForDebugEvent(&dbgEvent,INFINITE);            if (!ret)            {                printf("WaitForDebugEvent 出错:%d",GetLastError());                break;            }               EXCEPTION_DEBUG_INFO info = dbgEvent.u.Exception;            switch (dbgEvent.dwDebugEventCode)            {            case EXCEPTION_DEBUG_EVENT:                puts("EXCEPTION_DEBUG_EVENT");                switch (info.ExceptionRecord.ExceptionCode)                {                case EXCEPTION_BREAKPOINT:                    ret = BreakPointHandler(&info);                    break;                case EXCEPTION_ACCESS_VIOLATION:                    ret = MemBreakPointHandler(&info);                    break;                case EXCEPTION_SINGLE_STEP:                    ret = SingleStepHandler(&info);                    break;                default:                    ret = FALSE;                    break;                }                if (!ret)                {                    printf("出错:%d\n",GetLastError());                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                isContinue = ret;                break;            case CREATE_THREAD_DEBUG_EVENT:                puts("CREATE_THREAD_DEBUG_EVENT");                break;            case CREATE_PROCESS_DEBUG_EVENT:                puts("CREATE_PROCESS_DEBUG_EVENT");      #ifdef DBGBREAKPOINT                    puts("请输入要下断点的位置:");                scanf("%x",&buffer);                if (!ReadProcessMemory(pi.hProcess,(void*)buffer,&bInt3,1,NULL))                {                    puts("软件断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                if (!WriteProcessMemory(pi.hProcess,(void*)buffer,(void*)&Int3,1,NULL))                                        puts("软件断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }    #endif    #ifdef MemBREAKPOINT                buffer = (DWORD)dbgEvent.u.CreateProcessInfo.lpStartAddress;                   if (!VirtualProtectEx(pi.hProcess,(void*)buffer,1,PAGE_NOACCESS,&orignExcuteAccess))                {                    puts("内存执行断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                printf("内存访问断点的位置:0x%X\n",buffer);#endif    #ifdef HardwareBREAKPOINT                SuspendThread(pi.hThread);                puts("请输入要下断点的位置:");                scanf("%x",&buffer);                CONTEXT context;                context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;                GetThreadContext(pi.hThread,&context);                context.Dr0 = buffer;                context.Dr7 |= 1;                if (!SetThreadContext(pi.hThread,&context))                {                    puts("硬件断点设置失败!");                }                ResumeThread(pi.hThread);#endif                break;            case EXIT_THREAD_DEBUG_EVENT:                puts("EXIT_THREAD_DEBUG_EVENT");                break;            case EXIT_PROCESS_DEBUG_EVENT:                puts("EXIT_PROCESS_DEBUG_EVENT");                    break;            case LOAD_DLL_DEBUG_EVENT:                puts("LOAD_DLL_DEBUG_EVENT");                break;                 case UNLOAD_DLL_DEBUG_EVENT:                puts("UNLOAD_DLL_DEBUG_EVENT");                break;            case OUTPUT_DEBUG_STRING_EVENT:                puts("OUTPUT_DEBUG_STRING_EVENT");                break;            default:                break;            }            if (!isContinue)            {                break;            }            isContinue = ContinueDebugEvent(dbgEvent.dwProcessId,dbgEvent.dwThreadId,ret?DBG_CONTINUE:DBG_EXCEPTION_NOT_HANDLED);           }        CloseHandle(pi.hProcess);        CloseHandle(pi.hThread);    }    else    {        printf("创建进程失败:%d\n",GetLastError());    }    system("pause");    return 0;}BOOL BreakPointHandler(EXCEPTION_DEBUG_INFO* info){    if (SysInt3)    {        SysInt3 = FALSE;        return TRUE;    }            EXCEPTION_RECORD record = info->ExceptionRecord;    WriteProcessMemory(pi.hProcess,record.ExceptionAddress,&bInt3,1,NULL);    printf(">> BreakPointHandler - Addr : 0x%X\n",record.ExceptionAddress);            WaitUserInput();            CONTEXT context;    context.ContextFlags = CONTEXT_FULL |CONTEXT_DEBUG_REGISTERS;    GetThreadContext(pi.hThread,&context);    context.Eip--;    SetThreadContext(pi.hThread,&context);    return TRUE;}BOOL MemBreakPointHandler(EXCEPTION_DEBUG_INFO* info){    DWORD ExceptionADDR = info->ExceptionRecord.ExceptionInformation[1];    DWORD ExceptionAccess = info->ExceptionRecord.ExceptionInformation[0];            if (ExceptionADDR>>12 == buffer>>12)    //这里的判断是不对的,但为了做示例足够了    {        printf("内存执行断点:%x %x\n",ExceptionADDR,ExceptionAccess);        WaitUserInput();        if (!VirtualProtectEx(pi.hProcess,(void*)buffer,1,orignExcuteAccess,&orignExcuteAccess))        {            printf("内存断点修复失败:%d\n",GetLastError());        }        return TRUE;    }    return FALSE;}BOOL SingleStepHandler(EXCEPTION_DEBUG_INFO* info){            CONTEXT context;    context.ContextFlags = CONTEXT_FULL |CONTEXT_DEBUG_REGISTERS;    GetThreadContext(pi.hThread,&context);            if (context.Dr6&0xF)    {        puts("硬件断点被触发!");        context.Dr7&=~1;    }    else    {        printf("单步中……:0x%X\n",context.Eip);    }            WaitUserInput();    SetThreadContext(pi.hThread,&context);    return TRUE;}void WaitUserInput(){    bool p = true;    char cmd = 0;    while(p)    {                /*************清空缓冲区**************/        scanf("%*[^\n]");        scanf("%*c");        /*************************************/        printf("COMMAND>> ");        cmd = getchar();        switch (cmd)        {        case 'g':            p = false;            break;        default:            break;        }    }}

??硬件断点的实现十分简单,就不赘述了,如下是实验效果图:

单步步入

??在调试中我们经常一条指令一条指令的进行调试,这大大方便了我们查阅结果,CPU提供了这样的基址,就是在Eflag中的TF位实现的,如下图所示:

??好我们继续在之前的代码基础上扩展单步功能:

#include "stdafx.h"#include <windows.h>#include <stdlib.h>BOOL SysInt3 = TRUE;void WaitUserInput();BOOL BreakPointHandler(EXCEPTION_DEBUG_INFO* info);BOOL MemBreakPointHandler(EXCEPTION_DEBUG_INFO* info);BOOL SingleStepHandler(EXCEPTION_DEBUG_INFO* info);const UCHAR Int3 = 0xCC;UCHAR bInt3;PROCESS_INFORMATION pi ;DWORD orignExcuteAccess;DWORD buffer;#define DBGBREAKPOINT//#define MemBREAKPOINT//#define HardwareBREAKPOINTint main(int argc, char* argv[]){    char filename[]= "C:\\WINDOWS\\NOTEPAD.EXE";    STARTUPINFO si ={sizeof(STARTUPINFO)};    DEBUG_EVENT dbgEvent;    BOOL isContinue = TRUE;        BOOL ret =CreateProcess(NULL,filename,NULL,NULL,FALSE,DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi);        if (ret)    {        while (isContinue)        {            ret = WaitForDebugEvent(&dbgEvent,INFINITE);            if (!ret)            {                printf("WaitForDebugEvent 出错:%d",GetLastError());                break;            }                        EXCEPTION_DEBUG_INFO info = dbgEvent.u.Exception;            switch (dbgEvent.dwDebugEventCode)            {            case EXCEPTION_DEBUG_EVENT:                puts("EXCEPTION_DEBUG_EVENT");                switch (info.ExceptionRecord.ExceptionCode)                {                case EXCEPTION_BREAKPOINT:                    ret = BreakPointHandler(&info);                    break;                case EXCEPTION_ACCESS_VIOLATION:                    ret = MemBreakPointHandler(&info);                    break;                case EXCEPTION_SINGLE_STEP:                    ret = SingleStepHandler(&info);                    break;                default:                    ret = FALSE;                    break;                }                if (!ret)                {                    printf("出错:%d\n",GetLastError());                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                isContinue = ret;                break;            case CREATE_THREAD_DEBUG_EVENT:                puts("CREATE_THREAD_DEBUG_EVENT");                break;            case CREATE_PROCESS_DEBUG_EVENT:                puts("CREATE_PROCESS_DEBUG_EVENT");                        #ifdef DBGBREAKPOINT                                buffer = (DWORD)dbgEvent.u.CreateProcessInfo.lpStartAddress;                                if (!ReadProcessMemory(pi.hProcess,(void*)buffer,&bInt3,1,NULL))                {                    puts("软件断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                                if (!WriteProcessMemory(pi.hProcess,(void*)buffer,(void*)&Int3,1,NULL))                {                    puts("软件断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                #endif                #ifdef MemBREAKPOINT                buffer = (DWORD)dbgEvent.u.CreateProcessInfo.lpStartAddress;                            if (!VirtualProtectEx(pi.hProcess,(void*)buffer,1,PAGE_NOACCESS,&orignExcuteAccess))                {                    puts("内存执行断点设置失败!");                    isContinue = FALSE;                    TerminateProcess(pi.hProcess,0);                }                printf("内存访问断点的位置:0x%X\n",buffer);#endif                #ifdef HardwareBREAKPOINT                SuspendThread(pi.hThread);                puts("请输入要下断点的位置:");                scanf("%x",&buffer);                CONTEXT context;                context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;                GetThreadContext(pi.hThread,&context);                context.Dr0 = buffer;                context.Dr7 |= 1;                if (!SetThreadContext(pi.hThread,&context))                {                    puts("硬件断点设置失败!");                }                ResumeThread(pi.hThread);#endif                break;            case EXIT_THREAD_DEBUG_EVENT:                puts("EXIT_THREAD_DEBUG_EVENT");                break;            case EXIT_PROCESS_DEBUG_EVENT:                puts("EXIT_PROCESS_DEBUG_EVENT");                                break;            case LOAD_DLL_DEBUG_EVENT:                puts("LOAD_DLL_DEBUG_EVENT");                break;                 case UNLOAD_DLL_DEBUG_EVENT:                puts("UNLOAD_DLL_DEBUG_EVENT");                break;            case OUTPUT_DEBUG_STRING_EVENT:                puts("OUTPUT_DEBUG_STRING_EVENT");                break;            default:                break;            }                        if (!isContinue)            {                break;            }            isContinue = ContinueDebugEvent(dbgEvent.dwProcessId,dbgEvent.dwThreadId,ret?DBG_CONTINUE:DBG_EXCEPTION_NOT_HANDLED);                    }        CloseHandle(pi.hProcess);        CloseHandle(pi.hThread);    }    else    {        printf("创建进程失败:%d\n",GetLastError());    }    system("pause");    return 0;}BOOL BreakPointHandler(EXCEPTION_DEBUG_INFO* info){    if (SysInt3)    {        SysInt3 = FALSE;        return TRUE;    }        EXCEPTION_RECORD record = info->ExceptionRecord;    WriteProcessMemory(pi.hProcess,record.ExceptionAddress,&bInt3,1,NULL);    printf(">> BreakPointHandler - Addr : 0x%X\n",record.ExceptionAddress);        WaitUserInput();        SuspendThread(pi.hThread);    CONTEXT context;    context.ContextFlags = CONTEXT_FULL |CONTEXT_DEBUG_REGISTERS;    GetThreadContext(pi.hThread,&context);    context.Eip--;    SetThreadContext(pi.hThread,&context);    ResumeThread(pi.hThread);    return TRUE;}BOOL MemBreakPointHandler(EXCEPTION_DEBUG_INFO* info){    DWORD ExceptionADDR = info->ExceptionRecord.ExceptionInformation[1];    DWORD ExceptionAccess = info->ExceptionRecord.ExceptionInformation[0];        if (ExceptionADDR>>12 == buffer>>12)    //这里的判断是不对的,但为了做示例足够了    {        printf("内存执行断点:%x %x\n",ExceptionADDR,ExceptionAccess);        WaitUserInput();        if (!VirtualProtectEx(pi.hProcess,(void*)buffer,1,orignExcuteAccess,&orignExcuteAccess))        {            printf("内存断点修复失败:%d\n",GetLastError());        }        return TRUE;    }    return FALSE;}BOOL SingleStepHandler(EXCEPTION_DEBUG_INFO* info){        CONTEXT context;    context.ContextFlags = CONTEXT_FULL |CONTEXT_DEBUG_REGISTERS;    SuspendThread(pi.hThread);    GetThreadContext(pi.hThread,&context);        if (context.Dr6&0xF)    {        puts("硬件断点被触发!");        context.Dr7&=~1;    }    else    {        printf("单步中:0x%X\n",context.Eip);        context.EFlags &= ~0x100;    }        SetThreadContext(pi.hThread,&context);    ResumeThread(pi.hThread);    WaitUserInput();    return TRUE;}void WaitUserInput(){    bool p = true;    char cmd = 0;    while(p)    {                /*************清空缓冲区**************/        scanf("%*[^\n]");        scanf("%*c");        /*************************************/        printf("COMMAND>> ");        cmd = getchar();        switch (cmd)        {        case 'g':            p = false;            break;        case 't':            p=false;            SuspendThread(pi.hThread);            CONTEXT context;            context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;            GetThreadContext(pi.hThread,&context);            context.EFlags |= 0x100;            if (!SetThreadContext(pi.hThread,&context))            {                puts("单步设置失败!");            }            ResumeThread(pi.hThread);            break;        default:            break;        }    }}

??单步是实现比较简单,如下是效果图:

单步步过

??单步步过和单步步入不同,单步步入会逐个走指令,到达call执行会进入,而单步步过不会进入。单步步过是可以基于硬件断点或者软件断点实现。至于实现过程我就不赘述了。

小结

??本篇文章主要介绍了调试器断点和单步的实现相关基本知识,如果要真正的实现一个调试器,还需要大量的实现。在总结与提升篇,我还会详细介绍所有断点和单步异常的内核处理流程。

下一篇

??调试篇——总结与提升

知识共享许可协议
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟
转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15960780.html
posted @ 2022-03-03 17:00 寂静的羽夏 阅读(11) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员