首页
社区
课程
招聘
[原创]调试器开发技术-Linux调试器与Windows调试器实现细节探究
发表于: 2025-5-22 04:43 1109

[原创]调试器开发技术-Linux调试器与Windows调试器实现细节探究

2025-5-22 04:43
1109

大家好,我是武汉科锐逆向CR49班的一名学员,这篇文章的受众是面向哪些想要开发Linux Armv8a架构调试器的童鞋们,可以以此篇文章作为参考,我将利用我开发两款调试器的经验,用尽量简单的语言清晰的叙述,关键技术细节,此篇文章中因为篇幅有限我们当且仅探讨Linux调试器开发中的详细细节,以及部分windows调试器单步细节实现,对于windwos调试器由于我是用纯汇编实现的所以我会尽量翻译成C语言方便大家理解。

前置知识: C/C++ Linux 调试部分API,Windows 调试部分API, ArmV8a指令集, X86指令集

语言: C/C++

首先我们还是学术一点给出调试器的定义摘自Wiki
调试器(英语:Debugger)亦称调试程序、调试工具,指一种用于调试其它程序的计算机程序及工具。能够让代码在指令组模拟器(ISS)中可以检查运行状况以及选择性地运行,以便排错、调试。

Linux系统下和Windows系统下的调试原理是不同的,windows下的调试是基于异常派发机制来实现的(所以理论上windows的调试机制可玩性更高比如无痕调试,就是通过接管系统调试函数的某个未被PG保护的函数指针配合异常指令来实现无附加调试的),而Linux下的调试是基于信号机制 ,如下两个APIptracewaitpid来实现的。
首先我们讨论windows下的调试原理,windows下我们先给出异常传播流程图帮助大家理解。
图片描述
在windows中我们的调试器有两次机会处理其异常,

这就是Windows下的调试器的工作原理,我们这套流程可以通过windows自带的Dbghelp库来完成。
现在我们来说说Linux调试器的流程,Linux调试器与Windows调试器的最大不同是Linux系统是通过信号来实现的,我们通常使用如下两个API

来实现调试器的功能,在Arm-v8a架构中我们通常使用特殊断点指令BRK与winodows原理的前半段差不多,打开进程然后写入(这里一般会用ptrace的 PTRACE_POKEDATA因为可以写入代码段)特殊断点指令,然后触发异常后会发送信号到调试进程,在调试进程利用waitpid接收到之后会等待用户的输入操作,与windows最大的不同是没有所谓的多次异常的机会。其大体写法上其实都可以看作对某种信号的抽象的响应,只不过一个是异常传递,另一个是信号传递。
同样的我们还是给出Linux系统中调试器的原理图方便大家理解。
图片描述
其整体工作流程和Windows都差不多,这里就不在重复了。

虽说在两个平台下调试器的原理都差不多,但是由于断点指令在两个平台下的表现不同所以导致了在两个平台下的一些实现细节的不同,下面我们来探讨两个断点指令的不同,首先在Linux平台Arm-v8a架构下我们使用BRK指令这个指令会在执行之前会暂停,而Windows平台下x86汇编中的INT3指令会在指令执行之后才会断下,有如下例子

如上例在40A01000处我们有两条断点指令,其中在Linux的环境下断点的触发位置在40A01000而在Windows中断点的触发位置在40A1001处(即EIP=0x40A1001)我们辅以下图以助理解
图片描述
可以很明显的看到其断点位置的区别。正是这个区别导致了其断步配合的写法上有些许不同,也是本文的核心所在。

在了解具体实现细节之前我们需要知道断步配合是什么东西?
为什么需要断步配合?
在解答这个问题之前我们需要知道调试器作为一种侵入式的软件,如果不做特殊处理的话是一定会对程序的控制流程产生影响的,即当我们往正常的代码部分插入断点指令,则一定会使得这个位置发出异常而并非常规的执行逻辑。为了消除断点断下后调试器接受到信号之后被调试程序依旧需要保持原有的行为,我们就需要进行断步配合。
而所谓的断步配合 就是利用断点和单步相互配合使其被调试程序保持正常行为的一种编程方式。接下来我们详细讨论windows和linux下的断步配合的编写和详细实现细节。
首先是Windows,我们列出常用的断步配合的以下步骤

