Topic Overview
------------------
1.) Why SMART commands do not work in combination with storport
2.) How the SMART commands from CrystalDiskInfo version 8.11.2 traverse down the device stack
3.) Why START STOP UNIT command is not send to HDD / SSD drives
4.) How to fix the logon screen instant reboot bug if a CD-/DVD-ROM drive is connected
5.) Make storport from Windows 2003 x86 no SP leaked source code work with StorAhci driver
6.) How to compile our changed storport driver with SMART support based on leaked sources
7.) Patch original Windows 7 SP1 x86 storport to make it work with Windows 2003 Server
8.) How to apply the storport patches
9.) Credits and Download Links
1.) Why SMART commands do not work in combination with storport
------------------------------------------------------------------------
S.M.A.R.T. is short for "Self-Monitoring, Analysis and Reporting Technology" which is a monitoring system included in hard disk drives (HDDs) and
solid-state drives (SSDs). In general SMART commands are ATA commands, therefore a SCSI device and it's driver does not have to support this command
natively, although almost nearly all drives do. Since the introduction of storport in Windows 2003 Server the command was not send to the drive at
all. The first major change was done starting with Windows 8, where the SMART command was forwarded from storport to the manufacturer's driver. The
storport driver of Windows 7 has also the ability to forward this command, but was limited inside the code.
In the following tutorial we show two possible solutions to the problem. The first solution patches the storport driver of the leaked Windows Server
2003 source code archive. The second solution patches storport of Windows 7 and uses it in combination with ntoskrn7.sys, which is sort of a
self compiled extender library for ntoskrnl.exe to add support for missing functions called by Windows 7 storport.
2.) How the SMART commands from CrystalDiskInfo version 8.11.2 traverse down the device stack
--------------------------------------------------------------------------------------------------------
CrystalDiskInfo's Advanced Disk Search is able to find the drive at SCSI address 0:0:0 (PathId, TargetId, Lun). We can only find drive 0 this
way. All other HDDs can not be found. This is related to the used device handle "\\.\Scsi0:". If we send a command to the connected device
by using this handle, the command is only send to the AHCI adapter code of storport. The adapter receives the command in the function
RaDriverDeviceControlIrp
and calls RaidAdapterScsiMiniportIoctl
. In this function the PathId, TargetId and Lun is always
set to zero. In the following text we talk about CrystalDiskInfo without Advanced Disk Search. To run CrystalDiskInfo in the so called "normal mode"
start the program and go to Menu > Function > Advanced Feature > and make sure that Advanced Disk Search is
unchecked.
CrystalDiskInfo sends the I/O control code 0x7C088 DFP_RECEIVE_DRIVE_DATA
to read the SMART data of HDD and SSD drives. There are 3
locations in the C++ code where this I/O control code is used. The file AtaSmart.cpp uses this I/O in the following 3 functions:
- CAtaSmart::DoIdentifyDevicePd
→ command register ID_CMD 0xEC
- CAtaSmart::GetSmartAttributePd
→ command register SMART_CMD 0xB0 and features register READ_ATTRIBUTES 0xD0
- CAtaSmart::GetSmartThresholdPd
→ command register SMART_CMD 0xB0 and features register READ_THRESHOLDS 0xD1
The I/O control code DFP_RECEIVE_DRIVE_DATA
has the same value as SMART_RCV_DRIVE_DATA
, which is defined in
ntdddisk.h of the Windows Driver Development Kit. This I/O control code is changed by the driver disk.sys in the source code file
disk.c and the function DiskDeviceControl. Based on the values of the command and feature registers the following 3 commands are
sent to storport by the function DiskDeviceControl of disk.sys driver:
- IOCTL_SCSI_MINIPORT_IDENTIFY 0x1B0501
→ command register ID_CMD 0xEC
- IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS 0x1B0502
→ command register SMART_CMD 0xB0 and features register READ_ATTRIBUTES 0xD0
- IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS 0x1B0503
→ command register SMART_CMD 0xB0 and features register READ_THRESHOLDS 0xD1
These 3 I/O control codes are used for the ControlCode
field of the structure SRB_IO_CONTROL
. The function
IoBuildDeviceIoControlRequest
inside disk.sys uses this SRB_IO_CONTROL
structure in combination with the I/O
control code IOCTL_SCSI_MINIPORT
and sends the commands to storport by calling the function IoCallDriver
. The call stack
until the command reaches the problematic function RaUnitDeviceControlIrp
looks like follows:
Inside storport driver we have a problem in the function RaUnitDeviceControlIrp
in the file
storport\storage\port\raid\port\unit.c. The I/O control code 0x4D008 IOCTL_SCSI_MINIPORT
is missing for Windows Server 2003 R2
SP2. In storport of Server 2008 SP2 the IOCTL code is recognized, but is redirected to RaidAdapterScsiMiniportIoctl
, which sets the
PathId, TargetId and Lun always to zero.
On storport of Windows 7 with SP1 we see a change here, because RaUnitDeviceControlIrp
recognizes the I/O control code
IOCTL_SCSI_MINIPORT
and calls RaUnitScsiMiniportIoctl
. In this function the SRB_IO_CONTROL
structure field
Signature
is checked against the string "HYBRDISK"
and the structure field ControlCode
is checked against
IOCTL_SCSI_MINIPORT_NVCACHE
. Only if these two structure fields match, the request is send to the function
RaidAdapterScsiMiniportIoctlWithAddress
. Any other values of these two structure fields call RaUnitUnknownIoctl
and return
the NT status value STATUS_INVALID_DEVICE_REQUEST
.
On Windows 8 without SP RaUnitDeviceControlIrp
recognizes the IOCTL IOCTL_SCSI_MINIPORT
and calls
RaUnitScsiMiniportIoctl
as for Windows 7 with SP1. But on Windows 8 this function then calls the new function
RaidAdapterScsiMiniportIoctlWithAddress
, no matter what the "Signature"
and "ControlCode"
fields of the
structure SRB_IO_CONTROL
contain. The function RaidAdapterScsiMiniportIoctlWithAddress
accepts a SCSI PathId (this is equal
to the "Bus Number" in device manager), TargetId and Lun for the target device address as arguments. Here the correct device is selected before the
request is sent to the HDD drive.
To prove our assumption we changed the leaked source code of Windows 2003 Server storport and rebuild the functions
RaUnitScsiMiniportIoctl
and RaidAdapterScsiMiniportIoctlWithAddress
from Windows 7 SP1. We added a little change and now do
not check the "Signature"
and "ControlCode"
field of the SRB_IO_CONTROL
structure. After we use the changed
storport driver in combination with our StorAhci driver CrystalDiskInfo shows the SMART values for all connected HDDs and SSDs.
3.) Why START STOP UNIT command is not send to HDD / SSD drives
-------------------------------------------------------------------------
The SCSI START STOP UNIT command does work with storport driver version 5.2.3790.3959 from Windows 2003 Server SP2. To make it also work with the
storport from the leaked source code archive, we have to change the following two additional code locations.
- edit the file C:\storport\storage\port\raid\port\power.c and change the function
RaidUnitSetPowerIrp
so that it does not
trigger the if
in case of PowerSystemShutdown
if (PowerState.SystemState > PowerSystemShutdown) {
- edit the file C:\storport\storage\port\raid\port\power.c and change the function
RaidUnitSetSystemPowerIrp
to
NTSTATUS
RaidUnitSetSystemPowerIrp(
IN PRAID_UNIT_EXTENSION Unit,
IN PIRP Irp
)
{
NTSTATUS Status;
POWER_STATE DevicePowerState;
POWER_STATE SystemPowerState;
DebugPower (("Unit %p, Irp %p, Set System Power, ret SUCCESS\n",
Unit, Irp));
SystemPowerState = RaidPowerStateFromIrp (Irp);
DevicePowerState.DeviceState = DeviceStateTable [SystemPowerState.SystemState];
Status = PoRequestPowerIrp (Unit->DeviceObject, IRP_MN_SET_POWER, DevicePowerState, NULL, Irp, NULL);
if (Status == STATUS_PENDING)
{
Status = STATUS_SUCCESS;
}
PoStartNextPowerIrp (Irp);
return RaidCompleteRequest (Irp, Status);
}
After we apply these changes the additional call to PoRequestPowerIrp
, which is missing in the source code completely, triggers the
storage ClassPnP driver and the function ClasspPowerDownCompletion
. This function sends the SYNCHRONIZE_CACHE
command
opcode 0x35 and after that the START_STOP_UNIT
command opcode 0x1B. The START_STOP_UNIT
command opcode 0x1B is received by
StorAhci in AhciHwStartIo
and converted from SCSI to an ATA command. At the end the function AtaStartStopUnitRequest
is
called, which sends IDE_COMMAND_STANDBY_IMMEDIATE
to the drive.
4.) How to fix the logon screen instant reboot bug if a CD-/DVD-ROM drive is connected
---------------------------------------------------------------------------------------------
If a CD-/DVD-ROM drive is connected and the storport driver of the leaked source code is used, we get an instant reboot at the windows logon
screen. This is related to a bug in the function RaUnitFlushQueueSrb
. In the last call to RaidSrbFromIrp
the driver should
use FlushIrp
, but uses Irp
instead. This can throw an exception if Irp
is NULL. To fix the bug we changed the
function RaUnitFlushQueueSrb
.
- edit the file C:\storport\storage\port\raid\port\unit.c and change the function
RaUnitFlushQueueSrb
to
NTSTATUS
RaUnitFlushQueueSrb(
IN PRAID_UNIT_EXTENSION Unit,
IN PIRP FlushIrp
)
{
PIRP Irp;
PSCSI_REQUEST_BLOCK Srb;
LIST_ENTRY listHead;
PIRP nextIrp = NULL;
PLIST_ENTRY nextEntry;
ASSERT (RaidIsUnitQueueFrozen (Unit));
InitializeListHead (&listHead);
for (Irp = RaidRemoveIoQueue (&Unit->IoQueue);
Irp != NULL;
Irp = RaidRemoveIoQueue (&Unit->IoQueue)) {
InsertTailList (&listHead,&Irp->Tail.Overlay.ListEntry);
}
RaidThawUnitQueue (Unit);
while (!IsListEmpty (&listHead))
{
nextEntry = RemoveHeadList (&listHead);
nextIrp = CONTAINING_RECORD (nextEntry, IRP, Tail.Overlay.ListEntry);
Srb = RaidSrbFromIrp (nextIrp);
Srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED;
nextIrp->IoStatus.Information = 0;
RaidCompleteRequest (nextIrp, STATUS_UNSUCCESSFUL);
nextIrp = NULL;
}
Srb = RaidSrbFromIrp (FlushIrp);
Srb->SrbStatus = SRB_STATUS_SUCCESS;
FlushIrp->IoStatus.Information = 0;
return RaidCompleteRequest (FlushIrp, STATUS_SUCCESS);
}
5.) Make storport from Windows 2003 x86 no SP leaked source code work with StorAhci driver
---------------------------------------------------------------------------------------------------
The reason why StorAhci does not work out of the box with the retail Windows Server 2003 x86 no SP is that there are 5 notification types missing
inside storport.sys driver. These notification types are needed to make StorAhci work. Inside the file storage\port\raid\port\port.c we have
to add the following notification types to the function StorPortNotification
:
- EnablePassiveInitialization 0x1000
→ Enable the passive AHCI adapter initialization.
- InitializeDpc 0x1001
→ Initialize the deferred procedure call object. This DPC is used inside StorAhci to complete a request for a HDD drive.
- IssueDpc 0x1002
→ Call the DPC which was initialized before.
- AcquireSpinLock 0x1003
→ Acquire the spin lock associated with the raid adapter interrupt object.
- ReleaseSpinLock 0x1004
→ Release the spin lock of the raid adapter.
In addition we have to add the HwPassiveInitRoutine
structure member to the _RAID_ADAPTER_EXTENSION
in adapter.h
and change the function RaidAdapterStartMiniport
in adapter.c to call our new passive initialization routine. All the necessary
steps are detailed in the next topic.
6.) How to compile our changed storport driver with SMART support based on leaked sources
--------------------------------------------------------------------------------------------------
In the following we do not explain how to get the leaked source code archive nt5src.7z due to legal reasons. You have to do the
following steps to build the storport driver with SMART support based on it:
- unpack the following directories from leaked source code archive nt5src.7z to C:\storport_orig:
- nt5src\Source\Win2K3\NT\drivers\inc
- nt5src\Source\Win2K3\NT\drivers\storage
- right click C:\storport_orig in Windows Explorer > Properties > uncheck "Read-only" > Apply > Apply changes to this folder, subfolders
and files > 2 x OK
- copy C:\storport_orig to C:\storport
- delete the following files:
- C:\storport\inc\volmgr.h
- C:\storport\inc\volmgrx.h
- delete all directories in C:\storport\storage except the port directory
- delete the following directories:
- C:\storport\storage\port\diskdump
- C:\storport\storage\port\scsi
- C:\storport\storage\port\lib\ia64
- C:\storport\storage\port\raid\ext
- C:\storport\storage\port\raid\test
- copy C:\storport\storage\port\raid\port\raidport.src to C:\storport\storage\port\raid\port\raidport.def
- edit the file C:\storport\storage\port\raid\port\raidport.def and apply the following changes:
- remove the comment section at the start of the file
- remove the two comments at the end of the file
- remove the
_AMD64_
definition
- edit the file C:\storport\storage\dirs and remove all listed directories except port
- edit the file C:\storport\storage\port\dirs and remove scsi and diskdump
- edit the file C:\storport\storage\port\raid\dirs and remove miniport
- edit the file C:\storport\storage\port\lib\precomp.h and correct the include path for portlib.h to
#include <..\..\..\inc\portlib.h>
- edit the file C:\storport\storage\port\raid\port\precomp.h and correct the include path for portlib.h to
#include "..\..\..\..\..\inc\portlib.h"
- edit the file C:\storport\storage\port\raid\port\sources and change LINKLIBS and DLLDEF to
LINKLIBS=$(DDK_LIB_PATH)\hal.lib \
$(DDK_LIB_PATH)\ntoskrnl.lib \
..\storlib\$(O)\storlib.lib \
..\..\lib\$(O)\portlib.lib
DLLDEF=raidport.def
- edit the file C:\storport\storage\port\raid\storlib\debug.c and comment out
vDbgPrintExWithPrefix
and
DbgPrompt
- edit the file C:\storport\storage\port\raid\port\adapter.h and add 3 bit fields to the structure
_RAID_ADAPTER_EXTENSION
by
changing it to
BOOLEAN WmiInitialized : 1;
BOOLEAN BusInterfaceInternal : 1;
BOOLEAN InHwInitialize : 1;
BOOLEAN EnablePPnpPowerSrb : 1;
- add a function pointer for
HW_PASSIVE_INITIALIZE_ROUTINE
at the end of the structure by changing it to
RAID_ADAPTER_PARAMETERS Parameters;
PHW_PASSIVE_INITIALIZE_ROUTINE HwPassiveInitRoutine;
} RAID_ADAPTER_EXTENSION, *PRAID_ADAPTER_EXTENSION;
- edit the file C:\storport\storage\port\raid\port\adapter.h and add the function declaration for
RaidGetSrbIoctlFromIrp
and
RaidAdapterScsiMiniportIoctlWithAddress
NTSTATUS
RaidAdapterScsiMiniportIoctl(
IN PRAID_ADAPTER_EXTENSION Adapter,
IN PIRP Irp
);
NTSTATUS
RaidGetSrbIoctlFromIrp(
IN PIRP Irp,
OUT PSRB_IO_CONTROL* SrbIoctlBuffer,
OUT ULONG* InputLength,
OUT ULONG* OutputLength
);
NTSTATUS
RaidAdapterScsiMiniportIoctlWithAddress(
IN PRAID_ADAPTER_EXTENSION Adapter,
IN PIRP Irp,
UCHAR PathId,
UCHAR TargetId,
UCHAR Lun
);
- edit the file C:\storport\storage\port\raid\port\adapter.c in the function
RaidAdapterStartMiniport
add the variable
BOOLEAN Succ
, add a few additional checks and add a function call to HW_PASSIVE_INITIALIZE_ROUTINE
. At the end of the
function RaidAdapterStartMiniport
change the code to
RaidAdapterAcquireHwInitializeLock (Adapter, &LockContext);
Adapter->Flags.InHwInitialize = TRUE;
RaidAdapterEnableInterrupts (Adapter);
Status = RaCallMiniportHwInitialize (&Adapter->Miniport);
Adapter->Flags.InHwInitialize = FALSE;
if (NT_SUCCESS (Status)) {
Adapter->Flags.InitializedMiniport = TRUE;
}
RaidAdapterReleaseHwInitializeLock (Adapter, &LockContext);
if(NT_SUCCESS (Status) && Adapter->HwPassiveInitRoutine != NULL)
{
Succ = Adapter->HwPassiveInitRoutine (&Adapter->Miniport.PrivateDeviceExt->HwDeviceExtension);
Status = RaidNtStatusFromBoolean (Succ);
}
return Status;
}
- edit the file C:\storport\storage\port\raid\port\adapter.c and add the function
RaidAdapterScsiMiniportIoctlWithAddress
after the function RaidAdapterScsiMiniportIoctl
NTSTATUS
RaidAdapterScsiMiniportIoctlWithAddress(
IN PRAID_ADAPTER_EXTENSION Adapter,
IN PIRP Irp,
UCHAR PathId,
UCHAR TargetId,
UCHAR Lun
)
{
NTSTATUS Status;
PSCSI_REQUEST_BLOCK Srb;
PEXTENDED_REQUEST_BLOCK Xrb;
RAID_MEMORY_REGION SrbExtensionRegion;
PSRB_IO_CONTROL SrbIoctl;
ULONG InputLength;
ULONG OutputLength;
ASSERT_ADAPTER (Adapter);
ASSERT (Irp != NULL);
PAGED_CODE();
Srb = NULL;
Xrb = NULL;
RaidCreateRegion (&SrbExtensionRegion);
Status = RaidGetSrbIoctlFromIrp (Irp, &SrbIoctl, &InputLength, &OutputLength);
if (!NT_SUCCESS (Status)) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
goto done;
}
Srb = RaidAllocateSrb (Adapter->DeviceObject);
if (Srb == NULL) {
Status = STATUS_NO_MEMORY;
goto done;
}
Xrb = RaidAllocateXrb (NULL, Adapter->DeviceObject);
if (Xrb == NULL) {
Status = STATUS_NO_MEMORY;
goto done;
}
RaidBuildMdlForXrb (Xrb, SrbIoctl, InputLength);
Srb->OriginalRequest = Xrb;
Srb->Length = sizeof (SCSI_REQUEST_BLOCK);
Srb->Function = SRB_FUNCTION_IO_CONTROL;
Srb->PathId = PathId;
Srb->TargetId = TargetId;
Srb->Lun = Lun;
Srb->SrbFlags = SRB_FLAGS_DATA_IN ;
Srb->DataBuffer = SrbIoctl;
Srb->DataTransferLength = InputLength;
Srb->TimeOutValue = SrbIoctl->Timeout;
Xrb->Srb = Srb;
Xrb->SrbData.OriginalRequest = Srb->OriginalRequest;
Xrb->SrbData.DataBuffer = Srb->DataBuffer;
Status = RaidDmaAllocateCommonBuffer (&Adapter->Dma,
RaGetSrbExtensionSize (Adapter),
&SrbExtensionRegion);
if (!NT_SUCCESS (Status)) {
goto done;
}
Srb->SrbExtension = RaidRegionGetVirtualBase (&SrbExtensionRegion);
RaidAdapterMapBuffers (Adapter, Irp);
KeInitializeEvent (&Xrb->u.CompletionEvent,
NotificationEvent,
FALSE);
RaidXrbSetCompletionRoutine (Xrb,
RaidXrbSignalCompletion);
Status = RaidAdapterRaiseIrqlAndExecuteXrb (Adapter, Xrb);
if (NT_SUCCESS (Status)) {
KeWaitForSingleObject (&Xrb->u.CompletionEvent,
Executive,
KernelMode,
FALSE,
NULL);
Status = RaidSrbStatusToNtStatus (Srb->SrbStatus);
}
done:
if (NT_SUCCESS (Status)) {
Irp->IoStatus.Information = min (OutputLength,
Srb->DataTransferLength);
} else {
Irp->IoStatus.Information = 0;
}
if (RaidIsRegionInitialized (&SrbExtensionRegion)) {
RaidDmaFreeCommonBuffer (&Adapter->Dma,
&SrbExtensionRegion);
RaidDeleteRegion (&SrbExtensionRegion);
Srb->SrbExtension = NULL;
}
if (Xrb != NULL) {
RaidFreeXrb (Xrb, FALSE);
Srb->OriginalRequest = NULL;
}
if (Srb != NULL) {
RaidFreeSrb (Srb);
Srb = NULL;
}
return RaidCompleteRequest (Irp,
Status);
}
- edit the file C:\storport\storage\port\raid\port\port.c and comment out the function definition for
vDbgPrintExWithPrefix
- edit the file C:\storport\storage\port\raid\port\port.c and add new notifications to the function
StorPortNotification
by changing the code to
KIRQL RaidAdapterAcquireInterruptLock(RAID_ADAPTER_EXTENSION *Adapter)
{
KIRQL irql;
irql = PASSIVE_LEVEL;
if(Adapter->Interrupt)
{
irql = KeAcquireInterruptSpinLock(Adapter->Interrupt);
}
return irql;
}
VOID RaidAdapterReleaseInterruptLock(RAID_ADAPTER_EXTENSION *Adapter,KIRQL OldIrq)
{
if(Adapter->Interrupt)
{
KeReleaseInterruptSpinLock(Adapter->Interrupt,OldIrq);
}
}
VOID StorAcquireSpinLock(PVOID HwDeviceExtension,STOR_SPINLOCK SpinLock,PVOID LockContext,
PSTOR_LOCK_HANDLE LockHandle)
{
PRAID_ADAPTER_EXTENSION Adapter;
Adapter = RaidpPortGetAdapter (HwDeviceExtension);
LockHandle->Lock = SpinLock;
switch(LockHandle->Lock)
{
case InterruptLock:
{
LockHandle->Context.OldIrql = RaidAdapterAcquireInterruptLock(Adapter);
break;
}
case StartIoLock:
{
RaidAdapterAcquireStartIoLock(Adapter,(PLOCK_CONTEXT)&LockHandle->Context);
break;
}
case DpcLock:
{
KeAcquireInStackQueuedSpinLock((PKSPIN_LOCK)&((PSTOR_DPC)LockContext)->Lock,
(PKLOCK_QUEUE_HANDLE)&LockHandle->Context);
break;
}
}
}
VOID StorReleaseSpinLock(PVOID HwDeviceExtension,PSTOR_LOCK_HANDLE LockHandle)
{
PRAID_ADAPTER_EXTENSION Adapter;
Adapter = RaidpPortGetAdapter (HwDeviceExtension);
switch(LockHandle->Lock)
{
case InterruptLock:
{
RaidAdapterReleaseInterruptLock(Adapter,LockHandle->Context.OldIrql);
break;
}
case StartIoLock:
{
RaidAdapterReleaseStartIoLock(Adapter,(PLOCK_CONTEXT)&LockHandle->Context);
break;
}
case DpcLock:
{
KeReleaseInStackQueuedSpinLock((PKLOCK_QUEUE_HANDLE)&LockHandle->Context);
break;
}
}
}
VOID StorInitializeDpc(PVOID HwDeviceExtension,PSTOR_DPC Dpc,PHW_DPC_ROUTINE HwDpcRoutine)
{
KeInitializeDpc((PRKDPC)Dpc, (PKDEFERRED_ROUTINE) HwDpcRoutine, HwDeviceExtension);
Dpc->Lock = 0;
}
BOOLEAN StorIssueDpc(PVOID HwDeviceExtension,PSTOR_DPC Dpc,PVOID SystemArgument1,
PVOID SystemArgument2)
{
return KeInsertQueueDpc((PRKDPC)Dpc,SystemArgument1,SystemArgument2);
}
#define TRACE_MESSAGE_SEQUENCE 1 // Message should include a sequence number
#define TRACE_MESSAGE_GUID 2 // Message includes a GUID
#define TRACE_MESSAGE_TIMESTAMP 8 // Message includes a timestamp
#define TRACE_MESSAGE_SYSTEMINFO 32 // Message includes system information TID,PID
__drv_maxIRQL(HIGH_LEVEL) NTKERNELAPI NTSTATUS WmiTraceMessage(__in TRACEHANDLE LoggerHandle,
__in ULONG MessageFlags,__in LPCGUID MessageGuid,__in USHORT MessageNumber,...);
NTSTATUS WPP_SF_(TRACEHANDLE LoggerHandle,USHORT MessageNumber,LPCGUID MessageGuid)
{
return WmiTraceMessage(LoggerHandle,TRACE_MESSAGE_SEQUENCE | TRACE_MESSAGE_GUID |
TRACE_MESSAGE_TIMESTAMP | TRACE_MESSAGE_SYSTEMINFO,MessageGuid,
MessageNumber,NULL);
}
PULONG _WPP_GLOBAL_Control = NULL;
TRACEHANDLE unk_20008 = 0x7CF4010094F40100;
GUID aGnkh = {0x68AAAD67,0x0700,0x6A21,0x5D,0x1A,0x7E,0xC9,0xB8,0xA2,0x38,0x9D};
BOOLEAN StorEnablePassiveInitialization(PVOID HwDeviceExtension,
PHW_PASSIVE_INITIALIZE_ROUTINE HwPassiveInitializeRoutine)
{
PRAID_ADAPTER_EXTENSION Adapter;
Adapter = RaidpPortGetAdapter (HwDeviceExtension);
if(Adapter->Flags.InHwInitialize == FALSE)
{
if(_WPP_GLOBAL_Control != NULL)
{
WPP_SF_(unk_20008,0x0B,&aGnkh);
}
return FALSE;
}
Adapter->HwPassiveInitRoutine = HwPassiveInitializeRoutine;
return TRUE;
}
VOID
StorPortNotification(
IN SCSI_NOTIFICATION_TYPE NotificationType,
IN PVOID HwDeviceExtension,
...
)
{
PRAID_ADAPTER_EXTENSION Adapter;
PSCSI_REQUEST_BLOCK Srb;
PHW_INTERRUPT HwTimerRoutine;
ULONG Timeout;
PHW_PASSIVE_INITIALIZE_ROUTINE HwPassiveInitializeRoutine;
PSTOR_DPC Dpc;
PHW_DPC_ROUTINE HwDpcRoutine;
PVOID SystemArgument1;
PVOID SystemArgument2;
PLONG Succ;
STOR_SPINLOCK SpinLock;
PVOID LockContext;
PSTOR_LOCK_HANDLE LockHandle;
va_list ap;
Adapter = RaidpPortGetAdapter (HwDeviceExtension);
va_start(ap, HwDeviceExtension);
switch (NotificationType) {
case EnablePassiveInitialization:
HwPassiveInitializeRoutine = va_arg (ap, PHW_PASSIVE_INITIALIZE_ROUTINE);
Succ = va_arg (ap, PLONG);
*Succ = StorEnablePassiveInitialization(HwDeviceExtension,
HwPassiveInitializeRoutine);
break;
case InitializeDpc:
Dpc = va_arg (ap, PSTOR_DPC);
HwDpcRoutine = va_arg (ap, PHW_DPC_ROUTINE);
StorInitializeDpc(HwDeviceExtension, Dpc, HwDpcRoutine);
break;
case IssueDpc:
Dpc = va_arg (ap, PSTOR_DPC);
SystemArgument1 = va_arg (ap, PVOID);
SystemArgument2 = va_arg (ap, PVOID);
Succ = va_arg (ap, PLONG);
*Succ = StorIssueDpc(HwDeviceExtension, Dpc, SystemArgument1, SystemArgument2);
break;
case AcquireSpinLock:
SpinLock = va_arg (ap, STOR_SPINLOCK);
LockContext = va_arg (ap, PVOID);
LockHandle = va_arg (ap, PSTOR_LOCK_HANDLE);
StorAcquireSpinLock(HwDeviceExtension, SpinLock, LockContext, LockHandle);
break;
case ReleaseSpinLock:
LockHandle = va_arg (ap, PSTOR_LOCK_HANDLE);
StorReleaseSpinLock(HwDeviceExtension, LockHandle);
break;
case RequestComplete:
Srb = va_arg (ap, PSCSI_REQUEST_BLOCK);
RaidAdapterRequestComplete (Adapter, RaidGetAssociatedXrb (Srb));
break;
- edit the file C:\storport\storage\port\raid\port\unit.c and add function
RaUnitScsiMiniportIoctl
to ALLOC_PRAGMA
#pragma alloc_text(PAGE, RaUnitStorageQueryPropertyIoctl)
#pragma alloc_text(PAGE, RaUnitScsiMiniportIoctl)
- edit the file C:\storport\storage\port\raid\port\unit.c and add function call
RaUnitScsiMiniportIoctl
to function
RaUnitDeviceControlIrp
case IOCTL_STORAGE_QUERY_PROPERTY:
Status = RaUnitStorageQueryPropertyIoctl (Unit, Irp);
break;
case IOCTL_SCSI_MINIPORT:
Status = RaUnitScsiMiniportIoctl (Unit, Irp);
break;
- edit the file C:\storport\storage\port\raid\port\unit.c and add function
RaUnitScsiMiniportIoctl
after the function
RaUnitStorageQueryPropertyIoctl
NTSTATUS RaUnitScsiMiniportIoctl(IN PRAID_UNIT_EXTENSION Unit,IN PIRP Irp)
{
NTSTATUS Status;
PSRB_IO_CONTROL SrbIoctl;
ULONG InputLength;
ULONG OutputLength;
PAGED_CODE();
ASSERT_UNIT(Unit);
Status = RaidGetSrbIoctlFromIrp(Irp,&SrbIoctl,&InputLength,&OutputLength);
if(!NT_SUCCESS(Status))
{
return RaidCompleteRequest(Irp,STATUS_INVALID_PARAMETER);
}
if(Irp->RequestorMode != KernelMode)
{
return RaidCompleteRequest(Irp,STATUS_ACCESS_DENIED);
}
return RaidAdapterScsiMiniportIoctlWithAddress(Unit->Adapter,Irp,Unit->Address.PathId,
Unit->Address.TargetId,Unit->Address.Lun);
}
- edit the file C:\storport\storage\port\raid\port\power.c and change the function
RaidUnitSetPowerIrp
so that it does not
trigger the if
in case of PowerSystemShutdown
if (PowerState.SystemState > PowerSystemShutdown) {
- edit the file C:\storport\storage\port\raid\port\power.c and change the function
RaidUnitSetSystemPowerIrp
to
NTSTATUS
RaidUnitSetSystemPowerIrp(
IN PRAID_UNIT_EXTENSION Unit,
IN PIRP Irp
)
{
NTSTATUS Status;
POWER_STATE DevicePowerState;
POWER_STATE SystemPowerState;
DebugPower (("Unit %p, Irp %p, Set System Power, ret SUCCESS\n",
Unit, Irp));
SystemPowerState = RaidPowerStateFromIrp (Irp);
DevicePowerState.DeviceState = DeviceStateTable [SystemPowerState.SystemState];
Status = PoRequestPowerIrp (Unit->DeviceObject, IRP_MN_SET_POWER, DevicePowerState, NULL, Irp, NULL);
if (Status == STATUS_PENDING)
{
Status = STATUS_SUCCESS;
}
PoStartNextPowerIrp (Irp);
return RaidCompleteRequest (Irp, Status);
}
- edit the file C:\storport\storage\port\raid\port\unit.c and change the function
RaUnitFlushQueueSrb
to
NTSTATUS
RaUnitFlushQueueSrb(
IN PRAID_UNIT_EXTENSION Unit,
IN PIRP FlushIrp
)
{
PIRP Irp;
PSCSI_REQUEST_BLOCK Srb;
LIST_ENTRY listHead;
PIRP nextIrp = NULL;
PLIST_ENTRY nextEntry;
ASSERT (RaidIsUnitQueueFrozen (Unit));
InitializeListHead (&listHead);
for (Irp = RaidRemoveIoQueue (&Unit->IoQueue);
Irp != NULL;
Irp = RaidRemoveIoQueue (&Unit->IoQueue)) {
InsertTailList (&listHead,&Irp->Tail.Overlay.ListEntry);
}
RaidThawUnitQueue (Unit);
while (!IsListEmpty (&listHead))
{
nextEntry = RemoveHeadList (&listHead);
nextIrp = CONTAINING_RECORD (nextEntry, IRP, Tail.Overlay.ListEntry);
Srb = RaidSrbFromIrp (nextIrp);
Srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED;
nextIrp->IoStatus.Information = 0;
RaidCompleteRequest (nextIrp, STATUS_UNSUCCESSFUL);
nextIrp = NULL;
}
Srb = RaidSrbFromIrp (FlushIrp);
Srb->SrbStatus = SRB_STATUS_SUCCESS;
FlushIrp->IoStatus.Information = 0;
return RaidCompleteRequest (FlushIrp, STATUS_SUCCESS);
}
- Start > Programs > Windows Driver Kits > WDK 7600.16385.1 > Build Environments > Windows Server 2003 > x86 Free Build Environment
- type the following commands in the WDK build environment:
cd C:\storport\storage
build -cewgZ
- the final storport.sys driver is located in C:\storport\storage\port\raid\port\objfre_wnet_x86\i386
7.) Patch original Windows 7 SP1 x86 storport to make it work with Windows 2003 Server
----------------------------------------------------------------------------------------------
We patch the original Windows 7 SP1 x86 storport.sys Version 6.1.7601.17514 to function properly with Windows 2003 Server. Therefore we
have to do the following steps:
- download Dependency Walker v2.2
- unpack depends22_x86.zip to C:\depends22_x86
- run C:\depends22_x86\depends.exe > Menu > File > Open... > choose Windows 7 SP1 x86 storport.sys > Open > we see a message that
errors were detected > OK
- on the upper left window click on NTOSKRNL.EXE > in the upper right window sort the column Function and after that the column
PI > this way all red marked imports are on top sorted by name
- we have the following missing imports in NTOSKRNL.EXE:
- replace the missing imports like NTOSKRNL_Emu does it and compile ntoskrn7.sys
- patch the following bytes in storport.sys of Windows 7 SP1 x86:
Offset Size in Bytes Comment
-----------------------------------------------------------------------------------------------------------------
0x120 2 MajorSubsystemVersion and MinorSubsystemVersion changed from 6.1 to 5.1
0x130 4 checksum of executable corrected
0x170 8 Directory Security (Certificate Table) RVA and size set to zero, because the
certificate was removed
0xFE79 2 disable compare of SRB_IO_CONTROL Signature to string "HYBRDISK" to enable SMART
commands for CrystalDiskInfo
0xFE85 2 disable compare of SRB_IO_CONTROL ControlCode to IOCTL_SCSI_MINIPORT_NVCACHE to
enable SMART commands for CrystalDiskInfo
0x1CE9B 5 imports section ntoskrnl.exe replaced by ntoskrn7.sys
0x1CE05 0 the security cookie check is not patched for a Windows 7 driver
0x22A00 0x1B80 only present in original driver file, code signing certificate removed in patched
driver file
The driver ntoskrn7.sys is compiled with WDK version 7600.16385.1 and the Windows Server 2003 x86 Free Build Environment. The example source
code is included in the file storport_for_Windows_2003_with_SMART_support.rar. This archive also holds the prepatched storport.sys
of Windows 7 SP1 x86.
8.) How to apply the storport patches
-----------------------------------------
To apply the Windows 2003 Server storport patch based on the leaked source code do the following:
- rename C:\WINDOWS\system32\dllcache\storport.sys to storport.sys_
- rename C:\WINDOWS\system32\drivers\storport.sys to storport.sys_
- we will get a Windows File Protection warning dialog where we press Cancel and Yes
- copy the patched storport_for_Windows_2003_with_SMART_support\storport\bin\storport.sys to C:\WINDOWS\system32\drivers
- we will get a Windows File Protection warning dialog where we press Cancel and Yes
- reboot the computer
We tested the SSD read/write performance afterwards with CrystalDiskMark 6.0.1.
To apply the Windows 7 storport and ntoskrn7 patch do the following:
- rename C:\WINDOWS\system32\dllcache\storport.sys to storport.sys_
- rename C:\WINDOWS\system32\drivers\storport.sys to storport.sys_
- we will get a Windows File Protection warning dialog where we press Cancel and Yes
- copy the patched storport_for_Windows_2003_with_SMART_support\ntoskrnl7\bin\storport.sys to C:\WINDOWS\system32\drivers
- we will get a Windows File Protection warning dialog where we press Cancel and Yes
- copy storport_for_Windows_2003_with_SMART_support\ntoskrnl7\bin\ntoskrn7.sys to C:\WINDOWS\system32\drivers
- reboot the computer
Here we also tested the SSD read/write performance afterwards with CrystalDiskMark 6.0.1.
Attention: These drivers have not been tested extensively and should not be use in a production environment!
These patches were done for educational and proof of concept purposes only.
9.) Credits and Download Links
----------------------------------
A BIG THANKS goes out to the following geniuses:
Much appreciated!
The complete storport package with all necessary files can be downloaded from Sourceforge.
Thanks for your attention and interest in this topic.
Greets Kai Schtrom
Version 1.1 May 05, 2021