调试寄存器(debug registers, DRx)理论及实践
导读:
标 题:DRx寄存器的使用(待续) (4千字)
发信人:hume
时 间:2003-06-18 17:33:11
详细信息:
调试寄存器(DRx)理论与实践
By Hume/冷雨飘心
前言+牢骚:
生活的苦痛就象烈火,时时煎熬着伤痕累累疲惫不堪的那颗心。我拼力挣扎,然而却无济于事……
太残酷了….上帝也在苦笑。
题记很多人问Drx调试寄存器的用法,网上实际上有很多资料,但是很多人还是不肯去翻,于是我来写点东西,算是给对DRx尚有疑惑的解答吧。不要在QQ上问我任何问题,如果有错误,请来信指明:Humewen@21cn.com。
1.一点理论
=====
本文假设你已经知道调试器的使用方法以及bpx,bpm,int 3,异常都分别是什么,本文不是一篇超级扫盲教程。
Intel 80386以上的CPU提供了调试寄存器以用于软件调试。386和486包括6个调试寄存器:Dr0,Dr1,Dr2,Dr3,Dr6和Dr7。这些寄存器全是32位,如下图所示:|---------------|----------------|
Dr0| 用于一般断点的线性地址
|---------------|----------------|
Dr1| 用于一般断点的线性地址
|---------------|----------------|
Dr2| 用于一般断点的线性地址
|---------------|----------------|
Dr3| 用于一般断点的线性地址
|---------------|----------------|
Dr4| 保留
|---------------|----------------|
Dr5| 保留
|---------------|----------------|
Dr6| |BBB BBB B |
| |TSD 3 2 1 0 |
|---------------|----------------|
Dr7|RWE LEN ... RWE LEN | G GLGLGLGLGL |
| 3 3 ... 0 0 | D E E 3 3 2 21 100 |
|---------------|----------------|
31 15 0Dr0~3用于设置硬件断点,即在调试器中经常使用的bpm断点,由于只有4个断点寄存器,所以最多只能设置4个bpm断点。Dr7是一些控制位,用于控制断点的方式,Dr6用于显示是哪些引起断点的原因,如果是Dr0~3或单步(EFLAGS的TF)或由于GD置位时访问调试寄存器引起1号调试陷阱的话,则相应设置对应的位。下面对Dr6和Dr7的对应位做一些详细介绍:
调试控制寄存器Dr7:
==========
位0 L0和位1 G0:用于控制Dr0是全局断点还是局部断点,如果G0置位则是全局断点,L0置位则是局部断点。
G1L1~G3L3用于控制D1~Dr3,其功能同上。LEN0:占两个位,开始于位15,用于控制Dr0的断点长度,可能取值:
00 1字节
01 2字节
10 保留
11 4字节
RWE0:从第17位开始,占两个位,控制Dr0的断点是读、写还是执行断点或是I/O端口断点:
00 只执行
01 写入数据断点
10 I/O端口断点(只用于pentium+,需设置CR4的DE位)
11 读或写数据断点
RWE1~3,LEN1~3分别用于控制Dr1~3的断点方式,含义如上。还有一个GD位:用于保护DRx,如果GD位为1,则对Drx的任何访问都会导致进入1号调试陷阱。即IDT的对应入口,这样可以保证调试器在必要的时候完全控制Drx。
调试状态寄存器Dr6:
=========
该寄存器用于表示进入陷阱1的原因,各个位的含义如下:
B0~B3,如果其中任何一个位置位,则表示是相应的Dr0~3断点引发的调试陷阱。但还需注意的是,有时候不管GiLi如何设置,只要是遇到Drx指定的断点,总会设置Bi,如果看到多个Bi置位,则可以通过GiLi的情况判断究竟是哪个Dr寄存器引发的调试陷阱。
BD置位表示是GD位置位情况下访问调试寄存器引发的陷阱。
BT置位表示是因为TS置位即任务切换时TSS中TS位置1时切到第二个任务时第一条指令时引发的。
BS置位表示是单步中断引发的断点。。。。即EFLAGS的TF置位时引发的调试陷阱。注意I/O端口断点是586+以上CPU才有的功能,受CR4的DE位的控制,DE为1才有效。(DE是CR4的第3位)。
2、一点常识
======
如果你使用调试器的话,一定清楚bpx断点,bpx实际上就通过在代码中插入int 3(0xCC或0xCD03),将引发int 3中断。具体的intel IA-32保护模式异常机制并不是我三言两语能解释清楚的,如必要请参照相关资料。
Bpm和BPIO断点是利用CPU的硬件调试器设置的断点。
有些调试功能只在586+以上CPU才能使用,为增强兼容性用CPUID测试。(测试方法见我后续文章。)
关于调试API,是Windows提供给开发者的调试原API。具体实现涉及windows的内部机制,不是简单的int x就能解释清楚的,有兴趣者可参阅相关资料,或等我有时间胡说一通。3、一点实践
设置BPM断点很简单,只要相应设置Drx即可,产生的异常是STATUS_SINGLE_STEP,只要用调试API或SEH或VEH处理一下即可。下面是9X下测试BPM断点的一个例子:
A::->
-----------
COMMENT/*仅工作于9X下,BPM产生的异常类型是SINGLE_STEP
可用来对付调试器,反跟踪,至于效果,试试就知道了
这里采用SEH来设置9x下的Drx寄存器
还可采用GetThreadContext、SetThreadContext设置Drx的值
NT类系统由于安全机制,无法使用该办法
需使用Debug技术*/
include c:/hd/hhd.h
include c:/hd/drx.h
ASSUME FS:NOTHING
;~~~~~~~~~~~~~~~~~~~
.CODE
_StArT:
int 3
SLDT cx
JCXZ isNT
JMP @F
isNT:
MsgBox CTEXT("NT Series Not Work! only 9X!")
JMP _XXX_
@@:
call instSEHXH01 PROC C pExcept,pFrame,pContext,pDispatch Not minimal form
ASSUME ESI:PTR EXCEPTION_RECORD,EDI:PTR CONTEXT
MOV ESI,pExcept
MOV EDI,pContext
MOV EAX,1
TEST [ESI].ExceptionFlags,7
JNZ @@Not_handled
cmp [esi].ExceptionCode,STATUS_ILLEGAL_INSTRUCTION
jz illegal_instr
cmp [esi].ExceptionCode,STATUS_SINGLE_STEP
JZ BPM0_ISOK
jmp @@Not_handled
BPM0_ISOK:
MOV [EDI].regEip,OFFSET MSGbpmOK
JMP SEHexit//Set the Dr0 bpm Global BreakPoint
illegal_instr:
MOV [EDI].ContextFlags,CONTEXT_DEBUG_REGISTERS or CONTEXT_FULL
MOV [EDI].iDr7,M_INSTR0 or M_GDR0 or M_BYTE0
MOV [EDI].iDr0,OFFSET bpm01
//ByPASS the INVALID INSTRS
ADD [EDI].regEip,2SEHexit:
DEC EAX
@@Not_handled:
ret
XH01 ENDPinstSEH:
LEA eax,[esp-4]
XCHG eax,fs:[0]
push eaxNOP
DB 0Fh,0Bh INVALID INSTRS ON ALL PLATFORMS =UD2bpm01: SIMPLE ANTI DEBUGER
NOP
jmp bpm01_XXX_:
POP fs:[0]
pop EAX
invoke ExitProcess,0
MSGbpmOK:
MsgBox CTEXT("Hello BPM01 TEST SUC,Prepare TO EXIT")
JMP _XXX_
END _StArTWINNT下设置断点并非如此简单,由于严格的检查机制,原来的SEH的方法或简单的GetThreadContext方法已经失效。那么要我们写驱动吗?大可不必,因为WIndows还提供了调试API,使用调试API可以为目标程序设置断点:
WINNT下设置断点并非如此简单,由于严格的检查机制,原来的SEH的方法或简单的SetThreadContext方法已经失效。那么要我们写驱动吗?大可不必,因为WIndows还提供了调试API,使用调试API可以为目标程序设置断点。使用调试程序设置断点仍然使用GetThreadContext和SetThreadContext两个API。
在看下一个例子之前,还是让我们看看著名的IczeLion的教程中利用调试API检测程序执行了多少指令这一例子,这个例子在9X下运行正常,但在2K/Xp下却显示程序初始化失败。究其原因在于在2K/Xp下程序收到第一个断点异常时被调试程序的主线程内容并未准备好,是无效的!很奇怪但确实如此,这时根本不能通过CONTEXT设置断点或单步等。解决方案之一是Elicz提出来的,就是先在调试程序收到的第一个断点中断中给NtContinue设一bpm断点,截获其第一个参数,这个参数就是指向即将初始化为主线程有效CONTEXT内容的指针,通过在其内设置一个bpm断点,就可以中断在我们想要中断的任何位置。
下面是改造过的计算程序执行指令条数的例子,测试结果显示,一个很简单的程序,
include c:/hd/hhd.h
.DATA?
hInstance dd ?
;;-----------------------------------------
.CODE
_StArT:
nop
nop
mov hInstance,$invoke(GetModuleHandle,0)
MsgBox CTEXT("Hello World")invoke ExitProcess,0
END _StArT执行的内核代码+程序代码19196764条,如果只想获得程序执行的程序代码,还是有办法的,自己去想啦。
本文不是讲述调试API的使用,使用见IczeLion的教程:
;
include c:/hd/hhd.h
include c:/hd/drx.hBREAK_RVA EQU 401000H 断点。。。。
;~~~~~~~~~~~~~~~~~~~
.DATA
dwSScnt dd 0
sinstr dd 0
pi PROCESS_INFORMATION <>
sif STARTUPINFO
Dev DEBUG_EVENT <>
Rs CONTEXT
buf db 256 dup(?)
dwBuf dd 0
dwBytes dd 0
;;-----------------------------------------
.CODE
WipeContextBPdr0 Proc
invoke GetThreadContext,pi.hThread,addr Rs
mov Rs.iDr0,0
mov Rs.iDr7,0
invoke SetThreadContext,pi.hThread,addr Rs
ret
WipeContextBPdr0 Endp
;-----------------------------------------_StArT:
SLDT CX
JCXZ @F
MsgBox CTEXT("9x Not supported")
JMP pExit
@@:
invoke GetStartupInfo,addr sif
SUB EAX,EAX
invoke CreateProcess,0,CTEXT("target01.exe"),EAX,EAX,EAX,/
DEBUG_PROCESS or DEBUG_ONLY_THIS_PROCESS,EAX,EAX,addr sif,addr pi
JEAXZ crp_fail//some basic text macro
//why so many ppl use long names,aren't they tired ?
wmDevent EQU Dev.dwDebugEventCode
excCode EQU Dev.u.Exception.pExceptionRecord.ExceptionCode
excAddr EQU Dev.u.Exception.pExceptionRecord.ExceptionAddress
pDllName EQU Dev.u.LoadDll.lpImageName
lpBase EQU Dev.u.LoadDll.lpBaseOfDll
//.WHILE TRUE
invoke WaitForDebugEvent,addr Dev,INFINITE
mov
.IF wmDevent==EXIT_PROCESS_DEBUG_EVENT
MsgBox CTEXT("target Exit...")
.break
.ELSEIF wmDevent==LOAD_DLL_DEBUG_EVENT
//to see how many DLLS were LOADED
invoke wsprintf,addr buf,CTEXT("DLL BASE =: %08X"),lpBase
invoke MessageBox,0,addr buf,0,0
JMP DBG_con
.ELSEIF wmDevent==EXCEPTION_DEBUG_EVENT.if excCode==STATUS_BREAKPOINT
invoke GetThreadContext,pi.hThread,addr Rs
invoke GetProcAddress,$invoke(GetModuleHandle,CTEXT("NTDLL.DLL")),CTEXT("NtContinue")
JEAXZ end_debug
mov Rs.iDr0,EAX 设置NtContinue的断点
mov Rs.iDr7,M_LDR0 or M_INSTR0invoke SetThreadContext,pi.hThread,addr Rs
JMP DBG_con
.elseif excCode==STATUS_SINGLE_STEP
INC dwSScnt
.if dwSScnt==1//清除NtContinue的bpm断点
invoke WipeContextBPdr0
MOV EDX,Rs.regEsp
ADD EDX,4 //获取NtContinue的第一个参数pContext//读入pContext值,因为是两个进程要用ReadProcessMemory
invoke ReadProcessMemory,pi.hProcess,EDX,addr dwBuf,sizeof DWORD,addr dwBytes
//读入真正的CONTEXT值
invoke ReadProcessMemory,pi.hProcess,dwBuf,addr Rs,sizeof CONTEXT,addr dwBytes
mov Rs.iDr0,BREAK_RVA
mov Rs.iDr7,M_LDR0 or M_INSTR0
invoke WriteProcessMemory,pi.hProcess,dwBuf,addr Rs,sizeof CONTEXT,addr dwBytes
JMP DBG_con
.elseif dwSScnt==2
inc sinstr
invoke WipeContextBPdr0 //清除断点
invoke wsprintf,addr buf,CTEXT("Break Address: %08X"),excAddr
MsgBox addr buf
.endifcomment $
//ICZELION's Example,uncomment this
inc sinstr
invoke GetThreadContext,pi.hThread,addr Rs
invoke wsprintf,addr buf,CTEXT("XINSTR ADDR %08X"),Rs.regEip
MsgBox addr buf
or Rs.regFlag,100h
invoke SetThreadContext,pi.hThread,offset Rs
$JMP DBG_con
.endif.ENDIF
DBG_no: //continue Debug events without handling
invoke ContinueDebugEvent,Dev.dwProcessId,Dev.dwThreadId,DBG_EXCEPTION_NOT_HANDLED
.continue
DBG_con:
invoke ContinueDebugEvent,Dev.dwProcessId,Dev.dwThreadId,DBG_CONTINUE
.ENDW
invoke wsprintf,addr buf,CTEXT("total : %08d Instructions"),sinstr
MsgBox addr buf
end_debug:
invoke CloseHandle,pi.hProcess
invoke CloseHandle,pi.hThread
pExit:
invoke ExitProcess,0
crp_fail:
MsgBox CTEXT("Create Process Failed!")
jmp pExit
END _StArT只要能使用Drx的断点功能就可以配合SEH、调试API进行一些反跟踪等,具体怎么用,取决于你自己了。
〔完〕
=================================================================================================
一些常量定义(FOR MASM)
; for DR6
M_HIT0 EQU 1
M_HIT1 EQU 2
M_HIT2 EQU 4
M_HIT3 EQU 8
M_BD EQU 2000H DRX access
M_BS EQU 4000H single step
M_BT EQU 8000H task switch
;-----------------------------------------; for DR7
M_LDR0 EQU 01 Li mask for Dr7
M_LDR1 EQU M_LDR0 SHL 02
M_LDR2 EQU M_LDR1 SHL 02
M_LDR3 EQU M_LDR2 SHL 02
M_LDRALL EQU M_LDR0 or M_LDR1 or M_LDR2 or M_LDR3M_GDR0 EQU 02 Gi mask for Dr7
M_GDR1 EQU M_GDR0 SHL 02
M_GDR2 EQU M_GDR1 SHL 02
M_GDR3 EQU M_GDR2 SHL 02
M_GDRALL EQU M_GDR0 or M_GDR1 or M_GDR2 or M_GDR3;-----------------------------------------
M_LE EQU 01 SHL 08 局部断点精确相符
M_GE EQU M_LE SHL 01 全局断点精确相符;DRX access
M_GD EQU M_BD drx保护位置一即使在ring0也产生int 1;instruction fetch
M_INSTR0 EQU 00
M_INSTR1 EQU 00
M_INSTR2 EQU 00
M_INSTR3 EQU 00;memory write
M_WRITE0 EQU 01 SHL 16
M_WRITE1 EQU M_WRITE0 SHL 04
M_WRITE2 EQU M_WRITE1 SHL 04
M_WRITE3 EQU M_WRITE2 SHL 04;port watches: 586+ only and CR4 bit DE (=04 bit 3 in termiology) must be 1
;M_DE EQU 04
M_DE EQU 08
M_PORT0 EQU 02 SHL 16
M_PORT1 EQU M_PORT0 SHL 04
M_PORT2 EQU M_PORT1 SHL 04
M_PORT3 EQU M_PORT2 SHL 04;memory access Read or Write!
M_RW0 EQU 03 SHL 16
M_RW1 EQU M_RW0 SHL 04
M_RW2 EQU M_RW1 SHL 04
M_RW3 EQU M_RW2 SHL 04
M_RWALL EQU M_RW0 or M_RW1 or M_RW2 or M_RW3;lengths
M_BYTE0 EQU 00 use byte for instruction by default always
M_BYTE1 EQU 00 for data BreakPoints,these exist
M_BYTE2 EQU 00
M_BYTE3 EQU 00M_WORD0 EQU 01 SHL 18
M_WORD1 EQU M_WORD0 SHL 04
M_WORD2 EQU M_WORD1 SHL 04
M_WORD3 EQU M_WORD2 SHL 04M_DWORD0 EQU 03 SHL 18
M_DWORD1 EQU M_DWORD0 SHL 04
M_DWORD2 EQU M_DWORD1 SHL 04
M_DWORD3 EQU M_DWORD2 SHL 04
=============================================================================================
网上非盈利组织或团体个人转载时,请你尊重一下看雪论坛和作者,注明转自
|
|||
|
src="about:blank" frameborder="0" scrolling="no" style="border-width: 0px; border-style: initial; margin: 5px 0px 7px; overflow: hidden; width: 6px; display: inline; height: 385px;"> src="about:blank" frameborder="0" scrolling="no" style="border-width: 0px; border-style: initial; margin: 0px; overflow: hidden; width: 535px; display: inline; height: 397px;"> src="about:blank" frameborder="0" scrolling="no" style="border-width: 0px; border-style: initial; margin: 7px 0px; overflow: hidden; width: 8px; display: inline; height: 383px;">
id="hylandaClientFrame" src="http://ds.hylanda.com/loading.php?comType=2&hylandaCharSet=IEGB" frameborder="0" name="hylandaClientFrame" scrolling="no" style="overflow: hidden; width: 534px; display: block; height: 358px;">
本文转自
http://www.pediy.com/bbshtml/bbs6/pediy6751.htm
调试寄存器(debug registers, DRx)理论及实践相关推荐
- linux gdb 寄存器,x86 调试寄存器
英文官方介绍<Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3A: System Programmin ...
- 如何对抗硬件断点--- 调试寄存器
1.前言 在我跨入ollydbg的门的时候,就对ollydbg里面的各种断点充满了疑问,以前我总是不明白普通断点,内存断点,硬件断点有什么区别,他们为什么 有些时候不能混用,他们的原理是什么,在学习了 ...
- 视觉SLAM十四讲从理论到实践第二版源码调试笔记(理论基础1-6章)
2019-2020-2学期机器人工程专业需要开设SLAM技术课程,使用教材为视觉SLAM十四讲从理论到实践第二版. 为方便学生学习课程知识,将Arduino.ROS1.ROS2和SLAM集成到课程定制 ...
- 直流无刷电机FOC控制算法 理论到实践 —— 实践
直流无刷电机FOC控制算法 理论到实践 -- 实践 文章目录 直流无刷电机FOC控制算法 理论到实践 -- 实践 1. 前言 2. FOC控制整体流程 3. FOC实现 3.1 定时器实现 3.1.1 ...
- ARM NEON指令集优化理论与实践
ARM NEON指令集优化理论与实践 一.简介 NEON就是一种基于SIMD思想的ARM技术,相比于ARMv6或之前的架构,NEON结合了64-bit和128-bit的SIMD指令集,提供128-bi ...
- 调试寄存器 原理与使用:DR0-DR7
调试寄存器 原理与使用:DR0-DR7 下面介绍的知识性信息来自intel IA-32手册(可以在intel的开发手册或者官方网站查到),提示和补充来自学习调试器实现时的总结. 希望能给你带去有用的信 ...
- [转]Android 常见安全漏洞修复理论与实践
前言 前段时间公司对应用在爱加密上进行了安全扫描,本文将基于爱加密的漏洞分析报告,针对部分内容,介绍理论修复实践 最小化特权准则概念介绍 最小化特权准则,即指组件只能供自身应用调用,尽可能禁止其他应用 ...
- WCF从理论到实践(14):WCF解决方案模板 (转)
WCF从理论到实践(14):WCF解决方案模板 正所谓磨刀不误砍柴工,虽然VS2008为我们提供了WCFServiceLibrary项目模板,但在实际开发的时候,我们通常更喜欢按照自己的方式来建立WC ...
- 安卓逆向_12 --- jeb工具的使用 ( 动态调试 smali 代码 【 普通调试 和 debug调试 】)
From:https://www.52pojie.cn/forum.php?mod=viewthread&tid=742250 jeb 动态调试 smali 代码:https://www.bi ...
最新文章
- 深入浅出Android动态载入jar包技术
- 一个有趣的算法问题:如何定义一个分数类
- SAP Spartacus HTTP拦截器Interceptor
- 00后确实卷,公司新来的卷王,我们这帮老油条真干不过.....
- 须使用visual c 内联汇编语言开发,在VisualC 中使用内联汇编
- Postgresql之split_part()切割函数,取最后一部分
- 大数据_Spark_框架简介---Spark工作笔记0001
- python批量读取文件内容_Python之批量读取文件【面试必学】
- thinkphp5部署于Linux中nginx多站点解决方案
- Java数据结构笔记1——稀疏数组sparse array
- 芒果广告不能用百度了,怎么办?
- C++程序设计一、二(二元一次方程与函数、函数指针)
- 三菱伺服自动调谐_三菱MR-J3-60B-RJ004 更为先进的实时自动调谐通过设定响应值,全闭环控制型驱动器 MR-J3-60B-RJ004 - 广州凌控...
- 教你轻松快速学会用Calibre TXT转MOBI
- android开机加速器,安卓手机开机如何加速
- 正则十八式-第三式:龙跃于渊
- 二极管的三种击穿形式
- 将结构体转换为二进制
- 未来电信业的发展方向 VOIP的中国之路
- STM32和51单片机有什么区别