而在Linux情况下我们一般如下进行处理

接下来我们讨论下反汇编的代码处理,原理也很简单在反汇编的时候先判断断点然后写入原有的指令之后再重新写入断点

为了使得能够写入代码段内存,所以我们采用ptrace的PTRACE_POKEDATA 分块写入内存

调试器的开发难度并不在编程思路上,而是在边界条件的处理上,当我们没法处理好边界条件时将会出现各种难以调试的bug,最后谢谢大家的关注,如果感觉文章对您有帮助可以点点赞和收藏。

; Linux
40A01000 D4 20 00 00 BRK
 
; Windows
40A01000 CC INT3
; Linux
40A01000 D4 20 00 00 BRK
 
; Windows
40A01000 CC INT3
bool g_stepSingle = false;
DWORD g_dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
 
 
void HandleSingleStep(Regs& reg)
{   
    // 获取寄存器
    CONTEXT Ctx;
    if (GetRegs(&Ctx) < 0)
        return -1;
    //...
    if (g_stepSingle)
    {
        // 重新设置断点
        SetBreakPoint(Ctx.Eip + 1);
        g_dwContinueStatus  = DBG_CONTINUE;
    }
    //...
}
 
void HandleDbg()
{
    //... 这里需要做一些判断比如是否为自己的断点
    // 获取寄存器
    CONTEXT Ctx;
    if (GetRegs(&Ctx) < 0)
        return;
    if (IsMyBreak(Ctx.Eip))
    {
        ReserveBreakPoint(Ctx.Eip);
        g_stepSingle = true;
        Ctx.Eip -= 1;
        // 跳到单步
        SetSingleStep();
         g_dwContinueStatus  = DBG_CONTINUE;
    }
}
bool g_stepSingle = false;
DWORD g_dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
 
 
void HandleSingleStep(Regs& reg)
{   
    // 获取寄存器
    CONTEXT Ctx;
    if (GetRegs(&Ctx) < 0)
        return -1;
    //...
    if (g_stepSingle)
    {
        // 重新设置断点
        SetBreakPoint(Ctx.Eip + 1);
        g_dwContinueStatus  = DBG_CONTINUE;
    }
    //...
}
 
