declib 4.0.0__tar.gz → 4.0.2__tar.gz
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-4.0.0 → declib-4.0.2}/PKG-INFO +9 -2
- {declib-4.0.0 → declib-4.0.2}/README.md +8 -1
- {declib-4.0.0 → declib-4.0.2}/declib/__init__.py +1 -1
- {declib-4.0.0 → declib-4.0.2}/declib/api/decompiler_client.py +3 -3
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/angr/interface.py +12 -2
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ghidra/interface.py +5 -1
- {declib-4.0.0 → declib-4.0.2}/declib/plugin_installer.py +1 -1
- {declib-4.0.0 → declib-4.0.2}/declib.egg-info/PKG-INFO +9 -2
- {declib-4.0.0 → declib-4.0.2}/pyproject.toml +1 -1
- {declib-4.0.0 → declib-4.0.2}/tests/test_client_server.py +23 -1
- {declib-4.0.0 → declib-4.0.2}/tests/test_decompilers.py +40 -1
- {declib-4.0.0 → declib-4.0.2}/LICENSE +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/__main__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/api/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/api/artifact_dict.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/api/artifact_lifter.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/api/decompiler_interface.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/api/decompiler_server.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/api/server_registry.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/api/type_definition_parser.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/api/type_parser.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/api/utils.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/artifact.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/comment.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/context.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/decompilation.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/enum.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/formatting.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/func.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/global_variable.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/patch.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/segment.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/stack_variable.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/struct.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/artifacts/typedef.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/cli/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/cli/decompiler_cli.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/configuration.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompiler_stubs/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompiler_stubs/angr_declib/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompiler_stubs/binja_declib/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompiler_stubs/ida_declib.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/angr/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/angr/artifact_lifter.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/angr/compat.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/binja/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/binja/artifact_lifter.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/binja/hooks.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/binja/interface.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ghidra/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ghidra/artifact_lifter.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ghidra/compat/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ghidra/compat/headless.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ghidra/compat/imports.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ghidra/compat/state.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ghidra/compat/transaction.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ghidra/hooks.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ida/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ida/artifact_lifter.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ida/compat.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ida/hooks.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ida/ida_ui.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/decompilers/ida/interface.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/logger.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/skills/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/skills/decompiler/SKILL.md +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/ui/__init__.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/ui/qt_objects.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/ui/utils.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib/ui/version.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib.egg-info/SOURCES.txt +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib.egg-info/dependency_links.txt +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib.egg-info/entry_points.txt +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib.egg-info/requires.txt +0 -0
- {declib-4.0.0 → declib-4.0.2}/declib.egg-info/top_level.txt +0 -0
- {declib-4.0.0 → declib-4.0.2}/setup.cfg +0 -0
- {declib-4.0.0 → declib-4.0.2}/tests/test_artifacts.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/tests/test_cli.py +0 -0
- {declib-4.0.0 → declib-4.0.2}/tests/test_decompiler_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: declib
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.2
|
|
4
4
|
Summary: Your Only Decompiler API Lib - A generic API to script in and out of decompilers
|
|
5
5
|
License: BSD 2 Clause
|
|
6
6
|
Project-URL: Homepage, https://github.com/binsync/declib
|
|
@@ -61,8 +61,15 @@ start a new process using a specified decompiler.
|
|
|
61
61
|
You can find various examples using DecLib in the [examples](./examples) folder. Examples that are plugins show off
|
|
62
62
|
more of the complicated API that allows you to use an abstracted UI, artifacts, and more.
|
|
63
63
|
|
|
64
|
+
### Agent CLI
|
|
65
|
+
|
|
64
66
|
If you want a simplified command line interface (especially well-suited for LLMs), see the
|
|
65
|
-
[
|
|
67
|
+
[decompiler CLI guide](./docs/decompiler_cli.md).
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
decompiler load /path/to/binary --backend ida
|
|
71
|
+
decompiler decompile main --id 1234
|
|
72
|
+
```
|
|
66
73
|
|
|
67
74
|
### UI Mode (default)
|
|
68
75
|
To use the same script everywhere, use the convenience function `DecompilerInterface.discover_interface()`, which will
|
|
@@ -28,8 +28,15 @@ start a new process using a specified decompiler.
|
|
|
28
28
|
You can find various examples using DecLib in the [examples](./examples) folder. Examples that are plugins show off
|
|
29
29
|
more of the complicated API that allows you to use an abstracted UI, artifacts, and more.
|
|
30
30
|
|
|
31
|
+
### Agent CLI
|
|
32
|
+
|
|
31
33
|
If you want a simplified command line interface (especially well-suited for LLMs), see the
|
|
32
|
-
[
|
|
34
|
+
[decompiler CLI guide](./docs/decompiler_cli.md).
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
decompiler load /path/to/binary --backend ida
|
|
38
|
+
decompiler decompile main --id 1234
|
|
39
|
+
```
|
|
33
40
|
|
|
34
41
|
### UI Mode (default)
|
|
35
42
|
To use the same script everywhere, use the convenience function `DecompilerInterface.discover_interface()`, which will
|
|
@@ -792,14 +792,14 @@ class DecompilerClient:
|
|
|
792
792
|
_l.info("DecompilerClient shut down complete")
|
|
793
793
|
|
|
794
794
|
def shutdown_server(self) -> None:
|
|
795
|
-
"""Ask the server to
|
|
795
|
+
"""Ask the server to stop, then disconnect.
|
|
796
796
|
|
|
797
797
|
Used by CLI commands like ``decompiler stop``. Regular usage should
|
|
798
798
|
prefer :meth:`shutdown`, which leaves the server running.
|
|
799
799
|
"""
|
|
800
800
|
if self._socket:
|
|
801
801
|
try:
|
|
802
|
-
self._send_request({"type": "
|
|
802
|
+
self._send_request({"type": "shutdown_server"})
|
|
803
803
|
except Exception:
|
|
804
804
|
pass
|
|
805
805
|
self.shutdown()
|
|
@@ -1216,4 +1216,4 @@ class DecompilerClient:
|
|
|
1216
1216
|
callback(gvar, **kwargs)
|
|
1217
1217
|
except Exception as e:
|
|
1218
1218
|
_l.error(f"Error in global variable change callback: {e}")
|
|
1219
|
-
return gvar
|
|
1219
|
+
return gvar
|
|
@@ -847,7 +847,13 @@ class AngrInterface(DecompilerInterface):
|
|
|
847
847
|
|
|
848
848
|
@staticmethod
|
|
849
849
|
def line_map_from_decompilation(dec):
|
|
850
|
-
|
|
850
|
+
try:
|
|
851
|
+
# angr >= 9.2.2xx vendors ailment; the standalone `ailment` package
|
|
852
|
+
# is a different module whose Label class fails isinstance checks
|
|
853
|
+
# against statements produced by modern angr.
|
|
854
|
+
from angr import ailment
|
|
855
|
+
except ImportError:
|
|
856
|
+
import ailment
|
|
851
857
|
from angr.analyses.decompiler.structured_codegen.c import CStructuredCodeWalker, CFunctionCall, CIfElse, CIfBreak
|
|
852
858
|
|
|
853
859
|
if dec is None or dec.codegen is None:
|
|
@@ -874,7 +880,11 @@ class AngrInterface(DecompilerInterface):
|
|
|
874
880
|
except Exception:
|
|
875
881
|
continue
|
|
876
882
|
|
|
877
|
-
ail_block_stmts = [
|
|
883
|
+
ail_block_stmts = [
|
|
884
|
+
stmt for stmt in ail_block.statements
|
|
885
|
+
if not isinstance(stmt, ailment.statement.Label)
|
|
886
|
+
and getattr(stmt, "ins_addr", None) is not None
|
|
887
|
+
]
|
|
878
888
|
if not ail_block_stmts:
|
|
879
889
|
continue
|
|
880
890
|
|
|
@@ -34,6 +34,10 @@ _l = logging.getLogger(__name__)
|
|
|
34
34
|
|
|
35
35
|
class GhidraDecompilerInterface(DecompilerInterface):
|
|
36
36
|
CACHE_TIMEOUT = 5
|
|
37
|
+
# GUI-backed Ghidra program APIs are not safe to enter concurrently from
|
|
38
|
+
# arbitrary socket worker threads. DecompilerServer routes calls through
|
|
39
|
+
# its main-thread dispatcher when this is enabled.
|
|
40
|
+
requires_main_thread_dispatch = True
|
|
37
41
|
_program: Optional["Program"]
|
|
38
42
|
flat_api: "FlatProgramAPI"
|
|
39
43
|
|
|
@@ -76,6 +80,7 @@ class GhidraDecompilerInterface(DecompilerInterface):
|
|
|
76
80
|
# main thread queue
|
|
77
81
|
self._main_thread_queue = queue.Queue()
|
|
78
82
|
self._results_queue = queue.Queue()
|
|
83
|
+
self.requires_main_thread_dispatch = not kwargs.get("headless", False)
|
|
79
84
|
|
|
80
85
|
super().__init__(
|
|
81
86
|
name="ghidra",
|
|
@@ -1430,4 +1435,3 @@ class GhidraDecompilerInterface(DecompilerInterface):
|
|
|
1430
1435
|
[code_unit for code_unit in self.currentProgram.getListing().getCodeUnits(func.getBody(), True)]
|
|
1431
1436
|
for func in self.currentProgram.getFunctionManager().getFunctions(True)
|
|
1432
1437
|
]
|
|
1433
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: declib
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.2
|
|
4
4
|
Summary: Your Only Decompiler API Lib - A generic API to script in and out of decompilers
|
|
5
5
|
License: BSD 2 Clause
|
|
6
6
|
Project-URL: Homepage, https://github.com/binsync/declib
|
|
@@ -61,8 +61,15 @@ start a new process using a specified decompiler.
|
|
|
61
61
|
You can find various examples using DecLib in the [examples](./examples) folder. Examples that are plugins show off
|
|
62
62
|
more of the complicated API that allows you to use an abstracted UI, artifacts, and more.
|
|
63
63
|
|
|
64
|
+
### Agent CLI
|
|
65
|
+
|
|
64
66
|
If you want a simplified command line interface (especially well-suited for LLMs), see the
|
|
65
|
-
[
|
|
67
|
+
[decompiler CLI guide](./docs/decompiler_cli.md).
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
decompiler load /path/to/binary --backend ida
|
|
71
|
+
decompiler decompile main --id 1234
|
|
72
|
+
```
|
|
66
73
|
|
|
67
74
|
### UI Mode (default)
|
|
68
75
|
To use the same script everywhere, use the convenience function `DecompilerInterface.discover_interface()`, which will
|
|
@@ -5,6 +5,7 @@ import tempfile
|
|
|
5
5
|
import threading
|
|
6
6
|
import time
|
|
7
7
|
import unittest
|
|
8
|
+
from types import MethodType
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
|
|
10
11
|
from declib.api.decompiler_server import DecompilerServer
|
|
@@ -217,6 +218,27 @@ class TestClientServer(unittest.TestCase):
|
|
|
217
218
|
# Test KeyError handling for non-existent function
|
|
218
219
|
with self.assertRaises(KeyError, msg="Should raise KeyError for non-existent function"):
|
|
219
220
|
self.client.functions[0xDEADBEEF] # Non-existent function
|
|
221
|
+
|
|
222
|
+
def test_shutdown_server_requests_server_stop(self):
|
|
223
|
+
"""Test that shutdown_server asks the remote server loop to stop."""
|
|
224
|
+
sent_requests = []
|
|
225
|
+
client = object.__new__(DecompilerClient)
|
|
226
|
+
client._socket = object()
|
|
227
|
+
client._event_listener_running = False
|
|
228
|
+
|
|
229
|
+
def fake_send_request(self, request):
|
|
230
|
+
sent_requests.append(request)
|
|
231
|
+
|
|
232
|
+
def fake_shutdown(self):
|
|
233
|
+
sent_requests.append({"type": "client_shutdown"})
|
|
234
|
+
|
|
235
|
+
client._send_request = MethodType(fake_send_request, client)
|
|
236
|
+
client.shutdown = MethodType(fake_shutdown, client)
|
|
237
|
+
|
|
238
|
+
client.shutdown_server()
|
|
239
|
+
|
|
240
|
+
self.assertEqual(sent_requests[0], {"type": "shutdown_server"})
|
|
241
|
+
self.assertEqual(sent_requests[1], {"type": "client_shutdown"})
|
|
220
242
|
|
|
221
243
|
def test_client_context_manager(self):
|
|
222
244
|
"""Test client context manager functionality"""
|
|
@@ -700,4 +722,4 @@ class TestClientServer(unittest.TestCase):
|
|
|
700
722
|
|
|
701
723
|
|
|
702
724
|
if __name__ == "__main__":
|
|
703
|
-
unittest.main()
|
|
725
|
+
unittest.main()
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import subprocess
|
|
4
|
+
import sys
|
|
4
5
|
import tempfile
|
|
5
6
|
import time
|
|
7
|
+
import types
|
|
6
8
|
import unittest
|
|
9
|
+
from unittest.mock import MagicMock, patch
|
|
7
10
|
from pathlib import Path
|
|
8
11
|
from collections import defaultdict
|
|
9
12
|
import os
|
|
10
13
|
|
|
11
14
|
from declib.api import DecompilerInterface
|
|
15
|
+
from declib.api.decompiler_server import DecompilerServer
|
|
12
16
|
from declib.artifacts import FunctionHeader, StackVariable, Struct, GlobalVariable, Enum, Comment, ArtifactFormat, \
|
|
13
17
|
Decompilation, Function, StructMember, Typedef, Segment
|
|
14
18
|
from declib.decompilers import IDA_DECOMPILER, ANGR_DECOMPILER, BINJA_DECOMPILER, GHIDRA_DECOMPILER
|
|
@@ -65,6 +69,41 @@ class TestHeadlessInterfaces(unittest.TestCase):
|
|
|
65
69
|
if self.deci is not None:
|
|
66
70
|
self.deci.shutdown()
|
|
67
71
|
|
|
72
|
+
def test_ghidra_server_dispatch_requirement(self):
|
|
73
|
+
import declib.decompilers.ghidra as ghidra_package
|
|
74
|
+
|
|
75
|
+
pyghidra = types.ModuleType("pyghidra")
|
|
76
|
+
pyghidra_core = types.ModuleType("pyghidra.core")
|
|
77
|
+
pyghidra_core._analyze_program = MagicMock()
|
|
78
|
+
pyghidra_core._get_language = MagicMock()
|
|
79
|
+
pyghidra_core._get_compiler_spec = MagicMock()
|
|
80
|
+
jpype = types.ModuleType("jpype")
|
|
81
|
+
jpype.JClass = type
|
|
82
|
+
|
|
83
|
+
modules = {
|
|
84
|
+
"pyghidra": pyghidra,
|
|
85
|
+
"pyghidra.core": pyghidra_core,
|
|
86
|
+
"jpype": jpype,
|
|
87
|
+
}
|
|
88
|
+
with patch.object(ghidra_package, "interface", create=True):
|
|
89
|
+
with patch.dict(sys.modules, modules):
|
|
90
|
+
sys.modules.pop("declib.decompilers.ghidra.interface", None)
|
|
91
|
+
from declib.decompilers.ghidra.interface import GhidraDecompilerInterface
|
|
92
|
+
|
|
93
|
+
with patch.object(GhidraDecompilerInterface, "_init_gui_components"), \
|
|
94
|
+
patch.object(GhidraDecompilerInterface, "_init_headless_components"):
|
|
95
|
+
for headless, expected in ((True, False), (False, True)):
|
|
96
|
+
with self.subTest(headless=headless):
|
|
97
|
+
deci = GhidraDecompilerInterface(
|
|
98
|
+
headless=headless,
|
|
99
|
+
config=MagicMock(),
|
|
100
|
+
)
|
|
101
|
+
server = object.__new__(DecompilerServer)
|
|
102
|
+
server.deci = deci
|
|
103
|
+
|
|
104
|
+
self.assertIs(deci.requires_main_thread_dispatch, expected)
|
|
105
|
+
self.assertIs(server.requires_main_thread, expected)
|
|
106
|
+
|
|
68
107
|
def test_readme_example(self):
|
|
69
108
|
# TODO: add angr
|
|
70
109
|
for dec_name in [IDA_DECOMPILER, GHIDRA_DECOMPILER, BINJA_DECOMPILER]:
|
|
@@ -919,4 +958,4 @@ class TestHeadlessInterfaces(unittest.TestCase):
|
|
|
919
958
|
deci.shutdown()
|
|
920
959
|
|
|
921
960
|
if __name__ == "__main__":
|
|
922
|
-
unittest.main()
|
|
961
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|