内核驱动的文件操作

在内核驱动中创建文件,并进行读写。用于保存驱动运行中的信息。

方式:
可以用两种方式实现该操作。
1、 在应用程序中创建文件,驱动运用IOCTL将信息传给应用层,应用程序对文件进行读写。Tdi_fw就是利用这种方式来记录驱动过滤信息,Tdi_fw的应用程序创建一个线程,循环利用IOCTL来读取驱动信息,并保存到文件。
2、 在驱动中完成所有的这些操作。在这里我们主要讨论这种方式。

实现:
利用ZwCreateFile创建磁盘文件,利用ZwReadFile、ZwWriteFile读写文件。
创建文件代码如下:
RtlInitUnicodeString (&usname,L"//SystemRoot//System32//LogFiles//passthru.log");
InitializeObjectAttributes(&oa, &usname, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
Status = ZwCreateFile(&hfile, GENERIC_WRITE, &oa, &iostatus, NULL,
   FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);

写文件的代码如下:
ZwWriteFile(hfile, NULL, NULL, NULL, &iostatus, PrintContent, count, NULL, NULL);

这些代码倒是不难,网上找一些例子就搞定了。
注意两点:
1、 记得要在结束时,ZwClose(hFile)。
2、 系统刚启动时,最好将文件创建于C:,或者系统文件下。否则会创建文件失败。
真正的问题在下面,这些Zw函数都只能运行在PASSIVE_LEVEL上。在DISPATCH_LEVEL上调用这几个函数会出现BSOD。这时问题就来了,我们在PtReceive函数里面也需要保存一些运行的信息,而PtReceive处于DISPATCH_LEVEL的级别。这个问题需要解决。

这里有两种方式:
1.运用工作队列WorkItem,WorkItem能排队注册的回调函数。当例程处于DISPATCH_LEVEL级别时将回调函数塞入队列,当进程降低到PASSIVE_LEVEL时,这些队列中的回调函数将会被系统调用。
2.运用PsCreateSystemThread方式注册一个线程,在注册一个事件,申请一段内存。
在我们有信息写入文件的时候,先将信息写入内存,然后Set这个事件。在这个线程中循环KeWaitForSingleObject这个Event。然后在调用ZwWriteFile将信息写入文件。这里要注意一点的是文件读写的同步问题。我实现的就是这种方案。


1、可以用用RtlStringCbPrintfA代替sprintf函数,对内存进行字符串初始化。例子如下:

#include "ntstrsafe.h"

CHAR pszDest[30];
ULONG cbDest = 30;
LPCSTR pszFormat = "%s %d + %d = %d.";
CHAR* pszTxt = "The answer is";

RtlStringCbPrintfA(pszDest, cbDest, pszFormat, pszTxt, 1, 2, 3);
KdPrint(("%s",pszDest));
需要注意的是这个函数还有另一个版本RtlStringCbPrintfW,专门用来对UNICODE进行超作。其他可能对我们比较有用的函数是RtlStringCbCopyA和RtlStringCbCatA用于拷贝和连接。操作都很简单,它们本身就是驱动下的sprintf的替代版本。

2、发现一个问题,就是安装完驱动重启后,在DriverEntry调用那个ZwCreateFile函数会返回失败。而在系统启动以后安装Passthru,就不会出现这个问题。原因是在操作系统启动时,系统初始化Passthru,调用我们的DriverEntry函数。这时候磁盘还没完全初始化好无法访问磁盘。
这个问题其实对我们来说没有什么影响,我们需要记录信息的时候,一定是在应用程序客户端已经开启后的事情了。这时候磁盘早已启动好了,我们不用担心。
这里的解决方法是,在应用程序一启动,打开驱动设备的时候,才ZwCreateFile。这样一定是可以的。

3、 运用WorkItem进行系统回调函数排队的方法也不难。在NDIS下可以使用NdisInitializeWorkItem和NdisScheduleWorkItem函数来排队我们在高irql下的不能执行的动作。等到进程下降到passive_level时,这些已经排队了的回调函数就会Dequeue。
这个方法我今天试过了,可以使用。但是从效率来讲,并没有什么突出的地方。而且根据CSDN的介绍,工作队列有两个需要注意的地方。一是不能执行太长的操作,会死锁。2是在排队工作项之前,最好释放所有的mutex、lock、semaphore,否则很容易死锁。反正这两种操作都不错(另一种线程的方法参见前面文档)。随便选一个用用就行了。
Regmon(2)


if(NT_SUCCESS(ntStatus))
{
创建符号连接,以便GUI能够指名,访问这个驱动/设备
创建每个需要处理的方法的分发点
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] =
DriverObject->MajorFunction[IRP_MJ_CREATE] =
DriverObject->MajorFunction[IRP_MJ_CLOSE] =
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RegmonDispatch;
#if DBG
DriverObject->DriverUnload = RegmonUnload;
#end if
}

