dissect.cstruct 4.0.dev3__tar.gz → 4.0.dev5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dissect_cstruct-4.0.dev3/dissect.cstruct.egg-info → dissect_cstruct-4.0.dev5}/PKG-INFO +53 -44
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/README.md +52 -43
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/compiler.py +8 -3
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5/dissect.cstruct.egg-info}/PKG-INFO +53 -44
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_bitfield.py +30 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_compiler.py +78 -1
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/COPYRIGHT +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/LICENSE +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/MANIFEST.in +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/__init__.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/bitbuffer.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/cstruct.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/exceptions.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/expression.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/parser.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/__init__.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/base.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/char.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/enum.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/flag.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/int.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/leb128.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/packed.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/pointer.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/structure.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/void.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/types/wchar.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect/cstruct/utils.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect.cstruct.egg-info/SOURCES.txt +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect.cstruct.egg-info/dependency_links.txt +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect.cstruct.egg-info/top_level.txt +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/examples/disk.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/examples/mirai.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/examples/pe.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/examples/protobuf.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/examples/secdesc.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/pyproject.toml +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/setup.cfg +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/__init__.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/conftest.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/data/testdef.txt +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/docs/Makefile +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/docs/conf.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/docs/index.rst +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_align.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_basic.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_bitbuffer.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_ctypes.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_expression.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_parser.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_base.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_char.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_custom.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_enum.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_flag.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_int.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_leb128.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_packed.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_pointer.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_structure.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_union.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_void.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_types_wchar.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/test_utils.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tests/utils.py +0 -0
- {dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dissect.cstruct
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.dev5
|
|
4
4
|
Summary: A Dissect module implementing a parser for C-like structures: structure parsing in Python made easy
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -84,12 +84,11 @@ refer to [the development guide](https://docs.dissect.tools/en/latest/contributi
|
|
|
84
84
|
All you need to do is instantiate a new cstruct instance and load some structure definitions in there. After that you can start using them from your Python code.
|
|
85
85
|
|
|
86
86
|
```python
|
|
87
|
-
from dissect import cstruct
|
|
87
|
+
from dissect.cstruct import cstruct
|
|
88
88
|
|
|
89
89
|
# Default endianness is LE, but can be configured using a kwarg or setting the 'endian' attribute
|
|
90
|
-
# e.g. cstruct
|
|
91
|
-
|
|
92
|
-
cparser.load("""
|
|
90
|
+
# e.g. cstruct(endian='>') or c_parser.endian = '>'
|
|
91
|
+
parser_def = """
|
|
93
92
|
#define SOME_CONSTANT 5
|
|
94
93
|
|
|
95
94
|
enum Example : uint16 {
|
|
@@ -99,31 +98,35 @@ enum Example : uint16 {
|
|
|
99
98
|
struct some_struct {
|
|
100
99
|
uint8 field_1;
|
|
101
100
|
char field_2[SOME_CONSTANT];
|
|
102
|
-
char field_3[field_1 & 1 * 5]; // Some random expression to calculate array length
|
|
101
|
+
char field_3[(field_1 & 1) * 5]; // Some random expression to calculate array length
|
|
103
102
|
Example field_4[2];
|
|
104
103
|
};
|
|
105
|
-
"""
|
|
104
|
+
"""
|
|
105
|
+
c_parser = cstruct().load(parser_def)
|
|
106
106
|
|
|
107
|
-
data = b
|
|
108
|
-
result =
|
|
107
|
+
data = b"\x01helloworld\x00\x00\x06\x00"
|
|
108
|
+
result = c_parser.some_struct(data) # Also accepts file-like objects
|
|
109
109
|
assert result.field_1 == 0x01
|
|
110
|
-
assert result.field_2 == b
|
|
111
|
-
assert result.field_3 == b
|
|
112
|
-
assert result.field_4 == [
|
|
110
|
+
assert result.field_2 == b"hello"
|
|
111
|
+
assert result.field_3 == b"world"
|
|
112
|
+
assert result.field_4 == [c_parser.Example.A, c_parser.Example.C]
|
|
113
113
|
|
|
114
|
-
assert
|
|
115
|
-
assert
|
|
116
|
-
assert
|
|
114
|
+
assert c_parser.Example.A == 0
|
|
115
|
+
assert c_parser.Example.C == 6
|
|
116
|
+
assert c_parser.Example(5) == c_parser.Example.B
|
|
117
117
|
|
|
118
118
|
assert result.dumps() == data
|
|
119
119
|
|
|
120
120
|
# You can also instantiate structures from Python by using kwargs
|
|
121
121
|
# Note that array sizes are not enforced
|
|
122
|
-
instance =
|
|
123
|
-
|
|
122
|
+
instance = c_parser.some_struct(
|
|
123
|
+
field_1=5, field_2="lorem", field_3="ipsum", field_4=[c_parser.Example.B, c_parser.Example.A]
|
|
124
|
+
)
|
|
125
|
+
assert instance.dumps() == b"\x05loremipsum\x05\x00\x00\x00"
|
|
126
|
+
|
|
124
127
|
```
|
|
125
128
|
|
|
126
|
-
By default, all structures are compiled into classes that provide optimised performance. You can disable this by passing a `compiled=False` keyword argument to the `.load()` call. You can also inspect the resulting source code by accessing the source attribute of the structure: `print(
|
|
129
|
+
By default, all structures are compiled into classes that provide optimised performance. You can disable this by passing a `compiled=False` keyword argument to the `.load()` call. You can also inspect the resulting source code by accessing the source attribute of the structure: `print(c_parser.some_struct.source)`.
|
|
127
130
|
|
|
128
131
|
More examples can be found in the `examples` directory.
|
|
129
132
|
|
|
@@ -135,20 +138,23 @@ Write simple C-like structures and use them to parse binary data, as can be seen
|
|
|
135
138
|
Aside from loading structure definitions, any of the supported types can be used individually for parsing data. For example, the following is all supported:
|
|
136
139
|
|
|
137
140
|
```python
|
|
138
|
-
from dissect import cstruct
|
|
139
|
-
|
|
141
|
+
from dissect.cstruct import cstruct
|
|
142
|
+
|
|
143
|
+
cs = cstruct()
|
|
140
144
|
# Default endianness is LE, but can be configured using a kwarg or setting the attribute
|
|
141
|
-
# e.g. cstruct
|
|
142
|
-
assert cs.uint32(b
|
|
143
|
-
assert cs.uint24[2](b
|
|
144
|
-
assert cs.char[None](b
|
|
145
|
+
# e.g. cstruct(endian='>') or cs.endian = '>'
|
|
146
|
+
assert cs.uint32(b"\x05\x00\x00\x00") == 5
|
|
147
|
+
assert cs.uint24[2](b"\x01\x00\x00\x02\x00\x00") == [1, 2] # You can also parse arrays using list indexing
|
|
148
|
+
assert cs.char[None](b"hello world!\x00") == b"hello world!" # A list index of None means null terminated
|
|
145
149
|
```
|
|
146
150
|
|
|
147
151
|
### Unions and nested structures
|
|
148
152
|
Unions and nested structures are supported, both anonymous and named.
|
|
149
153
|
|
|
150
154
|
```python
|
|
151
|
-
|
|
155
|
+
from dissect.cstruct import cstruct
|
|
156
|
+
|
|
157
|
+
c_def = """
|
|
152
158
|
struct test_union {
|
|
153
159
|
char magic[4];
|
|
154
160
|
union {
|
|
@@ -173,58 +179,61 @@ struct test_anonymous {
|
|
|
173
179
|
};
|
|
174
180
|
};
|
|
175
181
|
"""
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
# Default endianness is LE, but can be configured using a kwarg or setting the attribute
|
|
183
|
+
# e.g. cstruct(endian='>') or cs.endian = '>'
|
|
184
|
+
c = cstruct().load(c_def)
|
|
178
185
|
|
|
179
186
|
assert len(c.test_union) == 12
|
|
180
187
|
|
|
181
|
-
a = c.test_union(b
|
|
182
|
-
assert a.magic == b
|
|
188
|
+
a = c.test_union(b"ohaideadbeef")
|
|
189
|
+
assert a.magic == b"ohai"
|
|
183
190
|
assert a.c.a.a == 0x64616564
|
|
184
191
|
assert a.c.a.b == 0x66656562
|
|
185
|
-
assert a.c.b.b == b
|
|
192
|
+
assert a.c.b.b == b"deadbeef"
|
|
186
193
|
|
|
187
|
-
assert a.dumps() == b
|
|
194
|
+
assert a.dumps() == b"ohaideadbeef"
|
|
188
195
|
|
|
189
|
-
b = c.test_anonymous(b
|
|
190
|
-
assert b.magic == b
|
|
196
|
+
b = c.test_anonymous(b"ohai\x39\x05\x00\x00\x28\x23\x00\x00deadbeef")
|
|
197
|
+
assert b.magic == b"ohai"
|
|
191
198
|
assert b.a == 1337
|
|
192
199
|
assert b.b == 9000
|
|
193
|
-
assert b.c == b
|
|
200
|
+
assert b.c == b"deadbeef"
|
|
201
|
+
|
|
194
202
|
```
|
|
195
203
|
|
|
196
204
|
### Parse bit fields
|
|
197
205
|
Bit fields are supported as part of structures. They are properly aligned to their boundaries.
|
|
198
206
|
|
|
199
207
|
```python
|
|
200
|
-
|
|
208
|
+
from dissect.cstruct import cstruct
|
|
209
|
+
|
|
210
|
+
bit_def = """
|
|
201
211
|
struct test {
|
|
202
212
|
uint16 a:1;
|
|
203
|
-
uint16 b:1;
|
|
204
|
-
uint32 c;
|
|
213
|
+
uint16 b:1; // Read 2 bits from an uint16
|
|
214
|
+
uint32 c; // The next field is properly aligned
|
|
205
215
|
uint16 d:2;
|
|
206
216
|
uint16 e:3;
|
|
207
217
|
};
|
|
208
218
|
"""
|
|
209
|
-
bitfields = cstruct.
|
|
210
|
-
bitfields.load(bitdef)
|
|
219
|
+
bitfields = cstruct().load(bit_def)
|
|
211
220
|
|
|
212
|
-
d = b
|
|
221
|
+
d = b"\x03\x00\xff\x00\x00\x00\x1f\x00"
|
|
213
222
|
a = bitfields.test(d)
|
|
214
223
|
|
|
215
224
|
assert a.a == 0b1
|
|
216
225
|
assert a.b == 0b1
|
|
217
|
-
assert a.c ==
|
|
226
|
+
assert a.c == 0xFF
|
|
218
227
|
assert a.d == 0b11
|
|
219
228
|
assert a.e == 0b111
|
|
220
229
|
assert a.dumps() == d
|
|
221
230
|
```
|
|
222
231
|
|
|
223
|
-
### Enums
|
|
224
|
-
The API to access enum members and their values
|
|
232
|
+
### Enums and Flags
|
|
233
|
+
The API to access enum and flag members and their values in the same way as the native Enum types in Python 3. Functionally, it's best comparable to the IntEnum or IntFlag type.
|
|
225
234
|
|
|
226
235
|
### Custom types
|
|
227
|
-
You can implement your own types by subclassing `BaseType
|
|
236
|
+
You can implement your own types by subclassing `BaseType`, and adding them to your cstruct instance with `add_custom_type(name, type, size, alignment, ...)`
|
|
228
237
|
|
|
229
238
|
### Custom definition parsers
|
|
230
239
|
Don't like the C-like definition syntax? Write your own syntax parser!
|
|
@@ -59,12 +59,11 @@ refer to [the development guide](https://docs.dissect.tools/en/latest/contributi
|
|
|
59
59
|
All you need to do is instantiate a new cstruct instance and load some structure definitions in there. After that you can start using them from your Python code.
|
|
60
60
|
|
|
61
61
|
```python
|
|
62
|
-
from dissect import cstruct
|
|
62
|
+
from dissect.cstruct import cstruct
|
|
63
63
|
|
|
64
64
|
# Default endianness is LE, but can be configured using a kwarg or setting the 'endian' attribute
|
|
65
|
-
# e.g. cstruct
|
|
66
|
-
|
|
67
|
-
cparser.load("""
|
|
65
|
+
# e.g. cstruct(endian='>') or c_parser.endian = '>'
|
|
66
|
+
parser_def = """
|
|
68
67
|
#define SOME_CONSTANT 5
|
|
69
68
|
|
|
70
69
|
enum Example : uint16 {
|
|
@@ -74,31 +73,35 @@ enum Example : uint16 {
|
|
|
74
73
|
struct some_struct {
|
|
75
74
|
uint8 field_1;
|
|
76
75
|
char field_2[SOME_CONSTANT];
|
|
77
|
-
char field_3[field_1 & 1 * 5]; // Some random expression to calculate array length
|
|
76
|
+
char field_3[(field_1 & 1) * 5]; // Some random expression to calculate array length
|
|
78
77
|
Example field_4[2];
|
|
79
78
|
};
|
|
80
|
-
"""
|
|
79
|
+
"""
|
|
80
|
+
c_parser = cstruct().load(parser_def)
|
|
81
81
|
|
|
82
|
-
data = b
|
|
83
|
-
result =
|
|
82
|
+
data = b"\x01helloworld\x00\x00\x06\x00"
|
|
83
|
+
result = c_parser.some_struct(data) # Also accepts file-like objects
|
|
84
84
|
assert result.field_1 == 0x01
|
|
85
|
-
assert result.field_2 == b
|
|
86
|
-
assert result.field_3 == b
|
|
87
|
-
assert result.field_4 == [
|
|
85
|
+
assert result.field_2 == b"hello"
|
|
86
|
+
assert result.field_3 == b"world"
|
|
87
|
+
assert result.field_4 == [c_parser.Example.A, c_parser.Example.C]
|
|
88
88
|
|
|
89
|
-
assert
|
|
90
|
-
assert
|
|
91
|
-
assert
|
|
89
|
+
assert c_parser.Example.A == 0
|
|
90
|
+
assert c_parser.Example.C == 6
|
|
91
|
+
assert c_parser.Example(5) == c_parser.Example.B
|
|
92
92
|
|
|
93
93
|
assert result.dumps() == data
|
|
94
94
|
|
|
95
95
|
# You can also instantiate structures from Python by using kwargs
|
|
96
96
|
# Note that array sizes are not enforced
|
|
97
|
-
instance =
|
|
98
|
-
|
|
97
|
+
instance = c_parser.some_struct(
|
|
98
|
+
field_1=5, field_2="lorem", field_3="ipsum", field_4=[c_parser.Example.B, c_parser.Example.A]
|
|
99
|
+
)
|
|
100
|
+
assert instance.dumps() == b"\x05loremipsum\x05\x00\x00\x00"
|
|
101
|
+
|
|
99
102
|
```
|
|
100
103
|
|
|
101
|
-
By default, all structures are compiled into classes that provide optimised performance. You can disable this by passing a `compiled=False` keyword argument to the `.load()` call. You can also inspect the resulting source code by accessing the source attribute of the structure: `print(
|
|
104
|
+
By default, all structures are compiled into classes that provide optimised performance. You can disable this by passing a `compiled=False` keyword argument to the `.load()` call. You can also inspect the resulting source code by accessing the source attribute of the structure: `print(c_parser.some_struct.source)`.
|
|
102
105
|
|
|
103
106
|
More examples can be found in the `examples` directory.
|
|
104
107
|
|
|
@@ -110,20 +113,23 @@ Write simple C-like structures and use them to parse binary data, as can be seen
|
|
|
110
113
|
Aside from loading structure definitions, any of the supported types can be used individually for parsing data. For example, the following is all supported:
|
|
111
114
|
|
|
112
115
|
```python
|
|
113
|
-
from dissect import cstruct
|
|
114
|
-
|
|
116
|
+
from dissect.cstruct import cstruct
|
|
117
|
+
|
|
118
|
+
cs = cstruct()
|
|
115
119
|
# Default endianness is LE, but can be configured using a kwarg or setting the attribute
|
|
116
|
-
# e.g. cstruct
|
|
117
|
-
assert cs.uint32(b
|
|
118
|
-
assert cs.uint24[2](b
|
|
119
|
-
assert cs.char[None](b
|
|
120
|
+
# e.g. cstruct(endian='>') or cs.endian = '>'
|
|
121
|
+
assert cs.uint32(b"\x05\x00\x00\x00") == 5
|
|
122
|
+
assert cs.uint24[2](b"\x01\x00\x00\x02\x00\x00") == [1, 2] # You can also parse arrays using list indexing
|
|
123
|
+
assert cs.char[None](b"hello world!\x00") == b"hello world!" # A list index of None means null terminated
|
|
120
124
|
```
|
|
121
125
|
|
|
122
126
|
### Unions and nested structures
|
|
123
127
|
Unions and nested structures are supported, both anonymous and named.
|
|
124
128
|
|
|
125
129
|
```python
|
|
126
|
-
|
|
130
|
+
from dissect.cstruct import cstruct
|
|
131
|
+
|
|
132
|
+
c_def = """
|
|
127
133
|
struct test_union {
|
|
128
134
|
char magic[4];
|
|
129
135
|
union {
|
|
@@ -148,58 +154,61 @@ struct test_anonymous {
|
|
|
148
154
|
};
|
|
149
155
|
};
|
|
150
156
|
"""
|
|
151
|
-
|
|
152
|
-
|
|
157
|
+
# Default endianness is LE, but can be configured using a kwarg or setting the attribute
|
|
158
|
+
# e.g. cstruct(endian='>') or cs.endian = '>'
|
|
159
|
+
c = cstruct().load(c_def)
|
|
153
160
|
|
|
154
161
|
assert len(c.test_union) == 12
|
|
155
162
|
|
|
156
|
-
a = c.test_union(b
|
|
157
|
-
assert a.magic == b
|
|
163
|
+
a = c.test_union(b"ohaideadbeef")
|
|
164
|
+
assert a.magic == b"ohai"
|
|
158
165
|
assert a.c.a.a == 0x64616564
|
|
159
166
|
assert a.c.a.b == 0x66656562
|
|
160
|
-
assert a.c.b.b == b
|
|
167
|
+
assert a.c.b.b == b"deadbeef"
|
|
161
168
|
|
|
162
|
-
assert a.dumps() == b
|
|
169
|
+
assert a.dumps() == b"ohaideadbeef"
|
|
163
170
|
|
|
164
|
-
b = c.test_anonymous(b
|
|
165
|
-
assert b.magic == b
|
|
171
|
+
b = c.test_anonymous(b"ohai\x39\x05\x00\x00\x28\x23\x00\x00deadbeef")
|
|
172
|
+
assert b.magic == b"ohai"
|
|
166
173
|
assert b.a == 1337
|
|
167
174
|
assert b.b == 9000
|
|
168
|
-
assert b.c == b
|
|
175
|
+
assert b.c == b"deadbeef"
|
|
176
|
+
|
|
169
177
|
```
|
|
170
178
|
|
|
171
179
|
### Parse bit fields
|
|
172
180
|
Bit fields are supported as part of structures. They are properly aligned to their boundaries.
|
|
173
181
|
|
|
174
182
|
```python
|
|
175
|
-
|
|
183
|
+
from dissect.cstruct import cstruct
|
|
184
|
+
|
|
185
|
+
bit_def = """
|
|
176
186
|
struct test {
|
|
177
187
|
uint16 a:1;
|
|
178
|
-
uint16 b:1;
|
|
179
|
-
uint32 c;
|
|
188
|
+
uint16 b:1; // Read 2 bits from an uint16
|
|
189
|
+
uint32 c; // The next field is properly aligned
|
|
180
190
|
uint16 d:2;
|
|
181
191
|
uint16 e:3;
|
|
182
192
|
};
|
|
183
193
|
"""
|
|
184
|
-
bitfields = cstruct.
|
|
185
|
-
bitfields.load(bitdef)
|
|
194
|
+
bitfields = cstruct().load(bit_def)
|
|
186
195
|
|
|
187
|
-
d = b
|
|
196
|
+
d = b"\x03\x00\xff\x00\x00\x00\x1f\x00"
|
|
188
197
|
a = bitfields.test(d)
|
|
189
198
|
|
|
190
199
|
assert a.a == 0b1
|
|
191
200
|
assert a.b == 0b1
|
|
192
|
-
assert a.c ==
|
|
201
|
+
assert a.c == 0xFF
|
|
193
202
|
assert a.d == 0b11
|
|
194
203
|
assert a.e == 0b111
|
|
195
204
|
assert a.dumps() == d
|
|
196
205
|
```
|
|
197
206
|
|
|
198
|
-
### Enums
|
|
199
|
-
The API to access enum members and their values
|
|
207
|
+
### Enums and Flags
|
|
208
|
+
The API to access enum and flag members and their values in the same way as the native Enum types in Python 3. Functionally, it's best comparable to the IntEnum or IntFlag type.
|
|
200
209
|
|
|
201
210
|
### Custom types
|
|
202
|
-
You can implement your own types by subclassing `BaseType
|
|
211
|
+
You can implement your own types by subclassing `BaseType`, and adding them to your cstruct instance with `add_custom_type(name, type, size, alignment, ...)`
|
|
203
212
|
|
|
204
213
|
### Custom definition parsers
|
|
205
214
|
Don't like the C-like definition syntax? Write your own syntax parser!
|
|
@@ -26,6 +26,7 @@ from dissect.cstruct.types import (
|
|
|
26
26
|
Wchar,
|
|
27
27
|
WcharArray,
|
|
28
28
|
)
|
|
29
|
+
from dissect.cstruct.types.enum import EnumMetaType
|
|
29
30
|
from dissect.cstruct.types.packed import _struct
|
|
30
31
|
|
|
31
32
|
if TYPE_CHECKING:
|
|
@@ -108,6 +109,7 @@ class _ReadSourceGenerator:
|
|
|
108
109
|
preamble = """
|
|
109
110
|
r = {}
|
|
110
111
|
s = {}
|
|
112
|
+
o = stream.tell()
|
|
111
113
|
"""
|
|
112
114
|
|
|
113
115
|
if any(field.bits for field in self.fields):
|
|
@@ -149,7 +151,7 @@ class _ReadSourceGenerator:
|
|
|
149
151
|
|
|
150
152
|
if field.offset is not None and field.offset != current_offset:
|
|
151
153
|
# If a field has a set offset and it's not the same as the current tracked offset, seek to it
|
|
152
|
-
yield f"stream.seek({field.offset})"
|
|
154
|
+
yield f"stream.seek(o + {field.offset})"
|
|
153
155
|
current_offset = field.offset
|
|
154
156
|
|
|
155
157
|
if self.align and field.offset is None:
|
|
@@ -158,6 +160,9 @@ class _ReadSourceGenerator:
|
|
|
158
160
|
for field in self.fields:
|
|
159
161
|
field_type = self.cs.resolve(field.type)
|
|
160
162
|
|
|
163
|
+
if isinstance(field_type, EnumMetaType):
|
|
164
|
+
field_type = field_type.type
|
|
165
|
+
|
|
161
166
|
if not issubclass(field_type, SUPPORTED_TYPES):
|
|
162
167
|
raise TypeError(f"Unsupported type for compiler: {field_type}")
|
|
163
168
|
|
|
@@ -190,10 +195,10 @@ class _ReadSourceGenerator:
|
|
|
190
195
|
# Bit fields
|
|
191
196
|
elif field.bits:
|
|
192
197
|
if not prev_was_bits:
|
|
193
|
-
prev_bits_type =
|
|
198
|
+
prev_bits_type = field_type
|
|
194
199
|
prev_was_bits = True
|
|
195
200
|
|
|
196
|
-
if bits_remaining == 0 or prev_bits_type !=
|
|
201
|
+
if bits_remaining == 0 or prev_bits_type != field_type:
|
|
197
202
|
bits_remaining = (size * 8) - field.bits
|
|
198
203
|
bits_rollover = True
|
|
199
204
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dissect.cstruct
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.dev5
|
|
4
4
|
Summary: A Dissect module implementing a parser for C-like structures: structure parsing in Python made easy
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -84,12 +84,11 @@ refer to [the development guide](https://docs.dissect.tools/en/latest/contributi
|
|
|
84
84
|
All you need to do is instantiate a new cstruct instance and load some structure definitions in there. After that you can start using them from your Python code.
|
|
85
85
|
|
|
86
86
|
```python
|
|
87
|
-
from dissect import cstruct
|
|
87
|
+
from dissect.cstruct import cstruct
|
|
88
88
|
|
|
89
89
|
# Default endianness is LE, but can be configured using a kwarg or setting the 'endian' attribute
|
|
90
|
-
# e.g. cstruct
|
|
91
|
-
|
|
92
|
-
cparser.load("""
|
|
90
|
+
# e.g. cstruct(endian='>') or c_parser.endian = '>'
|
|
91
|
+
parser_def = """
|
|
93
92
|
#define SOME_CONSTANT 5
|
|
94
93
|
|
|
95
94
|
enum Example : uint16 {
|
|
@@ -99,31 +98,35 @@ enum Example : uint16 {
|
|
|
99
98
|
struct some_struct {
|
|
100
99
|
uint8 field_1;
|
|
101
100
|
char field_2[SOME_CONSTANT];
|
|
102
|
-
char field_3[field_1 & 1 * 5]; // Some random expression to calculate array length
|
|
101
|
+
char field_3[(field_1 & 1) * 5]; // Some random expression to calculate array length
|
|
103
102
|
Example field_4[2];
|
|
104
103
|
};
|
|
105
|
-
"""
|
|
104
|
+
"""
|
|
105
|
+
c_parser = cstruct().load(parser_def)
|
|
106
106
|
|
|
107
|
-
data = b
|
|
108
|
-
result =
|
|
107
|
+
data = b"\x01helloworld\x00\x00\x06\x00"
|
|
108
|
+
result = c_parser.some_struct(data) # Also accepts file-like objects
|
|
109
109
|
assert result.field_1 == 0x01
|
|
110
|
-
assert result.field_2 == b
|
|
111
|
-
assert result.field_3 == b
|
|
112
|
-
assert result.field_4 == [
|
|
110
|
+
assert result.field_2 == b"hello"
|
|
111
|
+
assert result.field_3 == b"world"
|
|
112
|
+
assert result.field_4 == [c_parser.Example.A, c_parser.Example.C]
|
|
113
113
|
|
|
114
|
-
assert
|
|
115
|
-
assert
|
|
116
|
-
assert
|
|
114
|
+
assert c_parser.Example.A == 0
|
|
115
|
+
assert c_parser.Example.C == 6
|
|
116
|
+
assert c_parser.Example(5) == c_parser.Example.B
|
|
117
117
|
|
|
118
118
|
assert result.dumps() == data
|
|
119
119
|
|
|
120
120
|
# You can also instantiate structures from Python by using kwargs
|
|
121
121
|
# Note that array sizes are not enforced
|
|
122
|
-
instance =
|
|
123
|
-
|
|
122
|
+
instance = c_parser.some_struct(
|
|
123
|
+
field_1=5, field_2="lorem", field_3="ipsum", field_4=[c_parser.Example.B, c_parser.Example.A]
|
|
124
|
+
)
|
|
125
|
+
assert instance.dumps() == b"\x05loremipsum\x05\x00\x00\x00"
|
|
126
|
+
|
|
124
127
|
```
|
|
125
128
|
|
|
126
|
-
By default, all structures are compiled into classes that provide optimised performance. You can disable this by passing a `compiled=False` keyword argument to the `.load()` call. You can also inspect the resulting source code by accessing the source attribute of the structure: `print(
|
|
129
|
+
By default, all structures are compiled into classes that provide optimised performance. You can disable this by passing a `compiled=False` keyword argument to the `.load()` call. You can also inspect the resulting source code by accessing the source attribute of the structure: `print(c_parser.some_struct.source)`.
|
|
127
130
|
|
|
128
131
|
More examples can be found in the `examples` directory.
|
|
129
132
|
|
|
@@ -135,20 +138,23 @@ Write simple C-like structures and use them to parse binary data, as can be seen
|
|
|
135
138
|
Aside from loading structure definitions, any of the supported types can be used individually for parsing data. For example, the following is all supported:
|
|
136
139
|
|
|
137
140
|
```python
|
|
138
|
-
from dissect import cstruct
|
|
139
|
-
|
|
141
|
+
from dissect.cstruct import cstruct
|
|
142
|
+
|
|
143
|
+
cs = cstruct()
|
|
140
144
|
# Default endianness is LE, but can be configured using a kwarg or setting the attribute
|
|
141
|
-
# e.g. cstruct
|
|
142
|
-
assert cs.uint32(b
|
|
143
|
-
assert cs.uint24[2](b
|
|
144
|
-
assert cs.char[None](b
|
|
145
|
+
# e.g. cstruct(endian='>') or cs.endian = '>'
|
|
146
|
+
assert cs.uint32(b"\x05\x00\x00\x00") == 5
|
|
147
|
+
assert cs.uint24[2](b"\x01\x00\x00\x02\x00\x00") == [1, 2] # You can also parse arrays using list indexing
|
|
148
|
+
assert cs.char[None](b"hello world!\x00") == b"hello world!" # A list index of None means null terminated
|
|
145
149
|
```
|
|
146
150
|
|
|
147
151
|
### Unions and nested structures
|
|
148
152
|
Unions and nested structures are supported, both anonymous and named.
|
|
149
153
|
|
|
150
154
|
```python
|
|
151
|
-
|
|
155
|
+
from dissect.cstruct import cstruct
|
|
156
|
+
|
|
157
|
+
c_def = """
|
|
152
158
|
struct test_union {
|
|
153
159
|
char magic[4];
|
|
154
160
|
union {
|
|
@@ -173,58 +179,61 @@ struct test_anonymous {
|
|
|
173
179
|
};
|
|
174
180
|
};
|
|
175
181
|
"""
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
# Default endianness is LE, but can be configured using a kwarg or setting the attribute
|
|
183
|
+
# e.g. cstruct(endian='>') or cs.endian = '>'
|
|
184
|
+
c = cstruct().load(c_def)
|
|
178
185
|
|
|
179
186
|
assert len(c.test_union) == 12
|
|
180
187
|
|
|
181
|
-
a = c.test_union(b
|
|
182
|
-
assert a.magic == b
|
|
188
|
+
a = c.test_union(b"ohaideadbeef")
|
|
189
|
+
assert a.magic == b"ohai"
|
|
183
190
|
assert a.c.a.a == 0x64616564
|
|
184
191
|
assert a.c.a.b == 0x66656562
|
|
185
|
-
assert a.c.b.b == b
|
|
192
|
+
assert a.c.b.b == b"deadbeef"
|
|
186
193
|
|
|
187
|
-
assert a.dumps() == b
|
|
194
|
+
assert a.dumps() == b"ohaideadbeef"
|
|
188
195
|
|
|
189
|
-
b = c.test_anonymous(b
|
|
190
|
-
assert b.magic == b
|
|
196
|
+
b = c.test_anonymous(b"ohai\x39\x05\x00\x00\x28\x23\x00\x00deadbeef")
|
|
197
|
+
assert b.magic == b"ohai"
|
|
191
198
|
assert b.a == 1337
|
|
192
199
|
assert b.b == 9000
|
|
193
|
-
assert b.c == b
|
|
200
|
+
assert b.c == b"deadbeef"
|
|
201
|
+
|
|
194
202
|
```
|
|
195
203
|
|
|
196
204
|
### Parse bit fields
|
|
197
205
|
Bit fields are supported as part of structures. They are properly aligned to their boundaries.
|
|
198
206
|
|
|
199
207
|
```python
|
|
200
|
-
|
|
208
|
+
from dissect.cstruct import cstruct
|
|
209
|
+
|
|
210
|
+
bit_def = """
|
|
201
211
|
struct test {
|
|
202
212
|
uint16 a:1;
|
|
203
|
-
uint16 b:1;
|
|
204
|
-
uint32 c;
|
|
213
|
+
uint16 b:1; // Read 2 bits from an uint16
|
|
214
|
+
uint32 c; // The next field is properly aligned
|
|
205
215
|
uint16 d:2;
|
|
206
216
|
uint16 e:3;
|
|
207
217
|
};
|
|
208
218
|
"""
|
|
209
|
-
bitfields = cstruct.
|
|
210
|
-
bitfields.load(bitdef)
|
|
219
|
+
bitfields = cstruct().load(bit_def)
|
|
211
220
|
|
|
212
|
-
d = b
|
|
221
|
+
d = b"\x03\x00\xff\x00\x00\x00\x1f\x00"
|
|
213
222
|
a = bitfields.test(d)
|
|
214
223
|
|
|
215
224
|
assert a.a == 0b1
|
|
216
225
|
assert a.b == 0b1
|
|
217
|
-
assert a.c ==
|
|
226
|
+
assert a.c == 0xFF
|
|
218
227
|
assert a.d == 0b11
|
|
219
228
|
assert a.e == 0b111
|
|
220
229
|
assert a.dumps() == d
|
|
221
230
|
```
|
|
222
231
|
|
|
223
|
-
### Enums
|
|
224
|
-
The API to access enum members and their values
|
|
232
|
+
### Enums and Flags
|
|
233
|
+
The API to access enum and flag members and their values in the same way as the native Enum types in Python 3. Functionally, it's best comparable to the IntEnum or IntFlag type.
|
|
225
234
|
|
|
226
235
|
### Custom types
|
|
227
|
-
You can implement your own types by subclassing `BaseType
|
|
236
|
+
You can implement your own types by subclassing `BaseType`, and adding them to your cstruct instance with `add_custom_type(name, type, size, alignment, ...)`
|
|
228
237
|
|
|
229
238
|
### Custom definition parsers
|
|
230
239
|
Don't like the C-like definition syntax? Write your own syntax parser!
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import io
|
|
2
|
+
|
|
1
3
|
import pytest
|
|
2
4
|
|
|
3
5
|
from dissect.cstruct.cstruct import cstruct
|
|
@@ -265,3 +267,31 @@ def test_bitfield_char(cs: cstruct, compiled: bool) -> None:
|
|
|
265
267
|
assert obj.d == b"i420"
|
|
266
268
|
|
|
267
269
|
assert obj.dumps() == buf
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def test_bitfield_dynamic(cs: cstruct, compiled: bool) -> None:
|
|
273
|
+
cdef = """
|
|
274
|
+
enum A : uint16 {
|
|
275
|
+
A = 0x0
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
struct test {
|
|
279
|
+
uint16 size : 4;
|
|
280
|
+
A b : 4;
|
|
281
|
+
char d[size];
|
|
282
|
+
};
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
cs.load(cdef, compiled=compiled)
|
|
286
|
+
assert verify_compiled(cs.test, compiled)
|
|
287
|
+
|
|
288
|
+
buf = io.BytesIO(b"\x00\x00\xF4\x00help")
|
|
289
|
+
buf.seek(2)
|
|
290
|
+
obj = cs.test(buf)
|
|
291
|
+
|
|
292
|
+
assert obj.size == 4
|
|
293
|
+
assert obj.b == 0xF
|
|
294
|
+
assert obj.d == b"help"
|
|
295
|
+
|
|
296
|
+
buf.seek(2)
|
|
297
|
+
assert obj.dumps() == buf.read()
|
|
@@ -323,4 +323,81 @@ def test_generate_bits_read(cs: cstruct, TestEnum: type[Enum]) -> None:
|
|
|
323
323
|
assert code == dedent(expected)
|
|
324
324
|
|
|
325
325
|
|
|
326
|
-
|
|
326
|
+
@pytest.mark.parametrize("other_type", ["int8", "uint64"])
|
|
327
|
+
def test_generate_fields_dynamic_after_bitfield(cs: cstruct, TestEnum: Enum, other_type: str) -> None:
|
|
328
|
+
_type = getattr(cs, other_type)
|
|
329
|
+
|
|
330
|
+
fields = [
|
|
331
|
+
Field("size", cs.uint16, offset=0),
|
|
332
|
+
Field("a", TestEnum, 4, offset=2),
|
|
333
|
+
Field("b", _type, 4),
|
|
334
|
+
Field("c", cs.char["size"], offset=3),
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
output = "\n".join(compiler._ReadSourceGenerator(cs, fields)._generate_fields())
|
|
338
|
+
|
|
339
|
+
expected = """
|
|
340
|
+
buf = stream.read(2)
|
|
341
|
+
if len(buf) != 2: raise EOFError()
|
|
342
|
+
data = _struct(cls.cs.endian, "H").unpack(buf)
|
|
343
|
+
|
|
344
|
+
r["size"] = type.__call__(_0, data[0])
|
|
345
|
+
s["size"] = 2
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
_t = _1
|
|
349
|
+
r["a"] = type.__call__(_t, bit_reader.read(_t.type, 4))
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
_t = _2
|
|
353
|
+
r["b"] = type.__call__(_t, bit_reader.read(_t, 4))
|
|
354
|
+
|
|
355
|
+
bit_reader.reset()
|
|
356
|
+
stream.seek(o + 3)
|
|
357
|
+
|
|
358
|
+
_s = stream.tell()
|
|
359
|
+
r["c"] = _3._read(stream, context=r)
|
|
360
|
+
s["c"] = stream.tell() - _s
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
assert output.strip() == dedent(expected).strip()
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@pytest.mark.parametrize("other_type", ["int8", "uint64"])
|
|
367
|
+
def test_generate_fields_dynamic_before_bitfield(cs: cstruct, TestEnum: Enum, other_type: str) -> None:
|
|
368
|
+
_type = getattr(cs, other_type)
|
|
369
|
+
|
|
370
|
+
fields = [
|
|
371
|
+
Field("size", cs.uint16, offset=0),
|
|
372
|
+
Field("a", _type, 4, offset=2),
|
|
373
|
+
Field("b", TestEnum, 4),
|
|
374
|
+
Field("c", cs.char["size"], offset=3),
|
|
375
|
+
]
|
|
376
|
+
|
|
377
|
+
output = "\n".join(compiler._ReadSourceGenerator(cs, fields)._generate_fields())
|
|
378
|
+
|
|
379
|
+
expected = """
|
|
380
|
+
buf = stream.read(2)
|
|
381
|
+
if len(buf) != 2: raise EOFError()
|
|
382
|
+
data = _struct(cls.cs.endian, "H").unpack(buf)
|
|
383
|
+
|
|
384
|
+
r["size"] = type.__call__(_0, data[0])
|
|
385
|
+
s["size"] = 2
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
_t = _1
|
|
389
|
+
r["a"] = type.__call__(_t, bit_reader.read(_t, 4))
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
_t = _2
|
|
393
|
+
r["b"] = type.__call__(_t, bit_reader.read(_t.type, 4))
|
|
394
|
+
|
|
395
|
+
bit_reader.reset()
|
|
396
|
+
stream.seek(o + 3)
|
|
397
|
+
|
|
398
|
+
_s = stream.tell()
|
|
399
|
+
r["c"] = _3._read(stream, context=r)
|
|
400
|
+
s["c"] = stream.tell() - _s
|
|
401
|
+
"""
|
|
402
|
+
|
|
403
|
+
assert output.strip() == dedent(expected).strip()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect.cstruct.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{dissect_cstruct-4.0.dev3 → dissect_cstruct-4.0.dev5}/dissect.cstruct.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|