Google
Web ntkernel.com
Loki Network Project

Windows Internals FAQ  

Frequently Asked Questions

Copyright (C) www.ntkernel.com 2000-2005

(prepared using NT Kernel Resources web BBS materials)


Q: How to install Windows NDIS intermediate driver based on Windows 2000 PASSTHRU sample on Windows 98?

A: You can use use the INF file below:

;---------------------------------------------------------------------------; 
; ;
; PASSTHRU.INF ;
; ;
; Windows 98 Installation for NDIS Intermediate Driver example PASSTHRU.SYS ;
; Based largely on NETLANE.INF ;
; ;
; Copyright (c) 1993-2000, Microsoft Corporation ;
; ;
;---------------------------------------------------------------------------;

[version]
signature="$CHICAGO$"
Class=NetTrans
ClassGUID={4d36e975-e325-11ce-bfc1-08002be10318}
provider=%V_MS%
DriverVer=08/24/2000


[Manufacturer]
%V_MS%=MS


[MS]
%PASSTHRU.DeviceDesc%= PASSTHRU.ndi, PASSTHRU
%PASSMINI.DeviceDesc%= PASSMINI.ndi, PASSMINI


[ControlFlags]
ExcludeFromSelect=PASSMINI

[SourceDiskNames]
1=,,,

[SourceDiskFiles]
Passthru.sys=1,,
Passthru.inf=1,,

;---------------------------------------------------------------------------;
; PASSTHRU -- Protocol edge ;
;---------------------------------------------------------------------------;

[PASSTHRU.ndi]
CopyFiles=PASSTHRU.Inf.CopyFiles
AddReg=PASSTHRU.ndi.reg
DeviceID=PASSTHRU
MaxInstance=4
DriverVer=06/08/2000


[PASSTHRU.ndi.reg]
HKR,Ndi,DeviceID,,PASSTHRU
HKR,Ndi,MaxInstance,,8
HKR,Ndi,NetType,,1
HKR,NDIS,LogDriverName,,PASSTHRU
HKR,NDIS,MiniportLogDriverName,,PASSMINI
HKR,NDIS,MajorNdisVersion,1,03
HKR,NDIS,MinorNdisVersion,1,0A
HKR,Ndi\Interfaces,DefUpper,,"PASSTHRU"
HKR,Ndi\Interfaces,DefLower,,"ndis3,ndis4,ndis5"
HKR,Ndi\Interfaces,UpperRange,,"PASSTHRU"
HKR,Ndi\Interfaces,LowerRange,,"ndis3,ndis4,ndis5"
HKR,Ndi\InstallInf,,"PassThru.inf"
HKR,Ndi\Install,,,"PASSTHRU.Install"
HKR,Ndi\Remove,,,"PASSTHRU.Remove"
HKR,Ndi,HelpText,,%PASSTHRU_HELP%
HKR,Ndi\Compatibility,RequireAll,,"PASSMINI"


[PASSTHRU.Install]
AddReg=PASSTHRU.AddReg, PASSTHRU.IMDevNode.AddReg
CopyFiles=PASSTHRU.CopyFiles


[PASSTHRU.AddReg]
HKR,,DevLoader,,*ndis
HKR,,DeviceVxDs,,passthru.sys
HKR,,IntermediateProtocol,,1


[PASSTHRU.IMDevNode.AddReg]
HKLM,System\CurrentControlSet\Services\Class\Ndis
HKLM,System\CurrentControlSet\Services\Class\Ndis,,,"Network intermediate drivers"
HKLM,System\CurrentControlSet\Services\Class\Ndis,Icon,,"-6"
HKLM,System\CurrentControlSet\Services\Class\Ndis,NoUseClass,,"1"
HKLM,System\CurrentControlSet\Services\Class\Ndis,DevLoader,,"*ndis"
HKLM,Enum\Root\NDIS\PASSTHRU
HKLM,Enum\Root\NDIS\PASSTHRU,DeviceDesc,,"PASSTHRU Protocol driver"
HKLM,Enum\Root\NDIS\PASSTHRU,Class,,"Ndis"
HKLM,Enum\Root\NDIS\PASSTHRU,ConfigFlags,1,10,00,00,00
HKLM,Enum\Root\NDIS\PASSTHRU,Driver,,"Ndis"


[PASSTHRU.IMDevNode.DelReg]
HKLM,Enum\Root\NDIS\PASSTHRU


[PASSTHRU.CopyFiles]
Passthru.sys,,,2

[PASSTHRU.Inf.CopyFiles]
Passthru.inf,,,2

[PASSTHRU.Remove]
DelReg=PASSTHRU.IMDevNode.DelReg

;---------------------------------------------------------------------------;
; PASSMINI -- Miniport edge ;
;---------------------------------------------------------------------------;

