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,409 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import logging
|
|
3
|
+
from collections import OrderedDict, defaultdict, ChainMap
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import pycparser
|
|
7
|
+
from pycparser import c_ast
|
|
8
|
+
from pycparser.c_parser import ParseError
|
|
9
|
+
|
|
10
|
+
# pycparser hack to parse type expressions
|
|
11
|
+
errorlog = logging.getLogger(name=__name__ + ".yacc")
|
|
12
|
+
errorlog.setLevel(logging.ERROR)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
l = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _patch_pycparser():
|
|
19
|
+
"""
|
|
20
|
+
Adds a `parse_type_with_name` method to pycparser.CParser that parses a bare
|
|
21
|
+
type expression (like "int *") rather than a full translation unit. pycparser
|
|
22
|
+
3.0 removed the ability to customize the start production via ply.yacc.
|
|
23
|
+
"""
|
|
24
|
+
if hasattr(pycparser.CParser, "parse_type_with_name"):
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
def parse_type_with_name(self, text, filename="", scope_stack=None) -> c_ast.Typename:
|
|
28
|
+
self.clex._filename = filename
|
|
29
|
+
self.clex._lineno = 1
|
|
30
|
+
self._scope_stack = [{}] if scope_stack is None else scope_stack
|
|
31
|
+
|
|
32
|
+
self.clex.input(text, filename)
|
|
33
|
+
self._tokens = pycparser.c_parser._TokenStream(self.clex)
|
|
34
|
+
|
|
35
|
+
return self._parse_type_name()
|
|
36
|
+
|
|
37
|
+
pycparser.CParser.parse_type_with_name = parse_type_with_name
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
_patch_pycparser()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CType:
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
type_=None,
|
|
47
|
+
size=0,
|
|
48
|
+
is_primitive=True,
|
|
49
|
+
is_array=False,
|
|
50
|
+
is_ptr=False,
|
|
51
|
+
is_unknown=False
|
|
52
|
+
):
|
|
53
|
+
self.type = type_
|
|
54
|
+
self._size = size
|
|
55
|
+
|
|
56
|
+
self.is_primitive = is_primitive
|
|
57
|
+
self.is_array = is_array
|
|
58
|
+
self.is_ptr = is_ptr
|
|
59
|
+
self.is_unknown = is_unknown
|
|
60
|
+
|
|
61
|
+
def __str__(self):
|
|
62
|
+
return f"<{self.__class__.__name__}: {self.type} {'[]' if self.is_array else ''}{'*' if self.is_ptr else ''}" \
|
|
63
|
+
f"{'U' if self.is_unknown else ''} ({self._size})>"
|
|
64
|
+
|
|
65
|
+
def __repr__(self):
|
|
66
|
+
return self.__str__()
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def type_str(self):
|
|
70
|
+
if isinstance(self.type, CType) and self.is_array:
|
|
71
|
+
return self.type.type_str + f"[{self._size}]"
|
|
72
|
+
|
|
73
|
+
return self.type
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def base_type(self):
|
|
77
|
+
if isinstance(self.type, str):
|
|
78
|
+
return self
|
|
79
|
+
elif isinstance(self.type, CType):
|
|
80
|
+
return self.type.base_type
|
|
81
|
+
|
|
82
|
+
return self.type
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def size(self):
|
|
86
|
+
if isinstance(self.type, CType) and self.is_array:
|
|
87
|
+
return self.type.size * self._size
|
|
88
|
+
|
|
89
|
+
return self._size
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class CTypeParser:
|
|
93
|
+
"""
|
|
94
|
+
Most of this code is ripped from angr's sim_type:
|
|
95
|
+
https://github.com/angr/angr/blob/master/angr/sim_type.py
|
|
96
|
+
|
|
97
|
+
It is highly simplified and drops a lot of support for real declaration parsing (like a struct dec).
|
|
98
|
+
Instead, we just use it to parse types.
|
|
99
|
+
"""
|
|
100
|
+
def __init__(
|
|
101
|
+
self,
|
|
102
|
+
sizeof_ptr=8,
|
|
103
|
+
sizeof_long=8,
|
|
104
|
+
sizeof_double=8,
|
|
105
|
+
sizeof_int=4,
|
|
106
|
+
sizeof_float=4,
|
|
107
|
+
sizeof_short=2,
|
|
108
|
+
sizeof_char=1,
|
|
109
|
+
sizeof_bool=1,
|
|
110
|
+
extra_types=None
|
|
111
|
+
):
|
|
112
|
+
# sizes
|
|
113
|
+
self.sizeof_ptr = sizeof_ptr
|
|
114
|
+
self.sizeof_long = sizeof_long
|
|
115
|
+
self.sizeof_double = sizeof_double
|
|
116
|
+
self.sizeof_int = sizeof_int
|
|
117
|
+
self.sizeof_float = sizeof_float
|
|
118
|
+
self.sizeof_short = sizeof_short
|
|
119
|
+
self.sizeof_char = sizeof_char
|
|
120
|
+
self.sizeof_bool = sizeof_bool
|
|
121
|
+
|
|
122
|
+
# hack in type parsing
|
|
123
|
+
self._type_parser_singleton = pycparser.CParser()
|
|
124
|
+
self.ALL_TYPES = {}
|
|
125
|
+
self.BASIC_TYPES = {}
|
|
126
|
+
self.STDINT_TYPES = {}
|
|
127
|
+
self.extra_types = extra_types or {}
|
|
128
|
+
self.SIZE_TO_TYPES = {}
|
|
129
|
+
self._init_all_types()
|
|
130
|
+
|
|
131
|
+
def _init_all_types(self):
|
|
132
|
+
self.BASIC_TYPES = {
|
|
133
|
+
"char": CType(type_="char", size=self.sizeof_char),
|
|
134
|
+
"signed char": CType(type_="signed char", size=self.sizeof_char),
|
|
135
|
+
"unsigned char": CType(type_="unsigned char", size=self.sizeof_char),
|
|
136
|
+
"short": CType(type_="short", size=self.sizeof_short),
|
|
137
|
+
"signed short": CType(type_="signed short", size=self.sizeof_short),
|
|
138
|
+
"unsigned short": CType(type_="unsigned short", size=self.sizeof_short),
|
|
139
|
+
"short int": CType(type_="short int", size=self.sizeof_short),
|
|
140
|
+
"signed short int": CType(type_="signed short int", size=self.sizeof_short),
|
|
141
|
+
"unsigned short int": CType(type_="unsigned short int", size=self.sizeof_short),
|
|
142
|
+
"int": CType(type_="int", size=self.sizeof_int),
|
|
143
|
+
"signed": CType(type_="signed", size=self.sizeof_int),
|
|
144
|
+
"unsigned": CType(type_="unsigned", size=self.sizeof_int),
|
|
145
|
+
"signed int": CType(type_="signed int", size=self.sizeof_int),
|
|
146
|
+
"unsigned int": CType(type_="unsigned int", size=self.sizeof_int),
|
|
147
|
+
"long": CType(type_="long", size=self.sizeof_long),
|
|
148
|
+
"signed long": CType(type_="signed long", size=self.sizeof_long),
|
|
149
|
+
"long signed": CType(type_="long signed", size=self.sizeof_long),
|
|
150
|
+
"unsigned long": CType(type_="unsigned long", size=self.sizeof_long),
|
|
151
|
+
"long int": CType(type_="long int", size=self.sizeof_long),
|
|
152
|
+
"signed long int": CType(type_="signed long int", size=self.sizeof_long),
|
|
153
|
+
"unsigned long int": CType(type_="unsigned long int", size=self.sizeof_long),
|
|
154
|
+
"long unsigned int": CType(type_="long unsigned int", size=self.sizeof_long),
|
|
155
|
+
"long long": CType(type_="long long", size=self.sizeof_long),
|
|
156
|
+
"signed long long": CType(type_="signed long long", size=self.sizeof_long),
|
|
157
|
+
"unsigned long long": CType(type_="unsigned long long", size=self.sizeof_long),
|
|
158
|
+
"long long int": CType(type_="long long int", size=self.sizeof_long),
|
|
159
|
+
"signed long long int": CType(type_="signed long long int", size=self.sizeof_long),
|
|
160
|
+
"unsigned long long int": CType(type_="unsigned long long int", size=self.sizeof_long),
|
|
161
|
+
"__int128": CType(type_="__int128", size=16),
|
|
162
|
+
"unsigned __int128": CType(type_="unsigned __int128", size=16),
|
|
163
|
+
"__int256": CType(type_="__int256", size=32),
|
|
164
|
+
"unsigned __int256": CType(type_="unsigned __int256", size=32),
|
|
165
|
+
"bool": CType(type_="bool", size=self.sizeof_bool),
|
|
166
|
+
"_Bool": CType(type_="_Bool", size=self.sizeof_bool),
|
|
167
|
+
"float": CType(type_="float", size=self.sizeof_float),
|
|
168
|
+
"double": CType(type_="double", size=self.sizeof_double),
|
|
169
|
+
"long double": CType(type_="double", size=self.sizeof_double),
|
|
170
|
+
"void": CType(type_="void", size=self.sizeof_ptr),
|
|
171
|
+
}
|
|
172
|
+
self.ALL_TYPES.update(self.BASIC_TYPES)
|
|
173
|
+
|
|
174
|
+
self.STDINT_TYPES = {
|
|
175
|
+
"int8_t": CType(type_="int8_t", size=1),
|
|
176
|
+
"uint8_t": CType(type_="uint8_t", size=1),
|
|
177
|
+
"byte": CType(type_="byte", size=1),
|
|
178
|
+
"int16_t": CType(type_="int16_t", size=2),
|
|
179
|
+
"uint16_t": CType(type_="uint16_t", size=2),
|
|
180
|
+
"word": CType(type_="word", size=2),
|
|
181
|
+
"int32_t": CType(type_="int32_t", size=4),
|
|
182
|
+
"uint32_t": CType(type_="uint32_t", size=4),
|
|
183
|
+
"dword": CType(type_="dword", size=4),
|
|
184
|
+
"int64_t": CType(type_="int64_t", size=8),
|
|
185
|
+
"uint64_t": CType(type_="uint64_t", size=8),
|
|
186
|
+
}
|
|
187
|
+
self.ALL_TYPES.update(self.STDINT_TYPES)
|
|
188
|
+
self.ALL_TYPES.update(self.extra_types)
|
|
189
|
+
|
|
190
|
+
for name, ctype in self.ALL_TYPES.items():
|
|
191
|
+
if name.startswith("unsigned"):
|
|
192
|
+
self.SIZE_TO_TYPES[ctype.size] = ctype
|
|
193
|
+
|
|
194
|
+
def extract_type_name(self, type_str: str) -> str | None:
|
|
195
|
+
"""
|
|
196
|
+
Normalizes types that may come in as declarations, removing any extraneous information that may be present
|
|
197
|
+
and just getting the name of that type.
|
|
198
|
+
|
|
199
|
+
In the case of:
|
|
200
|
+
"char *" that would return "char *"
|
|
201
|
+
"struct foo *" that would return "foo *"
|
|
202
|
+
"typedef int my_type" that would return "my_type"
|
|
203
|
+
"""
|
|
204
|
+
# XXX: this could be subverted by a type name that contains a scope or external ref ("extern")
|
|
205
|
+
is_defined = any(type_str.strip().startswith(t) for t in ["struct", "enum", "union", "typedef"])
|
|
206
|
+
if not is_defined:
|
|
207
|
+
return type_str
|
|
208
|
+
|
|
209
|
+
parsable_type = type_str.replace(";", "").strip()
|
|
210
|
+
type_name = None
|
|
211
|
+
try:
|
|
212
|
+
ast = self._type_parser_singleton.parse(text=parsable_type + ";")
|
|
213
|
+
if ast.ext:
|
|
214
|
+
type_name = ast.ext[0].name
|
|
215
|
+
except ParseError:
|
|
216
|
+
pass
|
|
217
|
+
|
|
218
|
+
if type_name is None:
|
|
219
|
+
# do a hackish parse to get the type name, which may be inside a defined type-in-place
|
|
220
|
+
# remove "struct", "enum", "union", and "typedef" keywords, select the final type
|
|
221
|
+
if any(parsable_type.startswith(t) for t in ["struct", "enum", "union", "typedef"]):
|
|
222
|
+
final_type = type_str.split(" ")[-1]
|
|
223
|
+
final_type = final_type.replace(";", "").strip()
|
|
224
|
+
if " " not in final_type:
|
|
225
|
+
# final sanity check that it really is just a name
|
|
226
|
+
type_name = final_type
|
|
227
|
+
|
|
228
|
+
return type_name
|
|
229
|
+
|
|
230
|
+
def parse_type(self, defn, predefined_types=None, arch=None) -> Optional[CType]: # pylint:disable=unused-argument
|
|
231
|
+
"""
|
|
232
|
+
Parse a simple type expression into a SimType
|
|
233
|
+
|
|
234
|
+
>>> self.parse_type('int *')
|
|
235
|
+
"""
|
|
236
|
+
return self.parse_type_with_name(defn, predefined_types=predefined_types, arch=arch)[0]
|
|
237
|
+
|
|
238
|
+
def parse_type_with_name(self, defn, predefined_types=None, arch=None): # pylint:disable=unused-argument
|
|
239
|
+
"""
|
|
240
|
+
Parse a simple type expression into a SimType, returning the a tuple of the type object and any associated name
|
|
241
|
+
that might be found in the place a name would go in a type declaration.
|
|
242
|
+
|
|
243
|
+
>>> self.parse_type_with_name('int *foo')
|
|
244
|
+
"""
|
|
245
|
+
if not defn:
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
if pycparser is None:
|
|
249
|
+
raise ImportError("Please install pycparser in order to parse C definitions")
|
|
250
|
+
|
|
251
|
+
defn = re.sub(r"/\*.*?\*/", r"", defn, flags=re.DOTALL)
|
|
252
|
+
defn = re.sub(r"//.*?$", r"", defn, flags=re.MULTILINE)
|
|
253
|
+
|
|
254
|
+
failed_parse = False
|
|
255
|
+
try:
|
|
256
|
+
node = self._type_parser_singleton.parse_type_with_name(text=defn)
|
|
257
|
+
except ParseError:
|
|
258
|
+
failed_parse = True
|
|
259
|
+
|
|
260
|
+
#
|
|
261
|
+
# in the event of a failed type parse it may just be a custom type, so we should try again
|
|
262
|
+
# with the struct specifier and see if it works out
|
|
263
|
+
#
|
|
264
|
+
if failed_parse:
|
|
265
|
+
try:
|
|
266
|
+
node = self._type_parser_singleton.parse_type_with_name(text="struct " + defn)
|
|
267
|
+
except Exception:
|
|
268
|
+
return (None, )
|
|
269
|
+
|
|
270
|
+
if not isinstance(node, pycparser.c_ast.Typename) and \
|
|
271
|
+
not isinstance(node, pycparser.c_ast.Decl):
|
|
272
|
+
raise pycparser.c_parser.ParseError("Got an unexpected type out of pycparser")
|
|
273
|
+
|
|
274
|
+
decl = node.type
|
|
275
|
+
extra_types = {} if not predefined_types else dict(predefined_types)
|
|
276
|
+
return self._decl_to_type(decl, extra_types=extra_types), node.name
|
|
277
|
+
|
|
278
|
+
def _decl_to_type(self, decl, extra_types=None) -> Optional[CType]:
|
|
279
|
+
if not decl:
|
|
280
|
+
return decl
|
|
281
|
+
|
|
282
|
+
if extra_types is None: extra_types = {}
|
|
283
|
+
|
|
284
|
+
if isinstance(decl, pycparser.c_ast.FuncDecl):
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
elif isinstance(decl, pycparser.c_ast.TypeDecl):
|
|
288
|
+
return self._decl_to_type(decl.type, extra_types)
|
|
289
|
+
|
|
290
|
+
elif isinstance(decl, pycparser.c_ast.Typedef):
|
|
291
|
+
return self._decl_to_type(decl.type, extra_types)
|
|
292
|
+
|
|
293
|
+
elif isinstance(decl, pycparser.c_ast.PtrDecl):
|
|
294
|
+
pts_to = self._decl_to_type(decl.type, extra_types)
|
|
295
|
+
if not pts_to:
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
return CType(type_=pts_to.type, size=self.sizeof_ptr, is_ptr=True, is_unknown=pts_to.is_unknown)
|
|
299
|
+
|
|
300
|
+
elif isinstance(decl, pycparser.c_ast.ArrayDecl):
|
|
301
|
+
elem_type = self._decl_to_type(decl.type, extra_types)
|
|
302
|
+
|
|
303
|
+
if decl.dim is None:
|
|
304
|
+
"""
|
|
305
|
+
r = SimTypeArray(elem_type)
|
|
306
|
+
r._arch = arch
|
|
307
|
+
return r
|
|
308
|
+
"""
|
|
309
|
+
return CType(type_=elem_type, is_array=True, size=0)
|
|
310
|
+
try:
|
|
311
|
+
size = self._parse_const(decl.dim, extra_types=extra_types)
|
|
312
|
+
except ValueError as e:
|
|
313
|
+
#l.warning("Got error parsing array dimension, defaulting to zero: %s", e)
|
|
314
|
+
size = 0
|
|
315
|
+
"""
|
|
316
|
+
r = SimTypeFixedSizeArray(elem_type, size)
|
|
317
|
+
r._arch = arch
|
|
318
|
+
"""
|
|
319
|
+
return CType(type_=elem_type, is_array=True, size=size)
|
|
320
|
+
|
|
321
|
+
elif isinstance(decl, pycparser.c_ast.Struct):
|
|
322
|
+
if decl is None:
|
|
323
|
+
return None
|
|
324
|
+
|
|
325
|
+
return CType(type_=decl.name, is_unknown=True)
|
|
326
|
+
|
|
327
|
+
elif isinstance(decl, pycparser.c_ast.Union):
|
|
328
|
+
return None
|
|
329
|
+
|
|
330
|
+
elif isinstance(decl, pycparser.c_ast.IdentifierType):
|
|
331
|
+
key = ' '.join(decl.names)
|
|
332
|
+
if key in extra_types:
|
|
333
|
+
return extra_types[key]
|
|
334
|
+
elif key in self.ALL_TYPES:
|
|
335
|
+
return self.ALL_TYPES[key]
|
|
336
|
+
else:
|
|
337
|
+
#raise TypeError("Unknown type '%s'" % key)
|
|
338
|
+
return CType(type_=key, is_unknown=True)
|
|
339
|
+
|
|
340
|
+
elif isinstance(decl, pycparser.c_ast.Enum):
|
|
341
|
+
# See C99 at 6.7.2.2
|
|
342
|
+
return self.ALL_TYPES['int']
|
|
343
|
+
|
|
344
|
+
raise ValueError("Unknown type!")
|
|
345
|
+
|
|
346
|
+
def _make_scope(self, predefined_types=None):
|
|
347
|
+
"""
|
|
348
|
+
Generate CParser scope_stack argument to parse method
|
|
349
|
+
"""
|
|
350
|
+
all_types = ChainMap(predefined_types or {}, self.ALL_TYPES)
|
|
351
|
+
scope = dict()
|
|
352
|
+
for ty in all_types:
|
|
353
|
+
if ty in self.BASIC_TYPES:
|
|
354
|
+
continue
|
|
355
|
+
if ' ' in ty:
|
|
356
|
+
continue
|
|
357
|
+
|
|
358
|
+
typ = all_types[ty]
|
|
359
|
+
scope[ty] = True
|
|
360
|
+
return [scope]
|
|
361
|
+
|
|
362
|
+
def _parse_const(self, c, extra_types=None):
|
|
363
|
+
if type(c) is pycparser.c_ast.Constant:
|
|
364
|
+
return int(c.value, base=0)
|
|
365
|
+
elif type(c) is pycparser.c_ast.BinaryOp:
|
|
366
|
+
if c.op == '+':
|
|
367
|
+
return self._parse_const(c.children()[0][1], extra_types=extra_types) + self._parse_const(
|
|
368
|
+
c.children()[1][1], extra_types=extra_types)
|
|
369
|
+
if c.op == '-':
|
|
370
|
+
return self._parse_const(c.children()[0][1], extra_types=extra_types) - self._parse_const(
|
|
371
|
+
c.children()[1][1], extra_types=extra_types)
|
|
372
|
+
if c.op == '*':
|
|
373
|
+
return self._parse_const(c.children()[0][1], extra_types=extra_types) * self._parse_const(
|
|
374
|
+
c.children()[1][1], extra_types=extra_types)
|
|
375
|
+
if c.op == '/':
|
|
376
|
+
return self._parse_const(c.children()[0][1], extra_types=extra_types) // self._parse_const(
|
|
377
|
+
c.children()[1][1], extra_types=extra_types)
|
|
378
|
+
if c.op == '<<':
|
|
379
|
+
return self._parse_const(c.children()[0][1], extra_types=extra_types) << self._parse_const(
|
|
380
|
+
c.children()[1][1], extra_types=extra_types)
|
|
381
|
+
if c.op == '>>':
|
|
382
|
+
return self._parse_const(c.children()[0][1], extra_types=extra_types) >> self._parse_const(
|
|
383
|
+
c.children()[1][1], extra_types=extra_types)
|
|
384
|
+
raise ValueError('Binary op %s' % c.op)
|
|
385
|
+
elif type(c) is pycparser.c_ast.UnaryOp:
|
|
386
|
+
if c.op == 'sizeof':
|
|
387
|
+
return self._decl_to_type(c.expr.type, extra_types=extra_types).size
|
|
388
|
+
else:
|
|
389
|
+
raise ValueError("Unary op %s" % c.op)
|
|
390
|
+
elif type(c) is pycparser.c_ast.Cast:
|
|
391
|
+
return self._parse_const(c.expr, extra_types=extra_types)
|
|
392
|
+
else:
|
|
393
|
+
raise ValueError(c)
|
|
394
|
+
|
|
395
|
+
def size_to_type(self, size: int) -> CType:
|
|
396
|
+
if not size:
|
|
397
|
+
raise ValueError("A type size must be greater than 0")
|
|
398
|
+
|
|
399
|
+
ctype = self.SIZE_TO_TYPES.get(size, None)
|
|
400
|
+
if ctype is None:
|
|
401
|
+
# one of two possible things have happend here:
|
|
402
|
+
# 1. this is a type that is larger than the simple types, in which case it's an array
|
|
403
|
+
# 2. this is a type with a non-aligned size, in which case we default to an array as well
|
|
404
|
+
array_size = size // self.sizeof_char
|
|
405
|
+
ctype = self.parse_type(f"char[{array_size}]")
|
|
406
|
+
if ctype is None:
|
|
407
|
+
raise RuntimeError(f"Failed to create a CType of array size {array_size}")
|
|
408
|
+
|
|
409
|
+
return ctype
|
declib/api/utils.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
import tqdm
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def progress_bar(items, gui=True, desc="Progressing..."):
|
|
7
|
+
if gui:
|
|
8
|
+
from declib.ui.utils import QProgressBarDialog
|
|
9
|
+
pbar = QProgressBarDialog(label_text=desc)
|
|
10
|
+
pbar.show()
|
|
11
|
+
callback_stub = pbar.update_progress
|
|
12
|
+
else:
|
|
13
|
+
t = tqdm.tqdm(desc=desc)
|
|
14
|
+
callback_stub = t.update
|
|
15
|
+
|
|
16
|
+
bucket_size = len(items) / 100.0
|
|
17
|
+
if bucket_size < 1:
|
|
18
|
+
callback_amt = int(1 / (bucket_size))
|
|
19
|
+
bucket_size = 1
|
|
20
|
+
else:
|
|
21
|
+
callback_amt = 1
|
|
22
|
+
bucket_size = math.ceil(bucket_size)
|
|
23
|
+
|
|
24
|
+
for i, item in enumerate(items):
|
|
25
|
+
yield item
|
|
26
|
+
if i % bucket_size == 0:
|
|
27
|
+
callback_stub(callback_amt)
|
|
28
|
+
|
|
29
|
+
if gui:
|
|
30
|
+
# close the progress bar since it may not hit 100%
|
|
31
|
+
pbar.close()
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import toml
|
|
4
|
+
|
|
5
|
+
from .formatting import TomlHexEncoder, ArtifactFormat
|
|
6
|
+
from .artifact import Artifact
|
|
7
|
+
from .comment import Comment
|
|
8
|
+
from .decompilation import Decompilation
|
|
9
|
+
from .enum import Enum
|
|
10
|
+
from .func import Function, FunctionHeader, FunctionArgument
|
|
11
|
+
from .global_variable import GlobalVariable
|
|
12
|
+
from .patch import Patch
|
|
13
|
+
from .segment import Segment
|
|
14
|
+
from .stack_variable import StackVariable
|
|
15
|
+
from .struct import Struct, StructMember
|
|
16
|
+
from .context import Context
|
|
17
|
+
from .typedef import Typedef
|
|
18
|
+
|
|
19
|
+
ART_NAME_TO_CLS = {
|
|
20
|
+
Function.__name__: Function,
|
|
21
|
+
FunctionHeader.__name__: FunctionHeader,
|
|
22
|
+
FunctionArgument.__name__: FunctionArgument,
|
|
23
|
+
StackVariable.__name__: StackVariable,
|
|
24
|
+
Comment.__name__: Comment,
|
|
25
|
+
GlobalVariable.__name__: GlobalVariable,
|
|
26
|
+
Enum.__name__: Enum,
|
|
27
|
+
Struct.__name__: Struct,
|
|
28
|
+
StructMember.__name__: StructMember,
|
|
29
|
+
Patch.__name__: Patch,
|
|
30
|
+
Decompilation.__name__: Decompilation,
|
|
31
|
+
Context.__name__: Context,
|
|
32
|
+
Typedef.__name__: Typedef,
|
|
33
|
+
Segment.__name__: Segment,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
ALL_ARTIFACTS = list(ART_NAME_TO_CLS.values())
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _dict_from_str(art_str: str, fmt=ArtifactFormat.TOML) -> dict:
|
|
40
|
+
if fmt == ArtifactFormat.TOML:
|
|
41
|
+
return toml.loads(art_str)
|
|
42
|
+
elif fmt == ArtifactFormat.JSON:
|
|
43
|
+
return json.loads(art_str)
|
|
44
|
+
else:
|
|
45
|
+
raise ValueError(f"Loading from format {fmt} is not yet supported.")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _art_from_dict(art_dict: dict) -> Artifact:
|
|
49
|
+
art_type_str = art_dict.get(Artifact.ART_TYPE_STR, None)
|
|
50
|
+
if art_type_str is None:
|
|
51
|
+
raise ValueError(f"Artifact type string not found in artifact data: {art_dict}. Is this a valid artifact?")
|
|
52
|
+
|
|
53
|
+
art_cls = ART_NAME_TO_CLS[art_type_str]
|
|
54
|
+
art = art_cls()
|
|
55
|
+
art.__setstate__(art_dict)
|
|
56
|
+
return art
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _load_arts_from_list(art_strs: list[str], fmt=ArtifactFormat.TOML) -> list[Artifact]:
|
|
60
|
+
arts = []
|
|
61
|
+
for art_str in art_strs:
|
|
62
|
+
data_dict = _dict_from_str(art_str, fmt=fmt)
|
|
63
|
+
art = _art_from_dict(data_dict)
|
|
64
|
+
arts.append(art)
|
|
65
|
+
return arts
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _load_arts_from_string(art_str: str, fmt=ArtifactFormat.TOML) -> list[Artifact]:
|
|
69
|
+
data_dict = _dict_from_str(art_str, fmt=fmt)
|
|
70
|
+
if isinstance(data_dict, dict):
|
|
71
|
+
data_dicts = list(data_dict.values())
|
|
72
|
+
elif isinstance(data_dict, list):
|
|
73
|
+
data_dicts = data_dict
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError(f"Unexpected data type: {type(data_dict)}")
|
|
76
|
+
|
|
77
|
+
arts = []
|
|
78
|
+
for v in data_dicts:
|
|
79
|
+
art = _art_from_dict(v)
|
|
80
|
+
arts.append(art)
|
|
81
|
+
|
|
82
|
+
return arts
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def load_many_artifacts(art_strings: list[str], fmt=ArtifactFormat.TOML) -> list[Artifact]:
|
|
86
|
+
"""
|
|
87
|
+
A helper function to load many dumped artifacts from a list of strings. Each string should have been dumped
|
|
88
|
+
using the `dumps` method of an artifact.
|
|
89
|
+
|
|
90
|
+
:param art_strings: A list of strings or a single string containing multiple dumped artifacts.
|
|
91
|
+
:param fmt: The format of the dumped artifacts.
|
|
92
|
+
"""
|
|
93
|
+
return _load_arts_from_list(art_strings, fmt=fmt)
|