Skip to content

Commit 54d283f

Browse files
committed
Make the Instance class dict compatible to allow for JSON serialization.
1 parent 5b4e10a commit 54d283f

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

dissect/cstruct/types/enum.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,14 @@ def default_array(self, count: int) -> List[EnumInstance]:
8484
return [self.default() for _ in range(count)]
8585

8686

87-
class EnumInstance:
87+
class EnumInstance(dict):
8888
"""Implements a value instance of an Enum"""
8989

9090
def __init__(self, enum: Enum, value: int):
9191
self.enum = enum
9292
self.value = value
93+
kwargs={self.enum.name:self.value}
94+
dict.__init__(self, **kwargs)
9395

9496
def __eq__(self, value: Union[int, EnumInstance]) -> bool:
9597
if isinstance(value, EnumInstance) and value.enum is not self.enum:

dissect/cstruct/types/instance.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
from dissect.cstruct.types import BaseType
55

66

7-
class Instance:
7+
class Instance(dict):
88
"""Holds parsed structure data."""
99

1010
__slots__ = ("_type", "_values", "_sizes")
1111

1212
def __init__(self, type_: BaseType, values: Dict[str, Any], sizes: Dict[str, int] = None):
13+
dict.__init__(self, **values)
1314
# Done in this manner to check if the attr is in the lookup
1415
object.__setattr__(self, "_type", type_)
1516
object.__setattr__(self, "_values", values)
@@ -46,6 +47,9 @@ def __bytes__(self) -> bytes:
4647
def _size(self, field: str) -> int:
4748
return self._sizes[field]
4849

50+
def __iter__(self):
51+
yield from self._values.items()
52+
4953
def write(self, stream: BinaryIO) -> int:
5054
"""Write this structure to a writable file-like object.
5155

examples/mirai_json.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env python
2+
from dissect.cstruct import cstruct, dumpstruct
3+
4+
import json
5+
import socket
6+
import struct
7+
8+
class CustomEncoder(json.JSONEncoder):
9+
def default(self, obj):
10+
if isinstance(obj, bytes):
11+
return obj.decode('utf-8', errors='surrogateescape')
12+
return json.JSONEncoder.default(self, obj)
13+
14+
protocol = cstruct()
15+
16+
protocol.load(
17+
"""
18+
enum AttackType : uint8 {
19+
ATK_OPT_DPORT = 7,
20+
ATK_OPT_DOMAIN = 8,
21+
ATK_OPT_NUM_SOCKETS = 24,
22+
};
23+
24+
struct AttackTarget {
25+
DWORD ipv4;
26+
BYTE netmask;
27+
};
28+
29+
struct AttackOption {
30+
AttackType type;
31+
uint8 value_length;
32+
char value[value_length];
33+
};
34+
35+
struct MiraiAttack {
36+
uint16 total_length;
37+
uint32 duration;
38+
uint8 attack_id;
39+
uint8 target_count;
40+
AttackTarget targets[target_count];
41+
uint8 num_opts;
42+
AttackOption attack_options[num_opts];
43+
};
44+
"""
45+
)
46+
47+
protocol.endian = ">"
48+
49+
if __name__ == "__main__":
50+
data = b"\x000\x00\x00\x00d\n\x01\x08\x08\x08\x08 \x03\x08\x16http://www.example.com\x07\x0280\x18\x045000"
51+
52+
record = protocol.MiraiAttack(data)
53+
print(json.dumps(record, cls=CustomEncoder))

0 commit comments

Comments
 (0)