[PASSMINI.ndi]
AddReg=PASSMINI.ndi.reg
DeviceID=PASSMINI
MaxInstance=8
DriverVer=06/08/2000


[PASSMINI.ndi.reg]
HKR,Ndi,DeviceID,,PASSMINI
HKR,Ndi,MaxInstance,,8
HKR,NDIS,LogDriverName,,PASSMINI
HKR,NDIS,MajorNdisVersion,1,03
HKR,NDIS,MinorNdisVersion,1,0A
HKR,Ndi\Interfaces,DefUpper,,"ndis3,ndis4,ndis5"
HKR,Ndi\Interfaces,DefLower,,"PASSTHRU"
HKR,Ndi\Interfaces,UpperRange,,"ndis3,ndis4,ndis5"
HKR,Ndi\Interfaces,LowerRange,,"PASSTHRU"
HKR,Ndi\Install,,,"PASSMINI.Install"
HKR,Ndi\Remove,,,"PASSMINI.Remove"
HKR,Ndi,HelpText,,%PASSMINI_HELP%
HKR,Ndi\Compatibility,RequireAll,,"PASSTHRU"


[PASSMINI.Install]
AddReg=PASSMINI.AddReg
CopyFiles=PASSMINI.CopyFiles


[PASSMINI.AddReg]
HKR,,DevLoader,,*ndis
HKR,,DeviceVxDs,,passthru.sys
HKR,,RealClass,,Net


[PASSMINI.CopyFiles]

[PASSMINI.Remove]
AddReg=PASSMINI.Rmv.AddReg

[PASSMINI.Rmv.AddReg]

;---------------------------------------------------------------------------;
; DIRECTORIES and STRINGS ;
;---------------------------------------------------------------------------;

[DestinationDirs]
DefaultDestDir = 11
PASSTHRU.CopyFiles = 11
PASSTHRU.Inf.CopyFiles = 17
PASSMINI.CopyFiles = 11


[strings]
V_MS="Microsoft"
V_CLASSNAME="Network Protocol"
PASSTHRU.DeviceDesc="PASSTHRU Protocol"
PASSTHRU_HELP="This implements the protocol edge of the PASSTHRU NDIS Intermedate Driver example."
PASSMINI.DeviceDesc="PASSTHRU Miniport"
PASSMINI_HELP="This creates the miniport edge of the PASSTHRU NDIS Intermedate Driver example."

Q: Where can I get the definition of NDIS_PROTOCOL_BLOCK structure? Does it depend of Windows version or NDIS version?

A: _NDIS_PROTOCOL_BLOCK structure definition depends of Windows and NDIS versions and it's just partially documented by Microsoft. Some it's definitions can be found in ndis.h. An example, for Windows XP NDIS_PROTOCOL_BLOCK defined as the following:

struct _NDIS_PROTOCOL_BLOCK 
{ 
  _NDIS_OPEN_BLOCK* 			OpenQueue; 
  _REFERENCE 				Ref;
  _KEVENT* 				DeregEvent; 
  _NDIS_PROTOCOL_BLOCK* 		NextProtocol; 
  _NDIS50_PROTOCOL_CHARACTERISTICS 	ProtocolCharacteristics; 
  _WORK_QUEUE_ITEM 			WorkItem; 
  _KMUTANT 				Mutex; 
  DWORD 				MutexOwner; 
  _UNICODE_STRING* 			BindDeviceName; 
  _UNICODE_STRING* 			RootDeviceName; 
  _NDIS_M_DRIVER_BLOCK* 		AssociatedMiniDriver; 
  _NDIS_MINIPORT_BLOCK* 		BindingAdapter; 
}; 

But in ndis.h for Windows ME you can find the following definition:

struct _NDIS_PROTOCOL_BLOCK 
{
  PNDIS_OPEN_BLOCK 		    OpenQueue; 	// queue of opens for this protocol 
  REFERENCE 			    Ref; 	// contains spinlock for OpenQueue 
  UINT 				    Length;	// of this NDIS_PROTOCOL_BLOCK struct
  NDIS50_PROTOCOL_CHARACTERISTICS   ProtocolCharacteristics;// handler addresses struct 
  _NDIS_PROTOCOL_BLOCK * 	    NextProtocol; // Link to next    
  ULONG 			    MaxPatternSize; 
#if defined(NDIS_WRAPPER) 
  //
  // Protocol filters
  // 
  struct    _NDIS_PROTOCOL_FILTER * ProtocolFilter[NdisMediumMax+1];
  WORK_QUEUE_ITEM 		    WorkItem;	// Used during NdisRegisterProtocol to 
						// notify protocols of existing drivers.
  KMUTEX 			    Mutex; 	// For serialization of Bind/Unbind requests 
  PKEVENT 			    DeregEvent;	// Used by NdisDeregisterProtocol 
#endif 
}; 