void HandleDbg()
{
    //... 这里需要做一些判断比如是否为自己的断点
    // 获取寄存器
    CONTEXT Ctx;
    if (GetRegs(&Ctx) < 0)
        return;
    if (IsMyBreak(Ctx.Eip))
    {
        ReserveBreakPoint(Ctx.Eip);
        g_stepSingle = true;
        Ctx.Eip -= 1;
        // 跳到单步
        SetSingleStep();
         g_dwContinueStatus  = DBG_CONTINUE;
    }
}
long WaitProcess()
{
    long result = waitpid(env.pid, nullptr, WNOHANG);
 
    if (result == -1)
    {
        printf("waitpid err: %s",strerror(errno));
        return result;
    }
 
    if (result == 0)
    {
        return result;
    }
 
    // 目标进程来了
    siginfo_t si{};
    result = ptrace(PTRACE_GETSIGINFO, env.pid, nullptr, &si);
    if (result == -1)
    {
        printf("ptrace PTRACE_GETSIGINFO err: %s",strerror(errno));
        return result;
    }
 
    /*
    如果断点来了可以通过sig_signo 大致做出区分其中,sig_code 做出细分即可
    */
    // printf("signo:%d, errno:%d, sigcode:%d\n", si.si_signo, si.si_errno, si.si_code);
    user_regs_struct reg{};
 
    /* DOR 调用 */
    CCmdReg* pReg = (CCmdReg*)g_cmd[(int)DOR_IDX::EnumCmdReg];
    CCmdBp* pBp = (CCmdBp*)g_cmd[(int)DOR_IDX::EnumCmdBp];
    CCmdStepInto* pStepIn = (CCmdStepInto*)g_cmd[(int)DOR_IDX::EnumStepInto];
    CCmdMem* pMem = (CCmdMem*)g_cmd[(int)DOR_IDX::EnumCmdMem];
    /* 读寄存器 */
    pReg->ReadReg(&reg);
    env.tag_currentAddress = reg.pc;
    env.tag_disasmAddress = reg.pc;
 
 
    uint8_t insn[12]{0};
 
    /* 断步配合 */
    if (env.tag_stepBpFlag)
    {
        pBp->SetBp(env.tag_stepBpReverseAddr);
        /* 重置断步配合标志 */
        env.tag_stepBpReverseAddr = 0;
        env.tag_stepBpFlag = false;
    }
 
    if (env.tag_resumeFlag)
    {
        env.tag_resumeFlag = false;
        CCmdResume* pResume = (CCmdResume*)g_cmd[(int)DOR_IDX::EnumCmdResume];
        pResume ->Resume();
        return result;
    }
 
 
    switch(si.si_signo)
    {
        case SIGSTOP:
        {
            printf("\nprocess stop.\n");
            break;
        }
        case SIGTRAP:
        {
            switch (si.si_code) {
                case TRAP_BRKPT:
                {
 
                    pReg->ShowReg(&reg);
                    if (env.tag_stepOverFlag)
                    {
                        pMem->WriteMem(env.tag_currentAddress, env.tag_stepOverBuffer, 4);
                        env.tag_stepOverFlag = false;
                    }
 
                    if (env.tag_stepBpResFlag)
                    {
                        pBp->SetBp(env.tag_stepBpResAddr);
                        env.tag_stepBpResFlag = false;
                        env.tag_stepBpResAddr = 0;
                    }
 
                    if (pBp->IsBpExist(reg.pc))
                    {
                        printf("\nHit bp! at 0x%lx\n", reg.pc);
 
                        //pMem->WriteMem(env.tag_currentAddress, env.tag_stepOverBuffer, 4);
                        pBp->ReserveMem(reg.pc);
 
                        env.tag_stepBpReverseAddr = reg.pc;
                        env.tag_stepBpFlag = true;
 
                        // pStepIn->SetSingleStep();
                    }
                    //}
 
                    pMem->ReadMem(reg.pc, insn, 12);
                    for (int i = 0; i < 3; i++)
                    {
                        env.disasm.Disasm(reg.pc + i * 4, insn + i * 4, 4);
                    }
 
                    break;
                }
                case TRAP_TRACE:
                {
                    if (env.tag_stepBpResFlag)
                    {
                        pBp->SetBp(env.tag_stepBpResAddr);
                        env.tag_stepBpResFlag = false;
                        env.tag_stepBpResAddr = 0;
                    }
 
                    if (env.tag_traceFlag)
                    {
                        if (reg.pc == env.tag_traceTargetAddr)
                        {
                            env.tag_traceTargetAddr = 0;
                            env.tag_traceFlag = false;
 
                        }
                        else
                        {
                            pMem->ReadMem(reg.pc, insn, 12);
                            for (int i = 0; i < 1; i++) {
                                env.disasm.Disasm(reg.pc + i * 4, insn + i * 4, 4);
                            }
                            pStepIn->SetSingleStep();
                            break;
                        }
                    }
 
 
                    pReg->ShowReg(&reg);
                    pMem->ReadMem(reg.pc, insn, 12);
                    for (int i = 0; i < 3; i++)
                    {
                        env.disasm.Disasm(reg.pc + i * 4, insn + i * 4, 4);
                    }
                    break;
                }
            }
            break;
        }
    }
    return result;
}
long WaitProcess()
{
    long result = waitpid(env.pid, nullptr, WNOHANG);
 
    if (result == -1)
    {
        printf("waitpid err: %s",strerror(errno));
        return result;
    }
 
    if (result == 0)
    {
        return result;
    }
 
    // 目标进程来了
    siginfo_t si{};
    result = ptrace(PTRACE_GETSIGINFO, env.pid, nullptr, &si);
    if (result == -1)
    {
        printf("ptrace PTRACE_GETSIGINFO err: %s",strerror(errno));
        return result;
    }
 
    /*
    如果断点来了可以通过sig_signo 大致做出区分其中,sig_code 做出细分即可
    */
    // printf("signo:%d, errno:%d, sigcode:%d\n", si.si_signo, si.si_errno, si.si_code);
    user_regs_struct reg{};
 
    /* DOR 调用 */
    CCmdReg* pReg = (CCmdReg*)g_cmd[(int)DOR_IDX::EnumCmdReg];
    CCmdBp* pBp = (CCmdBp*)g_cmd[(int)DOR_IDX::EnumCmdBp];
    CCmdStepInto* pStepIn = (CCmdStepInto*)g_cmd[(int)DOR_IDX::EnumStepInto];
    CCmdMem* pMem = (CCmdMem*)g_cmd[(int)DOR_IDX::EnumCmdMem];
    /* 读寄存器 */
    pReg->ReadReg(&reg);
    env.tag_currentAddress = reg.pc;
    env.tag_disasmAddress = reg.pc;
 
 
    uint8_t insn[12]{0};
 
    /* 断步配合 */
    if (env.tag_stepBpFlag)
    {
        pBp->SetBp(env.tag_stepBpReverseAddr);
        /* 重置断步配合标志 */
        env.tag_stepBpReverseAddr = 0;
        env.tag_stepBpFlag = false;
    }
 
    if (env.tag_resumeFlag)
    {
        env.tag_resumeFlag = false;
        CCmdResume* pResume = (CCmdResume*)g_cmd[(int)DOR_IDX::EnumCmdResume];
        pResume ->Resume();
        return result;
    }
 
 
    switch(si.si_signo)
    {
        case SIGSTOP:
        {
            printf("\nprocess stop.\n");
            break;
        }
        case SIGTRAP:
        {
            switch (si.si_code) {
                case TRAP_BRKPT:
                {
 
                    pReg->ShowReg(&reg);
                    if (env.tag_stepOverFlag)
                    {
                        pMem->WriteMem(env.tag_currentAddress, env.tag_stepOverBuffer, 4);
                        env.tag_stepOverFlag = false;
                    }
 
                    if (env.tag_stepBpResFlag)
                    {
                        pBp->SetBp(env.tag_stepBpResAddr);
                        env.tag_stepBpResFlag = false;
                        env.tag_stepBpResAddr = 0;
                    }
 
                    if (pBp->IsBpExist(reg.pc))
                    {
                        printf("\nHit bp! at 0x%lx\n", reg.pc);
 
                        //pMem->WriteMem(env.tag_currentAddress, env.tag_stepOverBuffer, 4);
                        pBp->ReserveMem(reg.pc);
 
                        env.tag_stepBpReverseAddr = reg.pc;
                        env.tag_stepBpFlag = true;
 
                        // pStepIn->SetSingleStep();
                    }
                    //}
 
                    pMem->ReadMem(reg.pc, insn, 12);
                    for (int i = 0; i < 3; i++)
                    {
                        env.disasm.Disasm(reg.pc + i * 4, insn + i * 4, 4);
                    }
 
                    break;
                }
                case TRAP_TRACE:
                {
                    if (env.tag_stepBpResFlag)
                    {
                        pBp->SetBp(env.tag_stepBpResAddr);
                        env.tag_stepBpResFlag = false;
                        env.tag_stepBpResAddr = 0;
                    }
 
                    if (env.tag_traceFlag)
                    {
                        if (reg.pc == env.tag_traceTargetAddr)
                        {
                            env.tag_traceTargetAddr = 0;
                            env.tag_traceFlag = false;
 
                        }
                        else
                        {
                            pMem->ReadMem(reg.pc, insn, 12);
                            for (int i = 0; i < 1; i++) {
                                env.disasm.Disasm(reg.pc + i * 4, insn + i * 4, 4);
                            }
                            pStepIn->SetSingleStep();
                            break;
                        }
                    }
 
 
                    pReg->ShowReg(&reg);
                    pMem->ReadMem(reg.pc, insn, 12);
                    for (int i = 0; i < 3; i++)
                    {
                        env.disasm.Disasm(reg.pc + i * 4, insn + i * 4, 4);
                    }
                    break;
                }
            }
            break;
        }
    }
    return result;
}
void Disasm()
{
    // ...
    if (IsMyBreakPoint())
    {
        ReserveBreakPoint();
        // 在这里进行反汇编操作,通常这里是单条指令的反汇编操作
        SetBreakPoint();
    }
    // ...
}
void Disasm()
{
    // ...
    if (IsMyBreakPoint())
    {
        ReserveBreakPoint();
        // 在这里进行反汇编操作,通常这里是单条指令的反汇编操作
        SetBreakPoint();
    }
    // ...
}
bool WriteMem(uint64_t addr, uint8_t * pszBuf, size_t  size) {
        printf("addr: 0x%lx size: %zu\n", addr, size);
        if (size == 0)
        {
            printf("写入为0");
            return true;
        }
        bool ret = true;
 
        /* 轮数 */
        int nBatch = size / sizeof(void *);
 
        /* 剩余字节数 */
        int nTail = size - sizeof(void *) * nBatch;
 
        /* 已经写入的字节数 */
        size_t nWrite = 0;
 
        /* 二级指针用于写入数据分块 */
        void **ppszBuffer = (void **) malloc(sizeof(void *));
 
        if (!ppszBuffer)
        {
            return false;
        }
 
 
        /* 写入轮数 */
        for (int i = 0; i < nBatch; i++) {
            memcpy(ppszBuffer, &(pszBuf[i * sizeof(void *)]), sizeof(void *));
 
            long result = ptrace(PTRACE_POKEDATA, env.pid, (void *) (addr + nWrite), *ppszBuffer);
 
            if (result == -1) {
                printf("Write err: %s\n", strerror(errno));
                ret = false;
                break;
            }
            /* 增加写入计数 */
            nWrite += sizeof(void *);
        }
        free(ppszBuffer);
 
        if (!ret)
        {
            return ret;
        }
 
 
        /* 尾部数据写入缓冲区 */
        uint8_t *pszTailData = (uint8_t *) malloc(sizeof(void *));
 
        if (!pszTailData)
        {
            return false;
        }
        /* 先读再写 */
        if (!ReadMem((uint64_t) (addr + nWrite), pszTailData, sizeof(void *)))
        {
            ret = false;
        }
 
        if (!ret)
        {
            free(pszTailData);
            return ret;
        }
 
 
        for (int i = 0; i < nTail; i++) {
            pszTailData[i] = pszBuf[nWrite + i];
        }
 
        /* 写回目标进程 */
        long result = ptrace(PTRACE_POKEDATA, env.pid, (void *) (addr + nWrite),
                             *(void **) pszTailData);
 
        if (result == -1) {
            printf("Write err: %s\n", strerror(errno));
            ret = false;
        } else {
            nWrite += nTail;
        }
 
        free(pszTailData);
        //printf("写入完成: %lu/%lu\n", nWrite, size);
 
        return ret;
    }
