Using struct to read binary files

Tim Chase python.list at tim.thechases.com
Thu Nov 26 22:22:47 EST 2009


> --------------------------------------------------------------------------------
> f = open("/tmp/mbrcontent", "rb")
> contents = f.read()
> f.close()
> 
> firstSectorAddress = contents[454:458]
> numSectors = contents[458:462]
> --------------------------------------------------------------------------------
> 
> On my machine, the bytes contained in firstSectorAddress are
> "\x3F\x00\x00\x00", and the bytes contained in numSectors are
> "\x20\x1F\x80\x01".  I know from doing a pen and paper calculation
> that the first sector address is 63, and the number of sectors is
> 25,173,792 (the numbers are stored in little-endian format).
> 
> How do I figure that out programmatically?  I think that I can use
> struct.unpack() to do this, but I'm not quite sure how to use it.

To pull out little-endian data, you can use struct.unpack() like

   >>> struct.unpack('<L', firstSectorAddress)
   (63,)
   >>> struct.unpack('<L', numSectors)
   (25173792,)

It's a curious problem I haven't toyed with boot-sectors since my 
ASM days back on a 286, so I threw together the following code 
which you're welcome to tear apart and remash as you see fit.

-tkc

########################################################
import struct

mbr = file('mbrcontent', 'rb').read()
partition_table = mbr[446:510]
signature = struct.unpack('<H', mbr[510:512])[0]
little_endian = (signature == 0xaa55) # should be True
print "Little endian:", little_endian
PART_FMT = (little_endian and '<' or '>') + (
   "B"  # status (0x80 = bootable (active), 0x00 = non-bootable)
        # CHS of first block
   "B"  # Head
   "B"  # Sector is in bits 5; bits 9 of cylinder are in bits 7-6
   "B"  # bits 7-0 of cylinder
   "B"  # partition type
        # CHS of last block
   "B"  # Head
   "B"  # Sector is in bits 5; bits 9 of cylinder are in bits 7-6
   "B"  # bits 7-0 of cylinder
   "L"  # LBA of first sector in the partition
   "L"  # number of blocks in partition, in little-endian format
   )
PART_SIZE = 16
fmt_size = struct.calcsize(PART_FMT)
# sanity check expectations
assert fmt_size == PART_SIZE, \
   "Partition format string is %i bytes, not %i" % (
   fmt_size, PART_SIZE)

def cyl_sector(sector_cyl, cylinder7_0):
   sector = sector_cyl & 0x1F # bits 5-0

   # bits 7-6 of sector_cyl contain bits 9-8 of the cylinder
   cyl_high = (sector_cyl >> 5) & 0x03
   cyl = (cyl_high << 8) | cylinder7_0
   return sector, cyl

for partition in range(4):
   print "Partition #%i" % partition,
   offset = PART_SIZE * partition
   (
     status,
     start_head, start_sector_cyl, start_cyl7_0,
     part_type,
     end_head, end_sector_cyl, end_cyl7_0,
     lba,
     blocks
     ) = struct.unpack(
       PART_FMT,
       partition_table[offset:offset + PART_SIZE]
       )
   if status == 0x80:
     print "Bootable",
   elif status:
     print "Unknown status [%s]" % hex(status),
   print "Type=0x%x" % part_type
   start = (start_head,) + cyl_sector(
     start_sector_cyl, start_cyl7_0)
   end = (end_head,) + cyl_sector(
     end_sector_cyl, end_cyl7_0)
   print " (Start: Heads:%i\tCyl:%i\tSect:%i)" % start
   print " (End:   Heads:%i\tCyl:%i\tSect:%i)" % end
   print " LBA:", lba
   print " Blocks:", blocks








More information about the Python-list mailing list