当前位置:首页 > 网络安全 > windows安全 > 《寒江独钓-Windows内核安全编程》学习笔记2-键盘过滤

《寒江独钓-Windows内核安全编程》学习笔记2-键盘过滤

2016-09-17 | 来源:网络
#pragmaonce #ifdef__cplusplus extern"C" { #endif #include<wdm.h> #include<ntddkbd.h> #ifdef__cplusplus } #endif #definePAGEDCODEcode_seg("PAGE") #defineLOCKEDCODEcode_seg() #defineINITC
#pragma once

#ifdef __cplusplus
extern "C"
{
#endif
#include <wdm.h>
#include <ntddkbd.h>
#ifdef __cplusplus
}
#endif 

#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")

#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")

#define KBD_DRIVER_NAME L"\\Driver\\Kbdclass"

typedef struct _DEVICE_EXTENSION {
	//结构大小
	ULONG NodeSize;
	//过滤设备对象
	PDEVICE_OBJECT pFilterDeviceObject;
	//同时调用时的保护锁
	KSPIN_LOCK IoRequestSpinLock;
	//进程间同步处理
	KEVENT IoInProgressEvent;
	//绑定的设备对象
	PDEVICE_OBJECT TargetDeviceObject;
	//绑定前底层设备对象
	PDEVICE_OBJECT LowerDeviceObject;

} C2P_DEVICE_EXTENSION, *PC2P_DEVICE_EXTENSION;

// 这个函数是事实存在的,只是文档中没有公开。声明一下
// 就可以直接使用了。
extern "C" 
{
	NTSTATUS ObReferenceObjectByName(PUNICODE_STRING ObjectName,ULONG Attributes,
		PACCESS_STATE AccessState,ACCESS_MASK DesiredAccess,POBJECT_TYPE ObjectType,
		KPROCESSOR_MODE AccessMode,PVOID ParseContext,PVOID *Object);

	//一个头文件中没有的全局变量
	extern POBJECT_TYPE* IoDriverObjectType;
}

ULONG gC2pKeyCount = 0;

PDRIVER_OBJECT gDriverObject=NULL;

NTSTATUS c2pDispatchGeneral(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);

NTSTATUS c2pPower(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);

NTSTATUS c2pPnP(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);

NTSTATUS c2pDispatchRead(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);

VOID c2pUnload( PDRIVER_OBJECT pDriverObject);

//遍历驱动下的设备 并绑定
NTSTATUS c2pAttachDevices(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath);

//读请求完成后的回调函数
NTSTATUS c2pReadComplete(PDEVICE_OBJECT pDeviceObject,PIRP pIrp,PVOID Context);

//根据扫描码得到按键
VOID MyPrintKeyStroke(UCHAR sch);

unsigned char asciiTbl[]={
	0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,	//normal
	0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x0D, 0x00, 0x61, 0x73,
	0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x7A, 0x78, 0x63, 0x76,
	0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
	0x32, 0x33, 0x30, 0x2E,
	0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x09,	//caps
	0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x5B, 0x5D, 0x0D, 0x00, 0x41, 0x53,
	0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3B, 0x27, 0x60, 0x00, 0x5C, 0x5A, 0x58, 0x43, 0x56,
	0x42, 0x4E, 0x4D, 0x2C, 0x2E, 0x2F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
	0x32, 0x33, 0x30, 0x2E,
	0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09,	//shift
	0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F, 0x50, 0x7B, 0x7D, 0x0D, 0x00, 0x41, 0x53,
	0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x5A, 0x58, 0x43, 0x56,
	0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
	0x32, 0x33, 0x30, 0x2E,
	0x00, 0x1B, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x08, 0x09,	//caps + shift
	0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x7B, 0x7D, 0x0D, 0x00, 0x61, 0x73,
	0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3A, 0x22, 0x7E, 0x00, 0x7C, 0x7A, 0x78, 0x63, 0x76,
	0x62, 0x6E, 0x6D, 0x3C, 0x3E, 0x3F, 0x00, 0x2A, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x38, 0x39, 0x2D, 0x34, 0x35, 0x36, 0x2B, 0x31,
	0x32, 0x33, 0x30, 0x2E
};

cpp

#include "ctrl2cap.h"

