YJX_Driver_037_驱动中的异常处理
1、
驱动下的异常处理
返回状态值
检查内存的可用性
异常处理try-except
异常处理try-finally
断言
【01:52】打开vs2003,更进一步的来了解 NTSTATUS
【02:12】选 第36课 的代码为例
【02:40】看的是 DDK里面的 ntdef.h中的定义
【03:20】高2位 分了4种状态
【04:33】宏NT_SUCCESS
【05:50】NTSTATUS 中自定义的位
【06:06】NTSTATUS 前16位,代表 错误的信息
facility : 设备
【06:53】STATUS_SUCCESS
【08:08】NTSTATUS 中 R位 是保留位
【08:26】检查内存可用性
“
”
【08:43】我们不使用那两个函数 也可以检查内存的(ZC: HOW?)
【09:25】MSDN中 ProbeForRead
【09:40】对齐方式一般 选1就行了,最小的对齐方式
【10:20】讲解 自己写 ProBeForRead的话,是这样的思路:从开始地址,用对齐的字节数,依次来读取内存.【10:40】可能实际上的实现 要稍微复杂一些
【11:53】当时 __except(1) 中直接是填入的 “1”,表示忽略这个错误 执行 __except(1){ … } 中的 代码块
【12:06】若 只是单独的判断 某一个指针是否可读/可写,我们可以用汇编来实现,
【12:28】“*pi = 1;” 这样也可以测试,同样会发出异常。直接 对指针进行 读/写 操作,同样会报异常(ZC: 这样就省了用ProbeForRead做另外的判断了?)
【12:45】用 ProbeForRead 有好处:它可以检测 很长一段/一大片 的内存
【13:03】结构化异常处理。在驱动力 共有两种方式
“
__try
{}
__except(filter_Value)
{}
”
【13:13】这种,实际上是 C++的异常处理方式(ZC: 卧槽,那C里面就不能用这个方式了哇?那C里面用什么?待查资料待测试)。这个在用户层同样适用,使用方式是一样的
【13:45】ECEPTION_EXECUTE_HANDLER [值为 1],的处理方式,代码跳转的方式
【14:08】在 except.h 中
【14:25】EXCEPTION_CONTINUE_EXECUTION [值为 -1],表示 重复的执行 那个错误的代码(ZC: 这个什么意思?一直重复执行 错误的代码就会变得不错误?好怪…)
【14:30】EXCEPTION_CONTINUE_SEARCH [值为 0],表示 把异常交给上一层异常处理结构来处理
【14:55】做 异常/内存检测 的测试
【15:10】新建 37.h
【16:45】一个是 手动操作 检测可读性,一个是 ProbeForRead检测可读性
【18:08】sys复制到 虚拟机中,进行测试
【18:53】“ProbeForRead 测试内存 不可用” 没有出现
【19:33】可能是 ProbeForRead的对齐参数 不对,从”1″改为”4″
【20:05】ProbeForRead 的信息还是没有出现,可能是打印指令在DebugView中显示的问题
【20:16】ProbeForRead 的 对齐参数,从 “4”改回”1″。修改 添加 打印信息
【21:05】用新sys测试
【21:33】没有 发出异常,【21:40】也就是说 0这个地址它有可能是可读的,那么我们把它读出来看一下(ZC: 肯定异常的哇)
【22:15】ZC: 这样子 读这个地址,会蓝屏的哇
【22:25】放到异常处理里面了…
【22:38】新sys 测试
【22:50】ZC: 这里 信息有了
【23:15】感觉 函数ProbeForRead,判断并不是很准确
ZC: 个人偏向于 是使用的不对,并非ProbeForRead的缺陷。待查资料,待测试,待验证…
【23:47】测试 函数ProbeForWrite
ProbeForWrite(pi, 4, 1);
【24:15】手动测试 的地方,也改成 测试 是否可写
【24:33】新sys 测试
【25:20】再次测试 ProbeForRead(pi, 14, 4); 看是不是对齐的原因
【25:38】新sys 测试
【25:55】没有发出异常信息… 测试结果: ProbeForRead不是很可靠…
ZC: 发现 中间的一个空的信息条,比较可疑,看源码 不应该有这个空的信息条打印出来啊,用WinDBG再测试看看,看结果怎么样
【27:22】另一种异常处理结构
“
__try
{}
__finally
{}
”
ZC: 这个能算是 另一种?他说的 2种,居然这个也算一种…
【28:18】测试 __try…__finally 的代码
【29:25】ZC: 这里,上面的 __try __finally出错了,就不会再执行到__finally{}之后的语句了吧?
【30:23】侧效。KdPrint()宏少一对() 是容易检测到的 侧效,还有一种侧效 是不容易检测到的。也就是 加括号的问题,侧效。
【30:55】sys 虚拟机 测试
【31:08】蓝屏了…
【31:31】∵ 异常实际上 没有被处理
ZC: 那我上面你的理解也不对了。那 __except(1){} 中的代码执行完后,是直接退出函数? 还是继续执行 __except(1){}下面的代码? 待测试。
【32:01】并不会忽略掉错误,只是对错误进行一个处理,处理之后,异常同样会传递到上一层(ZC: 这时才产生蓝屏?这个观点 与我的大不同,注意一下)
【33:12】看一下 断言,侧效 最后再讲
它提供了一个 宏,它最终也是调用了 函数RtlAssert
【33:43】断言 也是一种调试技巧
【34:10】断言 的测试代码:
ASSERT(p != NULL);
【34:20】看一下 断言 的定义(ntddk.h中)
【34:40】这个地方开始的
【35:20】MSDN中 找 函数RtlAssert,没找到,只找到了 RxAssert
【35:35】虚拟机中测试一下,看看效果
【35:45】需要 打开 WinDBG
【36:15】断言 产生
【36:22】这里,WinDBG问你 对断言采取何种处理手段:重复中断(Break repeatedly)、中断一次(break Once)、忽略(Ingore)、终止进程(terminate Process)、终止线程(terminate Thread),这里 可以输入的对应的命令为 boipt(分别对应上面提供的选项)
【36:29】这里 输入 “I”,回车
【36:38】断言 会提供相关信息,并定位出源码的相应位置
【37:03】断言 中的 表达式为false ==> 断言产生
【37:18】【37:33】看一下 侧效
【37:40】一般的问题也是 宏的情况
【39:30】测试一下,刚刚写的 宏/函数 ==> 函数test1()
“
#define add(a,b) a=a+b;b=a+b;
void test1()
{
int a=1,b=1;
if (true) add(a,b);
KdPrint((“a=%d , b=%d\n”, a, b));
}
”
【40:32】理想中的结果,a,b的值应该都是2 (ZC: 这里,a和b的值,不可能都是2!! a已经经过计算了啊,应该结果是 a为2,b为3。)
【41:01】放入 虚拟机 测试
【41:33】显示结果 a为2,b为3
【42:15】这里 计算结果是正确的,是∵宏中的两行代码都执行了,看一下像下面这样修改代码
【42:23】修改一下测试代码
“
#define add(a,b) a=a+b;b=a+b;
void test1()
{
int a=1,b=1;
if (false) add(a,b);
KdPrint((“a=%d , b=%d\n”, a, b));
}
”
【42:40】再次 虚拟机测试
【43:22】修改 打印信息,再 测试
【43:40】此时看到 a=1,b=2
【43:58】为什么?实际上 宏中 第1条语句没被执行,第2条语句执行了
【44:11】宏实际上被编译出来之后是这样的情况,∵我们没有加括号 ∴实际编译得到的情况是这样的:
“
#define add(a,b) a=a+b;b=a+b;
void test1()
{
int a=1,b=1;
//if (false) add(a,b);
if (false)
a=a+b;
b=a+b;
KdPrint((“a=%d , b=%d\n”, a, b));
}
”
【44:36】∵ 没有加括号,宏中第2条代码始终会被执行
【44:42】宏 只是简单的替换,并不会带括号。为了避免侧效的出现,一般我们在这里(手动的)加上括号。【44:55】如果我们这里不加括号,可以在宏中(手动)加括号
“
#define add(a,b) a=a+b;b=a+b;
void test1()
{
int a=1,b=1;
//if (false) add(a,b);
if (false)
{
a=a+b;
b=a+b;
}
KdPrint((“a=%d , b=%d\n”, a, b));
}
#define add(a,b) {a=a+b;b=a+b;}
void test1()
{
int a=1,b=1;
//if (false) add(a,b);
if (false)
{
a=a+b;
b=a+b;
}
KdPrint((“a=%d , b=%d\n”, a, b));
}
”
【45:00】编译能够通过
【45:15】测试这种代码情况
“
#define add(a,b) {a=a+b;b=a+b;}
void test1()
{
int a=1,b=1;
if (false) add(a,b);
KdPrint((“a=%d , b=%d\n”, a, b));
}
”
【45:35】可以看到 a=1,b=1
【45:40】说明,宏中的两句代码都没有执行
【45:47】侧效,就是 我们定义宏的时候,尽量给它加上{}。或者 使用宏的时候,不要简写,只有一个宏调用语句的时候也在宏调用语句的前后加上{},如果 宏写成函数形式(就是宏里面的内容已经被一个大的括号全部包住) 就不需要(在宏的外围再加括号)了。【46:10】但是 我们在使用宏的时候,并不一定知道宏里面是否有{}将全部的宏内容都包裹住(∴ 还是在调用宏的语句的两端加上{}吧)
2、
NTSTATUS
typedef LONG NTSTATUS;
NT_SUCCESS
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // ntsubauth
//
// Values are 32 bit values layed out as follows:
//
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +—+-+-+———————–+——————————-+
// |Sev|C|R| Facility | Code |
// +—+-+-+———————–+——————————-+
//
// where
//
// Sev – is the severity code
//
// 00 – Success
// 01 – Informational
// 10 – Warning
// 11 – Error
//
// C – is the Customer code flag
//
// R – is a reserved bit
//
// Facility – is the facility code
//
// Code – is the facility\’s status code
//
//
// Define the facility codes
R(Reserved)保留位
C (Customer) 客户位
Sev(Severity) 重要位 共2个二进制位 00表示成功 01表示信息 10表示警告 11表示错误
检测内存可用性
ProbeForRead
VOID ProbeForRead(
__in PVOID Address,
__in SIZE_T Length,
__in ULONG Alignment
);
ProbeForWrite
VOID ProbeForWrite(
__in PVOID Address,
__in SIZE_T Length,
__in ULONG Alignment
);
void Memaccess_Test()
{
KdPrint((“测试内存可用否\n”));
int i, *pi=NULL;
__try
{
i=*pi;
}
__except(1)
{
KdPrint((“测试内存 不可用\n”));
return;
}
}
void ProbeForRead_Test()
{
KdPrint((“测试内存可用否\n”));
int *pi=NULL;
__try
{
ProbeForRead(pi,4,1);
//i=*pi;
}
__except(1)
{
KdPrint((“ProbeForRead 测试内存 不可用\n”));
return;
}
}
结构化异常处理try except
__try
{
//这里如果出错 发出异常
}
__except(filter_Value)
{
}
filter_Value是以下三种值之一
EXCEPTION_CONTINUE_SEARCH 0 转向上一层异常处理 ==> 很少用到
EXCEPTION_CONTINUE_EXECUTION -1 重复执行错误指令 ==> 很少用到
EXCEPTION_EXECUTE_HANDLER 1 忽略该错误 转到 __except 块处理 ==> 常用到
结构化异常处理try finally
__try
{
}
__finally
{
}
断言
ASSERT
#if DBG
#define ASSERT( exp ) \
((!(exp)) ? \
(RtlAssert( #exp, __FILE__, __LINE__, NULL ),FALSE) : \
TRUE)
#else
#define ASSERT( exp ) ((void) 0)
RtlAssert
侧效(宏使用问题)
#define add(a,b) a=a+b; b=a+b;
if (??) add(a,b)