www.pudn.com > tdi_fw.zip > tdi_fw.c
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs) // // $Id: tdi_fw.c,v 1.7 2002/12/03 12:14:28 dev Exp $ /* * Simple TDI-based personal firewall */ #include#include #include "sock.h" #include "dispatch.h" #include "filter.h" #include "memtrack.h" #include "obj_tbl.h" #include "tdi_fw.h" #define IOCTL_TRANSFER_TYPE(ioctl) ((ioctl) & 3) /* context for tdi_skip_complete */ typedef struct { PIO_COMPLETION_ROUTINE old_cr; /* old (original) completion routine */ PVOID old_context; /* old (original) parameter for old_cr */ PIO_COMPLETION_ROUTINE new_cr; /* new (replaced) completion routine */ PVOID new_context; /* new (replaced) parameter for new_cr */ PFILE_OBJECT fileobj; /* FileObject from IO_STACK_LOCATION */ PDEVICE_OBJECT new_devobj; /* filter device object */ UCHAR old_control; /* old (original) irps->Control */ } TDI_SKIP_CTX; /* prototypes */ static NTSTATUS DeviceDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp); static VOID OnUnload(IN PDRIVER_OBJECT DriverObject); #ifndef USE_TDI_HOOKING static NTSTATUS c_n_a_device(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *oldobj, wchar_t *devname); static void d_n_d_device(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT fltobj); #else static NTSTATUS hook_tcpip(DRIVER_OBJECT *old_DriverObject, BOOLEAN b_hook); static NTSTATUS get_device_object(wchar_t *name, PDEVICE_OBJECT *devobj); #endif static NTSTATUS tdi_skip_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context); /* device objects for: */ PDEVICE_OBJECT g_tcpfltobj = NULL, // \Device\Tcp g_udpfltobj = NULL, // \Device\Udp g_ipfltobj = NULL, // \Device\RawIp g_devcontrol = NULL; // control device ULONG g_got_control = 0; // got control app (contains pid) #ifndef USE_TDI_HOOKING // original device objects PDEVICE_OBJECT g_tcpoldobj, g_udpoldobj, g_ipoldobj; #else // original driver object DRIVER_OBJECT g_old_DriverObject; BOOLEAN g_hooked = FALSE; #endif /* initialization */ NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath) { NTSTATUS status = STATUS_SUCCESS; int i; UNICODE_STRING name, linkname; memtrack_init(); #ifdef USE_TDI_HOOKING KdPrint(("[tdi_fw] WARNING! Using unstable working mode: TDI hooking!\n")); #endif status = ot_init(); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: ot_init: 0x%x\n", status)); goto done; } status = filter_init(); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: filter_init: 0x%x\n", status)); goto done; } for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) theDriverObject->MajorFunction[i] = DeviceDispatch; #if DBG // register UnLoad procedure theDriverObject->DriverUnload = OnUnload; #endif /* create control device and symbolic link */ RtlInitUnicodeString(&name, L"\\Device\\tdi_fw"); status = IoCreateDevice(theDriverObject, 0, &name, 0, 0, TRUE, // exclusive! only one control app! &g_devcontrol); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: IoCreateDevice(control): 0x%x!\n", status)); goto done; } RtlInitUnicodeString(&linkname, L"\\??\\tdi_fw"); status = IoCreateSymbolicLink(&linkname, &name); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: IoCreateSymbolicLink: 0x%x!\n", status)); goto done; } #ifndef USE_TDI_HOOKING status = c_n_a_device(theDriverObject, &g_tcpfltobj, &g_tcpoldobj, L"\\Device\\Tcp"); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x\n", status)); goto done; } status = c_n_a_device(theDriverObject, &g_udpfltobj, &g_udpoldobj, L"\\Device\\Udp"); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x\n", status)); goto done; } status = c_n_a_device(theDriverObject, &g_ipfltobj, &g_ipoldobj, L"\\Device\\RawIp"); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x\n", status)); goto done; } #else /* USE_TDI_HOOKING */ /* get device objects for tcp/udp/ip */ status = get_device_object(L"\\Device\\Tcp", &g_tcpfltobj); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: get_device_object(tcp): 0x%x\n", status)); goto done; } status = get_device_object(L"\\Device\\Udp", &g_udpfltobj); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: get_device_object(udp): 0x%x\n", status)); goto done; } status = get_device_object(L"\\Device\\RawIp", &g_ipfltobj); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: get_device_object(ip): 0x%x\n", status)); goto done; } /* hook tcpip */ status = hook_tcpip(&g_old_DriverObject, TRUE); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: hook_driver: 0x%x\n", status)); goto done; } g_hooked = TRUE; #endif /* USE_TDI_HOOKING */ status = STATUS_SUCCESS; done: if (status != STATUS_SUCCESS) { // cleanup OnUnload(theDriverObject); } return status; } /* deinitialization */ VOID OnUnload(IN PDRIVER_OBJECT DriverObject) { #ifndef USE_TDI_HOOKING d_n_d_device(DriverObject, g_tcpoldobj, g_tcpfltobj); d_n_d_device(DriverObject, g_udpoldobj, g_udpfltobj); d_n_d_device(DriverObject, g_ipoldobj, g_ipfltobj); #else if (g_hooked) hook_tcpip(&g_old_DriverObject, FALSE); #endif // delete control device and symbolic link if (g_devcontrol != NULL) { UNICODE_STRING linkname; RtlInitUnicodeString(&linkname, L"\\??\\tdi_fw"); IoDeleteSymbolicLink(&linkname); IoDeleteDevice(g_devcontrol); } filter_free(); ot_free(); memtrack_free(); } #ifndef USE_TDI_HOOKING /* create & attach device */ NTSTATUS c_n_a_device(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *oldobj, wchar_t *devname) { NTSTATUS status; UNICODE_STRING str; /* create filter device */ status = IoCreateDevice(DriverObject, 0, NULL, FILE_DEVICE_UNKNOWN, 0, TRUE, fltobj); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] c_n_a_device: IoCreateDevice(%S): 0x%x\n", devname, status)); return status; } (*fltobj)->Flags |= DO_DIRECT_IO; RtlInitUnicodeString(&str, devname); status = IoAttachDevice(*fltobj, &str, oldobj); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] DriverEntry: IoAttachDevice(%S): 0x%x\n", devname, status)); return status; } KdPrint(("[tdi_fw] DriverEntry: %S fileobj: 0x%x\n", devname, *fltobj)); return STATUS_SUCCESS; } /* detach & delete device */ void d_n_d_device(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT fltobj) { /* * Detaching of a filter driver at runtime is a high-risk deal */ #if 1 // for extremal guys only! if (oldobj != NULL && fltobj != NULL) { int i; for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) DriverObject->MajorFunction[i] = g_tcpoldobj->DriverObject->MajorFunction[i]; } #endif if (oldobj != NULL) IoDetachDevice(oldobj); if (fltobj != NULL) IoDeleteDevice(fltobj); } #else /* USE_TDI_HOOKING */ /* hook/unhook driver */ NTSTATUS hook_tcpip(DRIVER_OBJECT *old_DriverObject, BOOLEAN b_hook) { UNICODE_STRING drv_name; NTSTATUS status; PDRIVER_OBJECT new_DriverObject; int i; RtlInitUnicodeString(&drv_name, L"\\Driver\\Tcpip"); status = ObReferenceObjectByName(&drv_name, OBJ_CASE_INSENSITIVE, NULL, 0, IoDriverObjectType, KernelMode, NULL, &new_DriverObject); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] hook_driver: ObReferenceObjectByName\n")); return status; } for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { if (b_hook) { old_DriverObject->MajorFunction[i] = new_DriverObject->MajorFunction[i]; new_DriverObject->MajorFunction[i] = DeviceDispatch; } else new_DriverObject->MajorFunction[i] = old_DriverObject->MajorFunction[i]; } return STATUS_SUCCESS; } /* get device object by its name */ NTSTATUS get_device_object(wchar_t *name, PDEVICE_OBJECT *devobj) { UNICODE_STRING str; OBJECT_ATTRIBUTES oa; NTSTATUS status; HANDLE handle = NULL; IO_STATUS_BLOCK isb; PFILE_OBJECT fileobj; // !!! this is UGLY way but I don't know the shortest one RtlInitUnicodeString(&str, name); InitializeObjectAttributes(&oa, &str, OBJ_CASE_INSENSITIVE, 0, 0); status = ZwCreateFile(&handle, 0, &oa, &isb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, 0, NULL, 0); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] get_device_object: ZwCreateFile: 0x%x\n")); goto done; } status = ObReferenceObjectByHandle(handle, GENERIC_READ | GENERIC_WRITE, NULL, KernelMode, &fileobj, NULL); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] get_device_object: ObReferenceObjectByHandle: 0x%x\n")); goto done; } // and get DeviceObject by FileObject *devobj = fileobj->DeviceObject; ObDereferenceObject(fileobj); KdPrint(("[tdi_fw] get_device_object: \"%S\" devobj: 0x%x\n", name, *devobj)); done: if (handle != NULL) ZwClose(handle); return status; } #endif /* USE_TDI_HOOKING */ /* dispatch */ NTSTATUS DeviceDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP irp) { PIO_STACK_LOCATION irps; NTSTATUS status; // sanity check if (irp == NULL) { KdPrint(("[tdi_fw] DeviceDispatch: !irp\n")); return STATUS_SUCCESS; } irps = IoGetCurrentIrpStackLocation(irp); if (DeviceObject == g_tcpfltobj || DeviceObject == g_udpfltobj || DeviceObject == g_ipfltobj) { /* * This IRP is for filtered device */ int result; struct completion completion; memset(&completion, 0, sizeof(completion)); // Analyze MajorFunction switch (irps->MajorFunction) { case IRP_MJ_CREATE: /* create fileobject */ result = tdi_create(irp, irps, &completion); status = tdi_dispatch_complete(DeviceObject, irp, result, completion.routine, completion.context); break; case IRP_MJ_DEVICE_CONTROL: KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_DEVICE_CONTROL, minor 0x%x for 0x%08X\n", irps->MinorFunction, irps->FileObject)); if (KeGetCurrentIrql() == PASSIVE_LEVEL) { /* * try to convert it to IRP_MJ_INTERNAL_DEVICE_CONTROL * (works on PASSIVE_LEVEL only!) */ status = TdiMapUserRequest(DeviceObject, irp, irps); } else status = STATUS_NOT_IMPLEMENTED; // set fake status if (status != STATUS_SUCCESS) { // send IRP to original driver status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL); break; } // don't break! go to internal device control! case IRP_MJ_INTERNAL_DEVICE_CONTROL: { /* * Analyze ioctl for TDI driver */ int i; for (i = 0; g_tdi_ioctls[i].MinorFunction != 0; i++) if (g_tdi_ioctls[i].MinorFunction == irps->MinorFunction) { #if DBG // print description KdPrint(("[tdi_fw] DeviceDispatch: %s (0x%x) for 0x%x\n", g_tdi_ioctls[i].desc, irps->MinorFunction, irps->FileObject)); #endif if (g_tdi_ioctls[i].fn == NULL) { // send IRP to original driver status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL); break; } // call dispatch function result = g_tdi_ioctls[i].fn(irp, irps, &completion); // complete request status = tdi_dispatch_complete(DeviceObject, irp, result, completion.routine, completion.context); break; } // if dispatch function hasn't been found if (g_tdi_ioctls[i].MinorFunction == 0) { // send IRP to original driver status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL); } break; } case IRP_MJ_CLEANUP: /* cleanup fileobject */ result = tdi_cleanup(irp, irps, &completion); status = tdi_dispatch_complete(DeviceObject, irp, result, completion.routine, completion.context); break; case IRP_MJ_CLOSE: KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_CLOSE fileobj 0x%x\n", irps->FileObject)); // passthrough IRP status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, completion.routine, completion.context); break; default: KdPrint(("[tdi_fw] DeviceDispatch: major 0x%x, minor 0x%x for 0x%x\n", irps->MajorFunction, irps->MinorFunction, irps->FileObject)); // passthrough IRP status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, completion.routine, completion.context); } } else if (DeviceObject == g_devcontrol) { /* * this IRP is for control object */ // set default status status = STATUS_SUCCESS; if (irps->MajorFunction == IRP_MJ_CREATE) { // initialize for user-mode part (exclusive access - 1 user-mode part) filter_init_2(); g_got_control = (ULONG)PsGetCurrentProcessId(); } else if (irps->MajorFunction == IRP_MJ_CLOSE) { // cleanup for user-mode part filter_free_2(); g_got_control = 0; } if (irps->MajorFunction == IRP_MJ_DEVICE_CONTROL) { /* * control request */ ULONG ioctl = irps->Parameters.DeviceIoControl.IoControlCode, len = irps->Parameters.DeviceIoControl.InputBufferLength, size = irps->Parameters.DeviceIoControl.OutputBufferLength; char *out_buf; if (IOCTL_TRANSFER_TYPE(ioctl) == METHOD_NEITHER) { // this type of transfer unsupported out_buf = NULL; } else out_buf = (char *)irp->AssociatedIrp.SystemBuffer; // process control request status = process_request(ioctl, out_buf, &len, size); irp->IoStatus.Information = len; } else status = STATUS_SUCCESS; irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); } else { KdPrint(("[tdi_fw] DeviceDispatch: ioctl for unknown DeviceObject 0x%x\n", DeviceObject)); #ifndef USE_TDI_HOOKING // ??? just complete IRP status = irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(irp, IO_NO_INCREMENT); #else // call original handler status = g_old_DriverObject.MajorFunction[irps->MajorFunction]( DeviceObject, irp); #endif } return status; } /* * Dispatch routines call this function to complete their processing. * They _MUST_ call this function anyway. */ NTSTATUS tdi_dispatch_complete(PDEVICE_OBJECT devobj, PIRP irp, int filter, PIO_COMPLETION_ROUTINE cr, PVOID context) { PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(irp); NTSTATUS status; if (filter == FILTER_DENY) { /* * DENY: complete request with status "Access violation" */ KdPrint(("[tdi_fw] tdi_dispatch_complete: [DROP!]" " major 0x%x, minor 0x%x for devobj 0x%x; fileobj 0x%x\n", irps->MajorFunction, irps->MinorFunction, devobj, irps->FileObject)); status = irp->IoStatus.Status = STATUS_ACCESS_VIOLATION; IoCompleteRequest (irp, IO_NO_INCREMENT); } else if (filter == FILTER_ALLOW) { /* * ALLOW: pass IRP to the next driver */ #ifndef USE_TDI_HOOKING PDEVICE_OBJECT old_devobj = get_original_devobj(devobj, NULL); if (old_devobj == NULL) { KdPrint(("[tdi_fw] tdi_send_irp_to_old_driver: Unknown DeviceObject 0x%x!\n", devobj)); status = irp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest (irp, IO_NO_INCREMENT); return status; } #endif KdPrint(("[tdi_fw] tdi_dispatch_complete: [ALLOW.]" " major 0x%x, minor 0x%x for devobj 0x%x; fileobj 0x%x\n", irps->MajorFunction, irps->MinorFunction, devobj, irps->FileObject)); #ifndef USE_TDI_HOOKING if (cr == NULL || irp->CurrentLocation <= 1) { /* * we use _THIS_ way of sending IRP to old driver * a) to avoid NO_MORE_STACK_LOCATIONS * b) and if we haven't our completions - no need to copy stack locations! */ // stay on this location after IoCallDriver IoSkipCurrentIrpStackLocation(irp); #endif if (cr != NULL) { /* * set completion routine (this way is slow) */ // save old completion routine and context TDI_SKIP_CTX *ctx = (TDI_SKIP_CTX *)malloc_np(sizeof(*ctx)); if (ctx == NULL) { KdPrint(("[tdi_fw] tdi_send_irp_to_old_driver: malloc_np\n")); status = irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; IoCompleteRequest(irp, IO_NO_INCREMENT); return status; } ctx->old_cr = irps->CompletionRoutine; ctx->old_context = irps->Context; ctx->new_cr = cr; ctx->new_context = context; ctx->fileobj = irps->FileObject; ctx->new_devobj = devobj; ctx->old_control = IoGetNextIrpStackLocation(irp)->Control; #ifndef USE_TDI_HOOKING IoSetCompletionRoutine(irp, tdi_skip_complete, ctx, TRUE, TRUE, TRUE); #else // set completion for current irps irps->CompletionRoutine = tdi_skip_complete; irps->Context = ctx; irps->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; #endif } #ifndef USE_TDI_HOOKING } else { PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(irp), next_irps = IoGetNextIrpStackLocation(irp); memcpy(next_irps, irps, sizeof(*irps)); if (cr != NULL) { /* * this way for completion is more quicker then used above */ IoSetCompletionRoutine(irp, cr, context, TRUE, TRUE, TRUE); } else IoSetCompletionRoutine(irp, tdi_generic_complete, NULL, TRUE, TRUE, TRUE); } #endif /* call original driver */ #ifndef USE_TDI_HOOKING status = IoCallDriver(old_devobj, irp); #else status = g_old_DriverObject.MajorFunction[irps->MajorFunction](devobj, irp); #endif } else { /* FILTER_UNKNOWN */ /* * UNKNOWN: just complete the request */ status = irp->IoStatus.Status = STATUS_SUCCESS; // ??? IoCompleteRequest (irp, IO_NO_INCREMENT); } return status; } /* * completion routine for case if we use IoSkipCurrentIrpStackLocation way * or we USE_TDI_HOOKING */ NTSTATUS tdi_skip_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { TDI_SKIP_CTX *ctx = (TDI_SKIP_CTX *)Context; NTSTATUS status; PIO_STACK_LOCATION irps; if (Irp->IoStatus.Status != STATUS_SUCCESS) KdPrint(("[tdi_fw] tdi_skip_complete: status 0x%x\n", Irp->IoStatus.Status)); // restore IRP for using in our completion Irp->CurrentLocation--; Irp->Tail.Overlay.CurrentStackLocation--; irps = IoGetCurrentIrpStackLocation(Irp); KdPrint(("[tdi_fw] tdi_skip_complete: DeviceObject = 0x%x; FileObject = 0x%x\n", DeviceObject, irps->FileObject)); DeviceObject = irps->DeviceObject; if (ctx->new_cr != NULL) { // restore fileobject (it's NULL) irps->FileObject = ctx->fileobj; // set new device object in irps irps->DeviceObject = ctx->new_devobj; // call new completion status = ctx->new_cr(ctx->new_devobj, Irp, ctx->new_context); } else status = STATUS_SUCCESS; /* patch IRP back */ // restore routine and context irps->CompletionRoutine = ctx->old_cr; irps->Context = ctx->old_context; // restore device object irps->DeviceObject = DeviceObject; Irp->CurrentLocation++; Irp->Tail.Overlay.CurrentStackLocation++; if (ctx->old_cr != NULL) { if (status != STATUS_MORE_PROCESSING_REQUIRED) { // call old completion (see the old control) BOOLEAN b_call = FALSE; if (Irp->Cancel) { // cancel if (ctx->old_control & SL_INVOKE_ON_CANCEL) b_call = TRUE; } else { if (Irp->IoStatus.Status >= STATUS_SUCCESS) { // success if (ctx->old_control & SL_INVOKE_ON_SUCCESS) b_call = TRUE; } else { // error if (ctx->old_control & SL_INVOKE_ON_ERROR) b_call = TRUE; } } if (b_call) status = ctx->old_cr(DeviceObject, Irp, ctx->old_context); } else { /* * patch IRP to set IoManager to call completion next time */ // restore Control irps->Control = ctx->old_control; } } free(ctx); return status; } /* get original device object by filtered */ PDEVICE_OBJECT get_original_devobj(PDEVICE_OBJECT flt_devobj, int *proto) { #ifndef USE_TDI_HOOKING PDEVICE_OBJECT result; int ipproto; if (flt_devobj == g_tcpfltobj) { result = g_tcpoldobj; ipproto = IPPROTO_TCP; } else if (flt_devobj == g_udpfltobj) { result = g_udpoldobj; ipproto = IPPROTO_UDP; } else if (flt_devobj == g_ipfltobj) { result = g_ipoldobj; ipproto = IPPROTO_IP; } else { KdPrint(("[tdi_fw] get_original_devobj: Unknown DeviceObject 0x%x!\n", flt_devobj)); result = NULL; } if (result != NULL && proto != NULL) *proto = ipproto; return result; #else /* USE_TDI_HOOKING */ // just stub for original devobj; return proto by devobj int ipproto; if (flt_devobj == g_tcpfltobj) ipproto = IPPROTO_TCP; else if (flt_devobj == g_udpfltobj) ipproto = IPPROTO_UDP; else if (flt_devobj == g_ipfltobj) ipproto = IPPROTO_IP; else { KdPrint(("[tdi_fw] get_original_devobj: Unknown DeviceObject 0x%x!\n", flt_devobj)); return NULL; } if (proto != NULL) *proto = ipproto; return flt_devobj; #endif } /* * Completion routines must call this function at the end of their execution */ NTSTATUS tdi_generic_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { KdPrint(("[tdi_fw] tdi_generic_complete: STATUS = 0x%x\n", Irp->IoStatus.Status)); if (Irp->PendingReturned) { KdPrint(("[tdi_fw] tdi_generic_complete: PENDING\n")); IoMarkIrpPending(Irp); } return STATUS_SUCCESS; }