Kjetil's Information Center: A Blog About My Projects

Boot Record Dump

Here is a tool I wrote to dump information from boot records, the information normally stored in the first sector (boot sector) on a disk or partition. It attempts to automatically figure out if the record is a Master Boot Record (MBR) or a Volume Boot Record (VBR).

The boot record (file) can be extracted with the Unix "dd" tool, like so: 'dd bs=512 count=1 if=/dev/hda of=/tmp/br.dd'.

Here's an example of the information dumped from a MBR:

Master Boot Record (MBR)
------------------------
Serial: DCA41F6C
Partition Table:
 Boot Type   First LBA   First C:H:S    Last C:H:S    Size
   *  0x04   0x0000003f  0000:001:01 -> 0002:254:63  (23 MB)
      0x05   0x0000bc43  0003:000:01 -> 0123:254:63  (949 MB)
      0x00   0x00000000  0000:000:00 -> 0000:000:00  ---
      0x00   0x00000000  0000:000:00 -> 0000:000:00  ---
Code:
0x0000:  fa 33 c0 8e d0 bc 00 7c 8b f4 50 07 50 1f fb fc  |.3.....|..P.P...|
0x0010:  bf 00 06 b9 00 01 f2 a5 ea 1d 06 00 00 be be 07  |................|
0x0020:  b3 04 80 3c 80 74 0e 80 3c 00 75 1c 83 c6 10 fe  |...<.t..<.u.....|
0x0030:  cb 75 ef cd 18 8b 14 8b 4c 02 8b ee 83 c6 10 fe  |.u......L.......|
0x0040:  cb 74 1a 80 3c 00 74 f4 be 8b 06 ac 3c 00 74 0b  |.t..<.t.....<.t.|
0x0050:  56 bb 07 00 b4 0e cd 10 5e eb f0 eb fe bf 05 00  |V.......^.......|
0x0060:  bb 00 7c b8 01 02 57 cd 13 5f 73 0c 33 c0 cd 13  |..|...W.._s.3...|
0x0070:  4f 75 ed be a3 06 eb d3 be c2 06 bf fe 7d 81 3d  |Ou...........}.=|
0x0080:  55 aa 75 c7 8b f5 ea 00 7c 00 00 49 6e 76 61 6c  |U.u.....|..Inval|
0x0090:  69 64 20 70 61 72 74 69 74 69 6f 6e 20 74 61 62  |id partition tab|
0x00a0:  6c 65 00 45 72 72 6f 72 20 6c 6f 61 64 69 6e 67  |le.Error loading|
0x00b0:  20 6f 70 65 72 61 74 69 6e 67 20 73 79 73 74 65  | operating syste|
0x00c0:  6d 00 4d 69 73 73 69 6e 67 20 6f 70 65 72 61 74  |m.Missing operat|
0x00d0:  69 6e 67 20 73 79 73 74 65 6d 00 00 00 00 00 00  |ing system......|
0x00e0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x00f0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0100:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0110:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0120:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0130:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0140:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0150:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0160:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0170:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0180:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x0190:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x01a0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
0x01b0:  00 00 00 00 00 00 00 00                          |................|
          


Here's an example of the information dumped from a VBR:

