Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows IOCTL for reading raw CD-ROM data not returning error correction data

I am attempting to make a program that extracts raw sector data from optical discs of various types, starting with CDs (data and audio).

This is nothing special, and the concept itself is fine and working on Linux with similar IOCTLs.

The problem I am having just on Windows is that the ECC/EDC data is not correct (basically zeroed out). You can see this by comparing what the command returns with other programs output like IsoBuster or ImgBurn (or using equivalent commands on Linux).

Is this data something that the Windows security policy affects for some reason? Or a driver bug?

Note that I am seeing this happen on Windows 10. I get the same result when running as administrator.

To be clear about what data is affected, see the nice table here: https://en.wikipedia.org/wiki/CD-ROM#CD-ROM_format. On a normal data CD (mode 1) it is the last 288 bytes of a 2352 sector.

Code to demonstrate the issue:

#include <windows.h>
#include <winioctl.h>  // From the Win32 SDK \Mstools\Include
#include "ntddcdrm.h"  // From the Windows NT DDK \Ddk\Src\Storage\Inc


int main()
{

    HANDLE  hCD = CreateFile(L"\\\\.\\D:", GENERIC_READ,
        FILE_SHARE_READ,
        0, OPEN_EXISTING, 0,
        0);

    //the sector to read
    int lba = 0;

    RAW_READ_INFO rawReadInfo;
    rawReadInfo.DiskOffset.QuadPart = (long long)lba * 2048;
    rawReadInfo.SectorCount = 1;
    rawReadInfo.TrackMode = RawWithC2AndSubCode;
    DWORD x_size;

    char* buf = (char*)malloc(CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE);

    int rc = DeviceIoControl(hCD, IOCTL_CDROM_RAW_READ,
        &rawReadInfo, sizeof(rawReadInfo),
        buf, CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE,
        &x_size, NULL);

    printf("rc: %d\n", rc);
    printf("x_size: %d\n", x_size);

    int i;
    printf("\nSync, Addr, Mode (16 bytes)\n");
    for (i = 0; i < 16; i++) {
        printf("%hhX", buf[i]); //this data comes back fine
    }
    printf("\nData (2048 bytes)\n");
    for (i = 16; i < 16+2048; i++) {
        printf("%hhX", buf[i]); //this data comes back fine
    }
    printf("\nED, RZ, EC (288 bytes)\n");
    for (i = 16 + 2048; i < 16 + 2048 + 288; i++) {
        printf("%hhX", buf[i]); //this data comes back wrong
    }
    printf("\nC2, Sub (392 bytes)\n");
    for (i = 16 + 2048 + 288; i < 2744; i++) {
        printf("%hhX", buf[i]); //not sure if this is right or not
    }

    //dumb code to block terminal from closing
    char str[80];
    fgets(str, 10, stdin);


    return 0;
}

Side by side comparison between output and IsoBuster sector view: enter image description here

FYI for anyone coming here in the future, the next best thing to get this to work is definitely to use SPTI as described here: How to issue a READ CD command to a CD-ROM drive in Windows?. I don't want to consider that an answer to why the Windows IOCTL doesn't work though (omits the EDC)!

like image 401
hooby3dfx Avatar asked Jan 24 '26 21:01

hooby3dfx


1 Answers

The answer may be a little late but here it is, the short one: You cannot use the IOCTL_CDROM_RAW_READ control code to read mode-1 tracks in a raw way.

The long answer: When you look at x_size, you see that the function did not read the amount of data you exptected it to. You expected CD_RAW_SECTOR_WITH_C2_AND_SUBCODE_SIZE, i.e. 2744, but you received only 2456. Another giveaway for this fact are the 0xCD chars shown in your screenshot. Visual C initializes memory with the value 0xCD in debug mode to facilitate debugging. So this part of the array was probably not written to at all.

So which data did you actually receive from the function call? It seems to me like you received 12 bytes of sync data, then 4 bytes of sector header (3 bytes for address, 0x00 0x02 0x00 in your case, 1 byte for mode, 0x01 in your case), then 2048 bytes of user data, then 296 bytes of C2 error information, and then 96 bytes of subcode. You can identify the subcode easily because the MSB is set to one in all bytes, which is the case at the start of each track on a CD.

I didn't succeed in finding a documentation about the exact meaning of the C2 error information returned by this control code. But because it has just enough room to hold one bit for each byte of the raw sector, my guess is that each bit indicates whether the corresponding byte may still contain an error after C2 error correction. Whether this assumption still holds if only the user data of a data track is returned is unknown to me. Not to mention that the information is quite meaningless because the user data returned by the function should already be corrected by the subsequent P- and Q-parity, even though it may have contained C2 errors initially.

The behavior of other track modes when trying to read mode-1 data is as follows:

  • YellowMode2 and XAForm2 will fail.
  • RawWithC2 and RawWithSubCode will behave similar to RawWithC2AndSubCode.
  • CDDA will yield garbled data.

Also, note that your printf format pattern only prints one digit for each char if its value is below 0x10. If you wanted to always print 2 digits, "%02hhX" would be appropriate.

like image 65
BlackStar Avatar answered Jan 26 '26 14:01

BlackStar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!