//入口函数
#pragma INITCODE
extern "C" NTSTATUS DriverEntry (IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)
{
	KdPrint(("ctrl2cap Enter DriverEntry\n"));

	NTSTATUS status;
	//填写所有的分发函数的指针

	for (ULONG i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
	{
		pDriverObject->MajorFunction[i]=c2pDispatchGeneral;
	}

	// 单独的填写一个Read分发函数。因为要的过滤就是读取来的按键信息
	// 其他的都不重要。这个分发函数单独写。
	pDriverObject->MajorFunction[IRP_MJ_READ]=c2pDispatchRead;

	// 单独的填写一个IRP_MJ_POWER函数。这是因为这类请求中间要调用
	// 一个PoCallDriver和一个PoStartNextPowerIrp,比较特殊。
	pDriverObject->MajorFunction [IRP_MJ_POWER] = c2pPower; 

	// 我们想知道什么时候一个我们绑定过的设备被卸载了(比如从机器上
	// 被拔掉了?)所以专门写一个PNP(即插即用)分发函数
	pDriverObject->MajorFunction [IRP_MJ_PNP] = c2pPnP; 

	//卸载函数
	pDriverObject->DriverUnload=c2pUnload;

	gDriverObject=pDriverObject;

	KdPrint(("ctrl2cap DriverEntry end\n"));

	//绑定所有键盘设备
	status=c2pAttachDevices(pDriverObject,pRegistryPath);

	return STATUS_SUCCESS;
}

NTSTATUS c2pDispatchGeneral( PDEVICE_OBJECT pDeviceObject,PIRP pIrp )
{
	KdPrint(("Enter GeneralDispatchRoutin\n"));

	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);

	//建立一个字符串数组与IRP类型对应起来
	static char* irpname[] = 
	{
		"IRP_MJ_CREATE",
		"IRP_MJ_CREATE_NAMED_PIPE",
		"IRP_MJ_CLOSE",
		"IRP_MJ_READ",
		"IRP_MJ_WRITE",
		"IRP_MJ_QUERY_INFORMATION",
		"IRP_MJ_SET_INFORMATION",
		"IRP_MJ_QUERY_EA",
		"IRP_MJ_SET_EA",
		"IRP_MJ_FLUSH_BUFFERS",
		"IRP_MJ_QUERY_VOLUME_INFORMATION",
		"IRP_MJ_SET_VOLUME_INFORMATION",
		"IRP_MJ_DIRECTORY_CONTROL",
		"IRP_MJ_FILE_SYSTEM_CONTROL",
		"IRP_MJ_DEVICE_CONTROL",
		"IRP_MJ_INTERNAL_DEVICE_CONTROL",
		"IRP_MJ_SHUTDOWN",
		"IRP_MJ_LOCK_CONTROL",
		"IRP_MJ_CLEANUP",
		"IRP_MJ_CREATE_MAILSLOT",
		"IRP_MJ_QUERY_SECURITY",
		"IRP_MJ_SET_SECURITY",
		"IRP_MJ_POWER",
		"IRP_MJ_SYSTEM_CONTROL",
		"IRP_MJ_DEVICE_CHANGE",
		"IRP_MJ_QUERY_QUOTA",
		"IRP_MJ_SET_QUOTA",
		"IRP_MJ_PNP",
	};

	UCHAR type = stack->MajorFunction;
	
	KdPrint(("\t%s\n", irpname[type]));


	
	
	IoSkipCurrentIrpStackLocation(pIrp);

	//获取设备拓展
	PC2P_DEVICE_EXTENSION pDevExt=(PC2P_DEVICE_EXTENSION)pDeviceObject->DeviceExtension;

	KdPrint(("Leave GeneralDispatchRoutin\n"));


	return PoCallDriver(pDevExt->LowerDeviceObject,pIrp);
	
}

NTSTATUS c2pPower( PDEVICE_OBJECT pDeviceObject,PIRP pIrp )
{
	PC2P_DEVICE_EXTENSION pDeviceExt=(PC2P_DEVICE_EXTENSION)pDeviceObject->DeviceExtension;

	PoStartNextPowerIrp(pIrp);
	IoSkipCurrentIrpStackLocation(pIrp);
	return PoCallDriver(pDeviceExt->LowerDeviceObject,pIrp);
}

