The SCSI miniport driver allows the system to abstract from the details of the physical interfaces for specific equipment. In the interest of brevity, let us simply call it the minidriver, although this is actually only partially true. After all, along with the minidrivers for SCSI ports, there are also drivers for video and network miniports. However, since neither of these relates to the context of our discussion in any way, there won t be any misunderstanding.
Hierarchically, the miniport driver resides between the physical (virtual) devices connected to specific interface buses of the computer (IDE/PCI/SCSI) and the SCSI port driver. The miniport driver is a system-independent driver, which, at the same time, depends on specific features of the HBA (Host Bus Adapter), i.e., the physical/ virtual equipment that it serves. The miniport driver exports a range of functions of the ScsiPortXXX family, which are intended for use by higher-level drivers. Usually, it is implemented as a Dynamic Link Library (DLL), which, quite naturally, executes in ring 0 of the kernel level.
It is this driver that translates SCSI requests into commands for the device connected to it, creates virtual SCSI ports with names such as \Device\ScsiPortx , and ensures support for storage media having interfaces different from SCSI. For example, drivers such as ATAPI.SYS, serving CD-ROM drives with the ATAPI interface, and DISK.SYS, serving hard disks, are implemented in the form of miniport drivers.
Control of the miniport is carried out using special IOCTL code passed to the DeviceIoControl function and defined as IOCTL_SCSI_MINIPORT in the NTDDSCSI.H file. If you don t have a copy of NT DDK, here is its direct value: 0x40008 . Naturally, before calling on the DeviceIoControl function, you must first open an appropriate SCSI port using the CreateFile function. The code that carries out this task might appear as shown in the listing below. Pay special attention to the fact that the port name must appear as SCSIx: , rather than ScsiPortx ; the name must be terminated with a colon . Otherwise, the attempt will fail.
h = CreateFile ("\\. \SCSI1: ", GENERIC_READ GENERIC_WRITE, FILE_SHARE_READ FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
Here, you open the first (numbered from zero) SCSI port that, as you already know, corresponds to the first IDE channel, or, to put it in other words, to the secondary IDE controller (on the author s computer, the CD-ROM drive is connected to that particular controller). To detect the drive location on an unknown computer, you can use the IOCTL_SCSI_GET_INQUIRY_DATA IOCTL code, which makes the miniport driver list all the equipment at its disposal, after which it only remains to correctly determine its type (for more details, see NTDDK\SRC\STORAGE\CLASS\SPTI).
However, miniport control is carried out in a different way from that used to control the SCSI port! At this level, there are no standard commands, and you have to take into account the specific features of the implementation of specific hardware. Instead of SRB requests, the minidriver accepts the SRB_IO_CONTROL structure defined as follows :
typedef struct _SRB_IO_CONTROL { ULONG HeaderLength; // sizeof (SRB_IO_CONTROL) UCHAR Signature [8]; // Minidriver signature ULONG Timeout; // Max. waiting time for the request // to be completed (in seconds) ULONG ControlCode; // Command code ULONG ReturnCode; // Here we will get the return code. ULONG Length; // The length of entire transmitted buffer } SRB_IO_CONTROL, *PSRB_IO_CONTROL;
Well, you understand the meaning of the HeaderLength field, but what is the signature? The point is that the controlling codes of miniport drivers aren t standardized. On the contrary, they are defined by the drivers developers. Therefore, the control codes of one driver are unlikely to fit those of another. In order to avoid conflicts, each miniport driver contains a unique signature, which it compares carefully to the one passed on by an application in the signature field of the SRB_IO_CONTROL structure. If these signatures do not match, the driver responds with the following message: SRB_STATUS_INVALID_REQUEST . Unfortunately, the interfaces of the standard minidrivers ”ATAPI.SYS and DISK.SYS ”are not documented. As a result, it s a difficulty for those who are unable to disassemble. With regard to the disassembler, it immediately shows that the signatures of both drivers appear as SCSIDISK. The signature of the Alcohol 120% minidriver appears as Alcoholx (the latter doesn t present any special interest to us because it doesn t correspond to standards).
It is somewhat more difficult to understand program codes. Although the specialists constantly reading MSDN and therefore, having a sound knowledge in it, might recall that this specification describes the API for an application to issue SMART commands to an IDE drive under Microsoft Windows 95 and Windows NT. Under Windows 95, the API is implemented in a Vendor Specific Driver (VSD), Smartvsd.vxd. SMART functionality is implemented as a ˜pass-through mechanism, whereby the application sets up the IDE registers in a structure and passes them to the driver through the DeviceIoControl API.
Well, one of the drivers facilitates the manipulation of the registers of the IDE controller as needed, which means that it provides low-level access to the disk ” very well. The interface with the SMART driver is well documented (see MSDN ’ Specifications a Platforms ’ SMART IOCTL API Specification ). But the silence in relation to Windows NT appears to be somewhat irritating . Clearly, there are no VxDs in Windows NT. However, it is clearly stated in this document that SMART API is implemented there. If you use a few gray cells and a little bit of intuition work, you might be able to guess that SMART support in NT is through standard means! The only question that remains is as follows: How and by what means? Neither SDK nor DDK contain any information on this topic. However, careful study of the header files included with NT DDK can help. Look at what you can find in the scsi.h file.
SRB_IO_CONTROL structure // // SMART support in atapi // #define IOCTL_SCSI_MINIPORT_SMART_VERSION ((FILE_DEVICE_SCSI<<16) + 0x0500) #define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI<<16) + 0x0501) #define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS ((FILE_DEVICE_SCSI<<16) + 0x0502) #define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS ((FILE_DEVICE_SCSI<<16) + 0x0503) #define IOCTL SCSI MINIPORT ENABLE SMART ((FILE DEVICE SCSI<<16) + 0x0504) #define IOCTL_SCSI_MINIPORT_DISABLE_SMART ((FILE_DEVICE_SCSI<<16) + 0x0505) #define IOCTL_SCSI_MINIPORT_RETURN_STATUS ((FILE_DEVICE_SCSI<<16) + 0x0506) #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE ((FILE_DEVICE_SCSI<<16) + 0x0507) #define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES ((FILE_DEVICE_SCSI<<16) + 0x0508) #define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS ((FILE_DEVICE_SCSI<<16) + 0x0509) #define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE (FILE_DEVICE_SCSI<<16)+0x050a
You might suspect that, in Windows, NT SMART isn t implemented in the miniport driver, and disassembling of ATAPI.SYS actually confirms this. So why include IOCTL commands in the header file without documenting them? would be a fair question for Microsoft s technical writers. And, according to the license agreement, disassembling any OS components is prohibited . Instead of complaining, let s read the SMART IOCTL API Specification once again. In this document, you discover that in order to control the miniport driver under Windows NT, it is necessary to pass the code of one of the above-listed commands to the ControlCode field of the SRB_IO_CONTROL structure. For example, IOCTL_SCSI_MINIPORT_IDENTIFY .
Immediately following the end of the SRB_IO_CONTROL structure, there must be SENDCMDINPARAMS , defined as shown in Listing 4.20.
typedef struct _SENDCMDINPARAMS { DWORD cBufferSize; // Buffer size in bytes or zero IDEREGS irDriveRegs; // The structure containing // the values of IDE registers BYTE bDriveNumber; // Physical disk number, // starting from zero BYTE bReserved[3]; // Reserved DWORD dwReserved[4]; // Reserved BYTE bBuffer[1]; // The starting point of the input buffer } SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;
This means that the input buffer of the DeviceIoControl function must look as follows:
The first structural element, cBufferSize , containing the bBufferSize , is obvious and, therefore, of little interest. As for the IDREGS structure, it represents a virtual goldmine of information. Just look for yourself:
typedef struct _IDEREGS { BYTE bFeaturesReg; // IDE Features register BYTE bSectorCountReg; // IDE SectorCount register BYTE bSectorNumberReg; // IDE SectorNumber register BYTE bCylLowReg; // IDE CylLowReg register BYTE bCylHighReg; // IDE CylHighReg register BYTE bDriveHeadReg; // IDE DriveHead register BYTE bCommandReg; // Command register BYTE bReserved; // Reserved } IDEREGS, *PIDEREGS, *LPIDEREGS;
Anyone who has ever read the ATA/ATAPI specification and programmed devices with the IDE interface should immediately recognize the well-known registers ” Command, Drive/Head, Cylinder High, Cylinder Low, Sector Number, Sector Count , and Features . That they are listed in reverse order in the IDEREGS structure is only a minor implementation detail. The main fact is that using this structure, it is possible to do whatever you like with the drive, implementing all of the tricks of which it is capable. It is hard to believe that the security subsystem contains such a security loophole. This is aggravated further by the fact that administrative privileges are not required for controlling the miniport. Jumping with joy, let s fill in the remaining fields of the SENDCMDINPARAMS structure, namely: bDriveNumber ”the physical number of the drive, numbering from zero, and the buffer for passing the data.
Important | This is a buffer itself, rather than a pointer to a buffer. However, for the moment, you are not going to write any data to the disc, are we? Well, then let s leave this field blank. |
Alas! An attempt to feed the drive with a command other than those from the SMART family, will fail. After all, the miniport driver isn t as stupid as you supposed. It checks the contents of the IDEREGS structure before passing it to the IDE drive. The only exception has been made for the drive identification command ” 0xEC , about which Microsoft has openly informed us: There are three IDE commands supported in this driver, ID (0xEC), ATAPI ID (0xA1) , and SMART (0xB0) . The ˜subcommands of the SMART commands (featuring register values) are limited to the currently defined values ( 0xDO through 0xD6, 0xD8 through 0xEF ). SMART subcommand 0XD7 , write threshold value, is not allowed. Any other command or SMART subcommand will result in an error being returned from the driver. Any SMART command that is not currently implemented on the target drive will result in an ABORT error from the IDE interface .
At first glance, it seems that you have failed altogether. However, this is not so the case! After all, this check can be disabled. Let us disassemble the ATAPI.SYS driver and see what can be done. The following fragment is responsible for checking IDE commands passed to the drive in order to determine whether they belong to the allowed list.
.text:00013714 aScsidisk db 'SCSIDISK' ,0 ; DATA XREF: SCSI_MINIPORT + CC o ; here is our signature ^^^^^^^^ ; .text:000137DF .text:000137DF loc_137DF: ; CODE XREF: SCSI_MINIPORT+B5 j .text:000137DF mov [edi], ebx .text:000137E1 mov eax, [ebx+18h] .text:000137E4 push 8 ; The length of the string ; to be compared .text:000137E6 add eax, 4 .text:000137E9 push offset aScsidisk ; Pattern signature .text:000137EE push eax ; The signature passed ; by an application .text:000137EF call ds:RtlCompareMemory ; Do signatures match? .text:000137F5 cmp eax, 8 .text:000137F8 jnz oc_13898 ; No match, exiting .text:000137F8 .text:000137FE mov esi, [ebx+18h] .text:00013801 mov eax, [esi+10h] ; Getting Control code .text:00013804 cmp eax, 1B0500h ; IOCTL_SCSI_MINIPORT_SM .text:00013809 jz loc_1389F ; Processing SMART_VERSION .text:0001380F mov ecx, 1B0501h ; IOCTL_SCSI_MINIPORT_IDENTIFY .text:00013814 cmp eax, ecx ; .text:00013816 jz short loc_1382D ; Processing IDENTIFY .text:00013818 jbe short loc_13898 ; IF ControlCode < IDENTIFY THEN go to exit .text:0001381A cmp eax, 1B050Ah ; IOCTL_SCS I_MINI PORT_ENABLE_DISABLE .text:0001381F ja short loc 13898 ; IF ControlCode > ENABLE_DISAB go to exit .text:00013821 push ebx ; .text:00013822 push edi ; .text:00013823 call sub_12412 ; processing other SMART commands .text:00013828 jmp loc_1393E .text:0001382D ; ----------------------------------------------------------- .text:00012412 sub_12412 proc near ; CODE XREF: SCSI_MINIPORT+106 p .text:00012433 cmp [ebp+var 1E] , 0B0h ; SMART-command .text:00012437 jnz loc_12633 ; If this isnt SMART, go to exit .text:00012437 ; Checks start from here .text:0001243D movzx eax, [ebp+var_1C] .text:00012441 mov eax, [ebx+eax*4+0B0h]; Loading Drive/Head register into EAX .text:00012448 test al, 1 ; Comparing the least significant bit ; of AL to one .text:0001244A jz loc_1262F ; If the least significant bit ;is equal to zero, then exit .text:00012450 test al, 2 ; Comparing the next bit of AL to one .text:00012452 jnz loc_1262F ; If it isnt equal to zero, exit .text:00012458 mov al, [ebp+var_24] ; Loading the Feature register to AL .text:0001245B cmp al, 0D0h ; Is this SMART READ DATA? .text:0001245D mov [ebx+0CCh], al .text:00012463 jz loc_12523 ; If yes, start processing .text:00012469 cmp al, 0D1h ; Is it obsolete? .text:0001246B jz loc_12523 ; If yes, start its processesing .text:00012471 cmp al, 0D8h ; Is this SMART ENABLE OPERATIONS? .text:00012473 jz short loc_12491 ; If yes, start its processing .text:00012475 cmp al, 0D9h ; Is this SMART DISABLE OPERATIONS? .text:00012477 jz short loc_12491 ; If yes, start its processing .text:00012479 cmp al, 0DA ; Is this SMART RETURN STATUS? .text:0001247B jz short loc_12491 ; If yes, start its processing .text:0001247D cmp al, 0D2h ; Is this SMART ENBL/DSBL ATTRIBUTE AUTOSAVE? .text:0001247D cmp al, 0D2h ; It this really the case?! .text:0001247F jz short loc_12491 ; If yes, start its processing .text:00012481 cmp al, 0D4h ; Is this SMART EXECUTE OFF-LINE IMMEDIATE? .text:00012483 jz short loc_12491 ; If yes, start its processing .text:00012485 cmp al, 0D3h ; Is this SMART SAVE ATTRIBUTE VALUES? .text:00012487 jz short loc_12491 ; If yes, start its processing .text:00012489 cmp al, 0DBh ; Is this SMART ENABLE OPERATIONS? .text:0001248B jnz loc_12633 ; If no, then exit .text:00012491 .text:00012491 loc_12491: ; CODE XREF: sub_12412+61 j .text:00012491 ; Command processing starts from here .text:00012491 ; .text:00012491 push 1 .text:00012493 pop eax .text:00012494 cmp ds:0FFDF02C0h, eax .text:0001249A jnz short loc_124A5 .text:0001249C cmp dword ptr [ebx+4], 640h .text:000124A3 jz short loc_124A7 .text:000124A5 .text:000124A5 loc_124A5: ; CODE XREF: sub_12412+88 j .text:000124A5 xor eax, eax .text:000124A7 .text:000124A7 loc 124A7: ; CODE XREF: sub 12412+91 j .text:000124A7 ; Writing to the port starts from here! .text:000124A7 ; .text:000124A7 mov esi, ds :WRITE_PORT_UCHAR .text:000124AD test al, al .text:000124AF jz short loc 124C0 .text:000124B1 mov al, [ebp+var_1C] .text:000124B4 shr al, 1 .text:000124B6 and al, 1 .text:000124B8 push eax .text:000124B9 push 432h .text:000124BE call esi ; WRITE_PORT_UCHAR
Thus, in order to allow the driver to send any commands to the IDE drive, you must change the conditional jump located by the address 0x12437 (in the listing, it is highlighted and surrounded by a rectangle) for the unconditional jump passing the control to the write command by the address 0x12491 . After modifying the driver, don t forget to correct its checksum, which can be carried out, for example, using the EDITBIN.EXE utility supplied with Microsoft Visual Studio. Otherwise, Windows NT will refuse to load the hacked driver.
Naturally, I recommend that you carry out such an operation only with your own driver, because others are unlikely to be pleased by the newly-created security hole. Moreover, distribution of the modified version of ATAPI.SYS violates the licensing agreement and Microsoft s copyright. Draw your own conclusions. Nevertheless, your application can patch ATAPI.SYS on your own computer and on the computers of your users (naturally, you must inform them of the things that you are going to do, ask their permission or, at least, mention this aspect in companion documentation).
This method of interacting with the drive mustn t be neglected altogether, since it significantly complicates the cracking of protection mechanisms based on it. After all, not every hacker is well acquainted with specific features related to controlling the miniport. Therefore, with good probability, the vast majority will make fools of themselves .
The example program provided below demonstrates the passing of ATA commands to the IDE drive via the miniport driver.
int ATAPI_MINIPORT_DEMO(void) { int a; HANDLE h; Char*buf ; Int LU=0; DWORD returned; Int controller; CharScsiPort [16]; Charbuffer [sizeof (SRB_IO_CONTROL) + SENDIDLENGTH] ; SRB_IO_CONTROL *p = (SRB_IO_CONTROL *) buffer; SENDCMDINPARAMS *pin = (SENDCMDINPARAMS *) (buffer + sizeof (SRB_IO_CONTROL)) ; // Testing both IDE controllers in a loop for (controller = 0; controller < 2; controller++) { // Forming ScsiPort for each controller sprintf (ScsiPort, "\\.\Scsi%d:", controller); // Opening the required ScsiPort h = CreateFile (ScsiPort, GENERIC_READ GENERIC_WRITE, FILE_SHARE_READ FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) { // EXIT IF ERROR printf("-ERR: Unable to open ScsiPort%d\n", controller); return -1; } // Testing both devices on each of the IDE controllers for (LU = 0; LU < 2; LU++) { // Initializing the input buffer memset (buffer, 0, sizeof (buffer)); // PREPARING THE SRB IO CONTROL STRUCTURE, // intended for the miniport driver p > Timeout = 10000; // Wait p > Length = SENDIDLENGTH; // Max. length p > HeaderLength = sizeof (SRB_IO_CONTROL); // Header size p > ControlCode = IOCTL_SCSI_MINIPORT_IDENTIFY; // ^^^ code of the command sent to the driver // Signature for ATAPI.SYS - "SCSIDISK" strncpy ((char *) p > Signature, "SCSIDISK", 8); // PREPARING THE SENDCMDINPARAMS STRUCTURE, // containing ATA commands passed to the IDE drive pin > bDriveNumber = LU; pin > irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY; // SENDING THE REQUEST TO THE MINIPORT DRIVER if (DeviceIoControl (h, IOCTL_SCSI_MINIPORT, buffer, sizeof (SRB_IO_CONTROL) + sizeof (SENDCMDINPARAMS) - 1, buffer, sizeof (SRB_IO_CONTROL) + SENDIDLENGTH, &returned, 0)) if (buffer[98]!=0) {// In response, get the string with // the identifier of // the IDE- drive, which we display on // the screen. for (a=98; a < 136; a+=2) printf("%c%c", buffer[a+1], buffer[a]); printf("\n"); } } CloseHandle (h); // Close the descriptor of the given SCSI miniport. } return 0; }