用给定的C头解压缩深度嵌套的struct到字典中?

时间:2014-08-20 13:23:17

标签: python struct unpack

我正在寻找一种解包二进制数据的方法。数据由C头文件中的整个结构树(最多四层深,总结构大小几乎为64k)描述。

对于这个问题:Unpacking nested C structs in Python唯一的答案是将整个数据描述“压扁”成一个字符串,但我不愿意这样做,因为它不可维护。

我喜欢的东西是读取头文件,然后神奇地创建一个函数,该函数获取二进制数据并返回包含所有数据的(正确嵌套的)字典。

是否有任何Python模块可以完成此任务,或者它的某些部分?

还有其他方法可以在Python中处理这些数据吗?

3 个答案:

答案 0 :(得分:5)

我不知道如何可靠地完成解析C头文件的困难部分,因为您甚至可能依赖于来自其他包含文件的声明,例如,您可以使用依赖于struct stat的{​​{1}} {1}}以及其他标准包含文件。但是我稍后会回到这一点......

因此,我建议您手动执行部分,并使用分层描述,以便从struct module返回的平面字段列表中重建层次结构。

假设我们有一个用C语言描述的结构:

sys/stat.h

我先用手写 我的初始结构描述:

struct S4 {
    char c;
    char d;
};
struct my_struct {
    int a[4];
    struct {
        int b;
        struct {
            struct S4 z[2];
            int e;
        } y;
    } x;
    int f;
    struct {
        int g;
        struct {
            struct S4 v;
            int j;
        } u;
    } t;
};

提供字段名称及其类型(以'''\ S4: { c: b, d: b, c: 2x} { a: 4h, x: { b: h, y: { z: 2 S4, e: h}}, f: h, t: { g: h, u: { v: S4, j: h}}}\ ''' 模块格式)

然后解析器(使用PLY)解析它并返回一个对象,该对象可以将二进制结构转换为namedtuple,其中包含正确名称的整个结构,所有内部结构也都是名称元组(struct格式字符串来作为子产品) - 所有正确管理显式填充 - 可以使用命名结构(示例中为struct),必须在使用前声明它们 - 数组(简单元素或命名结构)通过在类型之前添加数字来实现(S4< => a : 4 hshort a[4];< => {{1}在示例中)

但它不是一个单一的文件轻量级解决方案,它需要大量的评论和文档。另一种方法是直接使用x : 2 S4和原始的C头文件,但它会更复杂,我不太喜欢导入标准C库头的想法。无法解释原因,但我觉得不那么健壮

以下是使用示例:

struct S4 x[2];

如果您有兴趣,我可以把它放在github或......

让我们回到困难的部分。我做了一些研究,但现在我只能给你一些可能的指示,而且还会有繁重的工作。您可以尝试使用pycparser但它只知道C(而不是C ++)并且文档不是那么广泛。或者如果您需要C ++,您可以尝试在Eli Bendersky(pycparser的作者)网站Parsing C++ in Python with Clang 中提出的CLang Python绑定,但这里的文档几乎不存在。

现在对于代码,即使我仍然认为它是一项正在进行的工作,因为缺乏评论和正式测试 - 但它确实有效

  

h_lexer.py

pycparser
  

h_yacc.py:

>>> s = '''\
S4: { c: b, d: b, c: 2x}
{ a: 4h, x: { b: h, y: { z: 2 S4, e: h}}, f: h, t: { g: h, u: { v: S4, j: h}}}\
'''
>>> ns = Named_struct(s)
>>> import struct
>>> b = struct.pack(ns.fmt, * range(15))
>>> b
b'\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x06\x00\x00\x07\x08\x00\x00\t\x00\n\x00\x0b\x00\x0c\r\x00\x00\x0e\x00'
>>> t = ns.unpack(b)
>>> t
_X_(a=[0, 1, 2, 3], x=x(b=4, y=y(z=[x(c=5, d=6), x(c=7, d=8)], e=9)), f=10, t=t(g=11, u=u(v=v(c=12, d=13), j=14)))
>>> t.x.y.z[1]
x(c=7, d=8)
>>> t.x.y.z[1].d
8
  

h_named_struct.py

import ply.lex as lex

states = ( ('value', 'exclusive'),)
tokens = ( 'BEG', 'NAME', 'COLON', 'END', 'COMMA', 'TYPE', 'PAD', 'NUMBER')
linepos = 0

def t_COLON(t):
    r':'
    t.lexer.begin('value')
    return t

def t_ANY_BEG(t):
    r'{'
    t.lexer.begin('INITIAL')
    return t

def t_value_TYPE(t):
    r'[cbB?hHiIlLqQnNfdp]|\d*[sP]'
    t.lexer.begin('INITIAL')
    return t

def t_value_PAD(t):
    r'x'
    t.lexer.begin('INITIAL')
    return t

def t_value_NUMBER(t):
    r'\d+'
    return t

def t_ANY_NAME(t):
    r'\w+'
    t.lexer.begin('INITIAL')
    return t

t_COMMA = ','
t_ANY_ignore= ' \t\r'
t_END=r'}'

def t_newline(t):
    r'\n'
    t.lexer.lineno += 1
    global linepos
    linepos = t.lexpos

# Error handling rule
def t_ANY_error(t):
    print("Illegal character '%s'" % t.value[0])
    t.lexer.skip(1)

def getpos(lexer):
    global linepos
    return lexer.lineno, lexer.lexpos - linepos

答案 1 :(得分:5)

您可以使用cffi python模块来解释C struct定义并最终解析原始数据。以下是要解析的一组嵌套结构的示例:

from cffi import FFI
ffi = FFI()

## Define a set of (possibly nested) structures
ffi.cdef("""
  typedef struct { int32_t inner; } Ax;
  typedef struct { Ax a1; Ax a2; } Bx;
  typedef struct { Bx b1; Bx b2; } Cx;
  typedef struct { Cx c1; Cx c2; } Dx;
""")

def parse_Dx(raw_data):
    D_struct = ffi.new("Dx*")
    D_buffer = ffi.buffer(D_struct)
    D_buffer[:] = raw_data # Assign raw bytes
    return D_struct

## Make up some data (of the right length)
data = "\x01\x00\x00\x00" * (ffi.sizeof("Dx") / ffi.sizeof("int32_t")) 

structure = parse_Dx(data)
print structure.c1.b1.a1.inner ## Print "1"

这种方法存在一些缺陷:

  1. cffi模块依赖于存在的编译器和您 需要使用您的代码为所有平台分发二进制文件
  2. 机器" endianness"会影响数字的表示 在内存中以及如何解析它们
  3. 你没有得到结构字段的名称。
  4. 尽管如此,它可能会涵盖您的用例。

答案 2 :(得分:1)

这是一个旧线程,但是我遇到了同样的问题,没有找到任何支持嵌套结构的python库。

因此,我编写了pycstruct库。

它支持结构,联合,位域和枚举。它还支持任何级别的所有字节顺序,对齐方式和嵌套。