bool WriteMem(uint64_t addr, uint8_t * pszBuf, size_t  size) {
        printf("addr: 0x%lx size: %zu\n", addr, size);
        if (size == 0)
        {
            printf("写入为0");
            return true;
        }
        bool ret = true;
 
        /* 轮数 */
        int nBatch = size / sizeof(void *);
 
        /* 剩余字节数 */
        int nTail = size - sizeof(void *) * nBatch;
 
        /* 已经写入的字节数 */
        size_t nWrite = 0;
 
        /* 二级指针用于写入数据分块 */
        void **ppszBuffer = (void **) malloc(sizeof(void *));
 
        if (!ppszBuffer)
        {
            return false;
        }
 
 
        /* 写入轮数 */
        for (int i = 0; i < nBatch; i++) {
            memcpy(ppszBuffer, &(pszBuf[i * sizeof(void *)]), sizeof(void *));
 
            long result = ptrace(PTRACE_POKEDATA, env.pid, (void *) (addr + nWrite), *ppszBuffer);
 
            if (result == -1) {
                printf("Write err: %s\n", strerror(errno));
                ret = false;
                break;
            }
            /* 增加写入计数 */
            nWrite += sizeof(void *);
        }
        free(ppszBuffer);
 
        if (!ret)
        {
            return ret;
        }
 
 
        /* 尾部数据写入缓冲区 */
        uint8_t *pszTailData = (uint8_t *) malloc(sizeof(void *));
 
        if (!pszTailData)
        {
            return false;
        }
        /* 先读再写 */
        if (!ReadMem((uint64_t) (addr + nWrite), pszTailData, sizeof(void *)))
        {
            ret = false;
        }
 
        if (!ret)
        {
            free(pszTailData);
            return ret;
        }
 
 
        for (int i = 0; i < nTail; i++) {
            pszTailData[i] = pszBuf[nWrite + i];
        }
 
        /* 写回目标进程 */
        long result = ptrace(PTRACE_POKEDATA, env.pid, (void *) (addr + nWrite),
                             *(void **) pszTailData);
 
        if (result == -1) {
            printf("Write err: %s\n", strerror(errno));
            ret = false;
        } else {
            nWrite += nTail;
        }
 
        free(pszTailData);
        //printf("写入完成: %lu/%lu\n", nWrite, size);
 
        return ret;
    }
