存档

‘内核编程’ 分类的存档
1,687 views

扩展内核函数 PsSetCreateProcessNotifyRoutine 等的蹲坑数量

2011年1月22日

Windows 内核提供的 PsSetCreateProcessNotifyRoutine 等函数能够容纳的回调函数的数量是有限的,目前仅区区 8 个 “蹲位”, 被占用满了以后, 再调用 PsSetCreateProcessNotifyRoutine 等函数将会失败.

因此, 我编写了一个驱动, 用以扩展 PsSetCreateProcessNotifyRoutine 等函数能够容纳回调函数的数量.

经过扩展后, 能够容纳的回调函数是无限的, 这个驱动有五个导出函数, 其使用方法与原始函数完全相同. 列表如下.

扩展后的函数名 原始函数名
Ext_PsSetCreateProcessNotifyRoutine PsSetCreateProcessNotifyRoutine
Ext_PsSetCreateThreadNotifyRoutine PsSetCreateThreadNotifyRoutine
Ext_PsRemoveCreateThreadNotifyRoutine PsRemoveCreateThreadNotifyRoutine
Ext_PsSetLoadImageNotifyRoutine PsSetLoadImageNotifyRoutine
Ext_PsRemoveLoadImageNotifyRoutine PsRemoveLoadImageNotifyRoutine

本驱动的启动方式建议为 boot 方式, 当其他驱动使用了本驱动的服务时, 不要卸载本驱动; 另外, 本驱动在 windows 2000 系统下不能卸载, 只能通过删除服务然后重启计算机的方式卸载本驱动.

本地下载 knlcbext.zip

项目主页 https://winutilities.svn.sourceforge.net/svnroot/winutilities/knlcbext/

内核编程

1,166 views

使用 NT 缓存管理器

2010年11月9日

使用 NT 缓存管理器 (中文版) 转载自 这里
英文原版 Cache Me if You Can: Using the NT Cache Manager

缓存管理器

在这篇文章中, 我们将对缓存管理器的一个运行时例程做个基本的描述. 此外, 对于这些例程使用的一些方法以及示例代码, 可以参考 Microsoft IFS Kit.

缓存管理器概述

缓存管理器是一个软件部分, 集成在 Windows Nt 内存管理器中, 以集成文件系统 (数据) 和虚拟内存系统. 一些操作系统完成它们自己的文件系统, 所以它们有自己的数据cache. 然而, 因为这些caches受物理内存限制, 它们在大小上受限 – 用作cache的这块内存在系统中也不能做其它用途使用了.

因此, 使用windows NT缓存管理器的一个关键优势是, 它可以使文件缓存和程序运行所需要的物理内存达到平衡. 当应用程序是I/O密集型的时候, 所谓的“平衡”就倾向于做缓存数据. 当应用程序非常耗内存, 那么用来缓存数据的内存可能会被减为0. 因此最终结果是系统更好地使用物理内存从而提供更好的性能. 文件系统使用缓存管理器的另一个重要原因是, 文件能这样被访问: 通过标准文件系统接口 (例如读和写) 或者通过内存管理器 (一个内存映像文件) . 当对同一个文件同时使用这两种方法的时候, 缓存管理器能够提供机制以保证数据一致性.

阅读全文…

内核编程

1,079 views

关于 windows 内核下的读写锁

2010年11月9日

参考 FILEMON 中的代码,以及 DDK 中的说明:

ERESOURCE 对象相当于读写锁, 即用于同时只能有一个 WRITER 写入, 多个 READER 读的情况:

函数名称 作用
ExInitializeResourceLite 初始化一个 ERESOURCE 对象
ExDeleteResourceLite 删除一个 ERESOURC 对象
ExAcquireResourceExclusiveLite 用于获取 ERESOURCE (写访问)
ExAcquireResourceSharedLite 用于获取 ERESOURCE (读访问)
ExReleaseResourceLite 用于释放 ERESOURCE

参考文献:
Driver Threads, Dispatcher Objects, and Resources

内核编程

924 views

[转]Windows文件系统的杂谈

2010年11月5日

author: jonathan
本文档的CopyRight归jonathan所有,可自由转载,转载时请保持文档的完整性。
/*————————————————————————————————————*/
1 杂谈
1.1 FCB/SCB的COMMON结构定义