if(!NT_SUCCESS(ntStatus))
{
DbgPrint((“Regmon: Failed   to create our device!/n”));
发生错误,清理环境(资源等)
if(GUIDevice) IoDeleteDevice(GUIDevice);
IoDeleteSymbolicLink(&deviceLinkUnicodeString);
return ntStatus;
}

初始化互斥量
MUTEX_INIT(StoreMutex);
MUTEX_INIT(HashMutex);
MUTEX_INIT(FilterMutex);

初始化根键长度
for(I = 0; I < NUMROOTKEYS; I ++)
{
RootKey[I].RootNameLen = strlen(RootKey[I].RootName;
}
for(I = 0; I < 2; I ++)
{
CurrentUser[I].RootNameLen = strlen(CurrentUser[I].RootName;
}

系统表数据结构指针,一个NTOSKRNL导出
ServiceTable = KeServiceDescriptorTable; 用户自定义的结构
DbgPrint((“HookRegistry:Servicetable:%x/n”, ServiceTable));
获取进程名称的偏移
ProcessNameOffset = GetProcessNameOffset(); 这个函数是牛人自己写的。

分配初始化的输出缓冲区
Store = ExAllocatePool(PagePool, sizeof(*Store));
if(!Store)
{
IoDeleteDevice(GUIDevice);
IoDeleteSymbolicLink(&deviceLinkUnicodeString);
return STATUS_INSUFFICENT_RESOURCES;
}
Store->Len = 0;
Store->Next = NULL;
NumStore = 1;

如果我们是个启动设备则开始记日志
if(startType != SERVICE_DEMAND_START)
{
初始化日志
BoolLogging = TRUE;

KeInitializeEvent(&LoggingEvent, SynchronizationEvent, FALSE);
GUIActive = TRUE;
Fileltet = BootFilter;
RegmonUpdateFilters();
HookRegisters();

告诉用户,日志模式已经开启
RtlInitUnicodeString(&bootMessageUnicodeString, bootMessage);
ZwDisplayString(&bootMessageUnicodeString);

注册关闭通知
IoRegisterShutdonwNotification注册一个驱动提供的系统关闭通知函数,在系统被完全关闭之前调用。
IoRegisterShutdown Notification(GUIDevice);

}
return STATUS_SUCCESS;
}

 

 

 


RegmonDispatch
这个函数处理设备的请求。需要显示处理的请求来自GUI。当然,当GUI建立,关闭合驱动的通信时,也需要处理Create,Close。
NTSTAUTS RegmonDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION irpStack; 当前堆栈
PVOID inputBuffer;
PVOID outputBuffer;
ULONG inputBufferLength;
ULONG outputBufferLength;
ULONG ioControlCode;
PSTORE_BUF old;
WORK_QUEUE_ITEM workItem;

设置处理成功
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

获取Irp中当前位置的指针,这就是代码和参数定位的地方
irpStack = IoGetCurrentIrpStackLocation(Irp);

inputBuffer = Irp->AssociatedIrp.SystemBuffer;
inputBufferLength = iprStack->Parameters.DeviceIoControl.InputBufferLength;
outputBuffer = Irp->AssociatedIrp.SystemBuffer;
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;

switch(irpStack->MajorFunction)
{
case IRP_MJ_CREATE:
       DbgPrint((“Regmon: IRP_MJ_CREATE/n”));
       关闭启动日志
       if(BootLogging)
       {
         BootLogging = FALSE;
         IoUnregisterShutdownNotification(DeviceObject);
         MUTEX_WAIT(StoreMutex);
         ExInitializeWorkItem(&workItem, RegmonCloseBootLog, 0);
这个函数已经过期,应该使用IoAllocateWorkItem。返回一个指向私有结构IO_WORKITEM的指针。驱动不应该以任何形式访问这个结构!
         ExQueueWorkItem(&workItem, CriticalWorkQueue);
         这个函数也过期了,应该使用IoQueueWorkItem。插入一个指定的工作项到队列中,这个队列由系统工作线程访问,系统工作线程从队列中移出一个工作项,并调用其中的回调函数。
         KeWaitForSingleObject(&LoggingEvent, Executive, KernelMode, FALSE, NULL);
         MUTEX_RELEASE(StoreMutex);
       }
       Sequence = 0;
       GUIActive = TRUE;
       DbgPrint((“GUI Active: %d/n”, GUIActive));
       break;
}
}