handleBreakPoint proc uses esi ebx edx ecx
    LOCAL ctx:CONTEXT
    LOCAL lpAddress:DWORD
 
    mov esi, offset g_dbgEvent
    add esi, 0ch
    assume esi:ptr EXCEPTION_RECORD
 
    ; lpAddress
    mov eax, [esi].ExceptionAddress
    mov dword ptr lpAddress, eax
 
    mov dword ptr g_dwCotinueStatus, DBG_EXCEPTION_NOT_HANDLED
 
     
    ; 这里需要满足 有g_stepOverFlag 同时还得让步过地址等于设置的步过地址
    mov edx, g_stepOverFlag
    mov ebx, g_stepBpAddress
    mov ecx, lpAddress
     
    .if edx == 1 && ebx == ecx
        ; 设置showAsm默认反汇编位置
        mov ebx, lpAddress
        mov dword ptr g_dwCurrentShowAddr, ebx
        ; 这里需要设置断点
        invoke getNode, lpAddress
        mov esi, eax
        assume esi:ptr BreakPointNode
        ; writeMemory proc uses esi ebx lpAddress:DWORD, lpBuffer:DWORD, dwSize:DWORD
        ; 还原代码
        lea eax, [esi].tag_szOrgCode
        invoke writeMemory, lpAddress, eax,1
        ; change eip - 1
        lea eax, ctx
        invoke getRegs, eax
        lea eax, ctx
        assume eax:ptr CONTEXT
        mov ebx, [eax].regEip
        dec ebx
        mov [eax].regEip, ebx
        invoke setRegs, eax
        ;invoke queryRegs
        ; 仅展示3
        invoke showAsm, lpAddress, 3
        mov eax, 0
        mov g_stepOverFlag, eax
        invoke removeBreakPoint, lpAddress
        invoke cmdHandle
        mov dword ptr g_dwCotinueStatus, DBG_CONTINUE
    .endif
 
 
    ; 判断是否为自己的断点
    invoke getNode, lpAddress
     
    .if eax != FAIL
        ; 设置showAsm默认反汇编位置
        mov ebx, lpAddress
        mov dword ptr g_dwCurrentShowAddr, ebx
        mov esi, eax
        assume esi:ptr BreakPointNode
        ; writeMemory proc uses esi ebx lpAddress:DWORD, lpBuffer:DWORD, dwSize:DWORD
        ; 还原代码
        lea eax, [esi].tag_szOrgCode
        invoke writeMemory, lpAddress, eax,1
        ; change eip - 1
        lea eax, ctx
        invoke getRegs, eax
        lea eax, ctx
        assume eax:ptr CONTEXT
        mov ebx, [eax].regEip
        dec ebx
        mov [eax].regEip, ebx
        invoke setRegs, eax
        ;invoke queryRegs
        ; 仅展示3
        invoke showAsm, lpAddress, 3
        ; 断步配合是为了保留将该断点设置为长期断点而不是临时断点
        mov dword ptr g_isBpFlag, 1
        mov eax, lpAddress
 
        mov dword ptr g_lpAddressForBpFlag, eax
        invoke setStepBp
        invoke cmdHandle
        mov dword ptr g_dwCotinueStatus, DBG_CONTINUE
    .endif
    ; 是否为系统断点
    xor eax, eax
    mov al, g_isSystemBp
    .if eax == 1
        mov ebx, lpAddress
        mov dword ptr g_dwCurrentShowAddr, ebx
        invoke showAsm, lpAddress, 3
        invoke setBreakPoint, 401000h
        ;invoke setHardBreakPoint, 401000h, 0, 1
        xor eax, eax
        mov g_isSystemBp, al
        invoke cmdHandle
        mov dword ptr g_dwCotinueStatus, DBG_CONTINUE
    .endif
     
 
    ret
