大家好,我是武汉科锐逆向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
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2025-5-23 14:52
被kanxue编辑
,原因: