172 lines
4.6 KiB
Python
172 lines
4.6 KiB
Python
import os
|
|
import enum
|
|
from destruct import Struct
|
|
|
|
from .attributes import Attribute
|
|
|
|
class MFTFileType(enum.IntEnum):
|
|
MFT = 0
|
|
MFTMirror = 1
|
|
LogFile = 2
|
|
Volume = 3
|
|
AttributeDefinition = 4
|
|
Root = 5
|
|
Bitmap = 6
|
|
BootSector = 7
|
|
BadClusters = 8
|
|
Secure = 9
|
|
UpcaseTable = 10
|
|
Extension = 11
|
|
Reserved12 = 12
|
|
Reserved13 = 13
|
|
Reserved14 = 14
|
|
Reserved15 = 15
|
|
Normal = -1
|
|
|
|
@classmethod
|
|
def _missing_(cls, v):
|
|
return cls.Normal
|
|
|
|
class MFTAttributeFlag(enum.IntFlag):
|
|
Compressed = 1
|
|
Encrypted = 0x4000
|
|
Sparse = 0x8000
|
|
|
|
class MFTAttributeResidentData(Struct):
|
|
length = UInt(32)
|
|
offset = UInt(16)
|
|
indexed = UInt(8)
|
|
_pad7 = Pad(1)
|
|
value = Ref(Capped(Attribute), reference=os.SEEK_CUR)
|
|
|
|
def on_length(self, spec, context):
|
|
spec.value.child.limit = self.length
|
|
spec.value.child.child.selector = self._type
|
|
|
|
def on_offset(self, spec, context):
|
|
spec.value.point = self.offset - 0x18
|
|
|
|
def parse(self, input, context):
|
|
data = super().parse(input, context)
|
|
return data.value
|
|
|
|
class DataRun(Struct):
|
|
size = UInt(8)
|
|
length = UInt(0)
|
|
offset = Int(0)
|
|
|
|
def on_size(self, spec, context):
|
|
spec.length.n = 8 * (self.size & 0xF)
|
|
spec.offset.n = 8 * (self.size >> 4)
|
|
|
|
def parse(self, input, context):
|
|
value = super().parse(input, context)
|
|
if not value.size:
|
|
return None
|
|
if not value.offset:
|
|
value.offset = None
|
|
return value
|
|
|
|
class MFTAttributeNonResidentData(Struct):
|
|
vcn_first = UInt(64)
|
|
vcn_last = UInt(64)
|
|
run_offset = UInt(16)
|
|
unit_size = UInt(16)
|
|
_pad14 = Pad(4)
|
|
alloc_size = UInt(64)
|
|
real_size = UInt(64)
|
|
data_size = UInt(64)
|
|
runs = Ref(Arr(DataRun, stop_value=None), reference=os.SEEK_CUR)
|
|
|
|
def on_run_offset(self, spec, context):
|
|
spec.runs.point = self.run_offset - 0x40
|
|
|
|
def parse(self, input, context):
|
|
value = super().parse(input, context)
|
|
offset = 0
|
|
runs = []
|
|
for run in value.runs:
|
|
if run.offset is not None:
|
|
offset += run.offset
|
|
runs.append((run.length, offset))
|
|
else:
|
|
runs.append((run.length, None))
|
|
value.runs = runs
|
|
return value
|
|
|
|
class MFTAttributeData(Struct):
|
|
length = UInt(32)
|
|
nonresident = Bool()
|
|
name_length = UInt(8)
|
|
name_offset = UInt(16)
|
|
flags = Enum(MFTAttributeFlag, UInt(16))
|
|
id = UInt(16)
|
|
name = Ref(Str(kind='raw', exact=True, elem_size=2, encoding='utf-16le'))
|
|
data = Capped(Switch(options={
|
|
True: MFTAttributeResidentData(),
|
|
False: MFTAttributeNonResidentData(),
|
|
}), exact=True)
|
|
|
|
def on_nonresident(self, spec, context):
|
|
spec.data.child.selector = not self.nonresident
|
|
spec.data.child.current._type = self._type
|
|
|
|
def on_name_length(self, spec, context):
|
|
spec.name.child.length = self.name_length
|
|
spec.data.limit = max(0, self.length - 0x10)
|
|
|
|
def on_name_offset(self, spec, context):
|
|
spec.name.reference = os.SEEK_CUR
|
|
spec.name.point = self.name_offset - 0x10
|
|
|
|
def parse(self, input, context):
|
|
value = super().parse(input, context)
|
|
return (value.name, value.data)
|
|
|
|
class MFTAttribute(Struct):
|
|
type = UInt(32)
|
|
data = Switch(options={
|
|
True: MFTAttributeData(),
|
|
False: Nothing,
|
|
})
|
|
|
|
def on_type(self, spec, context):
|
|
spec.data.selector = self.type < 0x1000 # != 0xFFFFFFFF
|
|
spec.data.current._type = self.type
|
|
|
|
def parse(self, input, context):
|
|
value = super().parse(input, context)
|
|
return value.data
|
|
|
|
class MFTFlag(enum.IntFlag):
|
|
InUse = 1
|
|
Directory = 2
|
|
|
|
class MFTRecord(Struct):
|
|
magic = Sig(b'FILE')
|
|
update_offset = UInt(16)
|
|
fixup_count = UInt(16)
|
|
logfile_seq = UInt(64)
|
|
seq = UInt(16)
|
|
link_count = UInt(16)
|
|
attr_offset = UInt(16)
|
|
flags = Enum(MFTFlag, UInt(16))
|
|
size_used = UInt(32)
|
|
size_alloc = UInt(32)
|
|
record_ref = UInt(64)
|
|
next_attr_id = UInt(16)
|
|
pad2a = Pad(2)
|
|
number = Enum(MFTFileType, UInt(32))
|
|
attributes = Ref(Arr(MFTAttribute, stop_value=None))
|
|
|
|
def on_attr_offset(self, spec, context):
|
|
spec.attributes.reference = os.SEEK_CUR
|
|
spec.attributes.point = self.attr_offset - 0x30
|
|
|
|
def on_attributes(self, spec, context):
|
|
attrs = {}
|
|
for name, attr in self.attributes:
|
|
attrs.setdefault(name, [])
|
|
attrs[name].append(attr)
|
|
self.attributes = attrs
|