handleBreakPoint endp
 
; todo hard break point
handleSingleStep proc uses esi ebx edx
    LOCAL ctx:CONTEXT
    ;LOCAL pStepOverAddress:DWORD
    LOCAL dwReadSize:DWORD
 
    LOCAL dwDr6:DWORD
    LOCAL dwDr7:DWORD
 
    lea esi, ctx
    invoke getRegs, esi
    assume esi:ptr CONTEXT
 
    ; 获取DR6寄存器
    mov eax, [esi].iDr6
    mov dwDr6, eax
 
    ; 获取DR7寄存器
    mov eax, [esi].iDr7
    mov dwDr7, eax
 
 
    mov esi, offset g_dbgEvent
    add esi, 0ch
    assume esi:ptr EXCEPTION_RECORD
    mov dword ptr g_dwCotinueStatus, DBG_EXCEPTION_NOT_HANDLED
 
    lea edx, ctx
    invoke getRegs, edx
    lea edx, ctx
    assume edx:ptr CONTEXT
    ; todo hard bp optinos !!!!
    ; B0
    mov eax, 01h
    and eax, dwDr6
    .if eax
        mov dword ptr g_dwCotinueStatus, DBG_CONTINUE
        mov eax, 00030000h
        and eax, dwDr7
        ; 判断该位是不是为0
        .if !eax
            ; 是0则需要设置L0为0
            mov eax, 0fffffffCh
            and eax,dwDr7
            mov dwDr7, eax
 
         
            mov [edx].iDr7, eax
            invoke setRegs, edx
            lea edx, ctx
        .endif
        invoke showAsm, [edx].regEip, 3
        ; 进入命令行
        invoke cmdHandle
    .endif
    ; B1
    mov eax, 02h
    and eax, dwDr6
    .if eax
        mov dword ptr g_dwCotinueStatus, DBG_CONTINUE
        mov eax, 00300000h
        and eax, dwDr7
        ; 判断该位是不是为0
        .if !eax
            ; 是0则需要设置L0为0
            mov eax, 0fffffff3h
            and eax,dwDr7
            mov dwDr7, eax
 
         
            mov [edx].iDr7, eax
            invoke setRegs, edx
            lea edx, ctx
        .endif
 
        invoke showAsm, [edx].regEip, 3
        ; 进入命令行
        invoke cmdHandle
    .endif
 
    ; B2
    mov eax, 04h
    and eax, dwDr6
    .if eax
        mov dword ptr g_dwCotinueStatus, DBG_CONTINUE
        mov eax, 03000000h
        and eax, dwDr7
        ; 判断该位是不是为0
        .if !eax
            ; 是0则需要设置L0为0
            mov eax, 0ffffffCfh
            and eax,dwDr7
            mov dwDr7, eax
 
         
            mov [edx].iDr7, eax
            invoke setRegs, edx
            lea edx, ctx
        .endif
        invoke showAsm, [edx].regEip, 3
        ; 进入命令行
        invoke cmdHandle
 
    .endif
    ; B3
    mov eax, 08h
    and eax, dwDr6
    .if eax
        mov dword ptr g_dwCotinueStatus, DBG_CONTINUE
        mov eax, 30000000h
        and eax, dwDr7
        ; 判断该位是不是为0
        .if !eax
            ; 是0则需要设置L0为0
            mov eax, 0ffffff3fh
            and eax,dwDr7
            mov dwDr7, eax
 
         
            mov [edx].iDr7, eax
            invoke setRegs, edx
            lea edx, ctx
        .endif
        invoke showAsm, [edx].regEip, 3
        ; 进入命令行
        invoke cmdHandle
 
    .endif
 
     
    .if g_isBpFlag
        xor eax, eax
        mov g_isBpFlag, eax
        mov dword ptr g_dwCotinueStatus, DBG_CONTINUE
 
        mov eax, 0fffh
        not eax
        and eax, g_lpAddressForBpFlag
        invoke resetBreakPoint, g_lpAddressForBpFlag
 
        ;invoke setBreakPoint, g_lpAddressForBpFlag
        ;.if eax == FAIL
        ;   invoke resetBreakPoint, g_lpAddressForBpFlag
        ;.endif
    .endif
 
     
    ; 判断单步条件
    mov eax, g_isStep
    .if eax
        ; 设置单步标志为FALSE
        xor eax, eax
        mov dword ptr g_isStep, eax
        ; 找寄存器环境
        lea esi, ctx
        ;invoke getRegs, esi
        assume esi:ptr CONTEXT
 
        invoke showAsm, [esi].regEip, 3
             
        ; 进入命令行
        invoke cmdHandle
        mov dword ptr g_dwCotinueStatus, DBG_CONTINUE
    .endif
 
 
    .if g_bmOtherPageFlag
        mov eax, g_bmOtherPageFlag
        xor eax, eax
        mov g_bmOtherPageFlag, eax
 
        invoke ezSetPageAttr, g_bmOtherPageAddress, 1h, PAGE_NOACCESS
    .endif
 
    .if g_bmFlagNode
        ; 将 g_bmFlagNode 置为 FALSE
        mov eax, g_bmFlagNode
        xor eax, eax
        mov g_bmFlagNode, eax
 
        ; 重新设置回
        mov edx, g_bmLasBmNodeAddress
        assume edx:ptr MemoryBreakPointNode
 
        invoke ezSetPageAttr, [edx].tag_page1,1h, PAGE_NOACCESS
        mov eax, [edx].tag_page2
        .if eax
            invoke ezSetPageAttr, [edx].tag_page2, 1h, PAGE_NOACCESS
        .endif
    .endif 
 
    ret
