Samsung NVMe Command Sample (C++)

This article only has code sample. If you need explanation, see this article.

#include <windows.h>
#include <Ntddscsi.h>
#include <tchar.h>

const TCHAR* SamsungTestPath = _T("\\\\.\\PhysicalDrive4");

struct SPTWith512Buffer {
    SCSI_PASS_THROUGH Spt;
    unsigned char SenseBuf[24];
    unsigned char DataBuf[512];
};

void SamsungNVMeIdentify(const TCHAR* Path) {
    SPTWith512Buffer sptwb;
    HANDLE hIoCtrl = CreateFile(Path, GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    DWORD dwReturned;
    size_t length;
    BOOL bRet;

    // ---- SENDING PART ---- //
    ZeroMemory(&sptwb, sizeof(SPTWith512Buffer));

    sptwb.Spt.Length = sizeof(SCSI_PASS_THROUGH);
    sptwb.Spt.PathId = 0;
    sptwb.Spt.TargetId = 0;
    sptwb.Spt.Lun = 0;
    sptwb.Spt.SenseInfoLength = 24;
    sptwb.Spt.DataTransferLength = IDENTIFY_BUFFER_SIZE;
    sptwb.Spt.TimeOutValue = 2;
    sptwb.Spt.DataBufferOffset = offsetof(SPTWith512Buffer, DataBuf);
    sptwb.Spt.SenseInfoOffset = offsetof(SPTWith512Buffer, SenseBuf);

    sptwb.Spt.CdbLength = 16;
    sptwb.Spt.Cdb[0] = 0xB5; // SECURITY PROTOCOL IN
    sptwb.Spt.Cdb[1] = 0xFE; // Samsung Protocol
    sptwb.Spt.Cdb[3] = 5;    // Identify
    sptwb.Spt.Cdb[9] = 0x40; // Transfer Length
    sptwb.Spt.DataIn = SCSI_IOCTL_DATA_OUT;
    sptwb.DataBuf[0] = 1;

    length = offsetof(SPTWith512Buffer, DataBuf)
        + sptwb.Spt.DataTransferLength;

    bRet = DeviceIoControl(hIoCtrl, IOCTL_SCSI_PASS_THROUGH,
        &sptwb, length, &sptwb, length, &dwReturned, NULL);

    // ---- RECEIVING PART ---- //
    ZeroMemory(&sptwb, sizeof(SPTWith512Buffer));

    sptwb.Spt.Length = sizeof(SCSI_PASS_THROUGH);
    sptwb.Spt.PathId = 0;
    sptwb.Spt.TargetId = 0;
    sptwb.Spt.Lun = 0;
    sptwb.Spt.SenseInfoLength = 24;
    sptwb.Spt.DataTransferLength = IDENTIFY_BUFFER_SIZE;
    sptwb.Spt.TimeOutValue = 2;
    sptwb.Spt.DataBufferOffset = offsetof(SPTWith512Buffer, DataBuf);
    sptwb.Spt.SenseInfoOffset = offsetof(SPTWith512Buffer, SenseBuf);

    sptwb.Spt.CdbLength = 16;
    sptwb.Spt.Cdb[0] = 0xA2; // SECURITY PROTOCOL IN
    sptwb.Spt.Cdb[1] = 0xFE; // Samsung Protocol
    sptwb.Spt.Cdb[3] = 5;    // Identify
    sptwb.Spt.Cdb[9] = 0x40; // Transfer Length
    sptwb.Spt.DataIn = SCSI_IOCTL_DATA_IN;

    length = offsetof(SPTWith512Buffer, DataBuf)
        + sptwb.Spt.DataTransferLength;

    bRet = DeviceIoControl(hIoCtrl, IOCTL_SCSI_PASS_THROUGH,
        &sptwb, length, &sptwb, length, &dwReturned, NULL);
}

int main() {
    SamsungNVMeIdentify(SamsungTestPath);
}

2 comments on “Samsung NVMe Command Sample (C++)

  1. So from what I understand from the code sample, the identify command is a security protocol command? I thought that the security protocols were for OPAL compliant devices. Is this unique to this device? NVMe 1.3 spec defines the identify command to be 6, and I thought that this was common to all NVMe devices.

    I have been successful in sending Identify command to NVMe devices, but I am using SCS to NVMe Translation, sending a SCSI Inquiry command.

    Thanks and I look forward to your reply.

    • As you know, SCSI inquiry command result contains only some part of the full NVMe identify information. Model number is not fully copied, serial is in different format(NVMe: original, NVMe-SCSI: EUI64), and many other fields are just omitted. So the article is for those people need real NVMe passthrough.

      Used protocol in this code sample is only for Samsung retail NVMe devices(other vendors and Samsung OEM devices don’t provide this).
      It’s a kind of backdoor that provides NVMe passthrough without any support from OS or driver. I think it’s implemented to keep compatible with their Magician tool on Windows 7 stock NVMe driver. So the protocol doesn’t follow the NVMe standard.

Leave a Reply

Your email address will not be published. Required fields are marked *