Read a sector from physical drive

773 Views Asked by At

I am trying to read a sector from my physical drive using SCSI commands.

I referenced other people's code and modified some, below is the code.

#include <stddef.h>
#include <stdio.h>
#include <iostream>
#include <windows.h>
#include <winioctl.h>
#define ULONG_PTR ULONG
//#include <ntddscsi.h> // SDK
//#include <spti.h>
#define wszDrive "\\\\.\\PhysicalDrive1"
// by using CreateFileW(), we need to add a L(means wchar_t) before the wszDrive string


#define SPT_CDB_LENGTH 32
#define SPT_SENSE_LENGTH 32
#define SPTWB_DATA_LENGTH 512




#define IOCTL_SCSI_BASE                 FILE_DEVICE_CONTROLLER
//
// NtDeviceIoControlFile IoControlCode values for this device.
//
// Warning:  Remember that the low two bits of the code specify how the
//           buffers are passed to the driver!
//
#define IOCTL_SCSI_PASS_THROUGH_DIRECT  CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)


//
// Define values for pass-through DataIn field.
//

#define SCSI_IOCTL_DATA_OUT          0
#define SCSI_IOCTL_DATA_IN           1
#define SCSI_IOCTL_DATA_UNSPECIFIED  2


typedef struct _SCSI_PASS_THROUGH_DIRECT {
    USHORT Length;// contains the value of sizeof(SCSI_PASS_THROUGH_DIRECT)
    UCHAR ScsiStatus;// reports the SCSI status that was returned by the HBA or the target device.
    UCHAR PathId;// indicate the SCSI port or bus for the request
    UCHAR TargetId;// indicates the target controller or device on the bus
    UCHAR Lun;// indicates the logical unit number of the device
    UCHAR CdbLength;//indicates the size in bytes of the SCSI command descriptor block
    UCHAR SenseInfoLength;// indicates the size in bytes of the request-sense buffer
    UCHAR DataIn;// indicates whether the SCSI command will read(SCSI_IOCTL_DATA_IN) or write(SCSI_IOCTL_DATA_OUT) data, or no data transferred(SCSI_IOCTL_DATA_UNSPECIFIED)
    ULONG DataTransferLength;//indicates the size in bytes of the data buffer.
    ULONG TimeOutValue;// indicates the interval in seconds that the request can execute before the OS-specific port driver might consider it timed out.
    PVOID DataBuffer;// pointer to the data buffer
    ULONG SenseInfoOffset;// contains an offset from the beginning of this structure to the request-sense buffer.
    UCHAR Cdb[16];// specifies the SCSI command descriptor block to be sent to the target drive.
}SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;




int main()
{
    HANDLE hDevice = INVALID_HANDLE_VALUE;
    
    SCSI_PASS_THROUGH_DIRECT sptd;
    
    ULONG length = 0; 
    DWORD bytesReturn; 
    BYTE myBuffer[512]; 
    int iRet;


    hDevice = CreateFile (wszDrive,
                          GENERIC_READ, // dwDesiredAccess: GENERIC_READ means allow read access
                          FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode: FILE_SHARE_READ | FILE_SHARE_WRITE means allow shared access
                          NULL, // lpSecurityAttributes: points to a SECURITY_ATTRIBUTE structure
                          OPEN_EXISTING, // dwCreationDisposition: OPEN_EXISTING(the opening file should be already existing)
                          0, // dwFlagsAndAttributes: some attributes
                          NULL); // hTemplateFile: if not 0, it points to a file handler. The newly created file will copy the attributes from this file.
    
    if(hDevice == INVALID_HANDLE_VALUE)
    {
        printf("Get disk handle failed\n");
        return 0;
    }
    else
    {
        printf("Get disk handle successfully\n");
    }

    
    
    int posSector = 14;   //starting at sector 14
    int readSectors = 1 ; // read 1 sector
    ZeroMemory(&sptd, sizeof(SCSI_PASS_THROUGH_DIRECT));
    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.PathId = 0;
    sptd.TargetId = 1;
    sptd.Lun = 0;
    sptd.CdbLength = 10;
    sptd.DataIn = SCSI_IOCTL_DATA_IN;
    sptd.SenseInfoLength = 24;
    sptd.DataTransferLength = 512 * readSectors;
    sptd.TimeOutValue = 2;
    sptd.DataBuffer = myBuffer;

    
    sptd.Cdb[0] = 0x28 ;
    sptd.Cdb[2] = (posSector>>24)&0xff; // start at sector posSector
    sptd.Cdb[3] = (posSector>>16)&0xff;
    sptd.Cdb[4] = (posSector>>8)&0xff;
    sptd.Cdb[5] = posSector&0xff;
    sptd.Cdb[7] = (readSectors>>8)&0xff; 
    sptd.Cdb[8] = readSectors&0xff;   //
    length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    
    
    
    iRet = DeviceIoControl(hDevice,
            IOCTL_SCSI_PASS_THROUGH_DIRECT,
            &sptd,
            length,
            &sptd,
            length,
            &bytesReturn,
            NULL);
    if (0 == iRet)
    {
        printf("Get disk data failed\n");
        printf("Error message: %u\n", GetLastError());
        return 0;
    }

    
    
    CloseHandle(hDevice);

    return 0;
}

I can get the HANDLE, I want to pass a SCSI_PASS_THROUGH_DIRECT structure to the device.

I read the document of Microsoft but still can't understand how the parameters(especially the CDB) of SCSI_PASS_THROUGH_DIRECT should be set.

The error code get from GetLastError() is 5(Access is denied).

Can someone please explain or give me some reference link to read? Thanks.

1

There are 1 best solutions below

1
aleck099 On

Run your code under Administrator.

Accessing physical drives needs the Administrator's permission.