Example #1
0
// Handles MTF VM-exit. Restores the last breakpoint event, re-enables stealth
// breakpoint and clears MTF;
_Use_decl_annotations_ void SbpHandleMonitorTrapFlag(EptData* ept_data) {
  NT_VERIFY(SbppIsSbpActive());

  const auto info = SbppRestoreLastPatchInfo();
  SbppEnablePageShadowingForExec(*info, ept_data);
  SbppSetMonitorTrapFlag(false);
}
Example #2
0
// Deal with EPT violation VM-exit.
_Use_decl_annotations_ void EptHandleEptViolation(EptData *ept_data) {
  const EptViolationQualification exit_qualification = {
      UtilVmRead(VmcsField::kExitQualification)};

  const auto fault_pa = UtilVmRead64(VmcsField::kGuestPhysicalAddress);
  const auto fault_va = exit_qualification.fields.valid_guest_linear_address
                            ? UtilVmRead(VmcsField::kGuestLinearAddress)
                            : 0;

  if (!exit_qualification.fields.ept_readable &&
      !exit_qualification.fields.ept_writeable &&
      !exit_qualification.fields.ept_executable) {
    const auto ept_entry = EptGetEptPtEntry(ept_data, fault_pa);
    if (!ept_entry || !ept_entry->all) {
      // EPT entry miss. It should be device memory.
      HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();

      if (!IsReleaseBuild()) {
        NT_VERIFY(EptpIsDeviceMemory(fault_pa));
      }
      EptpConstructTables(ept_data->ept_pml4, 4, fault_pa, ept_data);

      UtilInveptAll();
      return;
    }
  }
  HYPERPLATFORM_LOG_DEBUG_SAFE("[IGNR] OTH VA = %p, PA = %016llx", fault_va,
                               fault_pa);
}
VOID
Ucm_EvtSetDataRoleCompleted (
    _In_ UCSI_CONTROL Command,
    _In_ PVOID Context,
    _Inout_ PPPM_COMMAND_ACK_PARAMS CommandAckParams
)
/*++

Routine Description:

    Set data role command completion routine. Notifies UCM that the power direction has changed.

Arguments:

    Command - The UCSI command that was completed. In this case, it should be only SetUor.UsbOperationRole.

    Context - Platform policy manager context object.

    CommandAckParams - UCSI command acknowledge parameters.

--*/
{
    UCM_DATA_ROLE dataRole;
    PPPM_CONTEXT ppmCtx;
    WDFDEVICE device;
    PPPM_CONNECTOR connector;
    BOOLEAN success;

    UNREFERENCED_PARAMETER(CommandAckParams);

    PAGED_CODE();

    TRACE_FUNC_ENTRY(TRACE_FLAG_UCMNOTIFICATIONS);

    ppmCtx = (PPPM_CONTEXT)Context;
    device = Context_GetWdfDevice(ppmCtx);
    connector = Ppm_GetConnector(ppmCtx, Command.SetUor.ConnectorNumber);

    NT_VERIFY(Convert((UCSI_USB_OPERATION_ROLE)Command.SetUor.UsbOperationRole, dataRole));

    success = UCSI_CMD_SUCCEEDED(ppmCtx->UcsiDataBlock->CCI);

    if (success)
    {
        TRACE_INFO(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Data role successfully changed to %!UCM_DATA_ROLE!", device, dataRole);
    }
    else
    {
        TRACE_ERROR(TRACE_FLAG_UCMNOTIFICATIONS, "[Device: 0x%p] Data role change to %!UCM_DATA_ROLE! failed", device, dataRole);
    }

    //
    // Notify UCM that the data direction has changed.
    //

    UcmConnectorDataDirectionChanged(connector->Handle, success, dataRole);

    TRACE_FUNC_EXIT(TRACE_FLAG_UCMNOTIFICATIONS);
}
Example #4
0
// Terminates a given process as well as its child process, and wait for
// terminatation of the given process
_Use_decl_annotations_ static NTSTATUS EopmonpTerminateProcessTree(
    HANDLE process_handle, HANDLE pid) {
  PAGED_CODE();

  auto status = ZwTerminateProcess(process_handle, 0);
  HYPERPLATFORM_LOG_DEBUG(
      "Exploitation detected. Process %Iu is being terminated (status = %08x).",
      pid, status);
  if (status == STATUS_PROCESS_IS_TERMINATING) {
    return status;
  }
  status = EopmonpForEachProcess(EopmonpTerminateProcessIfChild, pid);
  NT_VERIFY(NT_SUCCESS(status));
  status = ZwWaitForSingleObject(process_handle, FALSE, nullptr);
  NT_VERIFY(NT_SUCCESS(status));
  return status;
}
Example #5
0
// Deal with L2 EPT violation VM-exit.
_Use_decl_annotations_ void EptHandleEptViolationEx(EptData *ept_data, EptData *ept_data02, ULONG_PTR guest_pa, bool is_range_of_ept12) {
	
	const EptViolationQualification exit_qualification = {
		UtilVmRead(VmcsField::kExitQualification) };
	ULONG_PTR fault_pa = 0;

	if (!guest_pa)
	{
		 fault_pa = UtilVmRead64(VmcsField::kGuestPhysicalAddress);
	}
	else
	{
		fault_pa = guest_pa;
	}
	
	const auto fault_va = reinterpret_cast<void *>(
		exit_qualification.fields.valid_guest_linear_address
		? UtilVmRead(VmcsField::kGuestLinearAddress)
		: 0);


	//GuestPhysicalAddress will be the guest physical adderss of EPT1-2 Entry , we disable it write in L2 first initial

	if (!exit_qualification.fields.ept_readable &&
		!exit_qualification.fields.ept_writeable &&
		!exit_qualification.fields.ept_executable) {
		const auto ept_entry = EptGetEptPtEntry(ept_data, fault_pa);
		if (!ept_entry || !ept_entry->all) {
			// EPT entry miss. It should be device memory.
			HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE();

			if (!IsReleaseBuild()) {
				NT_VERIFY(EptpIsDeviceMemory(fault_pa));
			}
			EptpConstructTables(ept_data->ept_pml4, 4, fault_pa, ept_data);

			UtilInveptGlobal();
			return;
		}
	}
	
	if (!exit_qualification.fields.ept_writeable && is_range_of_ept12)
	{
		EptCommonEntry* Ept01Pte = EptGetEptPtEntry(ept_data, UtilVmRead64(VmcsField::kGuestPhysicalAddress));
		if (Ept01Pte)
		{  
			EptCommonEntry* entry = (EptCommonEntry*)UtilVaFromPa(UtilVmRead64(VmcsField::kGuestPhysicalAddress));
			Ept01Pte->fields.write_access = true;
			HYPERPLATFORM_LOG_DEBUG_SAFE("Faced non-writable address but it is readble. :%p  %p", UtilVmRead64(VmcsField::kGuestPhysicalAddress), entry->fields.physial_address);
			UtilInveptGlobal();
		}
	}
	 
}
Example #6
0
// Terminates DdiMon
_Use_decl_annotations_ EXTERN_C void SbpTermination() {
  PAGED_CODE();

  auto ptrs = g_sbpp_breakpoints;
  auto status = UtilVmCall(HypercallNumber::kDdimonDisablePageShadowing, ptrs);
  NT_VERIFY(NT_SUCCESS(status));
  UtilSleep(500);

  g_sbpp_breakpoints = nullptr;
  delete ptrs;
}
Example #7
0
// Converts a pool tag in integer to a printable string
_Use_decl_annotations_ static std::array<char, 5> DdimonpTagToString(
    ULONG tag_value) {
  PoolTag tag = {tag_value};
  for (auto& c : tag.chars) {
    if (!c && isspace(c)) {
      c = ' ';
    }
    if (!isprint(c)) {
      c = '.';
    }
  }

  std::array<char, 5> str;
  auto status =
      RtlStringCchPrintfA(str.data(), str.size(), "%c%c%c%c", tag.chars[0],
                          tag.chars[1], tag.chars[2], tag.chars[3]);
  NT_VERIFY(NT_SUCCESS(status));
  return str;
}
Example #8
0
// Terminates a process if it is created from a dodgy process
_Use_decl_annotations_ static bool EopmonpTerminateProcessIfChild(
    HANDLE pid, void* context) {
  PAGED_CODE();

  const auto dodgy_pid = reinterpret_cast<HANDLE>(context);

  const auto process_handle = EopmonpOpenProcess(pid);
  if (!process_handle) {
    return true;
  }

  // Is this process created from the dodgy process?
  const auto parent_pid =
      EopmonpGetProcessParentProcessIdByHandle(process_handle);
  if (parent_pid != dodgy_pid) {
    goto exit;
  }

  // Is this process created later than the dodgy process?
  const auto create_time = EopmonpGetProcessCreateTimeQuadPart(pid);
  const auto parent_create_time =
      EopmonpGetProcessCreateTimeQuadPart(dodgy_pid);
  if (!create_time || !parent_create_time ||
      create_time <= parent_create_time) {
    goto exit;
  }

  // Yes, terminate this process as well as its child processes
  auto status = ZwTerminateProcess(process_handle, 0);
  HYPERPLATFORM_LOG_DEBUG(
      "Exploitation detected. Process %Iu is being terminated (status = %08x).",
      pid, status);
  status = EopmonpForEachProcess(EopmonpTerminateProcessIfChild, pid);
  NT_VERIFY(NT_SUCCESS(status));

exit:;
  ZwClose(process_handle);
  return true;
}
Example #9
0
File: mft.c Project: RPG-7/reactos
BOOLEAN
CompareFileName(PUNICODE_STRING FileName,
                PINDEX_ENTRY_ATTRIBUTE IndexEntry,
                BOOLEAN DirSearch)
{
    BOOLEAN Ret, Alloc = FALSE;
    UNICODE_STRING EntryName;

    EntryName.Buffer = IndexEntry->FileName.Name;
    EntryName.Length = 
    EntryName.MaximumLength = IndexEntry->FileName.NameLength * sizeof(WCHAR);

    if (DirSearch)
    {
        UNICODE_STRING IntFileName;
        if (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)
        {
            NT_VERIFY(NT_SUCCESS(RtlUpcaseUnicodeString(&IntFileName, FileName, TRUE)));
            Alloc = TRUE;
        }
        else
        {
            IntFileName = *FileName;
        }

        Ret = FsRtlIsNameInExpression(&IntFileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX), NULL);

        if (Alloc)
        {
            RtlFreeUnicodeString(&IntFileName);
        }

        return Ret;
    }
    else
    {
        return (RtlCompareUnicodeString(FileName, &EntryName, (IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == 0);
    }
}
Example #10
0
NTSTATUS
PostProcessSelectConfig(
    IN PUSB_FDO_CONTEXT fdoContext,
    IN PURB Urb)
{

    PUSB_CONFIGURATION_DESCRIPTOR config = Urb->UrbSelectConfiguration.ConfigurationDescriptor;
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    //
    // config can be null if the client driver has reset the configuration
    //
    if (config)
    {
        fdoContext->CurrentConfigValue = config->bConfigurationValue;
        SetConfigPointers(fdoContext);

        if (NT_VERIFY(fdoContext->InterfaceDescriptors))
        {
            Urb->UrbSelectConfiguration.ConfigurationHandle = fdoContext->ConfigurationDescriptor;
            Status = STATUS_SUCCESS;
        }
    }
    else
    {
        fdoContext->CurrentConfigValue = 0; // unconfigured		
        SetConfigPointers(fdoContext);
        Status = STATUS_SUCCESS;
    }

    ASSERT(fdoContext->ConfigBusy);
    fdoContext->ConfigBusy = FALSE;

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DPC,
        __FUNCTION__": returns Status %x\n",
        Status);
    return Status;
}
Example #11
0
NTSTATUS NTAPI
AfdPacketSocketWriteData(PDEVICE_OBJECT DeviceObject, PIRP Irp,
                         PIO_STACK_LOCATION IrpSp) {
    NTSTATUS Status = STATUS_SUCCESS;
    PTDI_CONNECTION_INFORMATION TargetAddress;
    PFILE_OBJECT FileObject = IrpSp->FileObject;
    PAFD_FCB FCB = FileObject->FsContext;
    PAFD_SEND_INFO_UDP SendReq;
    KPROCESSOR_MODE LockMode;

    UNREFERENCED_PARAMETER(DeviceObject);

    AFD_DbgPrint(MID_TRACE,("Called on %p\n", FCB));

    if( !SocketAcquireStateLock( FCB ) ) return LostSocket( Irp );

    FCB->EventSelectDisabled &= ~AFD_EVENT_SEND;

    /* Check that the socket is bound */
    if( FCB->State != SOCKET_STATE_BOUND &&
        FCB->State != SOCKET_STATE_CREATED)
    {
        AFD_DbgPrint(MIN_TRACE,("Invalid socket state\n"));
        return UnlockAndMaybeComplete(FCB, STATUS_INVALID_PARAMETER, Irp, 0);
    }

    if (FCB->SendClosed)
    {
        AFD_DbgPrint(MIN_TRACE,("No more sends\n"));
        return UnlockAndMaybeComplete(FCB, STATUS_FILE_CLOSED, Irp, 0);
    }

    if( !(SendReq = LockRequest( Irp, IrpSp, FALSE, &LockMode )) )
        return UnlockAndMaybeComplete(FCB, STATUS_NO_MEMORY, Irp, 0);

    if (FCB->State == SOCKET_STATE_CREATED)
    {
        if( FCB->LocalAddress ) ExFreePool( FCB->LocalAddress );
        FCB->LocalAddress =
        TaBuildNullTransportAddress( ((PTRANSPORT_ADDRESS)SendReq->TdiConnection.RemoteAddress)->
                                      Address[0].AddressType );

        if( FCB->LocalAddress ) {
            Status = WarmSocketForBind( FCB, AFD_SHARE_WILDCARD );

            if( NT_SUCCESS(Status) )
                FCB->State = SOCKET_STATE_BOUND;
            else
                return UnlockAndMaybeComplete( FCB, Status, Irp, 0 );
        } else
            return UnlockAndMaybeComplete
            ( FCB, STATUS_NO_MEMORY, Irp, 0 );
    }

    SendReq->BufferArray = LockBuffers( SendReq->BufferArray,
                                        SendReq->BufferCount,
                                        NULL, NULL,
                                        FALSE, FALSE, LockMode );

    if( !SendReq->BufferArray )
        return UnlockAndMaybeComplete( FCB, STATUS_ACCESS_VIOLATION,
                                       Irp, 0 );

    AFD_DbgPrint
        (MID_TRACE,("RemoteAddress #%d Type %u\n",
                    ((PTRANSPORT_ADDRESS)SendReq->TdiConnection.RemoteAddress)->
                    TAAddressCount,
                    ((PTRANSPORT_ADDRESS)SendReq->TdiConnection.RemoteAddress)->
                    Address[0].AddressType));

    Status = TdiBuildConnectionInfo( &TargetAddress,
                            ((PTRANSPORT_ADDRESS)SendReq->TdiConnection.RemoteAddress) );

    /* Check the size of the Address given ... */

    if( NT_SUCCESS(Status) ) {
        FCB->PollState &= ~AFD_EVENT_SEND;

        Status = QueueUserModeIrp(FCB, Irp, FUNCTION_SEND);
        if (Status == STATUS_PENDING)
        {
            Status = TdiSendDatagram(&FCB->SendIrp.InFlightRequest,
                                     FCB->AddressFile.Object,
                                     SendReq->BufferArray[0].buf,
                                     SendReq->BufferArray[0].len,
                                     TargetAddress,
                                     PacketSocketSendComplete,
                                     FCB);
            if (Status != STATUS_PENDING)
            {
                NT_VERIFY(RemoveHeadList(&FCB->PendingIrpList[FUNCTION_SEND]) == &Irp->Tail.Overlay.ListEntry);
                Irp->IoStatus.Status = Status;
                Irp->IoStatus.Information = 0;
                (void)IoSetCancelRoutine(Irp, NULL);
                UnlockBuffers(SendReq->BufferArray, SendReq->BufferCount, FALSE);
                UnlockRequest(Irp, IoGetCurrentIrpStackLocation(Irp));
                IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
            }
        }

        ExFreePool(TargetAddress);

        SocketStateUnlock(FCB);

        return STATUS_PENDING;
    }
    else
    {
        UnlockBuffers(SendReq->BufferArray, SendReq->BufferCount, FALSE);
        return UnlockAndMaybeComplete( FCB, Status, Irp, 0 );
    }
}
Example #12
0
NTSTATUS NTAPI
AfdConnectedSocketWriteData(PDEVICE_OBJECT DeviceObject, PIRP Irp,
                            PIO_STACK_LOCATION IrpSp, BOOLEAN Short) {
    NTSTATUS Status = STATUS_SUCCESS;
    PFILE_OBJECT FileObject = IrpSp->FileObject;
    PAFD_FCB FCB = FileObject->FsContext;
    PAFD_SEND_INFO SendReq;
    UINT TotalBytesCopied = 0, i, SpaceAvail = 0, BytesCopied, SendLength;
    KPROCESSOR_MODE LockMode;

    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Short);

    AFD_DbgPrint(MID_TRACE,("Called on %p\n", FCB));

    if( !SocketAcquireStateLock( FCB ) ) return LostSocket( Irp );

    FCB->EventSelectDisabled &= ~AFD_EVENT_SEND;

    if( FCB->Flags & AFD_ENDPOINT_CONNECTIONLESS )
    {
        PAFD_SEND_INFO_UDP SendReq;
        PTDI_CONNECTION_INFORMATION TargetAddress;

        /* Check that the socket is bound */
        if( FCB->State != SOCKET_STATE_BOUND || !FCB->RemoteAddress )
        {
            AFD_DbgPrint(MIN_TRACE,("Invalid parameter\n"));
            return UnlockAndMaybeComplete( FCB, STATUS_INVALID_PARAMETER, Irp,
                                           0 );
        }

        if( !(SendReq = LockRequest( Irp, IrpSp, FALSE, &LockMode )) )
            return UnlockAndMaybeComplete( FCB, STATUS_NO_MEMORY, Irp, 0 );

        /* Must lock buffers before handing off user data */
        SendReq->BufferArray = LockBuffers( SendReq->BufferArray,
                                            SendReq->BufferCount,
                                            NULL, NULL,
                                            FALSE, FALSE, LockMode );

        if( !SendReq->BufferArray ) {
            return UnlockAndMaybeComplete( FCB, STATUS_ACCESS_VIOLATION,
                                           Irp, 0 );
        }

        Status = TdiBuildConnectionInfo( &TargetAddress, FCB->RemoteAddress );

        if( NT_SUCCESS(Status) ) {
            FCB->PollState &= ~AFD_EVENT_SEND;

            Status = QueueUserModeIrp(FCB, Irp, FUNCTION_SEND);
            if (Status == STATUS_PENDING)
            {
                Status = TdiSendDatagram(&FCB->SendIrp.InFlightRequest,
                                         FCB->AddressFile.Object,
                                         SendReq->BufferArray[0].buf,
                                         SendReq->BufferArray[0].len,
                                         TargetAddress,
                                         PacketSocketSendComplete,
                                         FCB);
                if (Status != STATUS_PENDING)
                {
                    NT_VERIFY(RemoveHeadList(&FCB->PendingIrpList[FUNCTION_SEND]) == &Irp->Tail.Overlay.ListEntry);
                    Irp->IoStatus.Status = Status;
                    Irp->IoStatus.Information = 0;
                    (void)IoSetCancelRoutine(Irp, NULL);
                    UnlockBuffers(SendReq->BufferArray, SendReq->BufferCount, FALSE);
                    UnlockRequest(Irp, IoGetCurrentIrpStackLocation(Irp));
                    IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
                }
            }

            ExFreePool( TargetAddress );

            SocketStateUnlock(FCB);

            return STATUS_PENDING;
        }
        else
        {
            UnlockBuffers(SendReq->BufferArray, SendReq->BufferCount, FALSE);
            return UnlockAndMaybeComplete( FCB, Status, Irp, 0 );
        }
    }

    if (FCB->PollState & AFD_EVENT_CLOSE)
    {
        AFD_DbgPrint(MIN_TRACE,("Connection reset by remote peer\n"));

        /* This is an unexpected remote disconnect */
        return UnlockAndMaybeComplete(FCB, FCB->PollStatus[FD_CLOSE_BIT], Irp, 0);
    }

    if (FCB->PollState & AFD_EVENT_ABORT)
    {
        AFD_DbgPrint(MIN_TRACE,("Connection aborted\n"));

        /* This is an abortive socket closure on our side */
        return UnlockAndMaybeComplete(FCB, FCB->PollStatus[FD_CLOSE_BIT], Irp, 0);
    }

    if (FCB->SendClosed)
    {
        AFD_DbgPrint(MIN_TRACE,("No more sends\n"));

        /* This is a graceful send closure */
        return UnlockAndMaybeComplete(FCB, STATUS_FILE_CLOSED, Irp, 0);
    }

    if( !(SendReq = LockRequest( Irp, IrpSp, FALSE, &LockMode )) )
        return UnlockAndMaybeComplete
            ( FCB, STATUS_NO_MEMORY, Irp, 0 );

    SendReq->BufferArray = LockBuffers( SendReq->BufferArray,
                                        SendReq->BufferCount,
                                        NULL, NULL,
                                        FALSE, FALSE, LockMode );

    if( !SendReq->BufferArray ) {
        return UnlockAndMaybeComplete( FCB, STATUS_ACCESS_VIOLATION,
                                       Irp, 0 );
    }

    AFD_DbgPrint(MID_TRACE,("Socket state %u\n", FCB->State));

    if( FCB->State != SOCKET_STATE_CONNECTED ) {
        AFD_DbgPrint(MID_TRACE,("Socket not connected\n"));
        UnlockBuffers( SendReq->BufferArray, SendReq->BufferCount, FALSE );
        return UnlockAndMaybeComplete( FCB, STATUS_INVALID_CONNECTION, Irp, 0 );
    }

    AFD_DbgPrint(MID_TRACE,("FCB->Send.BytesUsed = %u\n",
                            FCB->Send.BytesUsed));

    SpaceAvail = FCB->Send.Size - FCB->Send.BytesUsed;

    AFD_DbgPrint(MID_TRACE,("We can accept %u bytes\n",
                            SpaceAvail));

    /* Count the total transfer size */
    SendLength = 0;
    for (i = 0; i < SendReq->BufferCount; i++)
    {
        SendLength += SendReq->BufferArray[i].len;
    }

    /* Make sure we've got the space */
    if (SendLength > SpaceAvail)
    {
        /* Blocking sockets have to wait here */
        if (SendLength <= FCB->Send.Size && !((SendReq->AfdFlags & AFD_IMMEDIATE) || (FCB->NonBlocking)))
        {
            FCB->PollState &= ~AFD_EVENT_SEND;
            return LeaveIrpUntilLater(FCB, Irp, FUNCTION_SEND);
        }

        /* Check if we can send anything */
        if (SpaceAvail == 0)
        {
            FCB->PollState &= ~AFD_EVENT_SEND;

            /* Non-overlapped sockets will fail if we can send nothing */
            if (!(SendReq->AfdFlags & AFD_OVERLAPPED))
            {
                UnlockBuffers( SendReq->BufferArray, SendReq->BufferCount, FALSE );
                return UnlockAndMaybeComplete( FCB, STATUS_CANT_WAIT, Irp, 0 );
            }
            else
            {
                /* Overlapped sockets just pend */
                return LeaveIrpUntilLater(FCB, Irp, FUNCTION_SEND);
            }
        }
    }

    for ( i = 0; SpaceAvail > 0 && i < SendReq->BufferCount; i++ )
    {
        BytesCopied = MIN(SendReq->BufferArray[i].len, SpaceAvail);

        AFD_DbgPrint(MID_TRACE,("Copying Buffer %u, %p:%u to %p\n",
                                i,
                                SendReq->BufferArray[i].buf,
                                BytesCopied,
                                FCB->Send.Window + FCB->Send.BytesUsed));

        RtlCopyMemory(FCB->Send.Window + FCB->Send.BytesUsed,
                      SendReq->BufferArray[i].buf,
                      BytesCopied);

        TotalBytesCopied += BytesCopied;
        SpaceAvail -= BytesCopied;
        FCB->Send.BytesUsed += BytesCopied;
    }

    Irp->IoStatus.Information = TotalBytesCopied;

    if( TotalBytesCopied == 0 ) {
        AFD_DbgPrint(MID_TRACE,("Empty send\n"));
        UnlockBuffers( SendReq->BufferArray, SendReq->BufferCount, FALSE );
        return UnlockAndMaybeComplete
        ( FCB, STATUS_SUCCESS, Irp, TotalBytesCopied );
    }

    if (SpaceAvail)
    {
        FCB->PollState |= AFD_EVENT_SEND;
        FCB->PollStatus[FD_WRITE_BIT] = STATUS_SUCCESS;
        PollReeval( FCB->DeviceExt, FCB->FileObject );
    }
    else
    {
        FCB->PollState &= ~AFD_EVENT_SEND;
    }

    /* We use the IRP tail for some temporary storage here */
    Irp->Tail.Overlay.DriverContext[3] = (PVOID)Irp->IoStatus.Information;

    Status = QueueUserModeIrp(FCB, Irp, FUNCTION_SEND);
    if (Status == STATUS_PENDING && !FCB->SendIrp.InFlightRequest)
    {
        TdiSend(&FCB->SendIrp.InFlightRequest,
                FCB->Connection.Object,
                0,
                FCB->Send.Window,
                FCB->Send.BytesUsed,
                SendComplete,
                FCB);
    }

    SocketStateUnlock(FCB);

    return STATUS_PENDING;
}