寒江独钓--Windows内核安全编程笔记(一)

第一章:内核上级指导

1、如果没有设置DriverUnload函数指针,则一个内核模块一旦被加载就不能卸载了。

2、makefile文件内容永远也不需要改动。

3、设置断点之前系统必须已经中断。

4、驱动加载之前,设置断点不方便,手工断点如下:

#if DBG

_asm int 3

#endif

如果不是调试状态执行会直接蓝屏,断点弹出之后可以设置新的断点

5、WinDbg为双机调试,Softice可以进行单机调试但已经不再更新,吴岩峰等人开发的Syser也可以进行单机调试,100%国产

第二章:内核编程环境及其特殊性

1、在可以容纳4GB内存控件的32位Windows系统上,低2G是用户空间,高2G是内核空间。

2、用户空间是各个进程隔离的,但是内核空间是共享的。也就是说,每个进程看见的高2G控件范围内的数据都应该是一样的。

3、内核空间受硬件保护。x86架构下R0层代码才可以访问内核空间,R3层的代码要调用R0层功能时,一般通过操作系统提供的一个入口(该入口中调用sysenter指令)来实现。

4、内核模块已经位于内核空间,作为R0代码执行,所以不受任何限制,可以任意修改内核。

5、内核模块实际上位于任何一个进程空间中,但是任意一段代码的任意一次执行,一定是位于某个进程空间中的,这个进程是哪一个?取决于请求的来源、处理的过程等,PsGetCurrentProcessId函数能得到当前进程的进程号,函数返回的Handle实际上是一个进程ID

6、并不是所以代码都运行在系统进程内,Windows所谓系统进程是一个名为“System”的进程,是Windows自身生成的一个特殊进程,DriverEntry函数被调用时,一般都位于系统进程内,这是因为Windows一般都用系统进程来加载内核模块,并不说明内核代码始终运行在System进程里。

7、使用宏NT_SUCCESS()可以判断一个返回值是否成功,NTSTATUS的值可以在WDK头文件(如:ntstatus.h)中查找到

NTSTATUS MyFunction()

{

NTSTATUS status;

status = ZwCreateFile(...);

if(!NT_SUCCESS(status))

{

return status;

}

...

}

8、字符串

typedef struct _UNICODE_STRING{

USHORT Length;

USHORT MaximumLength;

PWSTR Buffer;

}UNICODE_STRING *PUNICODE_STRING;

--------------------------------------------------------------------------------------

UNICODE_STRING str = RTL_CONSTANT_STRING(L"first: Hello,my salary!");

DbgPrint("%wZ", &str);

--------------------------------------------------------------------------------------

UNICODE_STRING str = RTL_CONSTANT_STRING(L"Hello");

KdPrint(("Buffer:%ws\nMaxinumLength:%d\nLength:%d", str.Buffer, str.MaximumLength, str.Length));

9、内核模块并不是生成一个进程,只是填写一组回调函数让Windows来调用,而且这组回调函数必须符合Windows内核规定。

10、一个内核模块的所有功能都由普通分发函数和快速IO分发函数提供给windows。

11、大部分“消息”都以请求(IRP)的方式传递。而设备对象是唯一可以接收请求的实体,任何一个“请求”都是发送给某个设备对象的。

12、因为我们总是在内核程序中生成一个DO,而一个内核程序是用一个驱动对象表示的,所以一个设备对象总是属于一个驱动对象。一个驱动对象中有n个设备,这些设备用这个指针连接起来作为一个单向的链表。

13、驱动对象生成多个设备对象,而windows向设备对象发送请求,但是这些请求如何处理呢?实际上,这些请求是被驱动对象的分发函数所捕获的。当windows内核向一个设备发送一个请求时,驱动对象的分发函数中的某一个会被调用。

14、如WriteFile这些操作最终在内核中会被IO管理器翻译成请求(IRP或者与之等效的其它形式,比如快速IO调用)发送往某个设备对象。

15、一个IRP往往要传递N个设备才能得以完成,在传递过程中有可能会有一些中间变换,导致请求的参数变化。为了保存这种参数变化,我们给每次中转都留一个栈空间,用来保存中间参数。所以一个请求并非简单的一个输入并等待一个输出,而是经过许多中转才得以完成

16、IO管理器就是将用户调用的API函数翻译成IRP或者将等价请求发送到内核各个不同的设备的关键组件。

17、常用的C运行时库中的函数,如果只涉及字符串和内存数据(而不涉及内存管理,比如内存的分配和释放),则是可以在内核程序里调用的。

18、任意一个函数可能有多个调用源,主要可以追溯到的调用源如下:

