declib 3.8.0__py3-none-any.whl
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.
- declib/__init__.py +9 -0
- declib/__main__.py +190 -0
- declib/api/__init__.py +13 -0
- declib/api/artifact_dict.py +153 -0
- declib/api/artifact_lifter.py +161 -0
- declib/api/decompiler_client.py +1219 -0
- declib/api/decompiler_interface.py +1261 -0
- declib/api/decompiler_server.py +782 -0
- declib/api/server_registry.py +171 -0
- declib/api/type_definition_parser.py +201 -0
- declib/api/type_parser.py +409 -0
- declib/api/utils.py +31 -0
- declib/artifacts/__init__.py +93 -0
- declib/artifacts/artifact.py +311 -0
- declib/artifacts/comment.py +49 -0
- declib/artifacts/context.py +61 -0
- declib/artifacts/decompilation.py +35 -0
- declib/artifacts/enum.py +53 -0
- declib/artifacts/formatting.py +27 -0
- declib/artifacts/func.py +433 -0
- declib/artifacts/global_variable.py +31 -0
- declib/artifacts/patch.py +49 -0
- declib/artifacts/segment.py +37 -0
- declib/artifacts/stack_variable.py +50 -0
- declib/artifacts/struct.py +184 -0
- declib/artifacts/typedef.py +59 -0
- declib/cli/__init__.py +3 -0
- declib/cli/decompiler_cli.py +1487 -0
- declib/configuration.py +184 -0
- declib/decompiler_stubs/__init__.py +0 -0
- declib/decompiler_stubs/angr_declib/__init__.py +4 -0
- declib/decompiler_stubs/binja_declib/__init__.py +4 -0
- declib/decompiler_stubs/ida_declib.py +8 -0
- declib/decompilers/__init__.py +8 -0
- declib/decompilers/angr/__init__.py +11 -0
- declib/decompilers/angr/artifact_lifter.py +46 -0
- declib/decompilers/angr/compat.py +262 -0
- declib/decompilers/angr/interface.py +949 -0
- declib/decompilers/binja/__init__.py +0 -0
- declib/decompilers/binja/artifact_lifter.py +32 -0
- declib/decompilers/binja/hooks.py +201 -0
- declib/decompilers/binja/interface.py +795 -0
- declib/decompilers/ghidra/__init__.py +0 -0
- declib/decompilers/ghidra/artifact_lifter.py +60 -0
- declib/decompilers/ghidra/compat/__init__.py +0 -0
- declib/decompilers/ghidra/compat/headless.py +156 -0
- declib/decompilers/ghidra/compat/imports.py +78 -0
- declib/decompilers/ghidra/compat/state.py +54 -0
- declib/decompilers/ghidra/compat/transaction.py +30 -0
- declib/decompilers/ghidra/hooks.py +242 -0
- declib/decompilers/ghidra/interface.py +1433 -0
- declib/decompilers/ida/__init__.py +0 -0
- declib/decompilers/ida/artifact_lifter.py +51 -0
- declib/decompilers/ida/compat.py +2054 -0
- declib/decompilers/ida/hooks.py +700 -0
- declib/decompilers/ida/ida_ui.py +80 -0
- declib/decompilers/ida/interface.py +659 -0
- declib/logger.py +101 -0
- declib/plugin_installer.py +259 -0
- declib/skills/__init__.py +24 -0
- declib/skills/decompiler/SKILL.md +316 -0
- declib/ui/__init__.py +33 -0
- declib/ui/qt_objects.py +146 -0
- declib/ui/utils.py +115 -0
- declib/ui/version.py +14 -0
- declib-3.8.0.dist-info/METADATA +138 -0
- declib-3.8.0.dist-info/RECORD +71 -0
- declib-3.8.0.dist-info/WHEEL +5 -0
- declib-3.8.0.dist-info/entry_points.txt +3 -0
- declib-3.8.0.dist-info/licenses/LICENSE +24 -0
- declib-3.8.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Dict, Optional, List
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
import toml
|
|
6
|
+
|
|
7
|
+
from .formatting import ArtifactFormat, TomlHexEncoder
|
|
8
|
+
|
|
9
|
+
from toml.tz import TomlTz
|
|
10
|
+
|
|
11
|
+
class Artifact:
|
|
12
|
+
"""
|
|
13
|
+
The Artifact class acts as the base for all other artifacts that can be produced by a decompiler (or decompiler
|
|
14
|
+
adjacent tool). In general, the comparisons of these derived classes should only be done on the attributes in
|
|
15
|
+
__slots__, except for the last_change property.
|
|
16
|
+
"""
|
|
17
|
+
LST_CHNG_ATTR = "last_change"
|
|
18
|
+
ADDR_ATTR = "addr"
|
|
19
|
+
ART_TYPE_STR = "artifact_type"
|
|
20
|
+
SCOPE_ATTR = "scope"
|
|
21
|
+
|
|
22
|
+
ATTR_ATTR_IGNORE_SET = "_attr_ignore_set"
|
|
23
|
+
__slots__ = (
|
|
24
|
+
LST_CHNG_ATTR,
|
|
25
|
+
ATTR_ATTR_IGNORE_SET,
|
|
26
|
+
SCOPE_ATTR
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def __init__(self, last_change: Optional[datetime.datetime] = None, scope: Optional[str] = None):
|
|
30
|
+
self.last_change = last_change
|
|
31
|
+
self.scope = scope
|
|
32
|
+
self._attr_ignore_set = set()
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def _normalize_datetime(dt):
|
|
36
|
+
"""
|
|
37
|
+
Convert TomlTz datetime objects to standard Python datetime objects.
|
|
38
|
+
TomlTz objects from TOML deserialization don't pickle correctly.
|
|
39
|
+
"""
|
|
40
|
+
if not isinstance(dt, datetime.datetime):
|
|
41
|
+
return dt
|
|
42
|
+
|
|
43
|
+
# If the datetime has a TomlTz tzinfo, convert it to standard timezone
|
|
44
|
+
if dt.tzinfo is not None and isinstance(dt.tzinfo, TomlTz):
|
|
45
|
+
# Get the offset and convert to standard timezone
|
|
46
|
+
offset = dt.utcoffset()
|
|
47
|
+
if offset is not None:
|
|
48
|
+
std_tz = datetime.timezone(offset)
|
|
49
|
+
# Replace the TomlTz with standard timezone
|
|
50
|
+
return dt.replace(tzinfo=std_tz)
|
|
51
|
+
|
|
52
|
+
return dt
|
|
53
|
+
|
|
54
|
+
def __getstate__(self) -> Dict:
|
|
55
|
+
state = {}
|
|
56
|
+
for k in self.slots:
|
|
57
|
+
value = getattr(self, k)
|
|
58
|
+
# Normalize datetime objects to ensure they pickle correctly
|
|
59
|
+
if isinstance(value, datetime.datetime):
|
|
60
|
+
value = self._normalize_datetime(value)
|
|
61
|
+
state[k] = value
|
|
62
|
+
return state
|
|
63
|
+
|
|
64
|
+
def __setstate__(self, state):
|
|
65
|
+
# When pickle calls __setstate__, __init__ is never called, so we need to
|
|
66
|
+
# initialize _attr_ignore_set before accessing self.slots (which uses it)
|
|
67
|
+
if not hasattr(self, '_attr_ignore_set'):
|
|
68
|
+
self._attr_ignore_set = set()
|
|
69
|
+
|
|
70
|
+
for k in self.slots:
|
|
71
|
+
if k in state:
|
|
72
|
+
setattr(self, k, state[k])
|
|
73
|
+
|
|
74
|
+
def __eq__(self, other):
|
|
75
|
+
if not isinstance(other, self.__class__):
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
for k in self.slots:
|
|
79
|
+
if k == self.LST_CHNG_ATTR:
|
|
80
|
+
continue
|
|
81
|
+
elif k == self.SCOPE_ATTR:
|
|
82
|
+
# special case scopes: a scope of None indicates that the artifact could be in any scope
|
|
83
|
+
this_scope = getattr(self, k)
|
|
84
|
+
that_scope = getattr(other, k)
|
|
85
|
+
if this_scope is None or that_scope is None:
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
if getattr(self, k) != getattr(other, k):
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
def __hash__(self):
|
|
94
|
+
long_str = ""
|
|
95
|
+
for attr in self.slots:
|
|
96
|
+
long_str += str(getattr(self, attr))
|
|
97
|
+
|
|
98
|
+
return hash(long_str)
|
|
99
|
+
|
|
100
|
+
def __repr__(self):
|
|
101
|
+
return self.__str__()
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def slots(self):
|
|
105
|
+
return [s for s in self.__slots__ if s != self.ATTR_ATTR_IGNORE_SET and s not in self._attr_ignore_set]
|
|
106
|
+
|
|
107
|
+
def copy(self) -> "Artifact":
|
|
108
|
+
new_obj = self.__class__()
|
|
109
|
+
for attr in self.slots:
|
|
110
|
+
attr_v = getattr(self, attr)
|
|
111
|
+
if isinstance(attr_v, list):
|
|
112
|
+
new_list = []
|
|
113
|
+
for lobj in attr_v:
|
|
114
|
+
if hasattr(lobj, "copy"):
|
|
115
|
+
new_list.append(lobj.copy())
|
|
116
|
+
setattr(new_obj, attr, new_list)
|
|
117
|
+
elif isinstance(attr_v, dict):
|
|
118
|
+
new_dict = {}
|
|
119
|
+
for dk, dv in attr_v.items():
|
|
120
|
+
new_dk = dk.copy() if hasattr(dk, "copy") else dk
|
|
121
|
+
new_dv = dv.copy() if hasattr(dv, "copy") else dv
|
|
122
|
+
new_dict[new_dk] = new_dv
|
|
123
|
+
setattr(new_obj, attr, new_dict)
|
|
124
|
+
elif isinstance(attr_v, Artifact):
|
|
125
|
+
setattr(new_obj, attr, attr_v.copy())
|
|
126
|
+
else:
|
|
127
|
+
setattr(new_obj, attr, attr_v)
|
|
128
|
+
|
|
129
|
+
return new_obj
|
|
130
|
+
|
|
131
|
+
#
|
|
132
|
+
# Serialization
|
|
133
|
+
#
|
|
134
|
+
|
|
135
|
+
def _to_c_string(self):
|
|
136
|
+
raise NotImplementedError
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def _from_c_string(cls, cstring) -> Dict:
|
|
140
|
+
raise NotImplementedError
|
|
141
|
+
|
|
142
|
+
def dumps(self, fmt=ArtifactFormat.TOML) -> str:
|
|
143
|
+
dict_data = self.__getstate__()
|
|
144
|
+
# encode the artifact type only for JSON format
|
|
145
|
+
if fmt == ArtifactFormat.JSON:
|
|
146
|
+
dict_data.update({self.ART_TYPE_STR: self.__class__.__name__})
|
|
147
|
+
|
|
148
|
+
if fmt == ArtifactFormat.TOML:
|
|
149
|
+
return toml.dumps(dict_data, encoder=TomlHexEncoder())
|
|
150
|
+
elif fmt == ArtifactFormat.JSON:
|
|
151
|
+
return json.dumps(dict_data)
|
|
152
|
+
elif fmt == ArtifactFormat.C_LANG:
|
|
153
|
+
return self._to_c_string()
|
|
154
|
+
else:
|
|
155
|
+
raise ValueError(f"Dumping to format {fmt} is not yet supported.")
|
|
156
|
+
|
|
157
|
+
def dump(self, fp, fmt=ArtifactFormat.TOML):
|
|
158
|
+
data = self.dumps(fmt=fmt)
|
|
159
|
+
fp.write(data)
|
|
160
|
+
|
|
161
|
+
@classmethod
|
|
162
|
+
def loads(cls, string, fmt=ArtifactFormat.TOML) -> "Artifact":
|
|
163
|
+
if fmt == ArtifactFormat.TOML:
|
|
164
|
+
dict_data = toml.loads(string)
|
|
165
|
+
elif fmt == ArtifactFormat.JSON:
|
|
166
|
+
dict_data = json.loads(string)
|
|
167
|
+
elif fmt == ArtifactFormat.C_LANG:
|
|
168
|
+
dict_data = cls._from_c_string(string)
|
|
169
|
+
else:
|
|
170
|
+
raise ValueError(f"Loading from format {fmt} is not yet supported.")
|
|
171
|
+
|
|
172
|
+
# remove the artifact type (if it exists)
|
|
173
|
+
dict_data.pop(Artifact.ART_TYPE_STR, None)
|
|
174
|
+
art = cls()
|
|
175
|
+
art.__setstate__(dict_data)
|
|
176
|
+
return art
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def load(cls, fp, fmt=ArtifactFormat.TOML):
|
|
180
|
+
data = fp.read()
|
|
181
|
+
return cls.loads(data, fmt=fmt)
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
def dumps_many(cls, artifacts: List["Artifact"], key_attr=ADDR_ATTR, fmt=ArtifactFormat.TOML) -> str:
|
|
185
|
+
artifacts_dict = {}
|
|
186
|
+
for art in artifacts:
|
|
187
|
+
k = getattr(art, key_attr)
|
|
188
|
+
if isinstance(k, int):
|
|
189
|
+
k = hex(k)
|
|
190
|
+
|
|
191
|
+
artifacts_dict[k] = art.__getstate__()
|
|
192
|
+
|
|
193
|
+
if fmt == ArtifactFormat.TOML:
|
|
194
|
+
return toml.dumps(artifacts_dict, encoder=TomlHexEncoder())
|
|
195
|
+
elif fmt == ArtifactFormat.JSON:
|
|
196
|
+
return json.dumps(artifacts_dict)
|
|
197
|
+
else:
|
|
198
|
+
raise ValueError(f"Dumping many to format {fmt} is not yet supported.")
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
def loads_many(cls, string: str, fmt=ArtifactFormat.TOML) -> List["Artifact"]:
|
|
202
|
+
if fmt == ArtifactFormat.TOML:
|
|
203
|
+
dict_data = toml.loads(string)
|
|
204
|
+
elif fmt == ArtifactFormat.JSON:
|
|
205
|
+
dict_data = json.loads(string)
|
|
206
|
+
else:
|
|
207
|
+
raise ValueError(f"Loading many from format {fmt} is not yet supported.")
|
|
208
|
+
|
|
209
|
+
arts = []
|
|
210
|
+
for _, v in dict_data.items():
|
|
211
|
+
art = cls()
|
|
212
|
+
art.__setstate__(v)
|
|
213
|
+
arts.append(art)
|
|
214
|
+
|
|
215
|
+
return arts
|
|
216
|
+
|
|
217
|
+
#
|
|
218
|
+
# Public API
|
|
219
|
+
#
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def scoped_name(self) -> str:
|
|
223
|
+
"""
|
|
224
|
+
Returns the name of the artifact with its scope, if it has one.
|
|
225
|
+
"""
|
|
226
|
+
if hasattr(self, "name"):
|
|
227
|
+
if self.scope:
|
|
228
|
+
return f"{self.scope}::{self.name}"
|
|
229
|
+
|
|
230
|
+
return self.name
|
|
231
|
+
return ""
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def commit_msg(self) -> str:
|
|
235
|
+
return f"Updated {self}"
|
|
236
|
+
|
|
237
|
+
def diff(self, other, **kwargs) -> Dict:
|
|
238
|
+
diff_dict = {}
|
|
239
|
+
if not isinstance(other, self.__class__):
|
|
240
|
+
for k in self.slots:
|
|
241
|
+
if k == self.LST_CHNG_ATTR:
|
|
242
|
+
continue
|
|
243
|
+
|
|
244
|
+
diff_dict[k] = {
|
|
245
|
+
"before": getattr(self, k),
|
|
246
|
+
"after": None
|
|
247
|
+
}
|
|
248
|
+
return diff_dict
|
|
249
|
+
|
|
250
|
+
for k in self.slots:
|
|
251
|
+
self_attr, other_attr = getattr(self, k), getattr(other, k)
|
|
252
|
+
if self_attr != other_attr:
|
|
253
|
+
if k == self.LST_CHNG_ATTR:
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
diff_dict[k] = {
|
|
257
|
+
"before": self_attr,
|
|
258
|
+
"after": other_attr
|
|
259
|
+
}
|
|
260
|
+
return diff_dict
|
|
261
|
+
|
|
262
|
+
@classmethod
|
|
263
|
+
def invert_diff(cls, diff_dict: Dict):
|
|
264
|
+
inverted_diff = {}
|
|
265
|
+
for k, v in diff_dict.items():
|
|
266
|
+
if k == "before":
|
|
267
|
+
inverted_diff["after"] = v
|
|
268
|
+
elif k == "after":
|
|
269
|
+
inverted_diff["before"] = v
|
|
270
|
+
elif isinstance(v, Dict):
|
|
271
|
+
inverted_diff[k] = cls.invert_diff(v)
|
|
272
|
+
else:
|
|
273
|
+
inverted_diff[k] = v
|
|
274
|
+
|
|
275
|
+
return inverted_diff
|
|
276
|
+
|
|
277
|
+
def reset_last_change(self):
|
|
278
|
+
"""
|
|
279
|
+
Resets the change time of the Artifact.
|
|
280
|
+
In subclasses, this should also reset all artifacts with nested artifacts
|
|
281
|
+
"""
|
|
282
|
+
self.last_change = None
|
|
283
|
+
|
|
284
|
+
def overwrite_merge(self, obj2: "Artifact", **kwargs):
|
|
285
|
+
"""
|
|
286
|
+
This function should really be overwritten by its subclass
|
|
287
|
+
"""
|
|
288
|
+
merge_obj = self.copy()
|
|
289
|
+
if not obj2 or merge_obj == obj2:
|
|
290
|
+
return merge_obj
|
|
291
|
+
|
|
292
|
+
for attr in self.slots:
|
|
293
|
+
a2 = getattr(obj2, attr)
|
|
294
|
+
if a2 is not None:
|
|
295
|
+
setattr(merge_obj, attr, a2)
|
|
296
|
+
|
|
297
|
+
return merge_obj
|
|
298
|
+
|
|
299
|
+
def nonconflict_merge(self, obj2: "Artifact", **kwargs):
|
|
300
|
+
obj1 = self.copy()
|
|
301
|
+
if not obj2 or obj1 == obj2:
|
|
302
|
+
return obj1
|
|
303
|
+
|
|
304
|
+
obj_diff = obj1.diff(obj2)
|
|
305
|
+
merge_obj = obj1.copy()
|
|
306
|
+
|
|
307
|
+
for attr in self.slots:
|
|
308
|
+
if attr in obj_diff and obj_diff[attr]["before"] is None:
|
|
309
|
+
setattr(merge_obj, attr, getattr(obj2, attr))
|
|
310
|
+
|
|
311
|
+
return merge_obj
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import textwrap
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from .artifact import Artifact
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Comment(Artifact):
|
|
8
|
+
__slots__ = Artifact.__slots__ + (
|
|
9
|
+
"addr",
|
|
10
|
+
"func_addr",
|
|
11
|
+
"comment",
|
|
12
|
+
"decompiled",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
addr: int = None,
|
|
18
|
+
comment: Optional[str] = None,
|
|
19
|
+
func_addr: int = None,
|
|
20
|
+
decompiled: bool = False,
|
|
21
|
+
**kwargs
|
|
22
|
+
):
|
|
23
|
+
super().__init__(**kwargs)
|
|
24
|
+
self.addr = addr
|
|
25
|
+
self.comment = self.linewrap_comment(comment) if comment else None
|
|
26
|
+
self.func_addr = func_addr
|
|
27
|
+
self.decompiled = decompiled
|
|
28
|
+
|
|
29
|
+
def __str__(self):
|
|
30
|
+
cmt_len = len(self.comment) if self.comment else 0
|
|
31
|
+
return f"<Comment: @{hex(self.addr)} len={cmt_len}>"
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def linewrap_comment(comment: str, width=100) -> str:
|
|
35
|
+
# Split the comment into lines based on existing newlines
|
|
36
|
+
lines = comment.split('\n')
|
|
37
|
+
# Wrap each line individually and preserve newlines
|
|
38
|
+
wrapped_lines = [textwrap.fill(line, width=width) for line in lines]
|
|
39
|
+
# Join the wrapped lines with newline characters
|
|
40
|
+
wrapped_text = '\n'.join(wrapped_lines)
|
|
41
|
+
return wrapped_text
|
|
42
|
+
|
|
43
|
+
def nonconflict_merge(self, obj2: "Comment", **kwargs) -> "Comment":
|
|
44
|
+
obj1: "Comment" = self.copy()
|
|
45
|
+
if not obj2 or obj1 == obj2:
|
|
46
|
+
return obj1
|
|
47
|
+
|
|
48
|
+
merge_comment = obj1
|
|
49
|
+
return merge_comment
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from .artifact import Artifact
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Context(Artifact):
|
|
7
|
+
ACT_VIEW_OPEN = "view_open"
|
|
8
|
+
ACT_MOUSE_CLICK = "mouse_click"
|
|
9
|
+
ACT_MOUSE_MOVE = "mouse_move"
|
|
10
|
+
ACT_UNKNOWN = "unknown"
|
|
11
|
+
|
|
12
|
+
__slots__ = Artifact.__slots__ + (
|
|
13
|
+
"addr",
|
|
14
|
+
"func_addr",
|
|
15
|
+
"line_number",
|
|
16
|
+
"col_number",
|
|
17
|
+
"screen_name",
|
|
18
|
+
"variable",
|
|
19
|
+
"action",
|
|
20
|
+
"extras",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
addr: Optional[int] = None,
|
|
26
|
+
func_addr: Optional[int] = None,
|
|
27
|
+
line_number: Optional[int] = None,
|
|
28
|
+
col_number: Optional[int] = None,
|
|
29
|
+
screen_name: Optional[str] = None,
|
|
30
|
+
variable: Optional[str] = None,
|
|
31
|
+
action: Optional[str] = None,
|
|
32
|
+
extras: Optional[dict] = None,
|
|
33
|
+
**kwargs
|
|
34
|
+
):
|
|
35
|
+
self.addr = addr
|
|
36
|
+
self.func_addr = func_addr
|
|
37
|
+
self.line_number = line_number
|
|
38
|
+
self.col_number = col_number
|
|
39
|
+
self.screen_name = screen_name
|
|
40
|
+
self.variable = variable
|
|
41
|
+
self.action: str = action or self.ACT_UNKNOWN
|
|
42
|
+
self.extras = extras or {}
|
|
43
|
+
super().__init__(**kwargs)
|
|
44
|
+
|
|
45
|
+
def __str__(self):
|
|
46
|
+
post_text = f" screen={self.screen_name}" if self.screen_name else ""
|
|
47
|
+
post_text += f" var={self.variable}" if self.variable else ""
|
|
48
|
+
if self.func_addr is not None:
|
|
49
|
+
post_text = f"@{hex(self.func_addr)}" + post_text
|
|
50
|
+
if self.addr is not None:
|
|
51
|
+
post_text = hex(self.addr) + post_text
|
|
52
|
+
if self.line_number is not None:
|
|
53
|
+
post_text += f" line={self.line_number}"
|
|
54
|
+
if self.col_number is not None:
|
|
55
|
+
post_text += f" col={self.col_number}"
|
|
56
|
+
if self.action != self.ACT_UNKNOWN:
|
|
57
|
+
post_text += f" action={self.action}"
|
|
58
|
+
if self.extras:
|
|
59
|
+
post_text += f" extras={self.extras}"
|
|
60
|
+
|
|
61
|
+
return f"<Context {post_text}>"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import toml
|
|
2
|
+
|
|
3
|
+
from .artifact import Artifact
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Decompilation(Artifact):
|
|
7
|
+
__slots__ = Artifact.__slots__ + (
|
|
8
|
+
"addr",
|
|
9
|
+
"text",
|
|
10
|
+
"line_map",
|
|
11
|
+
"decompiler",
|
|
12
|
+
"bs_func",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
addr: int = None,
|
|
18
|
+
text: str = None,
|
|
19
|
+
line_map: dict = None,
|
|
20
|
+
decompiler: str = None,
|
|
21
|
+
bs_func = None,
|
|
22
|
+
**kwargs
|
|
23
|
+
):
|
|
24
|
+
super().__init__(**kwargs)
|
|
25
|
+
self.addr = addr
|
|
26
|
+
self.text = text
|
|
27
|
+
self.line_map = line_map or {}
|
|
28
|
+
self.decompiler = decompiler
|
|
29
|
+
self.bs_func = bs_func
|
|
30
|
+
|
|
31
|
+
def __str__(self):
|
|
32
|
+
return f"//ADDR: {hex(self.addr)}\n// SOURCE: {self.decompiler}\n{self.text}"
|
|
33
|
+
|
|
34
|
+
def __repr__(self):
|
|
35
|
+
return f"<Decompilation: {self.decompiler}@{hex(self.addr)} len={len(self.text)}>"
|
declib/artifacts/enum.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
from .artifact import Artifact
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Enum(Artifact):
|
|
8
|
+
__slots__ = Artifact.__slots__ + (
|
|
9
|
+
"name",
|
|
10
|
+
"members",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
name: str = None,
|
|
16
|
+
members: Dict[str, int] = None,
|
|
17
|
+
**kwargs,
|
|
18
|
+
):
|
|
19
|
+
super().__init__(**kwargs)
|
|
20
|
+
self.name = name
|
|
21
|
+
# sorts map by the int value
|
|
22
|
+
self.members = self._order_members(members) if members else {}
|
|
23
|
+
|
|
24
|
+
def __str__(self):
|
|
25
|
+
scope_str = f" scope={self.scope}" if self.scope else ""
|
|
26
|
+
return f"<Enum: {self.name} member_count={len(self.members)}{scope_str}>"
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def _order_members(members):
|
|
30
|
+
return OrderedDict(sorted(members.items(), key=lambda kv: kv[1]))
|
|
31
|
+
|
|
32
|
+
def nonconflict_merge(self, enum2: "Enum", **kwargs):
|
|
33
|
+
enum1: Enum = self.copy()
|
|
34
|
+
if not enum2 or enum1 == enum2:
|
|
35
|
+
return enum1.copy()
|
|
36
|
+
|
|
37
|
+
master_state = kwargs.get("master_state", None)
|
|
38
|
+
local_names = {mem for mem in enum1.members}
|
|
39
|
+
if master_state:
|
|
40
|
+
for _, enum in master_state.get_enums().items():
|
|
41
|
+
local_names.union(set(enum.members.keys()))
|
|
42
|
+
else:
|
|
43
|
+
local_names = enum1.members
|
|
44
|
+
|
|
45
|
+
constants = {
|
|
46
|
+
value for value in enum1.members.values()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for name, constant in enum2.members.items():
|
|
50
|
+
if name in local_names or constant in constants:
|
|
51
|
+
continue
|
|
52
|
+
enum1.members[name] = constant
|
|
53
|
+
return enum1
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from toml import TomlEncoder
|
|
4
|
+
|
|
5
|
+
if typing.TYPE_CHECKING:
|
|
6
|
+
from ..api import CTypeParser, CType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ArtifactFormat:
|
|
10
|
+
TOML = "toml"
|
|
11
|
+
JSON = "json"
|
|
12
|
+
C_LANG = "c"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TomlHexEncoder(TomlEncoder):
|
|
16
|
+
def __init__(self, _dict=dict, preserve=False):
|
|
17
|
+
super(TomlHexEncoder, self).__init__(_dict, preserve=preserve)
|
|
18
|
+
self.dump_funcs[int] = lambda v: hex(v) if v >= 0 else v
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def ctype_from_size(size, type_parser: typing.Optional["CTypeParser"] = None) -> "CType":
|
|
22
|
+
if type_parser is None:
|
|
23
|
+
from ..api.type_parser import CTypeParser
|
|
24
|
+
type_parser = CTypeParser()
|
|
25
|
+
|
|
26
|
+
ctype = type_parser.size_to_type(size)
|
|
27
|
+
return ctype
|