As you can see this structure definition depends of NDIS_PROTOCOL_CHARACTERISTICS definition, which is NDIS version dependent. The Windows version dependence does not need any comments.

Q: netbt.sys calls tcpip!TCPSendData directly. How can this be explained?

A: Yes, that's true, tcpip.sys can be requested for the pointer to the internal routine tcpip!TCPSendData. You can see the processing of the particular request (IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER) in reversed engineered code of tcpip!TCPDispatch. This interface improves performance of kernel-mode tcpip.sys clients.


TDI_STATUS TCPDispatch(PDEVICE_OBJECT pDeviceObject,PIRP Irp) 
{ 
	TDI_STATUS Status; 
	PIO_STACK_LOCATION irpStack; 
	
// try-except exists only in the W2K/XP version of TCPIP.SYS 
__try 
{ 
	// IpMcastDeviceObject exists only in the W2K/XP version of TCPIP.SYS 
	if (pDeviceObject == IpMcastDeviceObject) 
		return IpMcastDispatch(pDeviceObject,Irp);
 
	if (pDeviceObject == IPDeviceObject) 
		return IPDispatch(pDeviceObject,Irp); 

	irpStack = IoGetCurrentIrpStackLocation(Irp); 
	Irp->IoStatus.Information = 0; 

	switch (irpStack->MajorFunction) 
	{ 
		case IRP_MJ_CREATE: 
			Status = TCPCreate(pDeviceObject,Irp,irpStack); 
			break;
 
		case IRP_MJ_CLOSE: 
			Status = TCPClose(Irp,irpStack); 
			break;

		case IRP_MJ_WRITE: 
			Status = STATUS_INVALID_DEVICE_REQUEST; 
			break;

		case IRP_MJ_DEVICE_CONTROL: 
			if (NT_SUCCESS(TdiMapUserRequest(pDeviceObject,Irp,irpStack))) 
				return TCPDispatchInternalDeviceControl(pDeviceObject,Irp); 
			else 
			{ 
				if (irpStack->Parameters.DeviceIoControl.IoControlCode
					== IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER)  
				{ 
					// exists only in W2K/XP 
					if (Irp->RequestorMode == UserMode) 
						ProbeForWrite (
						irpStack->Parameters.DeviceIoControl.Type3InputBuffer,
						sizeof(PULONG),
						4
						);  
						
					irpStack->Parameters.DeviceIoControl.Type3InputBuffer 
						= TCPSendData; 
					Status = STATUS_SUCCESS; 
				} 
				else 
					return TCPDispatchDeviceControl(Irp,irpStack); 
			} 
			break;
 
		case IRP_MJ_CLEANUP: 
			Status = TCPCleanup(pDeviceObject,Irp,irpStack); 
			break;
 
		case IRP_MJ_QUERY_SECURITY: 
			Status = STATUS_INVALID_DEVICE_REQUEST; 
			break; 

		case IRP_MJ_SYSTEM_CONTROL: // exists only in W2K/XP 
			return TCPEventTraceControl(pDeviceObject,Irp);
 
		case IRP_MJ_PNP_POWER: // exists only in W2K/XP 
			Status = TCPDispatchPnPPower(Irp,irpStack); 
			break; 

		default: 
			Status = STATUS_INVALID_DEVICE_REQUEST; 
	} 
} 
__except(EXCEPTION_EXECUTE_HANDLER) 
{ 
	// does something (I don't know what exactly) in the case of an exception 
	// ProbeForWrite can rise an exception 
} 
	Irp->IoStatus.Status = Status; 
	IoCompleteRequest(Irp,IO_NETWORK_INCREMENT); 
	return Status; 
} 

Q: How can I create named pipe in the kernel-mode (NtCreateNamedPipeFile/ZwCreateNamedPipeFile is not exported by ntoskrnl.exe)?

