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)

 

posted @ 2016-04-12 08:34  DebugSkill  阅读(368)  评论(0编辑  收藏  举报