Chapter 3: Practical Advice on Urgent System Recovery

Access via the CD-ROM Driver

In operating systems of the Windows family, management of device drivers is carried out by means of calls to the DeviceIoControl function responsible for sending special FSCTL/IOCTL commands. The FS prefix denotes that this command belongs to the file system management group , and is of no interest to us in the context of this book. The commands with the IO prefix relate to the input/output device, or, to be more precise ”to its driver.

The DeviceIoControl function simply passes on these commands as is, without going deeper into its physical sense. Therefore, it doesn t make any sense to look for a list of available IOCTL commands in the description of DeviceIoControl . The description doesn t contain anything like this! It merely provides the list of standard IOCTL commands, while all remaining information related to this topic is provided by Windows DDK.

There you will discover, in particular, that the IOCTL_CDROM_READ_TOC command is used for reading disc TOC, while for listing session block addresses of multi-session discs, there is the IOCTL_CDROM_GET_LAST_SESSION command. It is also necessary to pay attention to the IOCTL_CDROM_READ_Q_CHANNEL command, which ensures the retrieval of the information from the Q-channel of subcode (this is important for retrieving key marks).

Reading CD-ROM sectors in the Raw mode is carried out by the IOCTL_CDROM_RAW_READ command, the capabilities of which, unfortunately , are limited to CDDA discs only. Reading data from CDDATA discs, sector by sector, is not supported on Raw or on user levels. According to the adopted security policy, no application has the right to bypass the security subsystem. If this were not the case, intruders would easily be able to access confidential data by simply reading the disc at the sector level. Built-in drivers supplied as part of the Windows operating system fully comply with this requirement, although third-party developers may violate this restriction if they so chose. Windows NT DDK includes the source code of a demo CD-ROM driver (NTDDK\ src\storage\class\ cdrom \). After introducing some small modifications, this driver will agree to read discs of all types without asking any silly questions. To do this, simply open the cdrom.c file, find the string "if (rawReadInfo-> TrackMode == CDDA) {" , and go to the branch whose OperationCode is equal to SCSIOP_READ . Then, modify the code in such a way as to ensure that this branch gets control in all other cases.

Note  

The IRP_MJ_READ function, which is present in DDK and which, in the theory, ensures that it is possible to read individual logical blocks, is an internal function of the driver. Access to this function from the application level is closed and it doesn t make any sense to use it in combination with DeviceIoControl .

Table 4.1: Description of IOCTL commands of the standard CD-ROM driver (more detailed information is provided in Windows NT DDK)

IOCTL command

Description

IOCTL_CDROM_CHECK_VERIFY ,

IOCTL_STORAGE_CHECK_VERIFY (0x24800h)

Detects the fact of disc replacement

(opening/closing the tray)

IOCTL_CDROM_CLOSE_DOOR [*] ,

IOCTL_STORAGE_LOAD_MEDIA (0x2D480Ch)

Closes the drive tray

IOCTL_CDROM_FIND_NEW_DEVICES ,

IOCTL_STORAGE_FIND_NEW_DEVICES (0x24818h)

Lists new drives connected after OS startup or since the last call to this command

IOCTL_CDROM_GET_CONTROL

Reports the current position of audio playback

IOCTL_CDROM_GET_DRIVE_GEOMETRY (0x2404Ch)

Determines the disc type and its geometry (number of sectors on disc, sector size , etc.)

IOCTL_CDROM_GET_LAST_SESSION (0x24038h)

Lists starting addresses of sessions and writes them to the TOC buffer read by IOCTL_CDROM_READ_TOC

IOCTL_CDROM_GET_VOLUME (0x24014h)

Returns the current volume from CD-ROM

IOCTL_CDROM_PAUSE_AUDIO (0x2400Ch)

Temporarily pauses audio playback

IOCTL_CDROM_PLAY_AUDIO_MSF (0x24018h)

Initiates audio playback process from specified position up to the specified position

IOCTL_CDROM_RAW_READ (0x2403Eh)

Reads raw sectors from audio discs

