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.
Files changed (71) hide show
  1. declib/__init__.py +9 -0
  2. declib/__main__.py +190 -0
  3. declib/api/__init__.py +13 -0
  4. declib/api/artifact_dict.py +153 -0
  5. declib/api/artifact_lifter.py +161 -0
  6. declib/api/decompiler_client.py +1219 -0
  7. declib/api/decompiler_interface.py +1261 -0
  8. declib/api/decompiler_server.py +782 -0
  9. declib/api/server_registry.py +171 -0
  10. declib/api/type_definition_parser.py +201 -0
  11. declib/api/type_parser.py +409 -0
  12. declib/api/utils.py +31 -0
  13. declib/artifacts/__init__.py +93 -0
  14. declib/artifacts/artifact.py +311 -0
  15. declib/artifacts/comment.py +49 -0
  16. declib/artifacts/context.py +61 -0
  17. declib/artifacts/decompilation.py +35 -0
  18. declib/artifacts/enum.py +53 -0
  19. declib/artifacts/formatting.py +27 -0
  20. declib/artifacts/func.py +433 -0
  21. declib/artifacts/global_variable.py +31 -0
  22. declib/artifacts/patch.py +49 -0
  23. declib/artifacts/segment.py +37 -0
  24. declib/artifacts/stack_variable.py +50 -0
  25. declib/artifacts/struct.py +184 -0
  26. declib/artifacts/typedef.py +59 -0
  27. declib/cli/__init__.py +3 -0
  28. declib/cli/decompiler_cli.py +1487 -0
  29. declib/configuration.py +184 -0
  30. declib/decompiler_stubs/__init__.py +0 -0
  31. declib/decompiler_stubs/angr_declib/__init__.py +4 -0
  32. declib/decompiler_stubs/binja_declib/__init__.py +4 -0
  33. declib/decompiler_stubs/ida_declib.py +8 -0
  34. declib/decompilers/__init__.py +8 -0
  35. declib/decompilers/angr/__init__.py +11 -0
  36. declib/decompilers/angr/artifact_lifter.py +46 -0
  37. declib/decompilers/angr/compat.py +262 -0
  38. declib/decompilers/angr/interface.py +949 -0
  39. declib/decompilers/binja/__init__.py +0 -0
  40. declib/decompilers/binja/artifact_lifter.py +32 -0
  41. declib/decompilers/binja/hooks.py +201 -0
  42. declib/decompilers/binja/interface.py +795 -0
  43. declib/decompilers/ghidra/__init__.py +0 -0
  44. declib/decompilers/ghidra/artifact_lifter.py +60 -0
  45. declib/decompilers/ghidra/compat/__init__.py +0 -0
  46. declib/decompilers/ghidra/compat/headless.py +156 -0
  47. declib/decompilers/ghidra/compat/imports.py +78 -0
  48. declib/decompilers/ghidra/compat/state.py +54 -0
  49. declib/decompilers/ghidra/compat/transaction.py +30 -0
  50. declib/decompilers/ghidra/hooks.py +242 -0
  51. declib/decompilers/ghidra/interface.py +1433 -0
  52. declib/decompilers/ida/__init__.py +0 -0
  53. declib/decompilers/ida/artifact_lifter.py +51 -0
  54. declib/decompilers/ida/compat.py +2054 -0
  55. declib/decompilers/ida/hooks.py +700 -0
  56. declib/decompilers/ida/ida_ui.py +80 -0
  57. declib/decompilers/ida/interface.py +659 -0
  58. declib/logger.py +101 -0
  59. declib/plugin_installer.py +259 -0
  60. declib/skills/__init__.py +24 -0
  61. declib/skills/decompiler/SKILL.md +316 -0
  62. declib/ui/__init__.py +33 -0
  63. declib/ui/qt_objects.py +146 -0
  64. declib/ui/utils.py +115 -0
  65. declib/ui/version.py +14 -0
  66. declib-3.8.0.dist-info/METADATA +138 -0
  67. declib-3.8.0.dist-info/RECORD +71 -0
  68. declib-3.8.0.dist-info/WHEEL +5 -0
  69. declib-3.8.0.dist-info/entry_points.txt +3 -0
  70. declib-3.8.0.dist-info/licenses/LICENSE +24 -0
  71. declib-3.8.0.dist-info/top_level.txt +1 -0
@@ -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