大家好,我是武汉科锐逆向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下的调试是基于信号机制 ,如下两个APIptrace和waitpid来实现的。
首先我们讨论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(®);
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(®);
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(®);
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(®);
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(®);
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(®);
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
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 2025-5-23 14:52
被kanxue编辑
,原因: