不知道这是不是复制品,反正我的Google-fu很弱,如果我输入超过五个单词,Google几乎找不到任何相关的东西.

我正在try 解析位于"//?/X:/$MFT"的主文件表,我知道try 直接打开它将提高PermissionDenied.当然,我已经想出了一种绕过它的方法.通过打开"//?/X:",这创建了一个句柄,允许我读取 bootstrap 扇区,然后我可以从那里读取MFT…

我已经编写了代码,或者至少其中的绝大多数,我已经可以将所有的MFT读取到主内存中,但在这个阶段,内存使用率很高,信息组织得不好.但在this documentation的帮助下,我已经解析了所有我想要的信息.

你可以看到我的代码here.

正如您从我的代码中看到的,我使用大量偏移量将字节分割成块,并调用相应的函数迭代地解码这些块,我将向您展示我的意思:

from typing import NamedTuple


class Record_Header_Flags(NamedTuple):
    In_Use: bool
    Directory: bool
    Extension: bool
    Special_Index: bool


class Record_Header(NamedTuple):
    LogFile_Serial: int
    Written: int
    Hardlinks: int
    Flags: Record_Header_Flags
    Record_Size: int
    Base_Record: int
    Base_Writes: int
    Record_ID: int


HEADER_FLAGS = (1, 2, 4, 8)


def parse_signed_little_endian(data: bytes) -> int:
    return (
        -1 * (1 + sum((b ^ 0xFF) * (1 << i * 8) for i, b in enumerate(data)))
        if data[-1] & 128
        else int.from_bytes(data, "little")
    )


def parse_little_endian(data: bytes) -> int:
    return int.from_bytes(data, "little")


def parse_header_flags(data: bytes) -> Record_Header_Flags:
    flag = data[0]
    return Record_Header_Flags(*(bool(flag & bit) for bit in HEADER_FLAGS))


FILE_RECORD_HEADER = (
    (8, 16, parse_little_endian),
    (16, 18, parse_little_endian),
    (18, 20, parse_little_endian),
    (22, 24, parse_header_flags),
    (24, 28, parse_little_endian),
    (32, 38, parse_little_endian),
    (38, 40, parse_little_endian),
    (44, 48, parse_little_endian),
)


def parse_record_header(data: bytes) -> Record_Header:
    return Record_Header(
        *(func(data[start:end]) for start, end, func in FILE_RECORD_HEADER)
    )


data = b"FILE0\x00\x03\x00\x9dt \x13\x0c\x00\x00\x00\x08\x00\x02\x008\x00\x01\x00\xd8\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\xff\xff\x00\x00"
print(parse_record_header(data))
Record_Header(LogFile_Serial=51860501661, Written=8, Hardlinks=2, Flags=Record_Header_Flags(In_Use=True, Directory=False, Extension=False, Special_Index=False), Record_Size=472, Base_Record=0, Base_Writes=0, Record_ID=65535)

有人告诉我,这是低效和不毕达式的,正确的方法是使用structctypes的组合.

我知道我可以使用struct.unpack("<i", data)[0]解析4字节的小端序列,格式为:"<I"的4字节无符号LE,"<q"的8字节LE和"<Q"的8字节ULE.但有些值是6字节的序列.

我不知道如何使用ctypes种 struct .

此外,MFT使用非标准格式,如Windows文件时间:

from datetime import datetime, timedelta
from typing import NamedTuple

EPOCH = datetime(1601, 1, 1, 0, 0, 0)

def parse_NTFS_timestamp(data: bytes) -> datetime:
    return EPOCH + timedelta(seconds=int.from_bytes(data, "little") * 1e-7)

如何使用ctypesstruct来解析我给出的示例,并解析包含非标准编码的字节序列,例如0x10$STANDARD_INFORMATION中的时间戳字段?

推荐答案

下面是structctypes解析数据的示例. 参考struct Format Charactersctypes Structures and unions:

import ctypes as ct
import struct
from collections import namedtuple

class RecordHeader(ct.Structure):
    _fields_ = (('pad1', 8 * ct.c_uint8),
                ('LogFile_Serial', ct.c_uint64),
                ('Written', ct.c_uint16),
                ('HardLinks', ct.c_uint16),
                ('pad2', 2 * ct.c_uint8),
                ('Flags', ct.c_uint16),
                ('Record_Size', ct.c_uint32),
                ('pad3', 4 * ct.c_uint8),
                ('_Base_Record_Base_Writes', ct.c_uint64),
                ('pad4', 4 * ct.c_uint8),
                ('Record_ID', ct.c_uint32))

    # ctypes can't handle a 6-byte field.  Read as 8-byte field and parse with
    # properties using bit shifting and masking.
    @property
    def Base_Record(self):
        return self._Base_Record_Base_Writes & 0xFFFFFFFFFFFF

    @property
    def Base_Writes(self):
        return self._Base_Record_Base_Writes >> 48

    def __repr__(self):
        '''Display representation of the structure'''
        # Gather relevant fields and values.
        slist = [(k, getattr(self,k)) for k, _ in self._fields_ if not k.startswith('pad')]
        params = ', '.join([f'{k}={v}' for k, v in slist])
        # format the parameters for a nice display.  Add the properties as well.
        return f'RecordHeader({params}, Base_Record={self.Base_Record}, Base_Writes={self.Base_Writes})'

# OP data, but modified to test the Base_Record/Base_Writes fields with something non-zero.
data = b'FILE0\x00\x03\x00\x9dt \x13\x0c\x00\x00\x00\x08\x00\x02\x008\x00\x01\x00\xd8\x01\x00\x00\x00\x04\x00\x00\x88\x77\x66\x55\x44\x33\x22\x11\x05\x00\x00\x00\xff\xff\x00\x00'

# struct can't handle a 6-byte field either.  Read as an 8-byte field.  Process later as needed.
result = struct.unpack('<8xQHH2xHL4xQ4xL', data)
RHeader = namedtuple('RHeader', 'LogFile_Serial Written HardLinks Flags Record_Size Base_Record_Base_Writes Record_ID')
print(RHeader(*result))

# ctypes Structure example.  Processes the 6-byte/2-byte field.
rh = RecordHeader.from_buffer_copy(data)
print(rh)
print(f'{rh._Base_Record_Base_Writes=:#x} {rh.Base_Record=:#x} {rh.Base_Writes=:#x}')

输出:

RHeader(LogFile_Serial=51860501661, Written=8, HardLinks=2, Flags=1, Record_Size=472, Base_Record_Base_Writes=1234605616436508552, Record_ID=65535)
RecordHeader(LogFile_Serial=51860501661, Written=8, HardLinks=2, Flags=1, Record_Size=472, _Base_Record_Base_Writes=1234605616436508552, Record_ID=65535, Base_Record=56368583571336, Base_Writes=4386)
rh._Base_Record_Base_Writes=0x1122334455667788 rh.Base_Record=0x334455667788 rh.Base_Writes=0x1122

请注意,解析时间戳是另一个问题.这样一来,每次只能坚持一个人.用specific个例子问另一个问题,如原始数据和预期结果.添加您的编码try .

Python相关问答推荐

Image Font生成带有条形码Code 128的条形码时出现枕头错误OSErsor:无法打开资源

有什么方法可以避免使用许多if陈述

使用新的类型语法正确注释ParamSecdecorator (3.12)

从收件箱中的列中删除html格式

log 1 p numpy的意外行为

用NumPy优化a[i] = a[i-1]*b[i] + c[i]的迭代计算

将9个3x3矩阵按特定顺序排列成9x9矩阵

计算天数

让函数调用方程

在两极中过滤

使用Python和文件进行模糊输出

通过ManyToMany字段与Through在Django Admin中过滤

matplotlib + python foor loop

如何使用使用来自其他列的值的公式更新一个rabrame列?

导入错误:无法导入名称';操作';

什么是一种快速而优雅的方式来转换一个包含一串重复的列,而不对同一个值多次运行转换,

一个telegram 机器人应该发送一个测验如何做?""

如何在Airflow执行日期中保留日期并将时间转换为00:00

多个矩阵的张量积

Python日志(log)库如何有效地获取lineno和funcName?