📄 tdifuncs.c
字号:
/**********************************************************************
*
* Toby Opferman
*
* Example TDI Interface
*
* This example is for educational purposes only. I license this source
* out for use in learning how to write a device driver.
*
* Copyright (c) 2005, All Rights Reserved
**********************************************************************/
#define _X86_
typedef unsigned int UINT;
#include <wdm.h>
#include <tdi.h>
#include <tdikrnl.h>
#include "tdifuncs.h"
typedef struct _TDI_COMPLETION_CONTEXT
{
KEVENT kCompleteEvent;
} TDI_COMPLETION_CONTEXT, *PTDI_COMPLETION_CONTEXT;
#ifndef STATUS_CONTINUE_COMPLETION
#define STATUS_CONTINUE_COMPLETION STATUS_SUCCESS
#endif
#pragma alloc_text(PAGE, TdiFuncs_InitializeTransportHandles)
#pragma alloc_text(PAGE, TdiFuncs_FreeHandles)
#pragma alloc_text(PAGE, TdiFuncs_Send)
/* #pragma alloc_text(PAGE, TdiFuncs_CompleteIrp) */
#pragma alloc_text(PAGE, TdiFuncs_Connect)
#pragma alloc_text(PAGE, TdiFuncs_Disconnect)
#pragma alloc_text(PAGE, TdiFuncs_CloseTdiOpenHandle)
#pragma alloc_text(PAGE, TdiFuncs_DisAssociateTransportAndConnection)
#pragma alloc_text(PAGE, TdiFuncs_AssociateTransportAndConnection)
#pragma alloc_text(PAGE, TdiFuncs_OpenTransportAddress)
#pragma alloc_text(PAGE, TdiFuncs_SetEventHandler)
/**********************************************************************
*
* TdiFuncs_InitializeTransportHandles
*
* This function encapsulates all the dirty work of initializing
* the TDI Handles.
*
**********************************************************************/
NTSTATUS TdiFuncs_InitializeTransportHandles(PTDI_HANDLE pTdiHandle)
{
NTSTATUS NtStatus;
HANDLE hTransport, hConnection;
PFILE_OBJECT pfoTransport, pfoConnection;
NtStatus = TdiFuncs_OpenTransportAddress(&hTransport, &pfoTransport);
if(NT_SUCCESS(NtStatus))
{
/*
* The second step is to create a connection endpoint
*/
NtStatus = TdiFuncs_OpenConnection(&hConnection, &pfoConnection);
if(NT_SUCCESS(NtStatus))
{
/*
* The third step is to associate the transport and the connection objects
*/
NtStatus = TdiFuncs_AssociateTransportAndConnection(hTransport, pfoConnection);
if(NT_SUCCESS(NtStatus))
{
pTdiHandle->hConnection = hConnection;
pTdiHandle->hTransport = hTransport;
pTdiHandle->pfoConnection = pfoConnection;
pTdiHandle->pfoTransport = pfoTransport;
}
else
{
TdiFuncs_CloseTdiOpenHandle(hConnection, pfoConnection);
TdiFuncs_CloseTdiOpenHandle(hTransport, pfoTransport);
}
}
else
{
TdiFuncs_CloseTdiOpenHandle(hTransport, pfoTransport);
}
}
return NtStatus;
}
/**********************************************************************
*
* TdiFuncs_FreeHandles
*
* This function destroys the handles
*
**********************************************************************/
void TdiFuncs_FreeHandles(PTDI_HANDLE pTdiHandle)
{
TdiFuncs_DisAssociateTransportAndConnection(pTdiHandle->pfoConnection);
TdiFuncs_CloseTdiOpenHandle(pTdiHandle->hConnection, pTdiHandle->pfoConnection);
TdiFuncs_CloseTdiOpenHandle(pTdiHandle->hTransport, pTdiHandle->pfoTransport);
pTdiHandle->hConnection = NULL;
pTdiHandle->hTransport = NULL;
pTdiHandle->pfoConnection = NULL;
pTdiHandle->pfoTransport = NULL;
}
/**********************************************************************
*
* TdiFuncs_OpenTransportAddress
*
* This is the first step in creating a connection you need to
* create a Transport Address
*
**********************************************************************/
NTSTATUS TdiFuncs_OpenTransportAddress(PHANDLE pTdiHandle, PFILE_OBJECT *pFileObject)
{
NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;
UNICODE_STRING usTdiDriverNameString;
OBJECT_ATTRIBUTES oaTdiDriverNameAttributes;
IO_STATUS_BLOCK IoStatusBlock;
char DataBlob[sizeof(FILE_FULL_EA_INFORMATION) + TDI_TRANSPORT_ADDRESS_LENGTH + 300] = {0};
PFILE_FULL_EA_INFORMATION pExtendedAttributesInformation = (PFILE_FULL_EA_INFORMATION)&DataBlob;
UINT dwEASize = 0;
PTRANSPORT_ADDRESS pTransportAddress = NULL;
PTDI_ADDRESS_IP pTdiAddressIp = NULL;
/*
* Initialize the name of the device to be opened. ZwCreateFile takes an
* OBJECT_ATTRIBUTES structure as the name of the device to open. This is then
* a two step process.
*
* 1 - Create a UNICODE_STRING data structure from a unicode string.
* 2 - Create a OBJECT_ATTRIBUTES data structure from a UNICODE_STRING.
*
*/
RtlInitUnicodeString(&usTdiDriverNameString, L"\\Device\\Tcp");
InitializeObjectAttributes(&oaTdiDriverNameAttributes, &usTdiDriverNameString, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
/*
* The second step is to initialize the Extended Attributes data structure.
*
* EaName = TdiTransportAddress, 0, TRANSPORT_ADDRESS
* EaNameLength = Length of TdiTransportAddress
* EaValueLength = Length of TRANSPORT_ADDRESS
*/
RtlCopyMemory(&pExtendedAttributesInformation->EaName, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH);
pExtendedAttributesInformation->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
pExtendedAttributesInformation->EaValueLength = TDI_TRANSPORT_ADDRESS_LENGTH + sizeof(TRANSPORT_ADDRESS) + sizeof(TDI_ADDRESS_IP);
pTransportAddress = (PTRANSPORT_ADDRESS)(&pExtendedAttributesInformation->EaName + TDI_TRANSPORT_ADDRESS_LENGTH + 1);
/*
* The number of transport addresses
*/
pTransportAddress->TAAddressCount = 1;
/*
* This next piece will essentially describe what the transport being opened is.
*
* AddressType = Type of transport
* AddressLength = Length of the address
* Address = A data structure that is essentially related to the chosen AddressType.
*
*/
pTransportAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
pTransportAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IP);
pTdiAddressIp = (TDI_ADDRESS_IP *)&pTransportAddress->Address[0].Address;
/*
* The TDI_ADDRESS_IP data structure is essentially simmilar to the usermode sockets data structure.
*
* sin_port
* sin_zero
* in_addr
*
* NOTE: This is the _LOCAL ADDRESS OF THE CURRENT MACHINE_ Just as with sockets, if you don't
* care what port you bind this connection to then just use "0". If you also only have
* one network card interface, there's no reason to set the IP. "0.0.0.0" will simply
* use the current machine's IP. If you have multiple NIC's or a reason to specify
* the local IP address then you must set TDI_ADDRESS_IP to that IP. If you are creating
* a server side component you may want to specify the port, however usually to connect
* to another server you really don't care what port the client is opening.
*/
RtlZeroMemory(pTdiAddressIp, sizeof(TDI_ADDRESS_IP));
dwEASize = sizeof(DataBlob);
NtStatus = ZwCreateFile(pTdiHandle, FILE_READ_EA | FILE_WRITE_EA, &oaTdiDriverNameAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0, pExtendedAttributesInformation, dwEASize);
if(NT_SUCCESS(NtStatus))
{
NtStatus = ObReferenceObjectByHandle(*pTdiHandle, GENERIC_READ | GENERIC_WRITE, NULL, KernelMode, (PVOID *)pFileObject, NULL);
if(!NT_SUCCESS(NtStatus))
{
ZwClose(*pTdiHandle);
}
}
return NtStatus;
}
/**********************************************************************
*
* TdiFuncs_OpenConnection
*
* This is the second step in creating a connection you need to
* create a Connection Context
*
**********************************************************************/
NTSTATUS TdiFuncs_OpenConnection(PHANDLE pTdiHandle, PFILE_OBJECT *pFileObject)
{
NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;
UNICODE_STRING usTdiDriverNameString;
OBJECT_ATTRIBUTES oaTdiDriverNameAttributes;
IO_STATUS_BLOCK IoStatusBlock;
char DataBlob[sizeof(FILE_FULL_EA_INFORMATION) + TDI_CONNECTION_CONTEXT_LENGTH + 300] = {0};
PFILE_FULL_EA_INFORMATION pExtendedAttributesInformation = (PFILE_FULL_EA_INFORMATION)&DataBlob;
UINT dwEASize = 0;
/*
* Initialize the name of the device to be opened. ZwCreateFile takes an
* OBJECT_ATTRIBUTES structure as the name of the device to open. This is then
* a two step process.
*
* 1 - Create a UNICODE_STRING data structure from a unicode string.
* 2 - Create a OBJECT_ATTRIBUTES data structure from a UNICODE_STRING.
*
*/
RtlInitUnicodeString(&usTdiDriverNameString, L"\\Device\\Tcp");
InitializeObjectAttributes(&oaTdiDriverNameAttributes, &usTdiDriverNameString, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
/*
* The second step is to initialize the Extended Attributes data structure.
*
* EaName = TdiConnectionContext, 0, Your User Defined Context Data (Actually, a pointer to it I believe)
* EaNameLength = Length of TdiConnectionContext
* EaValueLength = Length (Total Length I think?)
*/
RtlCopyMemory(&pExtendedAttributesInformation->EaName, TdiConnectionContext, TDI_CONNECTION_CONTEXT_LENGTH);
pExtendedAttributesInformation->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
pExtendedAttributesInformation->EaValueLength = TDI_CONNECTION_CONTEXT_LENGTH; /* Must be at least TDI_CONNECTION_CONTEXT_LENGTH */
dwEASize = sizeof(DataBlob);
NtStatus = ZwCreateFile(pTdiHandle, FILE_READ_EA | FILE_WRITE_EA, &oaTdiDriverNameAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0, pExtendedAttributesInformation, dwEASize);
if(NT_SUCCESS(NtStatus))
{
NtStatus = ObReferenceObjectByHandle(*pTdiHandle, GENERIC_READ | GENERIC_WRITE, NULL, KernelMode, (PVOID *)pFileObject, NULL);
if(!NT_SUCCESS(NtStatus))
{
ZwClose(*pTdiHandle);
}
}
return NtStatus;
}
/**********************************************************************
*
* TdiFuncs_AssociateTransportAndConnection
*
* This is called to associate the transport with the connection
*
**********************************************************************/
NTSTATUS TdiFuncs_AssociateTransportAndConnection(HANDLE hTransportAddress, PFILE_OBJECT pfoConnection)
{
NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;
PIRP pIrp;
IO_STATUS_BLOCK IoStatusBlock = {0};
PDEVICE_OBJECT pTdiDevice;
TDI_COMPLETION_CONTEXT TdiCompletionContext;
KeInitializeEvent(&TdiCompletionContext.kCompleteEvent, NotificationEvent, FALSE);
/*
* The TDI Device Object is required to send these requests to the TDI Driver.
*
*/
pTdiDevice = IoGetRelatedDeviceObject(pfoConnection);
/*
* Step 1: Build the IRP. TDI defines several macros and functions that can quickly
* create IRP's, etc. for variuos purposes. While this can be done manually
* it's easiest to use the macros.
*
* http://msdn.microsoft.com/library/en-us/network/hh/network/34bldmac_f430860a-9ae2-4379-bffc-6b0a81092e7c.xml.asp?frame=true
*/
pIrp = TdiBuildInternalDeviceControlIrp(TDI_ASSOCIATE_ADDRESS, pTdiDevice, pfoConnection, &TdiCompletionContext.kCompleteEvent, &IoStatusBlock);
if(pIrp)
{
/*
* Step 2: Add the correct parameters into the IRP.
*/
TdiBuildAssociateAddress(pIrp, pTdiDevice, pfoConnection, NULL, NULL, hTransportAddress);
NtStatus = IoCallDriver(pTdiDevice, pIrp);
/*
* If the status returned is STATUS_PENDING this means that the IRP will not be completed synchronously
* and the driver has queued the IRP for later processing. This is fine but we do not want to return this
* thread, we are a synchronous call so we want to wait until it has completed. The EVENT that we provided
* will be set when the IRP completes.
*/
if(NtStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&TdiCompletionContext.kCompleteEvent, Executive, KernelMode, FALSE, NULL);
/*
* Find the Status of the completed IRP
*/
NtStatus = IoStatusBlock.Status;
}
}
return NtStatus;
}
/**********************************************************************
*
* TdiFuncs_SetEventHandler
*
* This is called to set an Event Handler Callback
*
**********************************************************************/
NTSTATUS TdiFuncs_SetEventHandler(PFILE_OBJECT pfoTdiFileObject, LONG InEventType, PVOID InEventHandler, PVOID InEventContext)
{
NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;
PIRP pIrp;
IO_STATUS_BLOCK IoStatusBlock = {0};
PDEVICE_OBJECT pTdiDevice;
LARGE_INTEGER TimeOut = {0};
UINT NumberOfSeconds = 60*3;
TDI_COMPLETION_CONTEXT TdiCompletionContext;
KeInitializeEvent(&TdiCompletionContext.kCompleteEvent, NotificationEvent, FALSE);
/*
* The TDI Device Object is required to send these requests to the TDI Driver.
*
*/
pTdiDevice = IoGetRelatedDeviceObject(pfoTdiFileObject);
/*
* Step 1: Build the IRP. TDI defines several macros and functions that can quickly
* create IRP's, etc. for variuos purposes. While this can be done manually
* it's easiest to use the macros.
*
* http://msdn.microsoft.com/library/en-us/network/hh/network/34bldmac_f430860a-9ae2-4379-bffc-6b0a81092e7c.xml.asp?frame=true
*/
pIrp = TdiBuildInternalDeviceControlIrp(TDI_SET_EVENT_HANDLER, pTdiDevice, pfoConnection, &TdiCompletionContext.kCompleteEvent, &IoStatusBlock);
if(pIrp)
{
/*
* Step 2: Set the IRP Parameters
*/
TdiBuildSetEventHandler(pIrp, pTdiDevice, pfoTdiFileObject, NULL, NULL, InEventType, InEventHandler, InEventContext);
NtStatus = IoCallDriver(pTdiDevice, pIrp);
/*
* If the status returned is STATUS_PENDING this means that the IRP will not be completed synchronously
* and the driver has queued the IRP for later processing. This is fine but we do not want to return this
* thread, we are a synchronous call so we want to wait until it has completed. The EVENT that we provided
* will be set when the IRP completes.
*/
if(NtStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&TdiCompletionContext.kCompleteEvent, Executive, KernelMode, FALSE, NULL);
/*
* Find the Status of the completed IRP
*/
NtStatus = IoStatusBlock.Status;
}
}
return NtStatus;
}
/**********************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -