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
declib/artifacts/func.py
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
from typing import Dict, Optional
|
|
2
|
+
|
|
3
|
+
from .artifact import Artifact
|
|
4
|
+
from .stack_variable import StackVariable
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# Function Header Classes
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
class FunctionArgument(Artifact):
|
|
12
|
+
__slots__ = Artifact.__slots__ + (
|
|
13
|
+
"offset",
|
|
14
|
+
"name",
|
|
15
|
+
"type",
|
|
16
|
+
"size",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
offset: int = None,
|
|
22
|
+
name: str = None,
|
|
23
|
+
type_: str = None,
|
|
24
|
+
size: int = None,
|
|
25
|
+
**kwargs
|
|
26
|
+
):
|
|
27
|
+
super().__init__(**kwargs)
|
|
28
|
+
self.offset = offset
|
|
29
|
+
self.name = name
|
|
30
|
+
self.type = type_
|
|
31
|
+
self.size = size
|
|
32
|
+
|
|
33
|
+
def __str__(self):
|
|
34
|
+
return f"<FuncArg: {self.type} {self.name}; @{self.offset}>"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class FunctionHeader(Artifact):
|
|
38
|
+
__slots__ = Artifact.__slots__ + (
|
|
39
|
+
"name",
|
|
40
|
+
"addr",
|
|
41
|
+
"type",
|
|
42
|
+
"args"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
name: str = None,
|
|
48
|
+
addr: int = None,
|
|
49
|
+
type_: str = None,
|
|
50
|
+
args: Optional[Dict[int, FunctionArgument]] = None,
|
|
51
|
+
**kwargs
|
|
52
|
+
):
|
|
53
|
+
super().__init__(**kwargs)
|
|
54
|
+
self.name = name
|
|
55
|
+
self.addr = addr
|
|
56
|
+
self.type = type_
|
|
57
|
+
self.args: Dict = args or {}
|
|
58
|
+
|
|
59
|
+
def __str__(self):
|
|
60
|
+
return f"<FuncHeader: {self.type} {self.name}(args={len(self.args or {})}); @{hex(self.addr)}>"
|
|
61
|
+
|
|
62
|
+
def __getstate__(self):
|
|
63
|
+
data_dict = super().__getstate__()
|
|
64
|
+
args_dict = data_dict["args"]
|
|
65
|
+
if args_dict is None:
|
|
66
|
+
return data_dict
|
|
67
|
+
|
|
68
|
+
new_args_dict = {hex(k): v.__getstate__() for k, v in args_dict.items()}
|
|
69
|
+
data_dict["args"] = new_args_dict
|
|
70
|
+
return data_dict
|
|
71
|
+
|
|
72
|
+
def __setstate__(self, state):
|
|
73
|
+
# Pop nested object data and reconstruct in local variable
|
|
74
|
+
args_dict = state.pop("args", {})
|
|
75
|
+
new_args_dict = {}
|
|
76
|
+
for k, v in args_dict.items():
|
|
77
|
+
fa = FunctionArgument()
|
|
78
|
+
fa.__setstate__(v)
|
|
79
|
+
new_args_dict[int(k, 0)] = fa
|
|
80
|
+
|
|
81
|
+
# Put reconstructed objects back in state
|
|
82
|
+
state["args"] = new_args_dict
|
|
83
|
+
|
|
84
|
+
# Let super set all attributes at once
|
|
85
|
+
super().__setstate__(state)
|
|
86
|
+
|
|
87
|
+
def diff(self, other, **kwargs) -> Dict:
|
|
88
|
+
diff_dict = {}
|
|
89
|
+
# early exit if the two do not match type
|
|
90
|
+
if not isinstance(other, FunctionHeader):
|
|
91
|
+
for k in ["name", "addr", "type"]:
|
|
92
|
+
diff_dict[k] = {
|
|
93
|
+
"before": getattr(self, k),
|
|
94
|
+
"after": None
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
diff_dict["args"] = {idx: arg.diff(None) for idx, arg in self.args.items()}
|
|
98
|
+
return diff_dict
|
|
99
|
+
|
|
100
|
+
# metadata
|
|
101
|
+
for k in ["name", "addr", "type"]:
|
|
102
|
+
if getattr(self, k) == getattr(other, k):
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
diff_dict[k] = {
|
|
106
|
+
"before": getattr(self, k),
|
|
107
|
+
"after": getattr(other, k)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# args
|
|
111
|
+
diff_dict["args"] = {}
|
|
112
|
+
for idx, self_arg in self.args.items():
|
|
113
|
+
try:
|
|
114
|
+
other_arg = other.args[idx]
|
|
115
|
+
except KeyError:
|
|
116
|
+
other_arg = None
|
|
117
|
+
|
|
118
|
+
diff_dict["args"][idx] = self_arg.diff(other_arg)
|
|
119
|
+
|
|
120
|
+
for idx, other_arg in other.args.items():
|
|
121
|
+
if idx in diff_dict["args"]:
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
diff_dict["args"][idx] = self.invert_diff(other_arg.diff(None))
|
|
125
|
+
|
|
126
|
+
return diff_dict
|
|
127
|
+
|
|
128
|
+
def reset_last_change(self):
|
|
129
|
+
if self.args:
|
|
130
|
+
for arg in self.args.values():
|
|
131
|
+
arg.reset_last_change()
|
|
132
|
+
|
|
133
|
+
def overwrite_merge(self, obj2: "Artifact", **kwargs):
|
|
134
|
+
fh2: "FunctionHeader" = obj2
|
|
135
|
+
merged_fh: "FunctionHeader" = self.copy()
|
|
136
|
+
if not fh2 or not isinstance(fh2, FunctionHeader) or self == fh2:
|
|
137
|
+
return merged_fh
|
|
138
|
+
|
|
139
|
+
if fh2.name is not None:
|
|
140
|
+
merged_fh.name = fh2.name
|
|
141
|
+
if fh2.type is not None:
|
|
142
|
+
merged_fh.type = fh2.type
|
|
143
|
+
|
|
144
|
+
# header args
|
|
145
|
+
for off, var in fh2.args.items():
|
|
146
|
+
if var is not None:
|
|
147
|
+
if off in merged_fh.args:
|
|
148
|
+
merged_var = merged_fh.args[off].overwrite_merge(var)
|
|
149
|
+
else:
|
|
150
|
+
merged_var = var
|
|
151
|
+
|
|
152
|
+
merged_fh.args[off] = merged_var
|
|
153
|
+
|
|
154
|
+
return merged_fh
|
|
155
|
+
|
|
156
|
+
def nonconflict_merge(self, fh2: "FunctionHeader", **kwargs):
|
|
157
|
+
fh1: "FunctionHeader" = self.copy()
|
|
158
|
+
if not fh2 or not isinstance(fh2, FunctionHeader):
|
|
159
|
+
return fh1
|
|
160
|
+
|
|
161
|
+
if fh1.name is None:
|
|
162
|
+
fh1.name = fh2.name
|
|
163
|
+
|
|
164
|
+
if fh1.type is None:
|
|
165
|
+
fh1.type = fh2.type
|
|
166
|
+
|
|
167
|
+
# header args
|
|
168
|
+
for off, var in fh2.args.items():
|
|
169
|
+
merge_var: FunctionArgument = fh1.args[off].copy() if off in fh1.args else var
|
|
170
|
+
merge_var = merge_var.nonconflict_merge(var)
|
|
171
|
+
fh1.args[off] = merge_var
|
|
172
|
+
|
|
173
|
+
return fh1
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
#
|
|
177
|
+
# Full Function Class
|
|
178
|
+
#
|
|
179
|
+
|
|
180
|
+
class Function(Artifact):
|
|
181
|
+
"""
|
|
182
|
+
The Function class describes a Function found a decompiler. There are three components to a function:
|
|
183
|
+
1. Metadata
|
|
184
|
+
2. Header
|
|
185
|
+
3. Stack Vars
|
|
186
|
+
|
|
187
|
+
The metadata contains info on changes and size. The header holds the return type,
|
|
188
|
+
and arguments (including their types). The stack vars contain StackVariables.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
__slots__ = Artifact.__slots__ + (
|
|
192
|
+
"addr",
|
|
193
|
+
"size",
|
|
194
|
+
"header",
|
|
195
|
+
"stack_vars",
|
|
196
|
+
"dec_obj",
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
def __init__(
|
|
200
|
+
self,
|
|
201
|
+
addr: int = None,
|
|
202
|
+
size: int = None,
|
|
203
|
+
header: Optional[FunctionHeader] = None,
|
|
204
|
+
stack_vars: Optional[Dict[int, StackVariable]] = None,
|
|
205
|
+
dec_obj: Optional[object] = None,
|
|
206
|
+
name: str = None,
|
|
207
|
+
**kwargs
|
|
208
|
+
):
|
|
209
|
+
super().__init__(**kwargs)
|
|
210
|
+
# never use dec_obj for comparison, dumping, etc.
|
|
211
|
+
self._attr_ignore_set.add("dec_obj")
|
|
212
|
+
|
|
213
|
+
self.addr = addr
|
|
214
|
+
self.size = size
|
|
215
|
+
self.header = header
|
|
216
|
+
if name is not None:
|
|
217
|
+
self.name = name
|
|
218
|
+
self.stack_vars: Dict[int, StackVariable] = stack_vars or {}
|
|
219
|
+
|
|
220
|
+
# a special property which can only be set while running inside the decompiler.
|
|
221
|
+
# contains a reference to the decompiler object associated with this function.
|
|
222
|
+
self.dec_obj = dec_obj
|
|
223
|
+
|
|
224
|
+
def __str__(self):
|
|
225
|
+
if self.header:
|
|
226
|
+
return f"<Function: {self.header.type} {self.name}(args={len(self.args)}); @{hex(self.addr)} " \
|
|
227
|
+
f"vars={len(self.stack_vars)} len={hex(self.size)}>"
|
|
228
|
+
|
|
229
|
+
return f"<Function: @{hex(self.addr)} len={hex(self.size)}>"
|
|
230
|
+
|
|
231
|
+
def __getstate__(self):
|
|
232
|
+
header = self.header.__getstate__() if self.header else None
|
|
233
|
+
stack_vars = {
|
|
234
|
+
hex(offset): stack_var.__getstate__() for offset, stack_var in self.stack_vars.items()
|
|
235
|
+
} if self.stack_vars else None
|
|
236
|
+
|
|
237
|
+
state = super().__getstate__()
|
|
238
|
+
# give alias for name and type for convenience
|
|
239
|
+
state["name"] = self.name
|
|
240
|
+
state["type"] = self.type
|
|
241
|
+
state["header"] = header
|
|
242
|
+
state["stack_vars"] = stack_vars
|
|
243
|
+
return state
|
|
244
|
+
|
|
245
|
+
def __setstate__(self, state):
|
|
246
|
+
# When pickle calls __setstate__, __init__ is never called
|
|
247
|
+
# Initialize _attr_ignore_set and add dec_obj to it (as done in __init__)
|
|
248
|
+
if not hasattr(self, '_attr_ignore_set'):
|
|
249
|
+
self._attr_ignore_set = set()
|
|
250
|
+
self._attr_ignore_set.add("dec_obj")
|
|
251
|
+
|
|
252
|
+
# XXX: this is a backport of the old state format. Remove this after a few releases.
|
|
253
|
+
if "metadata" in state:
|
|
254
|
+
metadata: Dict = state.pop("metadata")
|
|
255
|
+
metadata.update(state)
|
|
256
|
+
state = metadata
|
|
257
|
+
|
|
258
|
+
# Pop nested object data and reconstruct in local variables
|
|
259
|
+
header_dat = state.pop("header", None)
|
|
260
|
+
if header_dat:
|
|
261
|
+
header = FunctionHeader()
|
|
262
|
+
header.__setstate__(header_dat)
|
|
263
|
+
else:
|
|
264
|
+
header = None
|
|
265
|
+
|
|
266
|
+
# Handle name/type aliases that override header values
|
|
267
|
+
# We modify the header object directly instead of using property setters
|
|
268
|
+
# to avoid accessing self.header and self.addr before they're initialized
|
|
269
|
+
name_override = state.pop("name", None)
|
|
270
|
+
type_override = state.pop("type", None)
|
|
271
|
+
|
|
272
|
+
if name_override is not None and header is not None:
|
|
273
|
+
header.name = name_override
|
|
274
|
+
if type_override is not None and header is not None:
|
|
275
|
+
header.type = type_override
|
|
276
|
+
|
|
277
|
+
stack_vars_dat = state.pop("stack_vars", {})
|
|
278
|
+
stack_vars = {}
|
|
279
|
+
if stack_vars_dat:
|
|
280
|
+
for off, stack_var in stack_vars_dat.items():
|
|
281
|
+
sv = StackVariable()
|
|
282
|
+
sv.__setstate__(stack_var)
|
|
283
|
+
stack_vars[int(off, 0)] = sv
|
|
284
|
+
|
|
285
|
+
# Put reconstructed objects back in state
|
|
286
|
+
state["header"] = header
|
|
287
|
+
state["stack_vars"] = stack_vars
|
|
288
|
+
|
|
289
|
+
# Let super set all attributes at once
|
|
290
|
+
super().__setstate__(state)
|
|
291
|
+
|
|
292
|
+
# dec_obj is intentionally excluded from serialization (it's in
|
|
293
|
+
# _attr_ignore_set), so it is never present in `state`. Because Function
|
|
294
|
+
# uses __slots__, the attribute would otherwise be unset after a
|
|
295
|
+
# deserialization round-trip and any access to `func.dec_obj` (e.g. in
|
|
296
|
+
# get_dependencies or a backend's rename path) would raise AttributeError.
|
|
297
|
+
if not hasattr(self, "dec_obj"):
|
|
298
|
+
self.dec_obj = None
|
|
299
|
+
|
|
300
|
+
def diff(self, other, **kwargs) -> Dict:
|
|
301
|
+
diff_dict = {}
|
|
302
|
+
if not isinstance(other, Function):
|
|
303
|
+
# metadata
|
|
304
|
+
for k in ["addr", "size"]:
|
|
305
|
+
diff_dict[k] = {
|
|
306
|
+
"before": getattr(self, k),
|
|
307
|
+
"after": None
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# header
|
|
311
|
+
diff_dict["header"] = self.header.diff(other.header)
|
|
312
|
+
# args
|
|
313
|
+
diff_dict["stack_vars"] = {off: var.diff(None) for off, var in self.stack_vars.items()}
|
|
314
|
+
return diff_dict
|
|
315
|
+
|
|
316
|
+
# metadata
|
|
317
|
+
for k in ["addr", "size"]:
|
|
318
|
+
if getattr(self, k) == getattr(other, k):
|
|
319
|
+
continue
|
|
320
|
+
|
|
321
|
+
diff_dict[k] = {
|
|
322
|
+
"before": getattr(self, k),
|
|
323
|
+
"after": getattr(other, k)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
# header
|
|
327
|
+
if self.header:
|
|
328
|
+
diff_dict["header"] = self.header.diff(other.header)
|
|
329
|
+
elif other.header:
|
|
330
|
+
diff_dict["header"] = self.invert_diff(other.header.diff(None))
|
|
331
|
+
else:
|
|
332
|
+
diff_dict["header"] = {"before": None, "after": None}
|
|
333
|
+
|
|
334
|
+
# stack vars
|
|
335
|
+
diff_dict["stack_vars"] = {}
|
|
336
|
+
for off, self_var in self.stack_vars.items():
|
|
337
|
+
try:
|
|
338
|
+
other_var = other.stack_vars[off]
|
|
339
|
+
except KeyError:
|
|
340
|
+
other_var = None
|
|
341
|
+
|
|
342
|
+
diff_dict["stack_vars"][off] = self_var.diff(other_var)
|
|
343
|
+
|
|
344
|
+
for off, other_var in other.stack_vars.items():
|
|
345
|
+
if off in diff_dict["stack_vars"]:
|
|
346
|
+
continue
|
|
347
|
+
|
|
348
|
+
diff_dict["stack_vars"][off] = self.invert_diff(other_var.diff(None))
|
|
349
|
+
|
|
350
|
+
return diff_dict
|
|
351
|
+
|
|
352
|
+
def reset_last_change(self):
|
|
353
|
+
if self.header:
|
|
354
|
+
self.header.reset_last_change()
|
|
355
|
+
|
|
356
|
+
if self.stack_vars:
|
|
357
|
+
for sv in self.stack_vars.values():
|
|
358
|
+
sv.reset_last_change()
|
|
359
|
+
|
|
360
|
+
def overwrite_merge(self, obj2: "Artifact", **kwargs):
|
|
361
|
+
func2: "Function" = obj2
|
|
362
|
+
merged_func: "Function" = self.copy()
|
|
363
|
+
if not func2 or self == func2:
|
|
364
|
+
return merged_func
|
|
365
|
+
|
|
366
|
+
if merged_func.header is None:
|
|
367
|
+
merged_func.header = func2.header.copy() if func2.header else None
|
|
368
|
+
|
|
369
|
+
if merged_func.header:
|
|
370
|
+
merged_func.header = merged_func.header.overwrite_merge(func2.header)
|
|
371
|
+
|
|
372
|
+
for off, var in func2.stack_vars.items():
|
|
373
|
+
if var is not None:
|
|
374
|
+
if off in merged_func.stack_vars:
|
|
375
|
+
merged_var = merged_func.stack_vars[off].overwrite_merge(var)
|
|
376
|
+
else:
|
|
377
|
+
merged_var = var
|
|
378
|
+
|
|
379
|
+
merged_func.stack_vars[off] = merged_var
|
|
380
|
+
|
|
381
|
+
return merged_func
|
|
382
|
+
|
|
383
|
+
def nonconflict_merge(self, func2: "Artifact", **kwargs):
|
|
384
|
+
func1: "Function" = self.copy()
|
|
385
|
+
|
|
386
|
+
if not func2 or func1 == func2:
|
|
387
|
+
return func1
|
|
388
|
+
|
|
389
|
+
merge_func: "Function" = func1.copy()
|
|
390
|
+
|
|
391
|
+
if merge_func.header is None:
|
|
392
|
+
merge_func.header = func2.header.copy() if func2.header else None
|
|
393
|
+
elif func2.header is not None:
|
|
394
|
+
merge_func.header = merge_func.header.nonconflict_merge(func2.header)
|
|
395
|
+
|
|
396
|
+
# stack vars
|
|
397
|
+
for off, var in func2.stack_vars.items():
|
|
398
|
+
merge_var = func1.stack_vars[off].copy() if off in func1.stack_vars else var
|
|
399
|
+
merge_var = StackVariable.nonconflict_merge(merge_var, var)
|
|
400
|
+
|
|
401
|
+
merge_func.stack_vars[off] = merge_var
|
|
402
|
+
|
|
403
|
+
return merge_func
|
|
404
|
+
|
|
405
|
+
#
|
|
406
|
+
# Property Shortcuts (Alias)
|
|
407
|
+
#
|
|
408
|
+
|
|
409
|
+
@property
|
|
410
|
+
def name(self):
|
|
411
|
+
return self.header.name if self.header else None
|
|
412
|
+
|
|
413
|
+
@name.setter
|
|
414
|
+
def name(self, value):
|
|
415
|
+
# create a header if one does not exist for this function
|
|
416
|
+
if not self.header:
|
|
417
|
+
self.header = FunctionHeader(name=None, addr=self.addr)
|
|
418
|
+
self.header.name = value
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def type(self):
|
|
422
|
+
return self.header.type if self.header else None
|
|
423
|
+
|
|
424
|
+
@type.setter
|
|
425
|
+
def type(self, value):
|
|
426
|
+
# create a header if one does not exist for this function
|
|
427
|
+
if not self.header:
|
|
428
|
+
self.header = FunctionHeader(name=None, addr=self.addr)
|
|
429
|
+
self.header.type = value
|
|
430
|
+
|
|
431
|
+
@property
|
|
432
|
+
def args(self):
|
|
433
|
+
return self.header.args if self.header else {}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import toml
|
|
4
|
+
|
|
5
|
+
from .artifact import Artifact
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GlobalVariable(Artifact):
|
|
9
|
+
__slots__ = Artifact.__slots__ + (
|
|
10
|
+
"addr",
|
|
11
|
+
"name",
|
|
12
|
+
"type",
|
|
13
|
+
"size"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
addr: int = None,
|
|
19
|
+
name: str = None,
|
|
20
|
+
type_: Optional[str] = None,
|
|
21
|
+
size: int = None,
|
|
22
|
+
**kwargs
|
|
23
|
+
):
|
|
24
|
+
super().__init__(**kwargs)
|
|
25
|
+
self.addr = addr
|
|
26
|
+
self.name = name
|
|
27
|
+
self.type = type_
|
|
28
|
+
self.size = size
|
|
29
|
+
|
|
30
|
+
def __str__(self):
|
|
31
|
+
return f"<GlobalVar: {self.type} {self.name}; @{self.addr} len={self.size}>"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import codecs
|
|
2
|
+
|
|
3
|
+
import toml
|
|
4
|
+
|
|
5
|
+
from .artifact import Artifact
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Patch(Artifact):
|
|
9
|
+
"""
|
|
10
|
+
Describes a patch on the binary code.
|
|
11
|
+
"""
|
|
12
|
+
__slots__ = Artifact.__slots__ + (
|
|
13
|
+
"addr",
|
|
14
|
+
"name",
|
|
15
|
+
"bytes",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
addr: int = None,
|
|
21
|
+
bytes_: bytes = None,
|
|
22
|
+
name: str = None,
|
|
23
|
+
**kwargs
|
|
24
|
+
):
|
|
25
|
+
super(Patch, self).__init__(**kwargs)
|
|
26
|
+
self.addr = addr
|
|
27
|
+
self.name = name
|
|
28
|
+
self.bytes = bytes_
|
|
29
|
+
|
|
30
|
+
def __str__(self):
|
|
31
|
+
return f"<Patch: {self.name}@{hex(self.addr)} len={len(self.bytes)}>"
|
|
32
|
+
|
|
33
|
+
def __getstate__(self):
|
|
34
|
+
data_dict = super().__getstate__()
|
|
35
|
+
data_dict["bytes"] = codecs.encode(self.bytes, "hex").decode()
|
|
36
|
+
return data_dict
|
|
37
|
+
|
|
38
|
+
def __setstate__(self, state):
|
|
39
|
+
# Pop and decode bytes data
|
|
40
|
+
bytes_dat = state.pop("bytes", None)
|
|
41
|
+
decoded_bytes = None
|
|
42
|
+
if bytes_dat:
|
|
43
|
+
decoded_bytes = codecs.decode(bytes_dat, "hex")
|
|
44
|
+
|
|
45
|
+
# Put decoded bytes back in state
|
|
46
|
+
state["bytes"] = decoded_bytes
|
|
47
|
+
|
|
48
|
+
# Let super set all attributes at once
|
|
49
|
+
super().__setstate__(state)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from .artifact import Artifact
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Segment(Artifact):
|
|
7
|
+
__slots__ = Artifact.__slots__ + (
|
|
8
|
+
"name",
|
|
9
|
+
"start_addr",
|
|
10
|
+
"end_addr",
|
|
11
|
+
"permissions"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
name: str = None,
|
|
17
|
+
start_addr: int = None,
|
|
18
|
+
end_addr: int = None,
|
|
19
|
+
permissions: Optional[str] = None,
|
|
20
|
+
**kwargs
|
|
21
|
+
):
|
|
22
|
+
super().__init__(**kwargs)
|
|
23
|
+
self.name = name
|
|
24
|
+
self.start_addr = start_addr
|
|
25
|
+
self.end_addr = end_addr
|
|
26
|
+
self.permissions = permissions
|
|
27
|
+
|
|
28
|
+
def __str__(self):
|
|
29
|
+
perms_str = f" [{self.permissions}]" if self.permissions else ""
|
|
30
|
+
return f"<Segment: {self.name} {hex(self.start_addr) if self.start_addr else '?'}-{hex(self.end_addr) if self.end_addr else '?'}{perms_str}>"
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def size(self) -> Optional[int]:
|
|
34
|
+
"""Returns the size of the segment in bytes."""
|
|
35
|
+
if self.start_addr is not None and self.end_addr is not None:
|
|
36
|
+
return self.end_addr - self.start_addr
|
|
37
|
+
return None
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import toml
|
|
2
|
+
|
|
3
|
+
from .artifact import Artifact
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class StackVariable(Artifact):
|
|
7
|
+
"""
|
|
8
|
+
Describes a stack variable for a given function.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__slots__ = Artifact.__slots__ + (
|
|
12
|
+
"offset",
|
|
13
|
+
"name",
|
|
14
|
+
"type",
|
|
15
|
+
"size",
|
|
16
|
+
"addr",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
stack_offset: int = None,
|
|
22
|
+
name: str = None,
|
|
23
|
+
type_: str = None,
|
|
24
|
+
size: int = None,
|
|
25
|
+
addr: int = None,
|
|
26
|
+
**kwargs
|
|
27
|
+
):
|
|
28
|
+
super().__init__(**kwargs)
|
|
29
|
+
self.offset = stack_offset
|
|
30
|
+
self.name = name
|
|
31
|
+
self.type = type_
|
|
32
|
+
self.size = size
|
|
33
|
+
self.addr = addr
|
|
34
|
+
|
|
35
|
+
def __str__(self):
|
|
36
|
+
return f"<StackVar: {self.type} {self.name}; {hex(self.offset)}@{hex(self.addr)}>"
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def load_many(cls, svs_toml):
|
|
40
|
+
for sv_toml in svs_toml.values():
|
|
41
|
+
sv = StackVariable(None, None, None, None, None)
|
|
42
|
+
sv.__setstate__(sv_toml)
|
|
43
|
+
yield sv
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def dump_many(cls, svs):
|
|
47
|
+
d = { }
|
|
48
|
+
for v in sorted(svs.values(), key=lambda x: x.addr):
|
|
49
|
+
d[hex(v.addr)] = v.__getstate__()
|
|
50
|
+
return d
|