一个分析和操作二进制数据的框架
bal的Python项目详细描述
二进制抽象层(bal)
二进制抽象层(bal)包是一个用于分析和操作的小框架 二进制数据。 其指导原则是树是二进制数据的自然表示。 例如,固件可能如下所示:
-
zip数据
- 标题
- 代码
- 数据
- 图像
- 配置
-
小精灵
它在树上定义了3大类操作:转换,分析和修改
- 转换器处理二进制数据的序列化和反序列化。
- 分析器处理从树表示中提取信息。
- 修饰符处理二进制文件的任意修改。
安装
可以使用以下命令从pypi安装bal包:
pip install bal
要从存储库安装BAL模块,请克隆repo并运行:
pip install .
要安装BAL软件包并生成其文档的本地副本,请运行:
pip install .[docs]
make html-docs
要安装示例的核心BAL模块和依赖项,请运行:
pip install .[examples]
概念
树中的每个节点都表示为一个dataobject
。
dataobject
可以包装原始二进制数据的非结构化字符串或datamodel
(或两者都包装)。
数据模型是定义某种结构化数据的抽象类。
在反序列化原始二进制数据时创建数据模型。
它符合数据模型的典型定义。
此外,bal框架还定义了几个接口:
bal.context\u ioc.abstractconverter
converter负责将字节解包到数据模型中 (即反序列化)并将其数据模型
打包成字节(即序列化)。其方法 签名是不灵活的,因此可以由dataobject
直接调用它们。bal.context\u ioc.abstractmodifier
修饰符更新树中任何节点的内容。 它可以修改打包或解包的数据。它包含一个 未定义的签名。它可能会走整棵树,在路上打开包装。bal.context\u ioc.abstractanalyzer
分析器从树中提取数据。类型 返回的数据由具体的分析器实现定义。它包含一个analyze()
具有未定义签名的方法。它可能会走整棵树,在路上打开包装。bal.context\u ioc.balioccontext
ioc上下文提供了 控制反转模式。它抬起头来 给定接口的实现并返回新实例。它用于实例化 一个abstractconverter
,abstractmodifier
或abstractanalyzer
。- 对于
abstractmodifier
s和abstractanalyzer
s,提供扩展abstractmodifier
或abstractanalyzer
的接口,并返回接口的实现。 - 对于
abstractconverters
,将提供扩展datamodel
的接口,并返回abstractconverter
实现。此实现的pack()
方法将创建提供的datamodel
接口的实例,其unpack()
方法将序列化提供的datamodel
接口的实例。
- 对于
bal.context\u ioc.balioccontextfactory
创建balioccontext
的配置实例。它 为用户提供注册接口实现的方法。bal.context.balcontext
为每个树创建一个新上下文。它继承自T他balioccontext
。上下文包含对根数据对象的引用。它可以用作 用于昂贵或经常调用的分析器的缓存。它也可以用来 存储不完全适合树的数据(例如不相关的 节点)bal.context.balcontextfactory
正如所暗示的,它负责创建balcontext
。这个 工厂是加载将传递到上下文的外部配置的好地方。在 大多数设置,工厂将在应用程序启动时创建,并在应用程序启动时销毁 模具:bal.context.bal manager
bal管理器提供了一种使用键查找工厂的方法。它是 不是严格必要的,应该只用于需要动态检索多个不同 上下文工厂
API的完整文档可在 github.io
指南
本指南的所有代码都包含在。/example文件夹中。
第一步是声明一个新的datamodel
类,该类定义根的数据结构
节点及其子节点。
例如,一个xilinx比特流有3个子:报头、同步标记和配置包。
报头的格式未知,同步标记没有格式,数据包
是未知数据的数组。
classXilinxPacketsInterface(DataModel):""" An array of Xilinx register configuration packets. """classXilinxBitstreamHeaderInterface(DataModel):""" The Xilinx bitstream header contains unknown information. """classXilinxBitstreamSyncMarkerInterface(DataModel):""" The Xilinx bitstream sync marker """classXilinxBitstream(ClassModel[DataObject]):""" The root model for a Xilinx bitstream. It contains data objects for a header, sync marker, and packets. """def__init__(self,header,sync_marker,packets):""" :param DataObject[XilinxBitstreamHeaderInterface] header: :param DataObject[XilinxBitstreamSyncMarker] sync_marker: :param DataObject[XilinxPackets] packets: """super(XilinxBitstream,self).__init__((("header",self.get_header),("sync_marker",self.get_sync_marker),("packets",self.get_packets),))self.header=headerself.sync_marker=sync_markerself.packets=packetsdefget_header(self):returnself.headerdefget_sync_marker(self):returnself.sync_markerdefget_packets(self):returnself.packets
重要的是要注意,即使孩子的结构是未知的,一个接口 仍然是为他们创造的。正如我们稍后将看到的,它允许外部开发人员稍后定义 它们的格式和转换器。
既然我们有了模型,我们就可以创建根转换器了:
classXilinxBitstreamConverter(AbstractConverter):""" Converter for a Xilinx FPGA bitstream :param BALContext context: The BAL context. """def__init__(self,context):super(XilinxBitstreamConverter,self).__init__(context)self.context=contextdefunpack(self,data_bytes):sync_marker=self.context.format.sync_wordsync_marker_index=data_bytes.find(sync_marker)assertsync_marker_index>=0, \ "The sync marker is not present in the provided bitstream data"assertsync_marker_index+len(sync_marker)<len(data_bytes)-2, \ "The configuration data is expected to contain at least one word size worth of data"returnXilinxBitstream(DataObject.create_packed(self.context,data_bytes[:sync_marker_index],XilinxBitstreamHeaderInterface),DataObject.create_packed(self.context,data_bytes[sync_marker_index:sync_marker_index+len(sync_marker)],XilinxBitstreamSyncMarkerInterface,),DataObject.create_packed(self.context,data_bytes[sync_marker_index+len(sync_marker):],XilinxPacketsInterface,))defpack(self,data_model):""" :param XilinxBitstream data_model: :rtype: bytes """assertisinstance(data_model,XilinxBitstream)returnb"".join([data_model.get_header().pack(),self.context.format.sync_word,data_model.get_packets().pack()])
这已经变得有点复杂了。
转换器采用balcontext
作为参数,这意味着转换器实例必须
专用于特定比特流。
unpack()
方法不实例化其任何子对象datamodel
,它只创建
dataobject
用于包装该模型的压缩数据。
它为dataobject
提供包装数据模型的接口。
dataobject
使用接口提取有关打包数据的基本信息(即类型
以及接口名及其docstring中的描述)。
它在解包时也使用接口,为此查找转换器实现
balcontext中的接口(请记住,它继承自balioccontext)。
这是一个重要的属性,因为它允许树"懒洋洋地"解包。
用户可以精确地控制给定的子项何时被解包(如果它被解包的话),这可以
在许多用例中都会带来显著的更好的性能。
最后,我们需要一个balcontext
和balfactorycontext
实现:
classXilinxContext(BALContext):""" :param Dict[Type[DataModel],Type[AbstractConverter]] converters_by_type: :param Dict[Type[AnalyzerInterface],Type[AbstractAnalyzer]] analyzers_by_type: :param Dict[Type[ModifierInterface],Type[AbstractModifier]] modifiers_by_type: :param bytes bytes: The bytes making up the bitstream. """def__init__(self,converters_by_type,analyzers_by_type,modifiers_by_type,bytes):super(XilinxContext,self).__init__(converters_by_type,analyzers_by_type,modifiers_by_type)self._bitstream=DataObject.create_packed(self,bytes,XilinxBitstream)defget_data(self):""" :rtype: DataObject[XilinxBitstream] """returnself._bitstreamclassXilinxContextFactory(BALContextFactory):def__init__(self):super(XilinxContextFactory,self).__init__()defcreate(self,data):""" :param bytes bytes: The bytes for the Xilinx FPGA bitstream :rtype: XilinxContext """returnXilinxContext(self._converters_by_type,self._analyzers_by_type,self._modifiers_by_type,data)
因为我们的xilinx实现非常有限,所以上下文和它的工厂都很简单。
让我们看看实际的实现:
importwgetcontext_factory=XilinxContextFactory()# Register the XilinxBitsreamConvertercontext_factory.register_converter(XilinxBitstream,XilinxBitstreamConverter)lx9_bin=wget.download('https://redballoonsecurity.com/files/JwfEU4veQSNFao8h/lx9.bin')withopen(lx9_bin,"rb")asf:data=f.read()context=context_factory.create(data)bitstream_object=context.get_data()print("Bitstream object: {}".format(bitstream_object))print("Bitstream model type: {}".format(bitstream_object.get_model_type()))print("Bitstream model description: {}".format(bitstream_object.get_model_description()))print("\nUNPACKING\n")bitstream_object.unpack()print("Bitstream object: {}".format(bitstream_object))print("\nHEADER\n")header_object=bitstream_object.get_model().get_header()print("Bitstream header object: {}".format(header_object))print("Bitstream header model type: {}".format(header_object.get_model_type()))print("Bitstream header model description: {}".format(header_object.get_model_description()))
此脚本应打印:
Bitstream object: PackedXilinxBitstream(340604)
Bitstream model type: XilinxBitstream
Bitstream model description: The root model for a Xilinx bitstream. It contains a header and packets data objects.
UNPACKING
Bitstream object: XilinxBitstream({
header: PackedXilinxBitstreamHeaderInterface(16),
sync_marker: PackedXilinxBitstreamSyncMarkerInterface(4),
packets: PackedXilinxPacketsInterface(340584),
})
HEADER
Bitstream header object: PackedXilinxBitstreamHeaderInterface(16)
Bitstream header model type: XilinxBitstreamHeaderInterface
Bitstream header model description: The Xilinx bitstream header contains unknown information.
从输出中可以看到,bal框架已经有了关于 比特流的结构。 它使用接口上定义的docstring来获取数据模型的描述,即使 它们还不能打开包装。
这是给这个向导的。 接下来的步骤可能是实现xilinxpacketsinterface,xilinxbitstreamheaderface, 和xilinxbitstreamsyncmarkerinterface接口并实现各自的转换器。 如果你想了解更多关于编写一个完整的转换器、分析器和修饰符链的知识, 转到bal-xilinx项目。