IOCTL_CDROM_READ_Q_CHANNEL (0x2402Ch)

Reads data from the Q subcode channel

IOCTL_CDROM_READ_TOC (0x24000h)

Reads the disc TOC

IOCTL_CDROM_RESUME_AUDIO (0x24010h)

Resumes audio playback

IOCTL_CDROM_SEEK_AUDIO_MSF (0x24004h)

Positions optical head

IOCTL_CDROM_SET_VOLUME (0x24028h)

Sets volume from the CD-ROM

IOCTL_CDROM_STOP_AUDIO (0x24008h)

Stops audio playback

[*] “obsolete and currently removed from the DDK

The DeviceIoControl function is always preceded by a call to the CreateFile function, which returns the handle of the appropriate device specified in the following format: \\. \X: , where X is the letter of the drive, with which you are going to work. Note that the dwCreationDisposition flag must be set to OPEN_EXISTING , or your attempt to access the drive will fail. A typical example of a call to this function is provided in Listing 4.1.

Listing 4.1: An Example illustrating the opening of the device
image from book
 HANDLE hCD;                        // drive descriptor  hCD=CreateFile("\\.\X:", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0,0);  if (hCD == INVALID_HANDLE_VALUE)                   // error 
image from book
 
Note  

Windows NT registers the CD drive under the following name : \\. \CdRomx , where x is the number of the drive (starting from zero) that references the same drive as the drive letter, and provides the same set of functions.

The prototype of the DeviceIoControl function itself appears as shown in Listing 4.2.