NTSTATUS c2pPnP( PDEVICE_OBJECT pDeviceObject,PIRP pIrp )
{
	PC2P_DEVICE_EXTENSION pDeviceExt=NULL;

	PIO_STACK_LOCATION pIrpStack=NULL;
	NTSTATUS status=STATUS_SUCCESS;
	//KIRQL oldIrql;
	//KEVENT event;

	//获取真实设备
	pDeviceExt=(PC2P_DEVICE_EXTENSION)pDeviceObject->DeviceExtension;

	pIrpStack=IoGetCurrentIrpStackLocation(pIrp);

	switch (pIrpStack->MajorFunction)
	{
	case IRP_MN_REMOVE_DEVICE:
		KdPrint(("IRP_MN_REMOVE_DEVICE\n"));
		//首先把请求发下去
		IoSkipCurrentIrpStackLocation(pIrp);
		IoCallDriver(pDeviceExt->LowerDeviceObject,pIrp);

		//解除绑定
		IoDetachDevice(pDeviceExt->LowerDeviceObject);
		//删除设备
		IoDeleteDevice(pDeviceObject);
		status=STATUS_SUCCESS;
		break;
	default:
		//对于其他类型的IRP,直接下发
		IoSkipCurrentIrpStackLocation(pIrp);
		status=IoCallDriver(pDeviceExt->LowerDeviceObject,pIrp);
	}
	return status;
}

#define  DELAY_ONE_MICROSECOND  (-10)
#define  DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
#define  DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)

//解除绑定并删除设备
void c2pDetach( PDEVICE_OBJECT pDeviceObject ) 
{
	PC2P_DEVICE_EXTENSION pDeviceExt=NULL;

	BOOLEAN NoRequestOutstanding=FALSE;

	pDeviceExt=(PC2P_DEVICE_EXTENSION)pDeviceObject->DeviceExtension;

	__try
	{
		__try
		{
			IoDetachDevice(pDeviceExt->LowerDeviceObject);
			pDeviceExt->TargetDeviceObject=NULL;
			IoDeleteDevice(pDeviceObject);
			pDeviceExt->pFilterDeviceObject=NULL;
			DbgPrint(("Detach Finished\n"));
		}
		__except(EXCEPTION_EXECUTE_HANDLER){}
	}
	__finally{}
}

VOID c2pUnload( PDRIVER_OBJECT pDriverObject )
{
	PDEVICE_OBJECT pDeviceObject=NULL;
	PDEVICE_OBJECT pOldDeviceObject=NULL;

	PC2P_DEVICE_EXTENSION pDeviceExt=NULL;

	LARGE_INTEGER lDelay;

	PRKTHREAD CurrentThread;

	//delay some time
	lDelay=RtlConvertLongToLargeInteger(100*DELAY_ONE_MILLISECOND);
	CurrentThread=KeGetCurrentThread();
	//把当前线程设置为低实时模式,以便让它的运行尽量少影响其他程序
	KeSetPriorityThread(CurrentThread,LOW_REALTIME_PRIORITY);
	UNREFERENCED_PARAMETER(pDeviceObject);
	KdPrint(("DriverEntry unLoading...\n"));


	//遍历所有设备并一律解除绑定
	pDeviceObject=pDriverObject->DeviceObject;
	while (pDeviceObject)
	{
		//解除绑定并删除所有设备
		c2pDetach(pDeviceObject);

		pDeviceObject=pDeviceObject->NextDevice;

	}

	ASSERT(NULL==pDriverObject->DeviceObject);

	while (gC2pKeyCount)
	{
		KeDelayExecutionThread(KernelMode,FALSE,&lDelay);
	}
	KdPrint(("DriverEntry unLoad OK!\n")); 
	return;
}

//初始化扩展信息
NTSTATUS c2pDeviceExtInit( PC2P_DEVICE_EXTENSION pDeviceExt, PDEVICE_OBJECT pFilterDeviceObject, 
	PDEVICE_OBJECT pTargetDeviceObject, PDEVICE_OBJECT pLowerDeviceObject ) 
{
	memset(pDeviceExt,0,sizeof(C2P_DEVICE_EXTENSION));
	pDeviceExt->NodeSize=sizeof(C2P_DEVICE_EXTENSION);
	pDeviceExt->pFilterDeviceObject=pFilterDeviceObject;
	
	KeInitializeSpinLock(&(pDeviceExt->IoRequestSpinLock));
	KeInitializeEvent(&(pDeviceExt->IoInProgressEvent),NotificationEvent,FALSE);
	pDeviceExt->TargetDeviceObject=pTargetDeviceObject;
	pDeviceExt->LowerDeviceObject=pLowerDeviceObject;

	return STATUS_SUCCESS;
}

