通过发SRB实现物理磁盘读写

#include <ntddk.h>
#include <string.h>
#include <srb.h>
#include <scsi.h>

PDEVICE_OBJECT GetLastDiskDeviceObject(PDRIVER_OBJECT pDrvObj);
PDEVICE_OBJECT GetATADr0DeviceObject();
NTSTATUS
MyIoCallDriver(
               IN PDEVICE_OBJECT DeviceObject,
               IN OUT PIRP Irp
               );
NTSTATUS ScsiReadWriteDiskCompletion(PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID Context);
NTSTATUS ScsiReadWriteDisk(PDEVICE_OBJECT pDevObj, BOOLEAN bIsRead, ULONG ulSectorPos, PUCHAR lpDataBuff, ULONG ulSecCount);

//
//    全局变量
//
ULONG g_ulBytesPerSector = 0x200;

/**********************************************************************
* NAME
*  GetLastDiskDeviceObject
*
* DESCRIPTION
*   获取\DRIVER\DISK下最后一个设备
*
* ARGUMENTS
*   PDRIVER_OBJECT pDrvObj \DRIVER\DISK的驱动对象(DRIVER_OBJECT)
* RETURN VALUE
*  PDEVICE_OBJECT    成功返回 \DRIVER\DISK下最后一个设备(DR0), 失败返回 NULL
*
* @implemented
*/
PDEVICE_OBJECT GetLastDiskDeviceObject(PDRIVER_OBJECT pDrvObj)
{
    PDEVICE_OBJECT pCurDevObj = NULL;
    PDEVICE_OBJECT pLastDevObj = NULL;

    pCurDevObj = pDrvObj->DeviceObject;

    while(pCurDevObj)
    {
        if (pCurDevObj->DeviceType == FILE_DEVICE_DISK)
        {
            pLastDevObj = pCurDevObj;
        }
        pCurDevObj = pCurDevObj->NextDevice;
    }

    return pLastDevObj;
}

/**********************************************************************
* NAME
*  GetATADr0DeviceObject
*
* DESCRIPTION
*   获取ATA磁盘的DRO设备
*
* ARGUMENTS
*   N/A
* RETURN VALUE
*  PDEVICE_OBJECT    成功返回 \DRIVER\DISK的DR0设备, 失败返回 NULL
*
* @implemented
*/
PDEVICE_OBJECT GetATADr0DeviceObject()
{
    UNICODE_STRING usDiskName;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDRIVER_OBJECT pDiskDrvObj = NULL;

    RtlInitUnicodeString(&usDiskName, DISK_DRIVER_NAME);

    ntStatus = ObReferenceObjectByName(&usDiskName, 0x40, 0, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&pDiskDrvObj);

    if (!NT_SUCCESS(ntStatus))
    {
        return NULL;
    }

    PDEVICE_OBJECT pDr0DevObj = GetLastDiskDeviceObject(pDiskDrvObj);

    ObDereferenceObject(pDiskDrvObj);

    return pDr0DevObj;
}

NTSTATUS
FORCEINLINE
MyIoCallDriver(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp
    )
{
    PIO_STACK_LOCATION irpSp;
    PDRIVER_OBJECT driverObject;
    NTSTATUS status;

    //
    // Ensure that this is really an I/O Request Packet.
    //

    ASSERT( Irp->Type == IO_TYPE_IRP );
    //
    // Update the IRP stack to point to the next location.
    // 修改当前堆栈索引使之成为下个IO_STACK_LOCATION的索引
    // 如果索引值小于或者等于0时则说明出错
    //
    Irp->CurrentLocation--;

    if (Irp->CurrentLocation <= 0) {
        KeBugCheck( NO_MORE_IRP_STACK_LOCATIONS);
    }

    //
    // 设置IRP当前堆栈指针指向下一个IO_STACK_LOCATION
    //
    irpSp = IoGetNextIrpStackLocation( Irp );
    Irp->Tail.Overlay.CurrentStackLocation = irpSp;

    //
    // Save a pointer to the device object for this request so that it can
    // be used later in completion.
    //

    irpSp->DeviceObject = DeviceObject;

    //
    // Invoke the driver at its dispatch routine entry point.
    //

    driverObject = DeviceObject->DriverObject;

    //
    // Prevent the driver from unloading.
    //

    status = driverObject->MajorFunction[irpSp->MajorFunction]( DeviceObject,
                                                              Irp );
    return status;
}