Listing 4.2: The prototype of the DeviceIoControl function
image from book
 BOOL DeviceIoControl(HANDLE hDevice,            // Device descriptor    DWORD dwIoControlCode,     // IOCTL code of the command to be executed    LPVOID lpInBuffer,         // Pointer to the input buffer                               // (Irp   >AssociatedIrp.SystemBuffer)    DWORD nInBufferSize,       // Size of the input buffer, in bytes    LPVOID lpOutBuffer,        // Pointer to the output buffer                               // (Irp   >AssociatedIrp.SystemBuffer)    DWORD nOutBufferSize,      // Size of the output buffer, in bytes    LPDWORD lpBytesReturned,   // Pointer to the counter of returned bytes    LPOVERLAPPED lpOverlapped  // Pointer to the structure for asynchronous operations); 
image from book
 

Here:

  • hDevice ”the descriptor that was just returned by CreateFile .

  • dwIoControlCode ”the IOCTL code for our operation.

  • lpInBuffer ”the pointer to the buffer that contains the data prepared for passing to the device (as a rule, these are command arguments). In the course of function execution, the buffer contents are copied into Irp ˆ’ > AssociatedIrp.SystemBuffer . This is mentioned here in order to prevent you from feeding the entire IRP structure to DeviceIoControl when you see this form of abracadabra in DDK.

  • nInBufferSize ”the size of the input buffer in bytes. In the course of function execution, it is copied into the Parameters.DeviceIoControl.InputBufferLength Structure.

  • lpOutBuffer ”the pointer to the output buffer, where the contents of Irp ˆ’ > AssociatedIrp.SystemBuffer are returned.

  • nOutBuffersSize ”pointer to the double word, where the number of bytes returned by the driver via output buffer will be written.

If the operation has been accomplished successfully, the function will return a non-zero value. Otherwise, it will return zero. For more detailed information on the error, call GetLastError .

Passing of IOCTL commands to the device doesn t require that you have administrative privileges (except for cases where the device is opened with the GENERIC_WRITE flag). This significantly improves the ergonomic properties of protection mechanisms based on this function. (Let us consider protection mechanisms for a moment, or, to be more precise, their ability to resist the attempts at cracking them. Since, obviously, the DeviceIoControl function isn t employed in common programs very often, it unmasks the headquarters of the protection mechanism. Consequently, it becomes quite easy to "get a bearing " on it. It is enough to set a breakpoint to the DeviceIoControl function and wait until the IOCTL command passed to it takes one of the above-listed values. Setting a breakpoint to CreateFile is not wise, because it will produce a large number of garbage debugger popups ( CreateFile is called any time when a file is opened/created). However, it makes sense to search for the "\\.\" string in the program body. If you succeed, the only thing left to do is to click on the cross-reference and then press <Enter>. That s it! Here is the protection code for you.

In order to ensure a better understanding of this method of interaction between an application and the device driver, consider a key fragment of the function that carries out this type of interaction (error handling has been omitted for the sake of simplicity).

Listing 4.3: [/IOCTL.CDDA.raw.read.c] A function demonstrating techniques for reading raw sectors via a CDFS driver (intended for CDDA discs only )
image from book
  //--[ReadCDDA]---------------------------------------------------   //   //         Reads RAW sectors from CDDA discs   //         ==========================================   // ARC:   //  drive      - name of the device from which to read   //            (for example, "\\A\X:")   //  start_sector   - number of the first sector to read   //  n_sec      - number of sectors to read   //   // RET:   //  == 0       - error   //  != 0       - pointer to the buffer containing read sectors   //   // NOTE:   //  This function supports discs only of the types supported by   //  the CDFS driver, which is the one that it uses.   //  The built-in Windows NT driver supports only CDDA discs   //-----------------------------------------------------------------   char* ReadCDDA(char *drive, int start_sector, int n_sec)  {  // Supported track types   typedef enum _TRACK_MODE_TYPE {   YellowMode2,         // native MODE 2 (not CD-data)   XAForm2,         // XA MODE 2 Form 2 (Video-CD)   CDDA             // Audio-CD   } TRACK_MODE_TYPE, *PTRACK_MODE_TYPE;   // the arugment of the IOCTL_RAW_READ command   typedef struct __RAW_READ_INFO {   LARGE_INTEGER     DiskOffset;    // logical block offset in bytes   ULONG         SectorCount;       // number of sectors to read   TRACK_MODE_TYPE  TrackMode;      // mode of the track to read   } RAW_READ_INFO, *PRAW_READ_INFO;  #define CDROM_RAW_SECTOR_SIZE       2352  #define CDROM_SECTOR_SIZE           2048  int a;  HANDLE hCD;  DWORD x_size;  char *szDrive;  BOOL fResult = 0;  unsigned char *buf;  RAW_READ_INFO rawRead;  // PREPARING THE RAW_READ_INFO STRUCTURE, passed to the CD-ROM driver  rawRead.TrackMode = CDDA;  // disc type - Audio CD  rawRead.SectorCount = n_sec;  // number of sectors to read  rawRead.DiskOffset.QuadPart = start_sector  *  CDROM_SECTOR_SIZE;  //                                             ^^^^^^^^^^^^^^^^^  // The starting sector is specified by the number of its first byte,  // rather than by its logical number. Theoretically,  // pass-through numbering of bytes from the first  // to the last bytes of the disc ensures full abstraction  // from the hardware (sector size is returned by the  // IOCTRL_CDROM_GET_DRIVE_GEOMETRY command).  // In practice, however, driver architects  // have made a blunder, as a result of which the driver,  // instead of the pass-through byte numbers, accepts  // start_address * CDROM_SECTOR_SIZE,  // where CDROM_SECTOR_SIZE is the logical block size,  // which, in this case, is equal to the standard sector size  // of the CDDATA disc (2048 bytes), while the sector size  // of CDDA discs is 2352 bytes. Therefore, DiskOffset  // is equal to start_secor * CDROM_SECTOR_SIZE,  // while buffer size must be equal to  // start_secor * CDROM_RAW_SECTOR_SIZE  // ALLOCATING MEMORY  buf = malloc(CDROM_RAW_SECTOR_SIZE * n_sec);  // GETTING THE DEVICE DESCRIPTOR  hCD = CreateFile(drive, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);   if (hCD != INVALID_HANDLE_VALUE)  // PASSING THE IOCTL_CDROM_RAW_READ COMMAND TO THE DRIVER  fResult = DeviceIoControl(hCD, 0x2403E /* IOCTL_CDROM_RAW_READ */,                 &rawRead, sizeof(RAW_READ_INFO) ,                 buf, CDROM_RAW_SECTOR_SIZE*n_sec,                 &x_size, (LPOVERLAPPED) NULL);  // OUTPUT OF THE RESULT (if there is any)  if (fResult)      for (a = 0; a <= x_size; ++a) printf("%02X%s", buf[a], (a%24)?" ":"\n")   else      printf("-ERROR"); printf("\n");  // EXITING  CloseHandle(hCD); return (fResult)?buf:0;  } 
image from book
 

Studying TOC contents may be useful when analyzing some protected discs.

Listing 4.4 is another demo example. It illustrates the technique of reading the TOC (Table of Content) ”in audio CDs, it represents an analog of the partition table.

Listing 4.4: [/IOCTL.read.TOC.c] A sample program interacting with the CDFS driver via IOCTL and reading the TOC contents (with decryption)
image from book
  /*------------------------------------------------------   *   *                       READING AND DECODING TOC   *                       =======================   *   * build 0x001 @ 26.05.2003   --------------------------------------------------------*/  main(int argc, char **argv)  {         int     a;          HANDLE      hCD;          unsigned char *buf;          WORD    TOC_SIZE;          BYTE    n_track;          DWORD       x_size,b;          #define DEF_X   "\\.\G:"                // default drive          #define argCD   ((argc>1) ?argv[1] :DEF_X)  // CHECKING ARGUMENTS  if (argc < 2) {fprintf(stderr, "USAGE: IOCTL.read.TOC \\.\X:\n"); return 0;}  // TITLE  fprintf(stderr, "simple TOC reader via IOCTL\n");  // ALLOCATING MEMORY  buf = (char *) malloc(buf_len);  // OPENING THE DEVICE  hCD=CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);  // EXIT IN CASE OF ERROR  if (hCD == INVALID_HANDLE_VALUE)               {fprintf(stderr, "-ERR: %x\n", GetLastError()); return 0;}  // PASSING THE CDROM_READ_TOC COMMAND TO THE DRIVER  if (DeviceIoControl(hCD, 0x24000 /* IOCTL_READ_TOC */,                                 0, 0, buf, buf_len, &x_size, 0) != 0)          {  // GETTING TOC LENGTH (it is written in reverse order)  TOC_SIZE = buf[0]*0x100L + buf[1];                printf("TOC Data Length........%d\n", TOC_SIZE);  // DECODING OTHER INFORMATION  printf("First Session Number...%d\n", buf[2]);                printf("Last Session Number....%d\n\n", (n_track=buf[3]));                for (a = 1; a <= n_track; a++)                {                         printf("track %d\n{\n",a);                         printf("\treserved.............%x\n", buf[a * 8 - 4]);                         printf("\tADRcontrol..........%d\n", buf[a * 8 - 3]);                         printf("\ttrack number.........%d\n", buf[a * 8 - 2]);                         printf("\treserved.............%d\n", buf[a * 8 - 1]);                         printf("\treserved.............%d\n", buf[a * 8 + 0]);                         printf("\tmin..................%d\n", buf[a * 8 + 1]);                         printf("\tsec..................%d\n", buf[a * 8 + 2]);                         printf("\tframe................%d\n", buf[a * 8 + 3]);                         printf("}\n\n");                }                 // PRINTING TOC CONTENTS IN RAW FORMAT                printf("\n\t\t\t* * * RAW * * *\n");                for (a = 0; a < x_size; a++)                         printf("%02X%s", (unsigned char)buf[a], ((a+1)%22)?" ":"\n");                printf("\n\t\t\t* * * * * * *\n");           }  } 
image from book
 


CD Cracking Uncovered. Protection against Unsanctioned CD Copying
CD Cracking Uncovered: Protection Against Unsanctioned CD Copying (Uncovered series)
ISBN: 1931769338
EAN: 2147483647
Year: 2003
Pages: 60

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net