NTSTATUS c2pAttachDevices( PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath )
{
	
	NTSTATUS status=0;
	UNICODE_STRING uniNtNameString;
	PC2P_DEVICE_EXTENSION pDeviceExt;
	PDEVICE_OBJECT pFilterDeviceObject=NULL;
	PDEVICE_OBJECT pTargetDeviceObject=NULL;
	PDEVICE_OBJECT pLowerDeviceObject=NULL;

	PDRIVER_OBJECT pKbdDriverObject=NULL;

	KdPrint(("MyAttach\n"));

	//初始化一个字符串,Kdclass驱动的名字
	RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);

	//打开驱动对象
	status=ObReferenceObjectByName(&uniNtNameString,OBJ_CASE_INSENSITIVE,NULL,0,
		*IoDriverObjectType,KernelMode,NULL,(PVOID*)&pKbdDriverObject);
	//如果失败了 就直接返回
	if (!NT_SUCCESS(status))
	{
		KdPrint(("MyAttach: Couldn't get the MyTest Device Object\n")); 
		return status;
	}
	else
	{
		//解引用 
		ObDereferenceObject(pKbdDriverObject);
	}
	//这是设备链中的第一个设备
	pTargetDeviceObject=pKbdDriverObject->DeviceObject;
	while (pTargetDeviceObject)
	{
		//生成一个过滤设备
		status=IoCreateDevice(pDriverObject,sizeof(C2P_DEVICE_EXTENSION),NULL,
			pTargetDeviceObject->DeviceType,pTargetDeviceObject->Characteristics,FALSE,&pFilterDeviceObject);
		//如果失败了 就直接退出
		if (!NT_SUCCESS(status))
		{
			KdPrint(("MyAttach: Couldn't create the MyFilter Filter Device Object\n")); 
			return (status); 
		}
		
		//绑定设备 
		pLowerDeviceObject=IoAttachDeviceToDeviceStack(pFilterDeviceObject,pTargetDeviceObject);
		//如果绑定失败 删除创建的设备

		if (!pLowerDeviceObject)
		{
			KdPrint(("MyAttach: Couldn't attach to MyTest Device Object\n")); 
			IoDeleteDevice(pFilterDeviceObject);
			pFilterDeviceObject=NULL;
			return status;
		}
		KdPrint(("attch successful\n"));
		//初始化设备扩展
		pDeviceExt=(PC2P_DEVICE_EXTENSION)pFilterDeviceObject->DeviceExtension;
		c2pDeviceExtInit(pDeviceExt,pFilterDeviceObject,pTargetDeviceObject,pLowerDeviceObject);

		//设置重要标志位
		pFilterDeviceObject->DeviceType=pLowerDeviceObject->DeviceType; 
		pFilterDeviceObject->Characteristics=pLowerDeviceObject->Characteristics; 
		pFilterDeviceObject->StackSize=pLowerDeviceObject->StackSize+1; 
		pFilterDeviceObject->Flags |= pLowerDeviceObject->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE) ; 

		//继续绑定下一个设备
		pTargetDeviceObject=pTargetDeviceObject->NextDevice;
	}
	return status;
}

NTSTATUS c2pDispatchRead( PDEVICE_OBJECT pDeviceObject,PIRP pIrp )
{
	NTSTATUS status=STATUS_SUCCESS;
	PC2P_DEVICE_EXTENSION pDeviceExt=NULL;
	PIO_STACK_LOCATION currentIrpStack=NULL;
	KEVENT waitEvent;
	KeInitializeEvent(&waitEvent,NotificationEvent,FALSE);

	if (pIrp->CurrentLocation==1)
	{
		ULONG ReturnedInformation=0;
		KdPrint(("Dispatch encountered bogus current location\n")); 
		status=STATUS_INVALID_DEVICE_REQUEST;
		pIrp->IoStatus.Status=status;
		pIrp->IoStatus.Information=ReturnedInformation;
		IoCompleteRequest(pIrp,IO_NO_INCREMENT);

		return status;
	}
	//全局变量键计数器加1
	gC2pKeyCount++;
	
	pDeviceExt=(PC2P_DEVICE_EXTENSION)pDeviceObject->DeviceExtension;

	// 设置回调函数并把IRP传递下去。 之后读的处理也就结束了。
	// 剩下的任务是要等待读请求完成。

	currentIrpStack=IoGetCurrentIrpStackLocation(pIrp);
	IoCopyCurrentIrpStackLocationToNext(pIrp);

	IoSetCompletionRoutine(pIrp,c2pReadComplete,pDeviceObject,TRUE,TRUE,TRUE);

	return IoCallDriver(pDeviceExt->LowerDeviceObject,pIrp);
}