// fat
#define NTC_UNDEFINED                    ((NODE_TYPE_CODE)0x0000)
#define FAT_NTC_DATA_HEADER              ((NODE_TYPE_CODE)0x0500)
#define FAT_NTC_VCB                      ((NODE_TYPE_CODE)0x0501)
#define FAT_NTC_FCB                      ((NODE_TYPE_CODE)0x0502)
#define FAT_NTC_DCB                      ((NODE_TYPE_CODE)0x0503)
#define FAT_NTC_ROOT_DCB                 ((NODE_TYPE_CODE)0x0504)
#define FAT_NTC_CCB                      ((NODE_TYPE_CODE)0x0507)
#define FAT_NTC_IRP_CONTEXT              ((NODE_TYPE_CODE)0x0508)
// ntfs
#define NTC_UNDEFINED                    ((NODE_TYPE_CODE)0x0000)
#define NTFS_NTC_DATA_HEADER             ((NODE_TYPE_CODE)0x0700)
#define NTFS_NTC_VCB                     ((NODE_TYPE_CODE)0x0701)
#define NTFS_NTC_FCB                     ((NODE_TYPE_CODE)0x0702)
#define NTFS_NTC_SCB_INDEX               ((NODE_TYPE_CODE)0x0703)
#define NTFS_NTC_SCB_ROOT_INDEX          ((NODE_TYPE_CODE)0x0704)
#define NTFS_NTC_SCB_DATA                ((NODE_TYPE_CODE)0x0705)
#define NTFS_NTC_SCB_MFT                 ((NODE_TYPE_CODE)0x0706)
#define NTFS_NTC_SCB_NONPAGED            ((NODE_TYPE_CODE)0x0707)
#define NTFS_NTC_CCB_INDEX               ((NODE_TYPE_CODE)0x0708)
#define NTFS_NTC_CCB_DATA                ((NODE_TYPE_CODE)0x0709)
#define NTFS_NTC_IRP_CONTEXT             ((NODE_TYPE_CODE)0x070A)
#define NTFS_NTC_LCB                     ((NODE_TYPE_CODE)0x070B)
#define NTFS_NTC_PREFIX_ENTRY            ((NODE_TYPE_CODE)0x070C)
#define NTFS_NTC_QUOTA_CONTROL           ((NODE_TYPE_CODE)0x070D)
//cdfs
#define NTC_UNDEFINED                   ((NODE_TYPE_CODE)0x0000)
#define CDFS_NTC_DATA_HEADER            ((NODE_TYPE_CODE)0x0301)
#define CDFS_NTC_VCB                    ((NODE_TYPE_CODE)0x0302)
#define CDFS_NTC_FCB_PATH_TABLE         ((NODE_TYPE_CODE)0x0303)
#define CDFS_NTC_FCB_INDEX              ((NODE_TYPE_CODE)0x0304)
#define CDFS_NTC_FCB_DATA               ((NODE_TYPE_CODE)0x0305)
#define CDFS_NTC_FCB_NONPAGED           ((NODE_TYPE_CODE)0x0306)
#define CDFS_NTC_CCB                    ((NODE_TYPE_CODE)0x0307)
#define CDFS_NTC_IRP_CONTEXT            ((NODE_TYPE_CODE)0x0308)
#define CDFS_NTC_IRP_CONTEXT_LITE       ((NODE_TYPE_CODE)0x0309)

typedef struct _FSRTL_COMMON_FCB_HEADER {
  CSHORT NodeTypeCode;
  CSHORT NodeByteSize;
  UCHAR Flags;
  UCHAR IsFastIoPossible;
  UCHAR Flags2;
  UCHAR Reserved : 4;
  UCHAR Version : 4;
  PERESOURCE Resource;
  PERESOURCE PagingIoResource;
  LARGE_INTEGER AllocationSize;
  LARGE_INTEGER FileSize;
  LARGE_INTEGER ValidDataLength;
} FSRTL_COMMON_FCB_HEADER;
typedef FSRTL_COMMON_FCB_HEADER *  PFSRTL_COMMON_FCB_HEADER;

Flags2 & FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS

typedef struct _FSRTL_ADVANCED_FCB_HEADER {
  FSRTL_COMMON_FCB_HEADER;
  PFAST_MUTEX FastMutex;
  LIST_ENTRY FilterContexts;
  EX_PUSH_LOCK PushLock;
  PVOID* FileContextSupportPointer;
} FSRTL_ADVANCED_FCB_HEADER;

typedef FSRTL_ADVANCED_FCB_HEADER * PFSRTL_ADVANCED_FCB_HEADER;

typedef struct _FSRTL_PER_STREAM_CONTEXT {
  LIST_ENTRY Links;
  PVOID OwnerId;
  PVOID InstanceId;
  PFREE_FUNCTION FreeCallback;
} FSRTL_PER_STREAM_CONTEXT, * PFSRTL_PER_STREAM_CONTEXT;

