diff options
Diffstat (limited to 'external/construct/formats/filesystem')
-rw-r--r-- | external/construct/formats/filesystem/__init__.py | 4 | ||||
-rw-r--r-- | external/construct/formats/filesystem/ext2.py | 157 | ||||
-rw-r--r-- | external/construct/formats/filesystem/fat16.py | 226 | ||||
-rw-r--r-- | external/construct/formats/filesystem/mbr.py | 77 |
4 files changed, 464 insertions, 0 deletions
diff --git a/external/construct/formats/filesystem/__init__.py b/external/construct/formats/filesystem/__init__.py new file mode 100644 index 0000000..217ec83 --- /dev/null +++ b/external/construct/formats/filesystem/__init__.py @@ -0,0 +1,4 @@ +""" +file systems on-disk formats (ext2, fat32, ntfs, ...) +and related disk formats (mbr, ...) +""" diff --git a/external/construct/formats/filesystem/ext2.py b/external/construct/formats/filesystem/ext2.py new file mode 100644 index 0000000..954049e --- /dev/null +++ b/external/construct/formats/filesystem/ext2.py @@ -0,0 +1,157 @@ +""" +Extension 2 (ext2) +Used in Linux systems +""" +from construct import * + + +Char = SLInt8 +UChar = ULInt8 +Short = SLInt16 +UShort = ULInt16 +Long = SLInt32 +ULong = ULInt32 + +def BlockPointer(name): + return Struct(name, + ULong("block_number"), + OnDemandPointer(lambda ctx: ctx["block_number"]), + ) + +superblock = Struct("superblock", + ULong('inodes_count'), + ULong('blocks_count'), + ULong('reserved_blocks_count'), + ULong('free_blocks_count'), + ULong('free_inodes_count'), + ULong('first_data_block'), + Enum(ULong('log_block_size'), + OneKB = 0, + TwoKB = 1, + FourKB = 2, + ), + Long('log_frag_size'), + ULong('blocks_per_group'), + ULong('frags_per_group'), + ULong('inodes_per_group'), + ULong('mtime'), + ULong('wtime'), + UShort('mnt_count'), + Short('max_mnt_count'), + Const(UShort('magic'), 0xEF53), + UShort('state'), + UShort('errors'), + Padding(2), + ULong('lastcheck'), + ULong('checkinterval'), + ULong('creator_os'), + ULong('rev_level'), + Padding(235 * 4), +) + +group_descriptor = Struct("group_descriptor", + ULong('block_bitmap'), + ULong('inode_bitmap'), + ULong('inode_table'), + UShort('free_blocks_count'), + UShort('free_inodes_count'), + UShort('used_dirs_count'), + Padding(14), +) + +inode = Struct("inode", + FlagsEnum(UShort('mode'), + IXOTH = 0x0001, + IWOTH = 0x0002, + IROTH = 0x0004, + IRWXO = 0x0007, + IXGRP = 0x0008, + IWGRP = 0x0010, + IRGRP = 0x0020, + IRWXG = 0x0038, + IXUSR = 0x0040, + IWUSR = 0x0080, + IRUSR = 0x0100, + IRWXU = 0x01C0, + ISVTX = 0x0200, + ISGID = 0x0400, + ISUID = 0x0800, + IFIFO = 0x1000, + IFCHR = 0x2000, + IFDIR = 0x4000, + IFBLK = 0x6000, + IFREG = 0x8000, + IFLNK = 0xC000, + IFSOCK = 0xA000, + IFMT = 0xF000, + ), + UShort('uid'), + ULong('size'), + ULong('atime'), + ULong('ctime'), + ULong('mtime'), + ULong('dtime'), + UShort('gid'), + UShort('links_count'), + ULong('blocks'), + FlagsEnum(ULong('flags'), + SecureDelete = 0x0001, + AllowUndelete = 0x0002, + Compressed = 0x0004, + Synchronous = 0x0008, + ), + Padding(4), + Array(12, ULong('blocks')), + ULong("indirect1_block"), + ULong("indirect2_block"), + ULong("indirect3_block"), + ULong('version'), + ULong('file_acl'), + ULong('dir_acl'), + ULong('faddr'), + UChar('frag'), + Byte('fsize'), + Padding(10) , +) + +# special inodes +EXT2_BAD_INO = 1 +EXT2_ROOT_INO = 2 +EXT2_ACL_IDX_INO = 3 +EXT2_ACL_DATA_INO = 4 +EXT2_BOOT_LOADER_INO = 5 +EXT2_UNDEL_DIR_INO = 6 +EXT2_FIRST_INO = 11 + +directory_record = Struct("directory_entry", + ULong("inode"), + UShort("rec_length"), + UShort("name_length"), + Field("name", lambda ctx: ctx["name_length"]), + Padding(lambda ctx: ctx["rec_length"] - ctx["name_length"]) +) + +if __name__ == "__main__": + print (superblock.sizeof()) + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/construct/formats/filesystem/fat16.py b/external/construct/formats/filesystem/fat16.py new file mode 100644 index 0000000..5d6caf1 --- /dev/null +++ b/external/construct/formats/filesystem/fat16.py @@ -0,0 +1,226 @@ +# fat.py; ad-hoc fat16 reader +# by Bram Westerbaan <bram@westerbaan.name> +# +# references: +# http://en.wikipedia.org/wiki/File_Allocation_Table +# http://www.ecma-international.org/publications/standards/Ecma-107.htm +# +# example: +# with open("/dev/sdc1") as file: +# fs = FatFs(file) +# for rootdir in fs: +# print rootdir +import numbers +from io import BytesIO, BufferedReader +from construct import Struct, Byte, Bytes, ULInt16, ULInt32, Enum, \ + Array, Padding, Embed, Pass, BitStruct, Flag, Const + + +def Fat16Header(name): + return Struct(name, + Bytes("jumpInstruction", 3), + Bytes("creatingSystemId", 8), + ULInt16("sectorSize"), + Byte("sectorsPerCluster"), + ULInt16("reservedSectorCount"), + Byte("fatCount"), + ULInt16("rootdirEntryCount"), + ULInt16("sectorCount_small"), + Byte("mediaId"), + ULInt16("sectorsPerFat"), + ULInt16("sectorsPerTrack"), + ULInt16("sideCount"), + ULInt32("hiddenSectorCount"), + ULInt32("sectorCount_large"), + Byte("physicalDriveNumber"), + Byte("currentHead"), + Byte("extendedBootSignature"), + Bytes("volumeId", 4), + Bytes("volumeLabel", 11), + Const(Bytes("fsType", 8), "FAT16 "), + Bytes("bootCode", 448), + Const(Bytes("bootSectorSignature", 2), "\x55\xaa")) + +def BootSector(name): + header = Fat16Header("header") + return Struct(name, + Embed(header), + Padding(lambda ctx: ctx.sectorSize - header.sizeof())) + +def FatEntry(name): + return Enum(ULInt16(name), + free_cluster = 0x0000, + bad_cluster = 0xfff7, + last_cluster = 0xffff, + _default_ = Pass) + +def DirEntry(name): + return Struct(name, + Bytes("name", 8), + Bytes("extension", 3), + BitStruct("attributes", + Flag("unused"), + Flag("device"), + Flag("archive"), + Flag("subDirectory"), + Flag("volumeLabel"), + Flag("system"), + Flag("hidden"), + Flag("readonly")), + # reserved + Padding(10), + ULInt16("timeRecorded"), + ULInt16("dateRecorded"), + ULInt16("firstCluster"), + ULInt32("fileSize")) + +def PreDataRegion(name): + rde = DirEntry("rootdirs") + fe = FatEntry("fats") + return Struct(name, + Embed(BootSector("bootSector")), + # the remaining reserved sectors + Padding(lambda ctx: (ctx.reservedSectorCount - 1) + * ctx.sectorSize), + # file allocation tables + Array(lambda ctx: (ctx.fatCount), + Array(lambda ctx: ctx.sectorsPerFat * + ctx.sectorSize / fe.sizeof(), fe)), + # root directories + Array(lambda ctx: (ctx.rootdirEntryCount*rde.sizeof()) + / ctx.sectorSize, rde)) + +class File(object): + def __init__(self, dirEntry, fs): + self.fs = fs + self.dirEntry = dirEntry + + @classmethod + def fromDirEntry(cls, dirEntry, fs): + if dirEntry.name[0] in "\x00\xe5\x2e": + return None + a = dirEntry.attributes + #Long file name directory entry + if a.volumeLabel and a.system and a.hidden and a.readonly: + return None + if a.subDirectory: + return Directory(dirEntry, fs) + return File(dirEntry, fs) + + @classmethod + def fromDirEntries(cls, dirEntries, fs): + return filter(None, [cls.fromDirEntry(de, fs) + for de in dirEntries]) + + def toStream(self, stream): + self.fs.fileToStream(self.dirEntry.firstCluster, stream) + + @property + def name(self): + return "%s.%s" % (self.dirEntry.name.rstrip(), + self.dirEntry.extension) + + def __str__(self): + return "&%s %s" % (self.dirEntry.firstCluster, self.name) + +class Directory(File): + def __init__(self, dirEntry, fs, children=None): + File.__init__(self, dirEntry, fs) + self.children = children + if not self.children: + self.children = File.fromDirEntries(\ + self.fs.getDirEntries(\ + self.dirEntry.firstCluster), fs) + + @property + def name(self): + return self.dirEntry.name.rstrip() + + def __str__(self): + return "&%s %s/" % (self.dirEntry.firstCluster, self.name) + + def __getitem__(self, name): + for file in self.children: + if file.name == name: + return file + + def __iter__(self): + return iter(self.children) + +class FatFs(Directory): + def __init__(self, stream): + self.stream = stream + self.pdr = PreDataRegion("pdr").parse_stream(stream) + Directory.__init__(self, dirEntry = None, + fs = self, children = File.fromDirEntries( + self.pdr.rootdirs, self)) + + def fileToStream(self, clidx, stream): + for clidx in self.getLinkedClusters(clidx): + self.clusterToStream(clidx, stream) + + def clusterToStream(self, clidx, stream): + start, todo = self.getClusterSlice(clidx) + self.stream.seek(start) + while todo > 0: + read = self.stream.read(todo) + if not len(read): + print("failed to read %s bytes at %s" % (todo, self.stream.tell())) + raise EOFError() + todo -= len(read) + stream.write(read) + + def getClusterSlice(self, clidx): + startSector = self.pdr.reservedSectorCount \ + + self.pdr.fatCount * self.pdr.sectorsPerFat \ + + (self.pdr.rootdirEntryCount * 32) \ + / self.pdr.sectorSize \ + + (clidx-2) * self.pdr.sectorsPerCluster + start = startSector * self.pdr.sectorSize + length = self.pdr.sectorSize * self.pdr.sectorsPerCluster + return (start, length) + + def getLinkedClusters(self, clidx): + res = [] + while clidx != "last_cluster": + if not isinstance(clidx, numbers.Real): + print(clidx) + assert False + assert 2 <= clidx <= 0xffef + res.append(clidx) + clidx = self.getNextCluster(clidx) + assert clidx not in res + return res + + def getNextCluster(self, clidx): + ress = set([fat[clidx] for fat in self.pdr.fats]) + if len(ress)==1: + return ress.pop() + print("inconsistencie between FATs: %s points to" % clidx) + for i,fat in enumerate(self.pdr.fats): + print("\t%s according to fat #%s" % (fat[clidx], i)) + res = ress.pop() + print ("assuming %s" % res) + return res + + def getDirEntries(self, clidx): + try: + for de in self._getDirEntries(clidx): + yield de + except IOError: + print("failed to read directory entries at %s" % clidx) + + def _getDirEntries(self, clidx): + de = DirEntry("dirEntry") + with BytesIO() as mem: + self.fileToStream(clidx, mem) + mem.seek(0) + with BufferedReader(mem) as reader: + while reader.peek(1): + yield de.parse_stream(reader) + def __str__(self): + return "/" + + @property + def name(self): + return "" diff --git a/external/construct/formats/filesystem/mbr.py b/external/construct/formats/filesystem/mbr.py new file mode 100644 index 0000000..1fd5a62 --- /dev/null +++ b/external/construct/formats/filesystem/mbr.py @@ -0,0 +1,77 @@ +""" +Master Boot Record +The first sector on disk, contains the partition table, bootloader, et al. + +http://www.win.tue.nl/~aeb/partitions/partition_types-1.html +""" +from construct import * +from binascii import unhexlify +import six + + +mbr = Struct("mbr", + HexDumpAdapter(Bytes("bootloader_code", 446)), + Array(4, + Struct("partitions", + Enum(Byte("state"), + INACTIVE = 0x00, + ACTIVE = 0x80, + ), + BitStruct("beginning", + Octet("head"), + Bits("sect", 6), + Bits("cyl", 10), + ), + Enum(UBInt8("type"), + Nothing = 0x00, + FAT12 = 0x01, + XENIX_ROOT = 0x02, + XENIX_USR = 0x03, + FAT16_old = 0x04, + Extended_DOS = 0x05, + FAT16 = 0x06, + FAT32 = 0x0b, + FAT32_LBA = 0x0c, + NTFS = 0x07, + LINUX_SWAP = 0x82, + LINUX_NATIVE = 0x83, + _default_ = Pass, + ), + BitStruct("ending", + Octet("head"), + Bits("sect", 6), + Bits("cyl", 10), + ), + UBInt32("sector_offset"), # offset from MBR in sectors + UBInt32("size"), # in sectors + ) + ), + Const(Bytes("signature", 2), six.b("\x55\xAA")), +) + + + +if __name__ == "__main__": + cap1 = unhexlify(six.b( + "33C08ED0BC007CFB5007501FFCBE1B7CBF1B065057B9E501F3A4CBBDBE07B104386E00" + "7C09751383C510E2F4CD188BF583C610497419382C74F6A0B507B4078BF0AC3C0074FC" + "BB0700B40ECD10EBF2884E10E84600732AFE4610807E040B740B807E040C7405A0B607" + "75D2804602068346080683560A00E821007305A0B607EBBC813EFE7D55AA740B807E10" + "0074C8A0B707EBA98BFC1E578BF5CBBF05008A5600B408CD1372238AC1243F988ADE8A" + "FC43F7E38BD186D6B106D2EE42F7E239560A77237205394608731CB80102BB007C8B4E" + "028B5600CD1373514F744E32E48A5600CD13EBE48A560060BBAA55B441CD13723681FB" + "55AA7530F6C101742B61606A006A00FF760AFF76086A0068007C6A016A10B4428BF4CD" + "136161730E4F740B32E48A5600CD13EBD661F9C3496E76616C69642070617274697469" + "6F6E207461626C65004572726F72206C6F6164696E67206F7065726174696E67207379" + "7374656D004D697373696E67206F7065726174696E672073797374656D000000000000" + "0000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000002C4463B7BDB7BD00008001010007FEFFFF3F" + "000000371671020000C1FF0FFEFFFF761671028A8FDF06000000000000000000000000" + "000000000000000000000000000000000000000055AA")) + + print(mbr.parse(cap1)) + + + + + |