# Reading the MBR under Windows NT

04 May 1999

### Introduction

This article presents code to read the MBR (Master Boot Record) under Windows NT (along with some ancilliary functions). [A reprint of an October 1998 article linked on the home page.] The code is derived from some sample code by The Microsoft Corporation that was in their SDK.

With Windows NT you just open a disk handle and just read. Simple eh? Well, not exactly. See Reading Sectors: A Comparison Between Operating Systems for more information.

### Code

Here is the .C source code, .TXT source code, and here is some output text from tests on my computer.

Here is the <PRE></PRE> source code, but I left the < and > characters unescaped and some browsers will not show them.

    /*
        File: DISK.C
        Desc: Read first sector of disk/drive (MBR or BOOT)
        Plat: Windows NT
        Auth: diskwarez
        Date: 07-Oct-1998

        Note: I totally dis-like Windows' C style (to put it mildly).
              Where ever I could I use normal C style. I loathe using
              Windows' typedefs like VOID and LPVOID etc. Anyhoo, this
              code works and is so damn simple. Who ever said you
              CAN'T read the MBR under Windows NT. (Writing the MBR
              may be a different story.)
    */

    #include
    #include
    #include

    #if defined __STDC__ && defined _MSC_VER
    #error Windows' SHIT is not ANSI compatable (use /Ze or /ze)
    #endif
    #if defined __WATCOMC__ && defined NO_EXT_KEYS
    #error Windows' SHIT is not ANSI compatable (do not use -za)
    #endif
    #pragma warning(disable:4001)   /* single line comment */
    #pragma warning(disable:4115)   /* named type definition in parentheses */
    #pragma warning(disable:4201)   /* nameless struct/union */
    #pragma warning(disable:4209)   /* benign typedef */
    #pragma warning(disable:4214)   /* bit field types other than int */
    #pragma warning(disable:4515)   /* unreferenced inline function */
    #define WIN32_LEAN_AND_MEAN
    #define NO_INLINE_SHIT
    #include
    #include

    long winperror(char * message, ...);
    void *secmalloc(DISK_GEOMETRY *Geometry);

    int GetDiskGeometry(HANDLE hDisk, DISK_GEOMETRY *buf)
    {
    unsigned long count;

        return DeviceIoControl(hDisk,IOCTL_DISK_GET_DRIVE_GEOMETRY,
                               NULL,0,buf,sizeof(DISK_GEOMETRY),
                               &count,NULL);
    }

    int GetDiskPartitionInfo(HANDLE hDisk,PARTITION_INFORMATION *buf)
    {
    unsigned long count;

        return DeviceIoControl(hDisk,IOCTL_DISK_GET_PARTITION_INFO,
                               NULL,0,buf,sizeof(PARTITION_INFORMATION),
                               &count,NULL);
    }

    int LockVolume(HANDLE hDisk)
    {
    unsigned long count;

        return DeviceIoControl(hDisk,FSCTL_LOCK_VOLUME,
                               NULL,0,NULL,0,&count,NULL);
    }

    int UnlockVolume(HANDLE hDisk)
    {
    unsigned long count;

        return DeviceIoControl(hDisk,FSCTL_UNLOCK_VOLUME,
                               NULL,0,NULL,0,&count,NULL);
    }

    void PrintGeometry(char *DriveName,DISK_GEOMETRY *Geometry)
    {
    char *MediaType;

        if (DriveName)
            printf("Geometry for Drive %s\n",DriveName);

        switch (Geometry->MediaType)
        {
            case F5_1Pt2_512:  MediaType = "5.25, 1.2MB,  512 bytes/sector";break;
            case F3_1Pt44_512: MediaType = "3.5,  1.44MB, 512 bytes/sector";break;
            case F3_2Pt88_512: MediaType = "3.5,  2.88MB, 512 bytes/sector";break;
            case F3_20Pt8_512: MediaType = "3.5,  20.8MB, 512 bytes/sector";break;
            case F3_720_512:   MediaType = "3.5,  720KB,  512 bytes/sector";break;
            case F5_360_512:   MediaType = "5.25, 360KB,  512 bytes/sector";break;
            case F5_320_512:   MediaType = "5.25, 320KB,  512 bytes/sector";break;
            case F5_320_1024:  MediaType = "5.25, 320KB,  1024 bytes/sector";break;
            case F5_180_512:   MediaType = "5.25, 180KB,  512 bytes/sector";break;
            case F5_160_512:   MediaType = "5.25, 160KB,  512 bytes/sector";break;
            case RemovableMedia: MediaType = "Removable media other than floppy";break;
            case FixedMedia:   MediaType = "Fixed hard disk media";break;
            default:           MediaType = "Unknown";break;
        }
        printf("Media Type %s\n",MediaType);
        printf("Cylinders %d Tracks/Cylinder %d Sectors/Track %d\n",
                Geometry->Cylinders.LowPart,
                Geometry->TracksPerCylinder,
                Geometry->SectorsPerTrack);
    }

    HANDLE opendrive(char *drive)
    {
    char devname[64];

        if (isalpha(*drive))
           sprintf(devname,"\\\\.\\%c:",*drive);
        else
            sprintf(devname,"\\\\.\\PHYSICALDRIVE%c",*drive);

        return CreateFile(devname,GENERIC_READ | GENERIC_WRITE,
                          FILE_SHARE_READ | FILE_SHARE_WRITE,
                          NULL,OPEN_EXISTING,0,NULL);
    }

    int main()
    {
    int i;
    HANDLE dev;
    unsigned char *buf;
    unsigned long bytes,bread;
    DISK_GEOMETRY Geometry;
    PARTITION_INFORMATION partinfo;

    char *drive = "0";                  /* change this for other drives */

        printf("Drive: %s\n",drive);

        dev = opendrive(drive);

        if ( dev == INVALID_HANDLE_VALUE )
            return (int)winperror("CreateFile");

        LockVolume(dev);
        winperror("Lock");

        GetDiskPartitionInfo(dev,&partinfo);
        winperror("GetDiskPartitionInfo");

        GetDiskGeometry(dev,&Geometry);
        winperror("GetDIskGeometry");

        PrintGeometry(drive,&Geometry);

        buf = secmalloc(&Geometry);
        if (buf == NULL)
            abort();

        bytes = 512;

        ReadFile(dev, buf, bytes, &bread, NULL);
        winperror("ReadFile");

        /* print first 16 bytes */

        for (i = 0; i < 16; i++)
            printf("%02x ",(unsigned)buf[i]);
        for (i = 0; i < 16; i++)
            printf("%c",isprint(buf[i]) ? buf[i] : '.');

        /* print last 16 bytes */

        printf("\n");
        for (i = 512-16; i < 512; i++)
            printf("%02x ",(unsigned)buf[i]);
        for (i = 512-16; i < 512; i++)
            printf("%c",isprint(buf[i]) ? buf[i] : '.');

        printf("\n");

        /* hey, lets try the next sector... */

        ReadFile(dev, buf, bytes, &bread, NULL);
        winperror("ReadFile");

        for (i = 0; i < 16; i++)
            printf("%02x ",(unsigned)buf[i]);
        for (i = 0; i < 16; i++)
            printf("%c",isprint(buf[i]) ? buf[i] : '.');
        printf("\n");
        for (i = 512-16; i < 512; i++)
            printf("%02x ",(unsigned)buf[i]);
        for (i = 512-16; i < 512; i++)
            printf("%c",isprint(buf[i]) ? buf[i] : '.');

        /* simple eh? Looking into overlapped i/o for Seeking... */

        UnlockVolume(dev);

        return 0;
    }

    void *secmalloc(DISK_GEOMETRY *Geometry)
    {
    long NumBufs;
    long VirtBufSize;
    LPVOID IoBuffer;

        NumBufs = Geometry->Cylinders.LowPart;

        VirtBufSize = Geometry->BytesPerSector;

        IoBuffer = VirtualAlloc(NULL,VirtBufSize,MEM_COMMIT,PAGE_READWRITE);

        return IoBuffer;
    }

    long winperror(char * message, ...)
    {
    char *buf;
    va_list ap;
    long error;

        error = GetLastError();
        SetLastError(0);

        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
                      NULL,error,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
                      (LPTSTR)&buf,0,NULL);

        va_start(ap,message);
        vprintf(message,ap);
        va_end(ap);

        printf(" : ");
        if (error)
            printf("error = %lu, ",error);
        printf("%s",buf);

        LocalFree(buf);

        return error;
    }