OwnerId 和 InstanceId都需要有意义的值:
OwnerId = 可以是驱动对象的地址等
InstanceId = 可以是流上下文地址 (FsRtlGetPerStreamContextPointer)

1.2 Cleanup和Close的区别

在FCB或者SCB中都有如下两个变量:
CLONG CleanupCount; 打开未关闭文件句柄。
CLONG CloseCount; 打开未关闭文件对象。
在SCB中还有一变量:

    //
    // A count of the number of file objects opened on this stream
    // which represent user non-cached handles. We use this count to
    // determine when to flush and purge the data section in only
    // non-cached handles remain on the file.
    //
    CLONG NonCachedCleanupCount;

1.3 资源同步问题
在文件过滤驱动中,如要访问 ExAcquireResourceExclusive 等资源时,为了防止一般内核的 APC 调度,需调用如下接口:

FsRtlEnterFileSystem(KeEnterCriticalRegion)
FsRtlExitFileSystem(KeExitCriticalRegion)

关于设置 TopLevelIrp 也是很重要的,在访问ExAcquireResourceExclusive 等资源时,也需要考虑该接口调用:

if ( IoGetTopLevelIrp() == NULL ) {
        IoSetTopLevelIrp( Irp );
        return TRUE;
    } else {
        return FALSE;
    }

设置 IoSetTopLevelIrp 目的:当前调用的 irp 是否是原始的线程操作 irp 还是由于线程的其他操作从而又产生的新的 irp 操作。

1.4 文件对象状态跟踪
跟踪一个文件操作的状态,一般使用 FCB。而 FCB 的生命周期,又是以 CREATE 开始,以 CLOSE 结束。
但是文件系统内部也会通过 IoCreateStreamFileObject (会调用 IRP_MJ_CLEANUP) 和 IoCreateStreamFileObjectLite (会调用 IRP_MJ_CLEANUP 或者 IRP_MJ_CLOSE) 等创建文件对象,但是这些文件对象却不会调用 Create,为跟踪状态带来困难。
因此,必须在 IRP_MJ_READ、IRP_MJ_WRITE、IRP_MJ_SET_INFORMATION 和 IRP_MJ_GET_INFORMATION 中动态创建。

1.5 文件加密中注意要清缓存
1.5.1 文件加密关注重点是缓存问题
1.5.2 清缓存有两个时机:create 和 cleanup
1.5.3 清缓存方式
1.5.4 双缓存与双 FCB,个人认为本质是相同,不过是管理缓存的方法和机制作了改变,结果可能双 FCB 有更加稳定和高移植性

1.6 同步机制
spin_lock, event, resource, mutex, time, semaphore, thread, process, file等各种对象

1.7 IRP_MJ_FILE_SYSTEM_CONTROL
此接口调用的device是设备,但是这个设备参数哪里来的呢?
其实就是IrpSp->Parameters.MountVolume.Vpb->RealDevice中的设备。

内核编程

1,143 views

文件过滤驱动的东东,总结的比较全面

2010年10月21日

1> IFS 流程图
a.生成一个控制设备.当然此前你必须给控制设置指定名称.
b.设置Dispatch Functions.
c.设置Fast Io Functions.
d.编写一个my_fs_notify回调函数,在其中绑定刚激活的FS CDO.
e.使用wdff_reg_notify调用注册这个回调函数。
f.编写默认的dispatch functions.
g.处理IRP_MJ_FILE_SYSTEM_CONTROL,在其中监控Volumne的Mount和Dismount.
h.下一步自然是绑定Volumne了.
(全路径是在 FileObject->FileName.Buffer中得到的.)
2>一些必要知识
a.几个概念的区别
1.多数的storage drivers 是PNP管理的,存在一个设备节点(DEVNODE),(It is important to note that file systems and file system filter drivers are not PnP device drivers;),每个设备节点上维护一个Storage Device Stacks,这个就是因为每个存储设备,例如磁盘设备,可能包含一个或者多个逻辑卷(分区或者动态卷),这些卷就是通过这个Storage Device Stack来保存的。该设备点的信息就是functional device object (FDO)。剩下的就是physical device objects (PDO)代表各个分区。
2.通过下面的方式可以得到卷的名称
The Mount Manager responds to the arrival of a new storage volume by querying the volume driver for the following information:
·The volume’s nonpersistent device object name (or target name), located in the Device directory of the system object tree (for example: “\Device\HarddiskVolume1″)
·The volume’s globally unique identifier (GUID), also called the unique volume name
·A suggested persistent symbolic link name for the volume, such as a drive letter (for example, “\DosDevices\D:”)
3.文件系统和卷的区别
When a file system is mounted on a storage volume, it creates a file system volume device object (VDO) to represent the volume to the file system. The file system VDO is mounted on the storage device object by means of a shared object called a volume parameter block (VPB).
File System Stacks :File system drivers create two different types of device objects: control device objects (CDO) and volume device objects (VDO).
File System Control Device Objects (CDO)
File System Volume Device Objects (VDO)