NTSTATUS c2pReadComplete( PDEVICE_OBJECT pDeviceObject,PIRP pIrp,PVOID Context )
{
	KdPrint(("call the read callback\n"));

	PIO_STACK_LOCATION pIrpStack;
	ULONG buf_len=0;
	PUCHAR buf=NULL;
	ULONG numKeys;
	pIrpStack=IoGetCurrentIrpStackLocation(pIrp);
	PKEYBOARD_INPUT_DATA KeyData=NULL;
	//  如果这个请求是成功的。很显然,如果请求失败了,这么获取
	//   进一步的信息是没意义的。

	if (NT_SUCCESS(pIrp->IoStatus.Status))
	{
		 // 获得读请求完成后输出的缓冲区
		buf=(PUCHAR)pIrp->AssociatedIrp.SystemBuffer;
		KeyData=(PKEYBOARD_INPUT_DATA)pIrp->AssociatedIrp.SystemBuffer;

		// 获得这个缓冲区的长度。一般的说返回值有多长都保存在
		// Information中。
		
		buf_len=pIrp->IoStatus.Information;
		//处理缓冲区信息
		//… 这里可以做进一步的处理。我这里很简单的打印出所有的扫
		// 描码。
		numKeys=buf_len/sizeof(KEYBOARD_INPUT_DATA);

		for (ULONG i=0;i<numKeys;i++)
		{
			//打印按键信息
			KdPrint(("numkeys:%d ",numKeys));
			KdPrint(("scancode:%x ",KeyData->MakeCode));
			KdPrint(("%s\n",KeyData->Flags"UP":"Down"));

			MyPrintKeyStroke((UCHAR)KeyData->MakeCode);
		}

	}


	gC2pKeyCount--;
	if (pIrp->PendingReturned)
	{
		IoMarkIrpPending(pIrp);
	}

	return pIrp->IoStatus.Status;
}


// flags for keyboard status
#define	S_SHIFT				1
#define	S_CAPS				2
#define	S_NUM				4
static int kb_status = S_NUM;

VOID MyPrintKeyStroke( UCHAR sch )
{
	UCHAR ch=0;
	int off=0;

	if ((sch&0x80)==0) //make
	{
		if ((sch < 0x47) || 
			((sch >= 0x47 && sch < 0x54) && (kb_status & S_NUM))) // Num Lock
		{
			ch = asciiTbl[off+sch];
		}

		switch(sch)
		{
		case 0x3A:
			kb_status^=S_CAPS;
			break;;
		case 0x2A:
		case 0x36:
			kb_status|=S_SHIFT;
			break;
		case 0x45:
			kb_status^=S_NUM;
		}
	}
	else
	{
		if (sch==0xAA||sch==0xB6)
		{
			kb_status&=~S_SHIFT;
		}
	}
	if (ch>=0x20&&ch<0x7F)
	{
		KdPrint(("%C \n",ch));
	}
}

 需要注意的:

cpp文件中,需要加入 extern "C"

extern "C" 
{
	NTSTATUS ObReferenceObjectByName(PUNICODE_STRING ObjectName,ULONG Attributes,
		PACCESS_STATE AccessState,ACCESS_MASK DesiredAccess,POBJECT_TYPE ObjectType,
		KPROCESSOR_MODE AccessMode,PVOID ParseContext,PVOID *Object);

	//一个头文件中没有的全局变量
	extern POBJECT_TYPE* IoDriverObjectType;
}

 这里书上为extern POBJECT_TYPE  IoDriverObjectType;

应该为 extern POBJECT_TYPE* IoDriverObjectType;

评论

评论数10

表情
发表评论
网友评论仅供其表达个人看法,并不表明网易立场。
《《寒江独钓-Windows内核安全编程》学习笔记2-键盘过滤》更多评论

阅读下一篇

Windows平台下tomcat安全设置

原文作者:AkashKava译者:misthill Tomcat是一个世界上广泛使用的支持JSP和servlets的Web服务器。它在JAVA运行时上能够很好地运行并支持Web应用部署。 运行Tomcat很简单;到Tomcat网站下载安装程序就可进行T ... 查看全文

返回windows安全 返回网站首页