Python构造 - 解析可变数量的可变长度记录

时间:2017-10-29 18:37:35

标签: python construct

我使用construct 2.8对一些长期丢失的Pascal程序创建的某些文件的标题进行反向工程。

标题由许多不同的记录组成,其中一些是可选的,我不确定订单是否已修复。

例如,其中两条记录如下:

record_type

我已经确认了六打。

如何让解析器根据selected()成员为未知数量的记录选择正确的记录类型,直到遇到类型为0的记录(或到达文件末尾)为止?

3 个答案:

答案 0 :(得分:1)

为了记录,我是Construct开发人员。如果您希望此代码与当前版本一致,那么:

  • 字符串类需要encoding,必需
  • Embedded不支持IfThenElseSwitch

答案 1 :(得分:0)

你选择了一个有趣的挑战。看起来构造确实支持各种条件定义:http://construct.readthedocs.io/en/latest/misc.html#conditional

此外,我找到了一些示例,例如:https://github.com/construct/construct/blob/master/construct/examples/formats/executable/elf32.py

我可能还会定义标题和正文类型,例如:

header_body_record_filetype = cs.Struct(
    'file_type' / cs.PascalString(cs.Int16ub),
    'unknown' / cs.Int8ub
)

header_body_record_user = cs.Struct(
    'user' / cs.PascalString(cs.Int16ub)
)

header_record = cs.Struct(
    'record_type' / cs.Int8ub,
    'body' / Embedded(IfThenElse(this.record_type == "user",
        header_body_record_user,
        header_body_record_filetype,
    ))
)

答案 2 :(得分:0)

我已经解决了这个问题:

header = cs.Struct(
    'record_type' / cs.Int8ub,
    'record' / cs.Switch(cs.this.record_type, {header_record_type_0x01: header_record_0x01,
                                               header_record_type_filename: header_record_filename,
                                               header_record_type_filetype: header_record_filetype,
                                               header_record_type_user: header_record_user,
                                               header_record_type_end: header_record_end,
                                               header_record_type_image_metadata: header_record_image_metadata},
                         default=header_record_end
                         ),
    'offset' / cs.Tell
)

with open(sys.argv[1], 'rb') as f:
    h = f.read(2048)
    index = 0
    record_type = h[index]

    while record_type != 0:
        record = header.parse(h[index:])
        print(record)
        index += record.offset
        record_type = record.record_type

但我不知道这是否是最佳方式。

*对于某些值"最佳"。

修改

我发现RepeatUntil()构造隐藏在帮助页面的底部。所以现在我有了这个:

header = cs.Struct(
    'type' / cs.Enum(cs.Int8ub,
                     file_metadata=0x01,
                     filename=0x02,
                     file_type=0x03,
                     user=0x0A,
                     image_metadata=0x10,
                     end=0xFF),

    'record' / cs.Switch(cs.this.type, {'file_metadata': header_record_file_metadata,
                                        'filename': header_record_filename,
                                        'file_type': header_record_filetype,
                                        'user': header_record_user,
                                        'end': header_record_end,
                                        'image_metadata': header_record_image_metadata}),
    'size' / cs.Tell
)

with open(sys.argv[1], 'rb') as f:
    h = f.read(2048)
    records = cs.RepeatUntil(lambda obj, lst, ctx: obj.type == 'end', header).parse(h)
    print(records)

感觉更清洁,更符合构造的声明性质。