handleSingleStep endp
 
; mem bp work process
; 1. change target mem attr to PROCESS_NOACCESS
; 2. run code
; 3. recv exception C05
; 4. judge the condition, if the address between the page address and the pageaddr + offset
; then stop it and show the cmd and dasm
; else the address not in there that means,
; not our bp so we need to reset the attr in the page
; if we catch the C05 exception in our address then we need to set the
;  attr and run it then goto singel step to reset the page attr
handleBreakPoint proc uses esi ebx edx ecx
    LOCAL ctx:CONTEXT
    LOCAL lpAddress:DWORD
 
    mov esi, offset g_dbgEvent
    add esi, 0ch
    assume esi:ptr EXCEPTION_RECORD
 
    ; lpAddress
    mov eax, [esi].ExceptionAddress
    mov dword ptr lpAddress, eax
 
    mov dword ptr g_dwCotinueStatus, DBG_EXCEPTION_NOT_HANDLED
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2025-5-23 14:52 被kanxue编辑 ,原因:
收藏
免费 8
支持
分享
最新回复 (3)
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2025-5-22 09:29
0
雪    币: 116
活跃值: (2758)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
tql
2025-5-22 09:33
0
雪    币: 2733
活跃值: (3393)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
4
正好需要, 感謝分享!
2025-5-22 13:23
0
游客
登录 | 注册 方可回帖
返回