Volume Boot Record (VBR)
------------------------
Jump Code            : 0xeb 0x3c 0x90 (jmp short 0x3E)
OEM ID               : 'MSWIN4.1'
Volume Label         : 'USER       '
System ID            : 'FAT16   '
Bytes per Sector     : 512
Sectors per Cluster  : 64
Sectors per FAT      : 242
Sectors per Track    : 63
No of FATs           : 2
Reserved Sectors     : 1
Hidden Sectors       : 63
Small Sectors        : 0
Large Sectors        : 3951237
Root Entries         : 512
Heads                : 64
Current Head         : 0
Media Descriptor     : 0xF8 (Fixed Disk)
Physical Drive Number: 0x80
Signature            : 0x29
ID                   : E5165E0B
Boot Code:
0x0030:                                            33 c9  |..............3.|
0x0040:  8e d1 bc fc 7b 16 07 bd 78 00 c5 76 00 1e 56 16  |....{...x..v..V.|
0x0050:  55 bf 22 05 89 7e 00 89 4e 02 b1 0b fc f3 a4 06  |U."..~..N.......|
0x0060:  1f bd 00 7c c6 45 fe 0f 38 4e 24 7d 20 8b c1 99  |...|.E..8N$} ...|
0x0070:  e8 7e 01 83 eb 3a 66 a1 1c 7c 66 3b 07 8a 57 fc  |.~...:f..|f;..W.|
0x0080:  75 06 80 ca 02 88 56 02 80 c3 10 73 ed 33 c9 fe  |u.....V....s.3..|
0x0090:  06 d8 7d 8a 46 10 98 f7 66 16 03 46 1c 13 56 1e  |..}.F...f..F..V.|
0x00a0:  03 46 0e 13 d1 8b 76 11 60 89 46 fc 89 56 fe b8  |.F....v.`.F..V..|
0x00b0:  20 00 f7 e6 8b 5e 0b 03 c3 48 f7 f3 01 46 fc 11  | ....^...H...F..|
0x00c0:  4e fe 61 bf 00 07 e8 28 01 72 3e 38 2d 74 17 60  |N.a....(.r>8-t.`|
0x00d0:  b1 0b be d8 7d f3 a6 61 74 3d 4e 74 09 83 c7 20  |....}..at=Nt... |
0x00e0:  3b fb 72 e7 eb dd fe 0e d8 7d 7b a7 be 7f 7d ac  |;.r......}{...}.|
0x00f0:  98 03 f0 ac 98 40 74 0c 48 74 13 b4 0e bb 07 00  |.....@t.Ht......|
0x0100:  cd 10 eb ef be 82 7d eb e6 be 80 7d eb e1 cd 16  |......}....}....|
0x0110:  5e 1f 66 8f 04 cd 19 be 81 7d 8b 7d 1a 8d 45 fe  |^.f......}.}..E.|
0x0120:  8a 4e 0d f7 e1 03 46 fc 13 56 fe b1 04 e8 c2 00  |.N....F..V......|
0x0130:  72 d7 ea 00 02 70 00 52 50 06 53 6a 01 6a 10 91  |r....p.RP.Sj.j..|
0x0140:  8b 46 18 a2 26 05 96 92 33 d2 f7 f6 91 f7 f6 42  |.F..&...3......B|
0x0150:  87 ca f7 76 1a 8a f2 8a e8 c0 cc 02 0a cc b8 01  |...v............|
0x0160:  02 80 7e 02 0e 75 04 b4 42 8b f4 8a 56 24 cd 13  |..~..u..B...V$..|
0x0170:  61 61 72 0a 40 75 01 42 03 5e 0b 49 75 77 c3 03  |aar.@u.B.^.Iuw..|
0x0180:  18 01 27 0d 0a 49 6e 76 61 6c 69 64 20 73 79 73  |..'..Invalid sys|
0x0190:  74 65 6d 20 64 69 73 6b ff 0d 0a 44 69 73 6b 20  |tem disk...Disk |
0x01a0:  49 2f 4f 20 65 72 72 6f 72 ff 0d 0a 52 65 70 6c  |I/O error...Repl|
0x01b0:  61 63 65 20 74 68 65 20 64 69 73 6b 2c 20 61 6e  |ace the disk, an|
0x01c0:  64 20 74 68 65 6e 20 70 72 65 73 73 20 61 6e 79  |d then press any|
0x01d0:  20 6b 65 79 0d 0a 00 00 49 4f 20 20 20 20 20 20  | key....IO      |
0x01e0:  53 59 53 4d 53 44 4f 53 20 20 20 53 59 53 7f 01  |SYSMSDOS   SYS..|
0x01f0:  00 41 bb 00 07 60 66 6a 00 e9 3b ff 00 00        |.A...`fj..;.....|
          


Here's the code:

#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include <arpa/inet.h>

typedef struct partition_record_s {
  uint8_t bootable;
  uint8_t start_head;
  uint8_t start_cs_1;
  uint8_t start_cs_2;
  uint8_t type;
  uint8_t end_head;
  uint8_t end_cs_1;
  uint8_t end_cs_2;
  uint32_t start_lba;
  uint32_t no_of_sectors;
} partition_record_t;

#pragma pack(1)
typedef struct mbr_s {
  uint8_t code[440];
  uint32_t disk_signature;
  uint16_t reserved;
  partition_record_t partition[4];
  uint16_t boot_record_signature;
} mbr_t;
#pragma pack()

#pragma pack(1)
typedef struct vbr_s {
  uint8_t jump_code[3];
  char oem_id[8];
  uint16_t bytes_per_sector;
  uint8_t sectors_per_cluster;
  uint16_t reserved_sectors;
  uint8_t no_of_fats;
  uint16_t root_entries;
  uint16_t small_sectors;
  uint8_t media_descriptor;
  uint16_t sectors_per_fat;
  uint16_t sectors_per_track;
  uint16_t heads;
  uint32_t hidden_sectors;
  uint32_t large_sectors;
  uint8_t physical_drive_number;
  uint8_t current_head;
  uint8_t signature;
  uint32_t id;
  char volume_label[11];
  char system_id[8];
  uint8_t boot_code[448];
  uint16_t boot_record_signature;
} vbr_t;
#pragma pack()

static void print_string(char *s, int len)
{
  int i;
  for (i = 0; i < len; i++) {
    if (isprint(s[i]))
      fputc(s[i], stdout);
    else
      fputc('.', stdout);
  }
}

static void print_hex(uint8_t *data, int len, int offset_start)
{
  int i, col, row;
  char buffer[16];

  row = offset_start / 16;
  col = offset_start % 16;

  printf("0x%04x:  ", row * 0x10);
  i = 0;
  while (col-- > 0) {
    printf("   ");
    buffer[i++] = 0x0;
  }

  col = offset_start % 16;
  for (i = 0; i < len; i++) {
    printf("%02x ", data[i]);
    buffer[col] = data[i];
    col++;
    if (col == 16) {
      row++;
      col = 0;
      printf(" |");
      print_string(buffer, 16);
      printf("|\n");
      printf("0x%04x:  ", row * 0x10);
    }
  }

  for (i = col; i < 16; i++) {
    printf("   ");
    buffer[i] = 0x0;
  }
  printf(" |");

  if (col > 0) {
    print_string(buffer, 16);
    printf("|\n");
  } else {
    printf("\n");
  }
}

static void dump_mbr(mbr_t *mbr)
{
  int i;
  int start_cylinder, start_sector, end_cylinder, end_sector;

  printf("Serial: %08X\n", htonl(mbr->disk_signature));

  printf("Partition Table:\n");
  printf(" Boot Type   First LBA   First C:H:S    Last C:H:S    Size\n");
  for (i = 0; i < 4; i++) {
    start_sector = mbr->partition[i].start_cs_1 & 0x3F;
    start_cylinder = mbr->partition[i].start_cs_2;
    start_cylinder += (mbr->partition[i].start_cs_1 & 0xC0) << 2;

    end_sector = mbr->partition[i].end_cs_1 & 0x3F;
    end_cylinder = mbr->partition[i].end_cs_2;
    end_cylinder += (mbr->partition[i].end_cs_1 & 0xC0) << 2;

    printf("   %c  ", (mbr->partition[i].bootable == 0x80) ? '*' : ' ');
    printf("0x%02x   ", mbr->partition[i].type);
    printf("0x%08x  ", mbr->partition[i].start_lba);
    printf("%04d:%03d:%02d -> ",
      start_cylinder,
      mbr->partition[i].start_head,
      start_sector);
    printf("%04d:%03d:%02d  ",
      end_cylinder,
      mbr->partition[i].end_head,
      end_sector);

    if (mbr->partition[i].no_of_sectors == 0) {
      printf("---");
    } else {
      /* NOTE: Assumes standard sector size of 512 bytes. */
      printf("(%d MB)", mbr->partition[i].no_of_sectors / 2048);
    }

    printf("\n");
  }

  printf("Code:\n");
  print_hex(mbr->code, 440, 0x0);
}

static void dump_vbr(vbr_t *vbr)
{
  char *s;
  
  printf("Jump Code            : 0x%02x 0x%02x 0x%02x ",
    vbr->jump_code[0], vbr->jump_code[1], vbr->jump_code[2]);

  if (vbr->jump_code[0] == 0xEB) {
    printf("(jmp short 0x%02X)\n", vbr->jump_code[1] + 2);
  } else {
    printf("(?)\n");
  }

  printf("OEM ID               : '");
  print_string(vbr->oem_id, 8);
  printf("'\n");

  printf("Volume Label         : '");
  print_string(vbr->volume_label, 11);
  printf("'\n");

  printf("System ID            : '");
  print_string(vbr->system_id, 8);
  printf("'\n");

  printf("Bytes per Sector     : %d\n", vbr->bytes_per_sector);
  printf("Sectors per Cluster  : %d\n", vbr->sectors_per_cluster);
  printf("Sectors per FAT      : %d\n", vbr->sectors_per_fat);
  printf("Sectors per Track    : %d\n", vbr->sectors_per_track);
  printf("No of FATs           : %d\n", vbr->no_of_fats);
  printf("Reserved Sectors     : %d\n", vbr->reserved_sectors);
  printf("Hidden Sectors       : %d\n", vbr->hidden_sectors);
  printf("Small Sectors        : %d\n", vbr->small_sectors);
  printf("Large Sectors        : %d\n", vbr->large_sectors);
  printf("Root Entries         : %d\n", vbr->root_entries);
  printf("Heads                : %d\n", vbr->heads);
  printf("Current Head         : %d\n", vbr->current_head);

  switch (vbr->media_descriptor) {
  case 0xF0:
  case 0xF9:
  case 0xFC:
  case 0xFD:
  case 0xFE:
  case 0xFF:
    s = "Floppy Disk";
    break;
  case 0xF8:
    s = "Fixed Disk";
    break;
  default:
    s = "Unknown";
    break;
  }

  printf("Media Descriptor     : 0x%02X (%s)\n", vbr->media_descriptor, s);
  printf("Physical Drive Number: 0x%02X\n", vbr->physical_drive_number);
  printf("Signature            : 0x%02X\n", vbr->signature);
  printf("ID                   : %08X\n", htonl(vbr->id));

  printf("Boot Code:\n");
  print_hex(vbr->boot_code, 448, 0x3E);
}

static int is_printable(char *s, int len)
{
  int i;
  for (i = 0; i < len; i++) {
    if (! isprint(s[i]))
      return 0;
  }
  return 1;
}

int main(int argc, char *argv[])
{
  FILE *fh;
  int bytes;
  uint8_t sector[512];

  if (argc != 2) {
    fprintf(stderr, "Usage: %s <boot record file>\n", argv[0]);
    return 1;
  }

  fh = fopen(argv[1], "rb");
  if (fh == NULL) {
    fprintf(stderr, "Error: Unable to open file for reading.\n");
    return 1;
  }

  bytes = fread(&sector, sizeof(uint8_t), 512, fh);
  fclose(fh);

  if (bytes < 512) {
    fprintf(stderr, "Error: File is less than 512 bytes.\n");
    return 1;
  }

  if (sector[510] != 0x55 || sector[511] != 0xAA) {
    fprintf(stderr, "Error: File is not a valid boot record.\n");
    return 1;
  }

  if (is_printable(((vbr_t *)&sector)->oem_id, 8)) {
    printf("Volume Boot Record (VBR)\n");
    printf("------------------------\n");
    dump_vbr((vbr_t *)&sector);
  } else {
    printf("Master Boot Record (MBR)\n");
    printf("------------------------\n");
    dump_mbr((mbr_t *)&sector);
  }

  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 04/03-2012, Article Link