(1、入口函数DriverEntry和卸载函数DriverUnload。

(2、各种分发函数(包括普通分发函数和快速IO分发函数)

(3、处理请求时设置的完成函数。也就是说,该请求完成后会被系统调用的回调函数

(4、其它回调函数(如各类NDIS驱动程序的特征函数)

19、了解这段代码可能的调用源应该在哪里对处理函数可重入性和考虑运行中断级有很大的好处

20、何时需要保证函数的多线程安全性可以通过下面几条规则来简单判断:

(1、可能运行于多线程环境的函数,必须是多线程安全的,只运行于单线程环境的函数,则不需要多线程安全性。

(2、如果函数A的所有调用源只运行于同一单线程环境,则函数A也是只运行在单线程环境的。

(3、如果函数A的其中一个调用源是可能运行在多线程环境的,或者多个调用源可能运行于不同的可并发的多个线程环境,而且调用路径上没有采取多线程序列化成单线程的强制措施,则函数A也是可能运行在多线程环境的。

(4、如果在函数A所有的可能运行于多线程环境的调用路径上,都有多线程程序序列化成单线程的强制措施,则函数A是运行在单线程环境的。

(5、所谓的多线程序列化成单线程的强制措施是指如互斥体、自旋锁等同步手段。

(6、只使用函数内部资源,完全不实用全局变量、静态变量或者其它全局性资源的函数是多线程安全的。

(7、如果对某个全局变量或者静态变量的所有访问都被强制的同步手段限制为同一时刻只有一个线程访问则即是使用了这些全局变量和静态变量,岁函数的多线程安全性也是没有影响的。

21内核代码的主要调用源的运行环境

调用源              运行环境    原因
DriverEntry,DriverUnload 单线程 这两个函数由系统进程的单一线程调用。不会出现多线程同时调用的情况。
各种分发函数  多线程
没有任何文档保证分发函数是不会被多线程同时调用的。此外,分发函数不会和DriverEntry并发,但可能和DriverUnload并发

完成函数 多线程 完成函数随时可能被位置的线程调用
各种NDIS回调函数  多线程 和完成函数相同

22、中断级:Passive级和Dispatch级,Dispatch级比Passive级高,实际编程中,许多具有比较复杂功能的内核API都要求必须在Passive级执行,只有比较简单的函数能在Dispatch级执行。调用任何一个内核API之前,必须查看WDK文档,了解这个内核API的中断级要求

23、判断正在编写的代码可能的中断级:

(1、如果调用路径上更没有特殊的情况(导致中断级的提高或者降低),则一个函数执行时的中断级和它的调用源的中断级相同。

(2、如果在调用路径上有获取自旋锁,则中断级随之升高;如果调用路径上有释放自旋锁,则中断级随之下降

24、内核代码主要调用源的运行中断级

调用源    一般的运行中断级
DriverEntry,DriverUnload Passive级
各种分发函数 Passive级
完成函数 Dispatch级
各种NDIS回调函数 Dispatch级

25、如果当前代码确实运行在Dispatch级,但是又必须调用一个只能运行在Passive级的内核API,任意的降低中断级都会导致系统产生不可预料的后果。

26、指定函数位置的预编译指令:

#pragma alloc_text(INIT, DriverEntry)

#pragma alloc_text(PAGE, NdisProtUnload)

#pragma alloc_text(PAGE, NdisProtUnload)

#pragma alloc_text(PAGE, NdisProtClose)

#pragma alloc_text这个宏仅仅用来指定某个函数的可执行代码在编译出来后在sys文件中的位置。内核模块编译出来之后是一个PE格式的sys文件,这个文件的代码段(text段)中有不同的节(Section),不同的节被加载到内存中之后处理情况不同。读者需要关心的主要是3种节,INIT节的特点是在初始化完毕之后就被释放,也就是说就不再占用内存空间了,PAGE节的特点是位于可以进行分页交换的内存空间,这些控件在内存紧张时可以被交换到硬盘上以节省内存。如果未用上述的预编译指令处理,则代码默认位于PAGELK节,加载后位于不可分页交换的内存空间中。

函数DriverEntry显然只需要在初始化阶段执行一次,因此这个函数一般都用#pragma alloc_text(INIT, DriverEntry)使之位于初始化后立刻释放的空间内。为了节约内存,可以把很多函数放在PAGE节中。但是要注意:放在PAGE节中的函数不可以在Dispatch级调用,因为这种函数的调用可能诱发缺页中断。但是缺页中断处理不能在Dispatch级完成。为此,一般都用一个宏PAGED_CODE()进行测试。如果发现当前中断级为Dispatch级,则程序直接报异常让程序员及早发现。

#pragma alloc_text(PAGE, SfAttachToMountedDevice)

....

NTSTATUS

SfAttachToMountedDevice(

IN PDEVICE_OBJECT DeviceObject,

IN PDEVICE_OBJECT SFilterDeviceObject

)

{

PSFILTER_DEVICE_EXTENSION newDevExt =

SFilterDeviceObject->DeviceExtension;

NTSTATUS status;

ULONG i;

PAGED_CODE();

...

}

时间: 2024-11-10 23:05:59

寒江独钓--Windows内核安全编程笔记(一)的相关文章

《寒江独钓_Windows内核安全编程》中修改类驱动分发函数

最近在阅读<寒江独钓_Windows内核安全编程>一书的过程中,发现修改类驱动分发函数这一技术点,书中只给出了具体思路和部分代码,没有完整的例子. 按照作者的思路和代码,将例子补充完整,发现将驱动安装在WIN7 32位环境下,键盘失效. 经调试发现,可能的原因是替换了\\Driver\\Kbdclass类驱动的所有分发函数导致,如果只替换分发IRP_MJ_READ的函数,不会有问题,以下为代码 1 //替换分发函数 来实现过滤 2 #include <wdm.h> 3 #inclu

寒江独钓Windows内核编程——串口过滤

一.过滤的概念: 过滤是在不影响上层和下层接口的情况下,在Windows系统内核中加入新的层,从而不需要修改上层的软件或者下层的真是驱动程序,就加入了新的功能. 1.1 设备绑定的内核API 进行过滤的最主要的方法是对一个设备对象(Device Object)进行绑定.通过编程可以生成一个虚拟设备对象,并“绑定”(Attach)在一个真实的设备上.一旦绑定,则本来操作系统发送给真实设备的请求,就会首先发送到这个虚拟设备. 在WDK中,有多个内核API能实现绑定的功能.以下三个绑定API是从WDK

寒江独钓:键盘的过滤 学习笔记

先来名词热身: 一.符号链接:其实就是一个别名.可以用一个不同的名字来代表一个设备对象 二.PDO:是物理设备对象,可以理解为是设备栈最下面的那个设备对象. 函数介绍: 内核中:ZwCreateFile是很重要的函数,不但可以打开文件,还可以打开设备对象.在应用程序中跟它对应的是CreateFile函数.. 接下来是记录Windows如何获得按键,然后传递给各个应用程序. csrss这个进程里有个线程叫win32k!RawInputThread,这个线程总是调用nt!ZwReadFile来读入数

Windows驱动过滤--kdbclass过滤,寒江独钓加强版

寒江独钓键盘过滤,修改了下,过滤QQ密码写入buff,有 回车 或者buff满写入文件,因为irp完成,irp对应的内存快回收,所以用全局缓冲区.开启一个线程写入,开始打算用队例一个一个处理irp的,但是发现那样比较缓慢,只好这样了..创建进程回调代码加几行就行,这里没写,因为代码丢失了,算是个大概的代码吧.给初学的分享下.有错指出,谢谢. 前辈们不要见笑了. struct.h /************************************/ //author:DuanYueming

DataTable数据检索的性能分析(转寒江独钓)

我们知道在.NET平台上有很多种数据存储,检索解决方案-ADO.NET Entity Framework,ASP.NET Dynamic Data,XML, NHibernate,LINQ to SQL 等等,但是由于一些原因,如平台限制,比如说必须基于.NET Framework2.0及以下平台:遗留的或者第三方数据接口采用的就是DataTable等等,仍然需要使用DataTable作为数据存储结构.另一方面DataTable比较容易使用,一些数据访问的接口可能直接采用了DataTable结构

Windows内核安全与驱动开发

这篇是计算机中Windows Mobile/Symbian类的优质预售推荐<Windows内核安全与驱动开发>. 编辑推荐 本书适合计算机安全软件从业人员.计算机相关专业院校学生以及有一定C语言和操作系统基础知识的编程爱好者阅读. 内容简介 本书的前身是<天书夜读--从汇编语言到Windows内核编程>和<寒江独钓--Windows内核安全编程>.与Windows客户端安全软件开发相关的驱动程序开发是本书的主题.书中的程序使用环境从32位到64位,从Windows XP

windows内核情景分析之中断处理(毛德操)[转]

中断处理 每个cpu有一张中断表,简称IDT. IDT的整体布局:[异常->空白->5系->硬](推荐采用7字口诀的方式重点记忆) 异常:前20个表项存放着各个异常的描述符(IDT表不仅可以放中断描述符,还放置了所有异常的异常处理描述符,0x00-0x13) 保留:0x14-0x1F,忽略这块号段 空白:接下来存放一组空闲的保留项(0x20-0x29),供系统和程序员自己分配注册使用 5系:然后是系统自己注册的5个预定义的软中断向量(软中断指手动的INT指令) (0x2A-0x2E5个系

C++windows内核编程笔记day07_day08,可视化建菜单、加速键使用、绘图等

可视化操作创建的菜单,加载到窗口. 方法1:注册时指定菜单 wce.lpszMenuName=MAKEINTRESOURCE(IDR_MENUMAIN);//数字形式的资源ID转换为字符串形式的资源 方法2: //创建窗口时加载菜单资源 HMENU menumain= LoadMenu(g_hinstance,MAKEINTRESOURCE(IDR_MENUMAIN)); menumain 传入 CreateWindowEx();//倒数第三个参数 窗口指定小图标: 1.注册时指定 wce.hI

C++windows内核编程笔记day09_day10,对话框和窗口基本控件等的使用

//设置字体颜色 SetTextColor(hdc,RGB(255,0,0)); //窗口背景 //wce.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); //wce.hbrBackground=CreateSolidBrush(RGB(0,0,255)); //设置字体背景 SetBkColor(hdc,RGB(0,0,200)); //设置字体背景模式 SetBkMode(hdc,TRANSPARENT);//字体背景透明 //创建字体,成功返回字体,失败返回