还能继续小
Project->Settings打开Link属性页,将Object/library modules:下面编辑框中的各种lib全部删除,然后打上msvcrt.lib kernel32.lib user32.lib
Project->Settings的Link属性页里,在Project Options下面的编辑框里加上一句:/ALIGN:4096

这都是些简洁的方法


摘自:  http://blog.csdn.net/floweronwarmbed/archive/2009/06/01/4233452.aspx

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在VSCode中配置Linux内核驱动文件路径,需要在.vscode目录下编辑c_cpp_properties.json文件。具体操作如下: 1. 打开VSCode,在工作区或者项目文件夹中创建.vscode文件夹。 2. 在.vscode文件夹中创建一个名为c_cpp_properties.json的文件。 3. 在c_cpp_properties.json文件中添加以下代码: ```json { "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "/usr/src/linux-headers-$(uname -r)/include/**" ], "defines": [], "compilerPath": "/usr/bin/gcc", "cStandard": "gnu11", "cppStandard": "gnu++14", "intelliSenseMode": "linux-gcc-x64" } ], "version": 4 } ``` 其中,includePath中的路径指定了Linux内核文件的路径,这里使用了uname命令获取当前内核版本号,然后将其作为路径的一部分。 4. 保存c_cpp_properties.json文件。 现在,VSCode就可以正确地识别Linux内核驱动的头文件路径了。 ### 回答2: 要在VS Code中配置Linux内核驱动的头文件路径,您可以按照以下步骤进行操作: 1. 打开VS Code,并确保您已经安装了C/C++插件。 2. 打开您要配置的Linux内核驱动项目文件夹。 3. 在文件资源管理器中,找到并打开项目中的`.vscode`文件夹(如果没有此文件夹,请创建一个新的文件夹并命名为`.vscode`)。 4. 在`.vscode`文件夹中创建一个名为`c_cpp_properties.json`的新文件。 5. 在`c_cpp_properties.json`文件中,输入以下内容: ```json { "configurations": [ { "name": "Linux", "includePath": [ "/usr/src/linux-headers-$(uname -r)/include" ], "defines": [], "compilerPath": "/usr/bin/gcc", "cStandard": "c11", "cppStandard": "c++17", "intelliSenseMode": "gcc-x64" } ], "version": 4 } ``` 注意:上述配置假设您的Linux内核文件所在的路径为`/usr/src/linux-headers-$(uname -r)/include`,请根据您的实际路径进行修改。另外,确保您的`gcc`编译器路径为`/usr/bin/gcc`,并根据需要调整C和C++的标准版本。 6. 保存并关闭`c_cpp_properties.json`文件。 现在,当您在VS Code中打开Linux内核驱动项目时,它将会使用指定的头文件路径进行编译和IntelliSense。 ### 回答3: 要配置VS Code来使用Linux内核驱动的头文件路径,您可以按照以下步骤进行操作: 1. 首先,打开VS Code编辑器并进入您的项目文件夹。 2. 在项目文件夹中,找到并打开名为".vscode"的隐藏文件夹。如果该文件夹不存在,可以手动创建一个新的。 3. 在".vscode"文件夹中,创建一个名为"c_cpp_properties.json"的JSON格式文件,如果该文件已经存在,则打开它。 4. 在"c_cpp_properties.json"文件中,您将看到一个名为"configurations"的数组。在该数组中,您需要找到与您正在使用的编译器相对应的配置部分。 5. 在所选配置部分中,将"includePath"属性设置为适合您的情况的值。您需要将Linux内核驱动文件的路径添加到该属性中。 例如,如果您的Linux内核驱动文件位于"/usr/src/linux-headers-x.x.x/include"的路径下,您可以设置"includePath"属性如下: "includePath": [ "${workspaceFolder}/**", "/usr/src/linux-headers-x.x.x/include" ] 请注意,上述示例中的"x.x.x"代表您实际使用的Linux内核版本号。根据实际情况调整路径。 保存"c_cpp_properties.json"文件。 在完成上述步骤后,VS Code将会在进行代码补全和构建时使用您指定的Linux内核驱动文件的路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值