A: There are several ways to do this:

  • You can call the service through the service index in the Service Descriptor Table (SDT) using INT2E, like ntdll.dll does.
   .text:77F883D6 public ZwCreateNamedPipeFile 
   .text:77F883D6 ZwCreateNamedPipeFile proc near 
   .text:77F883D6 
   .text:77F883D6 arg_0 = byte ptr 4 
   .text:77F883D6 
   .text:77F883D6 mov eax, 26h ; NtCreateNamedPipeFile 
   .text:77F883DB lea edx, [esp+arg_0] 
   .text:77F883DF int 2Eh ; DOS 2+ internal - EXECUTE COMMAND 
   .text:77F883DF ; DS:SI -> counted CR-terminated command string 
   .text:77F883E1 retn 38h 
   .text:77F883E1 ZwCreateNamedPipeFile endp 
 
  • You can use the index of NtCreateNamedPipeFile in SDT to obtain service address from SDT (address of SDT is exported by ntoskrnl.exe under KeServiceDescriptorTable name) and then call the service routine in ntoskrnl.exe.
  • Both previously described approaches are universal in that sense that you can do the same for the calling any non-exported service from ntoskrnl.exe. The only disadvantage of them is that you need to know service index in SDT, which is the subject of changes between Windows versions (or even service packs). You can extract this index dynamically from ntdll.dll image to resolve this problem. However, I suppose it's a bit overwork. One more approach to create named pipe in the kernel-mode is to use IoCreateFile routine. The following code works just like NtCreateNamedPipeFile:

NTSTATUS  NtCreateNamedPipeFile (
   			OUT PHANDLE FileHandle,
   			IN ULONG DesiredAccess,
			IN POBJECT_ATTRIBUTES ObjectAttributes,
			OUT PIO_STATUS_BLOCK IoStatusBlock,
			IN ULONG ShareAccess,
			IN ULONG CreateDisposition,
			IN ULONG CreateOptions,
			IN ULONG NamedPipeType,
			IN ULONG ReadMode,
			IN ULONG CompletionMode,
			IN ULONG MaximumInstances,
			IN ULONG InboundQuota,
			IN ULONG OutboundQuota,
			IN PLARGE_INTEGER DefaultTimeout OPTIONAL 
			)  
{   
	NAMED_PIPE_CREATE_PARAMETERS NamedPipeParms;
	NTSTATUS Status;
	__try   
	{   
		if ( DefaultTimeout )
		{
		   	NamedPipeParms.TimeoutSpecified = TRUE;
		   	NamedPipeParms.DefaultTimeout.QuadPart = DefaultTimeout->QuadPart;
		}
		else
		{   
			NamedPipeParms.TimeoutSpecified = FALSE;
		} 
		  
		NamedPipeParms.NamedPipeType = NamedPipeType;
		NamedPipeParms.ReadMode = ReadMode;
		NamedPipeParms.CompletionMode = CompletionMode;
		NamedPipeParms.MaximumInstances = MaximumInstances;
		NamedPipeParms.InboundQuota = InboundQuota;
		NamedPipeParms.OutboundQuota = OutboundQuota;
		Status = IoCreateFile (
		   			FileHandle,
					DesiredAccess,
					ObjectAttributes,
					IoStatusBlock,
					NULL,
					0,
					ShareAccess,
					CreateDisposition,
					CreateOptions,
					NULL,
					0,
					CreateFileTypeNamedPipe,
					&NamedPipeParms,
					0   
					);

		return Status;
	}   
	__except (EXCEPTION_EXECUTE_HANDLER)   
	{
	   KdPrint (("NtCreateNamedPipeFile: Exception occured.\n"));
	   return STATUS_UNSUCCESSFUL;   
	}
}    

The routine can be called as the following:

#define NAMED_PIPE_NAME L"\\??\\pipe\\PipeClient"
...
// Some code skipped
...
	RtlInitUnicodeString ( &namedPipe, NAMED_PIPE_NAME );
	InitializeObjectAttributes ( 
		&attr,
 		&namedPipe,
		OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
    	NULL,
 		NULL
 		);
 	nTimeOut.QuadPart = -1000;
 
// Create Named Pipe
	ntStatus = NtCreateNamedPipeFile (
			&g_hNamedPipeHandle,
 			FILE_ANY_ACCESS,
			&attr,
			&ioStatusBlock,
			FILE_SHARE_READ | FILE_SHARE_WRITE,
			FILE_CREATE,
			0,
			FILE_PIPE_BYTE_STREAM_TYPE,
			FILE_PIPE_BYTE_STREAM_MODE,
		    FILE_PIPE_QUEUE_OPERATION,
			1,
			0,
			0,
			&nTimeOut
			);

	if (NT_SUCCESS (ntStatus))
	{
		KdPrint (("Pipe created succesfully\n")); 
	}

Please pay attention to two important notes:

  • nTimeOut, this parameter is declared as OPTIONAL but at least on my Windows 2000 SP2 box it's required.
  • When creating named pipe I've specified OBJ_KERNEL_HANDLE attribute. This allows to use named pipe handle in the arbitrary process context in the kernel (if not specified the handle is valid only in the context of creator process). The only problem is that OBJ_KERNEL_HANDLE was introduced Windows 2000 and it does not work in NT 4.0, where the best choice is to create named pipe in the context of System process.
  

Copyright © NT Kernel Resources, 2000-2009. Design & Programming by Multi Service