阅读全文…

内核编程

702 views

若干关于 file system driver stack

2010年10月21日

写这个文章的初衷是想知道究竟一个读写文件的 irp 都是怎样被处理的…..

大家都知道这样的一个读写文件 irp 是发送给 file system 的 driver 的, file system 把这个 irp 交给了下层的 device.

这个 device 叫 logical volume device, 它由 device 的 vbp 里面的 realdevice 指针指出 (不一定就会是这个 device, 而应该是这个 device 所在的 stack 的最上层的 device). 那这个 device 是个什么东西呢? 它是怎么来的呢? 按照道理, 这个 irp 应该被发送到 disk 的驱动去才对? 那么这个 device 是 disk 的驱动创建的么? 如果不是, 那中间都有些什么步骤呢?

本文就是回答上面这些问题的.

用 softice 看看就知道 file system 转发的 irp 并不是 vpb 指向的那个 device, 而是另外一个叫 volsnap 的 driver 创建的 device. 再跟踪看看, volsnap 创建的 device 是跟 vpb 的 device 在同一个 stack 里面, 而且 volsnap 创建的 device 的下一个 device 就是 vpb 的 device.

好了. 至少知道了确实 file system driver 要把 irp 转发到 vpb 指向的 device 去. 那接下来呢? irp 又会到哪里?

再跟踪下去. irp 到了一个由 partmgr.sys 创建的 device 里, 看看这个 partmgr… 它是一个 disk driver 的 filter (多用注册表,当初我在这里花了好长时间才发现这个东西是一个 filter), 所以 partmgr 把这个 irp 转发到了 disk driver. disk driver 接着发给了 acpi.sys, 然后 acpi.sys 转发给了 atapi.sys 创建的一个 device 去, 看看这个 device 的 stack, 已经是 1 了, irp 就到尽头了, 接下来的就不是 irp 的流程了, 而是所谓的 port/miniport 流程了.

大致的把整个的结构过了一次, 里面有很多的疑问, 下面要一一解释, 最好是能用 device tree 看看这个过程, 看看哪些是 pdo 哪些是 fdo, 看看每个 device 的 stack 值都是多少, 自己模仿下看看能不能了解 irp 的流程.

上面的整个 irp 的流程是很大的. 顶层 device 的 stack 达到了 8, 首先你就会奇怪这么庞大一个 device stack 是怎么建立起来的?

先说最上面的部分
file system 在受到一个 mount 的 fs control 的时候, 创建了一个无名的 device, 并且保存了 vpb 的 realdevice 所在的 stack 的最上层的 device, 以后所有的 irp 都转发到这个保存的指针上面. 注意这里并没有 attach 到那个 device 的 stack 上面, 而是直接使用指针 call driver 的. 这里会有一个问题, 在 file system mount 以后, 再 attach 到 volume device 的 stack 上的 device 收不到 file system 传递过来的 irp, 至于这个问题怎么解决, msdn 里面有说, 把这个 filter driver 设置成启动时加载,至于为什么这样作了就能行, 这个在后面会有解释.

阅读全文…

内核编程

733 views

驱动对象 设备对象 设备栈 乱杂谈

2010年10月21日

作者: JIURL
 
   用有限的几句话就舒舒服服的建立起对驱动对象和设备对象的概念是不可能的。刚开始是一片模糊,了解的多了,慢慢就清楚。下面的内容会使你对他们了解的清楚些。
  为了后面的叙述方便,首先列出驱动对象和设备对象的结构。
  驱动对象结构 DRIVER_OBJECT ,定义如下
  struct _DRIVER_OBJECT (sizeof=168)
  +00 int16 Type
  +02 int16 Size
  +04 struct _DEVICE_OBJECT *DeviceObject
  +08 uint32 Flags
  +0c void *DriverStart
  +10 uint32 DriverSize
  +14 void *DriverSection
  +18 struct _DRIVER_EXTENSION *DriverExtension
  +1c struct _UNICODE_STRING DriverName
  +1c uint16 Length
  +1e uint16 MaximumLength
  +20 uint16 *Buffer
  +24 struct _UNICODE_STRING *HardwareDatabase
  +28 struct _FAST_IO_DISPATCH *FastIoDispatch
  +2c function *DriverInit
  +30 function *DriverStartIo
  +34 function *DriverUnload
  +38 function *MajorFunction[28]