NTSTATUS ScsiReadWriteDiskCompletion(PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID Context)
{
    pIrp->UserIosb->Information = pIrp->IoStatus.Information;
    pIrp->UserIosb->Status = pIrp->IoStatus.Status;

    if (Context != NULL && pIrp->MdlAddress != NULL)
    {
        MmUnlockPages(pIrp->MdlAddress);
        IoFreeMdl(pIrp->MdlAddress);
    }

    KeSetEvent(pIrp->UserEvent, 0, FALSE);
    IoFreeIrp(pIrp);

    return STATUS_MORE_PROCESSING_REQUIRED; //交回我们的例程处理
}

/**********************************************************************
* NAME
*  ScsiReadWriteDisk
*
* DESCRIPTION
*   通过向DISK的DRO发送SRB(SCSI_REQUEST_BLOCK)的方式读写磁盘扇区
*
* ARGUMENTS
*   BOOLEAN bIsRead        TRUE表示读 FALSE 表示写
*   ULONG ulSectorPos        要读写的扇区号LBN
*   ULONG ulSectorCount    要读写的扇区个数
* RETURN VALUE
*  PDEVICE_OBJECT    成功返回 \DRIVER\DISK的DR0设备, 失败返回 NULL
*
* @implemented
*/
NTSTATUS ScsiReadWriteDisk(PDEVICE_OBJECT pDevObj, BOOLEAN bIsRead, ULONG ulSectorPos, PUCHAR lpDataBuff, ULONG ulSecCount)
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSCSI_REQUEST_BLOCK pSrb = NULL;
    PSENSE_DATA pSenseData = NULL;
    PCDB pCdb10 = NULL;
    KEVENT Event;
    PIRP pIrp = NULL;
    PMDL pMdl = NULL;
    IO_STATUS_BLOCK IoSB;

    pSrb = (PSCSI_REQUEST_BLOCK)ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK),0x12345678);

    if (pSrb == NULL)
        goto __end;

    pSenseData = (PSENSE_DATA)ExAllocatePoolWithTag(NonPagedPool, sizeof(SENSE_DATA),0x12345678);

    if (pSenseData == NULL)
        goto __end;

    RtlZeroMemory(pSrb, sizeof(SCSI_REQUEST_BLOCK));
    RtlZeroMemory(pSenseData, sizeof(SENSE_DATA));

    pSrb->Length = sizeof (SCSI_REQUEST_BLOCK);
    pSrb->Function = SRB_FUNCTION_EXECUTE_SCSI;
    //
    //    我们要读写的是IDE设备 所以不需要PATHID TARGETID LUN
    //
    pSrb->PathId = 0;
    pSrb->TargetId = 0;
    pSrb->Lun = 0;
    pSrb->NextSrb = NULL;
    pSrb->LinkTimeoutValue = -1;
    pSrb->SrbStatus = SRB_STATUS_PENDING;
    pSrb->ScsiStatus = SRB_STATUS_PENDING;
    pSrb->QueueAction = SRB_SIMPLE_TAG_REQUEST;

    pSrb->SenseInfoBuffer = pSenseData;
    pSrb->SenseInfoBufferLength = sizeof(SENSE_DATA);

    pSrb->DataBuffer = (PVOID)lpDataBuff;
    pSrb->DataTransferLength = ulSecCount;
    pSrb->QueueSortKey = ulSectorPos;

    if (bIsRead)
    {
        //
        //    READ
        //
        pSrb->SrbFlags |= SRB_FLAGS_DATA_IN;
        pSrb->SrbFlags |= SRB_FLAGS_ADAPTER_CACHE_ENABLE; //这里是啥意思?适配器开启缓存?
    }
    else
    {
        //
        //    WRITE
        //
        pSrb->SrbFlags |= SRB_FLAGS_DATA_OUT;
    }
    pSrb->SrbFlags |= SRB_FLAGS_DISABLE_AUTOSENSE;

    //
    //    填写CDB
    //
    pSrb->CdbLength = 0x0A; //CDB10
    pCdb10 = (PCDB)pSrb->Cdb;

    if (bIsRead)
    {
        pCdb10->CDB10.OperationCode = SCSIOP_READ;
    }
    else
    {
        pCdb10->CDB10.OperationCode = SCSIOP_WRITE;
    }
    pCdb10->CDB10.LogicalBlockByte0 = (UCHAR)(ulSectorPos >> 0x18);
    pCdb10->CDB10.LogicalBlockByte1 = (UCHAR)(ulSectorPos >> 0x10);
    pCdb10->CDB10.LogicalBlockByte2 = (UCHAR)(ulSectorPos >> 0x8);
    pCdb10->CDB10.LogicalBlockByte3 = (UCHAR)(ulSectorPos);
    pCdb10->CDB10.TransferBlocksLsb = (UCHAR)(ulSecCount >> 0x8);
    pCdb10->CDB10.TransferBlocksMsb = (UCHAR)(ulSecCount);
    //
    //    构造IRP来SRB请求
    //
    KeInitializeEvent(&Event, NotificationEvent, FALSE);
    pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);

    if (pIrp == NULL)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto __end;
    }

    pMdl = IoAllocateMdl((PVOID)lpDataBuff, ulSecCount * g_ulBytesPerSector, FALSE, FALSE, pIrp);

    if (pMdl == NULL)
    {
        IoFreeIrp(pIrp);
        pIrp = NULL;
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto __end;
    }

    MmProbeAndLockPages(pMdl, KernelMode, bIsRead ? IoReadAccess : IoWriteAccess);

    pIrp->UserIosb = &IoSB;
    RtlZeroMemory(&pIrp->IoStatus, sizeof(IO_STATUS_BLOCK));
    pIrp->UserEvent = &Event;
    pIrp->Cancel = FALSE;
    pIrp->CancelRoutine = NULL;
    pIrp->MdlAddress = pMdl;
    pIrp->AssociatedIrp.SystemBuffer = NULL;
    pIrp->Flags = IRP_NOCACHE | IRP_SYNCHRONOUS_API;
    pIrp->RequestorMode = KernelMode;
    pIrp->Tail.Overlay.Thread = PsGetCurrentThread();

    pSrb->OriginalRequest = pIrp;

    //
    //    填写IO_STACK_LOCATION
    //
    PIO_STACK_LOCATION pIrpSp = IoGetNextIrpStackLocation(pIrp);

    pIrpSp->DeviceObject = pDevObj;
    pIrpSp->MajorFunction = IRP_MJ_SCSI;
    pIrpSp->Parameters.Scsi.Srb = pSrb;

    IoSetCompletionRoutine(pIrp, ScsiReadWriteDiskCompletion, pSrb, TRUE, TRUE, TRUE);

    ntStatus = MyIoCallDriver(pDevObj, pIrp);

    if (ntStatus == STATUS_PENDING)
    {
        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
        ntStatus = STATUS_SUCCESS;
    }
    if (NT_SUCCESS(ntStatus) && pSrb->SrbStatus == SRB_STATUS_SUCCESS && pSrb->ScsiStatus == SCSISTAT_GOOD)
    {
        ntStatus = STATUS_SUCCESS;
    }
    else
    {
        ntStatus = STATUS_UNSUCCESSFUL;
    }

__end:
    if (pSrb->SenseInfoBuffer && pSrb->SenseInfoBuffer != pSenseData)
    {
        ExFreePool(pSrb->SenseInfoBuffer);
    }

    if (pSrb != NULL)
    {
        ExFreePool(pSrb);
    }

    if (pSenseData != NULL)
    {
        ExFreePool(pSenseData);
    }

    return ntStatus;
}




posted @ 2010-01-17 20:41  robinh00d  阅读(1603)  评论(0编辑  收藏  举报