首先 下载 ddk 并且安装http://download.microsoft.com/download/9/0/f/90f019ac-8243-48d3-91cf-81fc4093ecfd/1830_usa_ddk.iso
安装完之后开始菜单输入Windows Server 2003 Checked x86 Build Environment 打开这个中端并且输入build -cZ 进行编译
安装完目录结构如图
点开src 里面既是示例代码,各文件功能如下
src\general
包含各种内核模式驱动程序的示例,包括全面的“toaster”示例。
src\ime
输入法编辑器(IME)DLL和示例。
src\input
键盘和鼠标类过滤驱动程序。
src\kernel
其他内核模式驱动程序的示例。
src\mmedia
音频压缩管理器(ACM)软件编解码器。
src\network
各种类型的网络驱动程序示例。
src\print
用于打印机和绘图仪的示例驱动程序,以及附加的DLL和工具。
src\setup
示例INF文件和安装应用程序。
src\smartcard
串口和PCMCIA智能卡读卡器驱动程序。
src\storage
各种类型存储设备的示例驱动程序。
src\vdd
用于MS-DOS兼容性的虚拟设备驱动程序。
src\video
示例显示驱动程序和一个镜像驱动程序。
src\wdm
适用于支持Windows驱动模型(WDM)的各种设备的示例驱动程序。这些示例支持的设备类型包括音频、音视频流以及人机接口设备等。
.inf 文件在驱动程序安装中的主要作用:
只是一些配置信息,与运行代码无关,在这里知道一下即可,具体信息自行查阅。
驱动程序包包括以下组件:
设备
驱动程序文件
安装文件
其他文件
驱动过滤
由于NDIS中间层驱动位于协议驱动程序和网络驱动程序之间,所以它可以看到发生在一个系统中的所有网络流量。而不是像协议驱动那样只能看到从网络到来的封包。中间层驱动可用于封包过滤和防火墙开发。
以PassThru为例,进行NDIS驱动的介绍和在PassThru的的基本方法
先讲一下示例安装步骤
Passthru 作为一个服务(在提供的 INF/通知对象中称为“Passthru 驱动程序”)进行安装。安装步骤如下:
- 准备一个软盘(或安装目录),包含这些文件:netsf.inf、netsf_m.inf 和 passthru.sys。
- 在桌面上,右键单击“我的网络位置”图标,选择“属性”。
- 右键单击相关的本地连接图标,选择“属性”。
- 点击“安装”,然后点击“服务”,然后点击“添加”,然后点击“从磁盘安装”。
- 浏览到包含上面列出的文件的驱动器/目录。点击“确定”。这应该在网络服务列表中显示“Passthru 驱动程序”。选择此项并点击“确定”。这将安装 Passthru 驱动程序。
- 每次系统提示有关未签名文件安装的警告时,点击“确定”或“是”。这是必要的,因为通过 DDK 构建环境生成的二进制文件未签名。
需要两个 .INF 文件而不是一个,因为 Passthru 既作为协议(netsf.inf)也作为微型端口(netsf_.inf)进行安装。
NDIS驱动程序的数据处理流程
Passthru 包文件清单
Makefile | 编译期间用于创建对象和 sys 文件
Miniport.c | Passthru 驱动程序的微型端口相关功能
Netsf.inf | 服务的安装 INF(协议端安装)
Netsf\_m.inf | 微型端口的安装 INF(虚拟设备安装)
Passthru.c | DriverEntry 例程和 Passthru 微型端口和协议通用的任何例程
Passthru.h | Passthru 驱动程序使用的所有函数和数据结构的原型
Passthru.htm | Passthru 驱动程序的文档(此文件)
Passthru.rc | Passthru 驱动程序的资源文件
Precomp.h | 预编译头文件
Protocol.c | Passthru 驱动程序的协议相关功能
Sources | 编译和链接以创建 passthru 驱动程序的源文件列表。这可以修改以在早期 Windows 版本(如 Windows 2000)上创建二进制文件。
初始化和停止 Passthru 驱动程序的基本步骤(了解即可)
1. 在 DriverEntry 中,Passthru 驱动程序注册为一个协议和中间微型端口驱动程序。
2. 后来,NDIS 为每个配置绑定的底层 NDIS 适配器调用 Passthru 的 BindAdapterHandler, PtBindAdapter。
3. 在 BindAdapterHandler 的上下文中,并在成功打开底层适配器的绑定之后,Passthru 驱动程序查询保留的关键字“UpperBindings”,以获取对应于该特定绑定的将公开的虚拟适配器的设备名称列表。由于此驱动程序在底层绑定和虚拟适配器之间实现 1:1 的关系,所以此列表包含一个名称。在单个底层适配器上公开多个虚拟适配器的 “Mux” IM 驱动程序将处理 UpperBindings 中的多个条目。
4. 对于每个设备名称,Passthru 驱动程序调用 NdisIMInitializeDeviceInstanceEx。
5. 作为响应,NDIS 最终将回调 Passthru 微型端口的 MiniportInitialize 入口点 MPInitialize。
6. 在 MPInitialize 成功返回后,NDIS 会负责让上层协议绑定到新创建的虚拟适配器。
7. 来自上层协议对 Passthru 微型端口驱动程序的所有请求和发送都被重新封装并发送到 NDIS,以传递给底层 NDIS 适配器。
8. 从底层 NDIS 适配器绑定到达的所有指示都作为如果它们从 Passthru 的虚拟适配器生成的进行转发。
9. NDIS 调用 Passthru 驱动程序的 ProtocolUnbind 入口点以请求它关闭底层适配器与 Passthru 协议之间的绑定。在处理此操作时,Passthru 驱动程序首先为表示该特定绑定的虚拟适配器调用 NdisIMDeInitializeDeviceInstance。
10. NDIS 依次将关闭上层协议与虚拟 Passthru 适配器之间的所有绑定。
11. 在所有绑定都关闭后,NDIS 将调用虚拟适配器的 Passthru 驱动程序的 MiniportHalt 入口点(MPHalt)。
12. 然后 Passthru 协议通过调用 NdisCloseAdapter 来关闭与底层适配器的绑定,并完成在步骤 9 中发出的解绑请求。
接下来我们打开代码passthru.c文件进行解读
/*++
版权所有 (c) 1992-2000 Microsoft Corporation
模块名称:
passthru.c
摘要:
NDIS中间迷你端口驱动示例。这是一个透传驱动。
--*/
#include "precomp.h"
#pragma hdrstop
#pragma NDIS_INIT_FUNCTION(DriverEntry)
NDIS_HANDLE ProtHandle = NULL;
NDIS_HANDLE DriverHandle = NULL;
NDIS_MEDIUM MediumArray[4] =
{
NdisMedium802_3, // 以太网
NdisMedium802_5, // 令牌环
NdisMediumFddi, // FDDI
NdisMediumWan // NDISWAN
};
NDIS_SPIN_LOCK GlobalLock;
PADAPT pAdaptList = NULL;
LONG MiniportCount = 0;
NDIS_HANDLE NdisWrapperHandle;
//
// 用于支持来自用户模式的IOCTL:
//
#define LINKNAME_STRING L"\\DosDevices\\Passthru"
#define NTDEVICE_STRING L"\\Device\\Passthru"
NDIS_HANDLE NdisDeviceHandle = NULL;
PDEVICE_OBJECT ControlDeviceObject = NULL;
enum _DEVICE_STATE
{
PS_DEVICE_STATE_READY = 0, // 就绪状态,可以创建/删除
PS_DEVICE_STATE_CREATING, // 正在进行创建操作
PS_DEVICE_STATE_DELETING // 正在进行删除操作
} ControlDeviceState = PS_DEVICE_STATE_READY;
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
例程描述:
当此驱动程序加载时,第一个要调用的入口点。在NDIS中注册为中间驱动程序。
参数:
DriverObject - 指向此驱动程序的系统驱动程序对象结构的指针
RegistryPath - 此驱动程序的系统注册表路径
返回值:
如果所有初始化都成功,则为STATUS_SUCCESS,如果不成功,则为STATUS_XXX
错误代码。
--*/
{
NDIS_STATUS Status;
NDIS_PROTOCOL_CHARACTERISTICS PChars;
NDIS_MINIPORT_CHARACTERISTICS MChars;
NDIS_STRING Name;
Status = NDIS_STATUS_SUCCESS;
NdisAllocateSpinLock(&GlobalLock);
NdisMInitializeWrapper(&NdisWrapperHandle, DriverObject, RegistryPath, NULL);
do
{
//
// 在NDIS中注册迷你端口。注意,注册的是迷你端口
// 而不是协议。迷你端口必须在协议之前注册,
// 因为协议的BindAdapter处理程序可以在任何时间启动,
// 在这种情况下,它必须准备好启动驱动程序实例。
//
NdisZeroMemory(&MChars, sizeof(NDIS_MINIPORT_CHARACTERISTICS));
MChars.MajorNdisVersion = PASSTHRU_MAJOR_NDIS_VERSION;
MChars.MinorNdisVersion = PASSTHRU_MINOR_NDIS_VERSION;
MChars.InitializeHandler = MPInitialize;
MChars.QueryInformationHandler = MPQueryInformation;
MChars.SetInformationHandler = MPSetInformation;
MChars.ResetHandler = NULL;
MChars.TransferDataHandler = MPTransferData;
MChars.HaltHandler = MPHalt;
#ifdef NDIS51_MINIPORT
MChars.CancelSendPacketsHandler = MPCancelSendPackets;
MChars.PnPEventNotifyHandler = MPDevicePnPEvent;
MChars.AdapterShutdownHandler = MPAdapterShutdown;
#endif // NDIS51_MINIPORT
//
// 我们将禁用检查挂起超时,因此不需要检查挂起处理程序!
//
MChars.CheckForHangHandler = NULL;
MChars.ReturnPacketHandler = MPReturnPacket;
//
// 发送或SendPackets处理程序应该指定其中之一。
// 如果指定了SendPackets处理程序,则忽略SendHandler
//
MChars.SendHandler = NULL; // MPSend;
MChars.SendPacketsHandler = MPSendPackets;
Status = NdisIMRegisterLayeredMiniport(NdisWrapperHandle,
&MChars,
sizeof(MChars),
&DriverHandle);
if (Status != NDIS_STATUS_SUCCESS)
{
break;
}
#ifndef WIN9X
NdisMRegisterUnloadHandler(NdisWrapperHandle, PtUnload);
#endif
//
// 现在注册协议。
//
NdisZeroMemory(&PChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
PChars.MajorNdisVersion = PASSTHRU_PROT_MAJOR_NDIS_VERSION;
PChars.MinorNdisVersion = PASSTHRU_PROT_MINOR_NDIS_VERSION;
//
// 确保协议名称与安装此协议的INF中的服务名称
// (从INF)匹配。这是为了确保NDIS能够正确确定绑定,
// 并调用我们来绑定下面的迷你端口。
//
NdisInitUnicodeString(&Name, L"Passthru"); // 协议名称
PChars.Name = Name;
PChars.OpenAdapterCompleteHandler = PtOpenAdapterComplete;
PChars.CloseAdapterCompleteHandler = PtCloseAdapterComplete;
PChars.SendCompleteHandler = PtSendComplete;
PChars.TransferDataCompleteHandler = PtTransferDataComplete;
PChars.ResetCompleteHandler = PtResetComplete;
PChars.RequestCompleteHandler = PtRequestComplete;
PChars.ReceiveHandler = PtReceive;
PChars.ReceiveCompleteHandler = PtReceiveComplete;
PChars.StatusHandler = PtStatus;
PChars.StatusCompleteHandler = PtStatusComplete;
PChars.BindAdapterHandler = PtBindAdapter;
PChars.UnbindAdapterHandler = PtUnbindAdapter;
PChars.UnloadHandler = PtUnloadProtocol;
PChars.ReceivePacketHandler = PtReceivePacket;
PChars.PnPEventHandler= PtPNPHandler;
NdisRegisterProtocol(&Status,
&ProtHandle,
&PChars,
sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
if (Status != NDIS_STATUS_SUCCESS)
{
NdisIMDeregisterLayeredMiniport(DriverHandle);
break;
}
NdisIMAssociateMiniport(DriverHandle, ProtHandle);
}
while (FALSE);
if (Status != NDIS_STATUS_SUCCESS)
{
NdisTerminateWrapper(NdisWrapperHandle, NULL);
}
return(Status);
}
NDIS_STATUS
PtRegisterDevice(
VOID
)
/*++
例程描述:
注册一个ioctl接口 - 由NDIS在我们调用NdisMRegisterDevice时创建的用于此目的的设备对象。
每当初始化新的迷你端口实例时,都会调用此例程。但是,我们只在初始化第一个迷你端口实例时创建一个全局设备对象。此例程通过ControlDeviceState和MiniportCount变量处理潜在的竞争条件,与PtDeregisterDevice一起。
注意:不要从DriverEntry调用此函数;这将阻止驱动程序被卸载(例如,在卸载时)。
参数:
无
返回值:
如果成功注册设备对象,则返回NDIS_STATUS_SUCCESS。
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
UNICODE_STRING DeviceName;
UNICODE_STRING DeviceLinkUnicodeString;
PDRIVER_DISPATCH DispatchTable[IRP_MJ_MAXIMUM_FUNCTION+1];
DBGPRINT(("==>PtRegisterDevice\n"));
NdisAcquireSpinLock(&GlobalLock);
++MiniportCount;
if (1 == MiniportCount)
{
ASSERT(ControlDeviceState != PS_DEVICE_STATE_CREATING);
//
// 另一个线程可能正在代表另一个迷你端口实例运行PtDeregisterDevice。
// 如果是这样,请等待其退出。
//
while (ControlDeviceState != PS_DEVICE_STATE_READY)
{
NdisReleaseSpinLock(&GlobalLock);
NdisMSleep(1);
NdisAcquireSpinLock(&GlobalLock);
}
ControlDeviceState = PS_DEVICE_STATE_CREATING;
NdisReleaseSpinLock(&GlobalLock);
NdisZeroMemory(DispatchTable, (IRP_MJ_MAXIMUM_FUNCTION+1) * sizeof(PDRIVER_DISPATCH));
DispatchTable[IRP_MJ_CREATE] = PtDispatch;
DispatchTable[IRP_MJ_CLEANUP] = PtDispatch;
DispatchTable[IRP_MJ_CLOSE] = PtDispatch;
DispatchTable[IRP_MJ_DEVICE_CONTROL] = PtDispatch;
NdisInitUnicodeString(&DeviceName, NTDEVICE_STRING);
NdisInitUnicodeString(&DeviceLinkUnicodeString, LINKNAME_STRING);
//
// 创建一个设备对象并注册我们的调度处理程序
//
Status = NdisMRegisterDevice(
NdisWrapperHandle,
&DeviceName,
&DeviceLinkUnicodeString,
&DispatchTable[0],
&ControlDeviceObject,
&NdisDeviceHandle
);
NdisAcquireSpinLock(&GlobalLock);
ControlDeviceState = PS_DEVICE_STATE_READY;
}
NdisReleaseSpinLock(&GlobalLock);
DBGPRINT(("<==PtRegisterDevice: %x\n", Status));
return (Status);
}
NTSTATUS
PtDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
例程描述:
处理发送到此设备的IRP。
参数:
DeviceObject - 指向设备对象的指针
Irp - 指向I/O请求数据包的指针
返回值:
NTSTATUS - 总是返回STATUS_SUCCESS - 在添加用于处理ioctls的实际代码之前,将其更改。
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(DeviceObject);
DBGPRINT(("==>Pt Dispatch\n"));
irpStack = IoGetCurrentIrpStackLocation(Irp);
switch (irpStack->MajorFunction)
{
case IRP_MJ_CREATE:
break;
case IRP_MJ_CLEANUP:
break;
case IRP_MJ_CLOSE:
break;
case IRP_MJ_DEVICE_CONTROL:
//
// 在此处添加代码以处理发送到透传的ioctl命令。
//
break;
default:
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
DBGPRINT(("<== Pt Dispatch\n"));
return status;
}
NDIS_STATUS
PtDeregisterDevice(
VOID
)
/*++
例程描述:
注销ioctl接口。每当迷你端口实例被停止时,都会调用此函数。当最后一个迷你端口实例被停止时,我们请求NDIS删除设备对象。
参数:
NdisDeviceHandle - 由NdisMRegisterDevice返回的句柄
返回值:
如果一切正常,返回NDIS_STATUS_SUCCESS
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
DBGPRINT(("==>PassthruDeregisterDevice\n"));
NdisAcquireSpinLock(&GlobalLock);
ASSERT(MiniportCount > 0);
--MiniportCount;
if (0 == MiniportCount)
{
//
// 所有迷你端口实例已停止。注销控制设备。
//
ASSERT(ControlDeviceState == PS_DEVICE_STATE_READY);
//
// 阻止PtRegisterDevice(),同时释放控制
// 设备锁并注销设备。
//
ControlDeviceState = PS_DEVICE_STATE_DELETING;
NdisReleaseSpinLock(&GlobalLock);
if (NdisDeviceHandle != NULL)
{
Status = NdisMDeregisterDevice(NdisDeviceHandle);
NdisDeviceHandle = NULL;
}
NdisAcquireSpinLock(&GlobalLock);
ControlDeviceState = PS_DEVICE_STATE_READY;
}
NdisReleaseSpinLock(&GlobalLock);
DBGPRINT(("<== PassthruDeregisterDevice: %x\n", Status));
return Status;
}
VOID
PtUnload(
IN PDRIVER_OBJECT DriverObject
)
//
// PassThru驱动卸载函数
//
{
UNREFERENCED_PARAMETER(DriverObject);
DBGPRINT(("PtUnload: 进入\n"));
PtUnloadProtocol();
NdisIMDeregisterLayeredMiniport(DriverHandle);
NdisFreeSpinLock(&GlobalLock);
DBGPRINT(("PtUnload: 完成!\n"));
}
先试着运行起来,首先在开始菜单搜索这个运行终端
然后 cd切到这个位置
输入sc create Passthru type= kernel binPath=passthru.sys进行工程创建
显示创建成功
启动:sc start driver
停止:sc stop driver
删除:sc delete driver
然后输入运行命令sc start Passthru
发现禁止加载
此时遇到问题,设计到复杂的签名问题不在赘述,改用windbg进行调试。
下载并安装 https://windbg.download.prss.microsoft.com/dbazure/prod/1-0-0/windbg.appinstaller
然后打开刚刚的passthru.sys 文件发现调试成功 成功调用函数
在 PtDispatch
函数的 IRP_MJ_DEVICE_CONTROL
分支中添加处理抓包请求的代码,从箭头这里入手进行改动即可