阅读全文…

内核编程

1,753 views

TrueCrypt 源码

2010年7月6日

TrueCrypt 可编译版本, 主要做了一些微调, 使得能通过vs2008, 并且不再吐出警告信息, 源码TrueCrypt-6-3-a-Src 下载, 要完整编译本源代码, 需要自己下载以下编译工具:

  • vs 2008 或者 VS 2010 download
  • nasm 下载地址 nasm 下载后将 nasm.exe 文件解压出来, 放到 system32 目录里
  • gzip 下载地址 gzip 下载后将 gzip.exe 文件解压出来, 放到 system32 目录里
  • vc1.5 下载地址这里(本地) 下载后解压到一个文件夹, 如 d:\vc1.5 , 然后创建一个环境变量 MSVC16_ROOT = d:\vc1.5 这是微软提供的最后一个能生成 16 位代码的编译工具
  • WDK 下载地址 微软提供, 安装后用目标目录创建环境变量 BASEDIR, 如 BASEDIR = D:\WinDDK\7600.16385.1 这个工具用于编译驱动

另:

内核编程, 技术心得 ,

1,173 views

Tips of Windows Programming

2010年6月11日

窗口总在最前

	::SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0,
		SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);

窗口总保持激活状态, 这段代码可以放在定时器内.

void KeepWindowAlwaysActive(HWND hWnd)
{
	HWND hCurWnd = NULL;
	DWORD lMyID = 0;
	DWORD lCurID = 0;

	hCurWnd = ::GetForegroundWindow();
	if(hCurWnd != hWnd)
	{
		lMyID = ::GetCurrentThreadId();
		lCurID = ::GetWindowThreadProcessId(hCurWnd, NULL);
		::AttachThreadInput(lMyID, lCurID, TRUE);
		::SetForegroundWindow(hWnd);
		::AttachThreadInput(lMyID, lCurID, FALSE);
	}
}

怎么跟自旋锁打交道

The code within a critical region guarded by an spin lock must neither be pageable nor make any references to pageable data.
The code within a critical region guarded by a spin lock can neither call any external function that might access pageable data or raise an exception, nor can it generate any exceptions.
The caller should release the spin lock with KeReleaseSpinLock as quickly as possible.

阅读全文…

内核编程, 技术心得 , , , ,

864 views

驱动开发中应该注意的事项

2010年6月10日

1. 一定不要在没有标注 I/O 请求数据包 (IRP) 挂起 (IoMarkIrpPending) 的情况下通过调度例程返回 STATUS_PENDING。
2. 一定不要通过中断服务例程 (ISR) 调用 KeSynchronizeExecution。 它会使系统死锁。
3. 一定不要将 DeviceObject->Flags 设置为 DO_BUFFERED_IO 和 DO_DIRECT_IO。 它会扰乱系统并最终导致致命错误。 而且,一定不要在 DeviceObject->Flags 中设置 METHOD_BUFFERED、METHOD_NEITHER、METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT,因为这些值只在定义 IOCTL 时使用。
4. 一定不要通过页面缓冲池分配调度程序对象。 如果这样做,将会偶尔导致系统故障检测 (Bugcheck)。
5. 当运行于 IRQL >= DISPATCH_LEVEL 时,一定不要通过页面缓冲池分配内存,或访问页面缓冲池中的内存。 这是一个致命错误。
6. 一定不要在 IRQL >= DISPATCH_LEVEL 上等候核心调度程序对象出现非零间隔。 这是一个致命错误。
7. 在 IRQL >= DISPATCH_LEVEL 上执行时,一定不要调用任何导致调用线程发生直接或间接等待的函数。 这是一个致命错误。
8. 一定不要把中断请求级别 (IRQL) 降低到低于您的顶级例程被调用的级别。
9. 如果没有调用过 KeRaiseIrql(),则一定不要调用 KeLowerIrql()。
10. 一定不要使处理器 (KeStallExecutionProcessor) 停止运转的时间超过 50 微秒。
11. 一定不要使旋转锁 (Spin Lock) 保持锁定状态的时间超过您的需要。 要使系统获得更好的总体性能,请不要使任何系统范围内有效的旋转锁的锁定时间超过 25 微秒。
12. 当 IRQL 大于 DISPATCH_LEVEL 时,一定不要调用 KeAcquireSpinLock 和 KeReleaseSpinLock,或 KeAcquireSpinLockAtDpcLevel 和 KeReleaseSpinLockFromDpcLevel。
阅读全文…

内核编程, 技术心得