Skip to content

Instantly share code, notes, and snippets.

@dreamlayers
Created March 12, 2026 16:23
Show Gist options
  • Select an option

  • Save dreamlayers/7a61129a1ca0d5a8911d6d107fb177bb to your computer and use it in GitHub Desktop.

Select an option

Save dreamlayers/7a61129a1ca0d5a8911d6d107fb177bb to your computer and use it in GitHub Desktop.
Extract files from raw image of FAT12 disk
#!/usr/bin/env python3
# Extract files from raw image of FAT12 disk
# This is incomplete. Directories are not supported
import sys
with open(sys.argv[1], 'rb') as f:
b = f.read()
sector_size = 512
def read_sectors(start, length):
offs = sector_size * start
bcount = sector_size * length
return b[offs:offs+bcount]
def read_word(buf, offs):
return buf[offs+1] * 256 + buf[offs]
bsect = read_sectors(0, 1)
sector_size = read_word(bsect, 0xB)
print('Sector size', sector_size)
sec_per_clus = bsect[0xD]
print('Sectors per cluster', sec_per_clus)
res_sect = read_word(bsect, 0x0E)
print('Reserved sectors', res_sect)
fats = bsect[0x10]
print('Number of FATs', fats)
assert fats == 2
root_entries = read_word(bsect, 0x11)
print('Root directory entries', root_entries)
assert (root_entries * 32) % sector_size == 0
fat_size = read_word(bsect, 0x16)
print('Sectors per FAT', fat_size)
fat = read_sectors(res_sect, fat_size)
assert fat == read_sectors(res_sect + fat_size, fat_size)
def get_fatent(i):
o = (i // 2) * 3;
if i % 2:
return (fat[o+2] << 4) | (fat[o+1] >> 4)
else:
return ((fat[o+1] & 0xF) << 8) | fat[o]
def read_cluster(num):
num -= 2
offs = sector_size * sec_per_clus * num + \
sector_size * (res_sect + fats * fat_size) + \
root_entries * 32
return b[offs:offs+sec_per_clus*sector_size]
def get_fat_chain(cluster):
data = b''
while True:
data += read_cluster(cluster)
cluster = get_fatent(cluster)
if cluster == 4095:
return data
d = read_sectors(res_sect + fat_size * 2, 5)
def get_dent(i):
dent = d[i*32:(i+1)*32]
if dent[0] == 0:
return False
elif dent[0] == 0xE5:
return True
name = dent[0:11].decode('ascii')
ext = name[8:11].rstrip(' ')
if ext != '':
ext = '.' + ext
name = name[0:8].rstrip(' ') + ext
print(name)
cluster = read_word(dent, 26)
data = get_fat_chain(cluster)
dlen = len(data)
size = read_word(dent, 28) + read_word(dent, 30) * 0x10000
print(f'Alloc {dlen}, size = {size}')
assert dlen >= size
#assert dlen - size < sec_per_clus * sector_size
with open(name, 'wb') as f:
f.write(data[0:size])
return True
for i in range(0, root_entries):
if not get_dent(i):
break
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment