rust-deobf 0.1.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 (58) hide show
  1. rust_deobf/__init__.py +190 -0
  2. rust_deobf/__main__.py +9 -0
  3. rust_deobf/_version.py +10 -0
  4. rust_deobf/bench_emu.py +902 -0
  5. rust_deobf/cli.py +792 -0
  6. rust_deobf/data/libfunc_sigs.json +1391 -0
  7. rust_deobf/data/libfunc_sigs_std.json.gz +0 -0
  8. rust_deobf/data/runtime_sigs.json +234 -0
  9. rust_deobf/data/rust_versions.json +474 -0
  10. rust_deobf/decompiler_bridge.py +1771 -0
  11. rust_deobf/decompiler_selfcheck.py +260 -0
  12. rust_deobf/decompress.py +1153 -0
  13. rust_deobf/detect_vm.py +175 -0
  14. rust_deobf/disasm.py +193 -0
  15. rust_deobf/dump_unpacked.py +369 -0
  16. rust_deobf/emu_backend.py +273 -0
  17. rust_deobf/export_rules.py +618 -0
  18. rust_deobf/floss_export.py +186 -0
  19. rust_deobf/gen_runtime_sigs.py +257 -0
  20. rust_deobf/gen_sigdb.py +1912 -0
  21. rust_deobf/ghidra_annotate.py +591 -0
  22. rust_deobf/ghidra_dump_annotations.py +23 -0
  23. rust_deobf/llm_triage.py +285 -0
  24. rust_deobf/mcp_server.py +610 -0
  25. rust_deobf/recover.py +514 -0
  26. rust_deobf/recover_antianalysis.py +431 -0
  27. rust_deobf/recover_apihash.py +254 -0
  28. rust_deobf/recover_arm64.py +344 -0
  29. rust_deobf/recover_artifacts.py +335 -0
  30. rust_deobf/recover_async.py +3132 -0
  31. rust_deobf/recover_behavior.py +167 -0
  32. rust_deobf/recover_blobs.py +649 -0
  33. rust_deobf/recover_callsite.py +659 -0
  34. rust_deobf/recover_cfg.py +558 -0
  35. rust_deobf/recover_cryptify.py +199 -0
  36. rust_deobf/recover_crypto.py +305 -0
  37. rust_deobf/recover_dynamic.py +271 -0
  38. rust_deobf/recover_embedded.py +336 -0
  39. rust_deobf/recover_emulate.py +3834 -0
  40. rust_deobf/recover_enums.py +403 -0
  41. rust_deobf/recover_go.py +390 -0
  42. rust_deobf/recover_idioms.py +3458 -0
  43. rust_deobf/recover_imports.py +311 -0
  44. rust_deobf/recover_lang.py +254 -0
  45. rust_deobf/recover_libfuncs.py +1118 -0
  46. rust_deobf/recover_rust_meta.py +507 -0
  47. rust_deobf/recover_rust_strings.py +713 -0
  48. rust_deobf/recover_stackstrings.py +341 -0
  49. rust_deobf/recover_types.py +427 -0
  50. rust_deobf/soc_triage.py +387 -0
  51. rust_deobf/unpack.py +219 -0
  52. rust_deobf/unpack_emulate.py +922 -0
  53. rust_deobf-0.1.0.dist-info/METADATA +976 -0
  54. rust_deobf-0.1.0.dist-info/RECORD +58 -0
  55. rust_deobf-0.1.0.dist-info/WHEEL +5 -0
  56. rust_deobf-0.1.0.dist-info/entry_points.txt +2 -0
  57. rust_deobf-0.1.0.dist-info/licenses/LICENSE +201 -0
  58. rust_deobf-0.1.0.dist-info/top_level.txt +1 -0
rust_deobf/__init__.py ADDED
@@ -0,0 +1,190 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Ve (VAHPEM) <ve0912084386@gmail.com>
3
+ """rust_deobf - the installed form of rust-deobf's scripts/ directory.
4
+
5
+ pyproject.toml maps the `rust_deobf` package onto scripts/ unchanged, so every script keeps working
6
+ both ways: `python3 scripts/recover.py` from a clone, and `rust-deobf recover` / `import rust_deobf`
7
+ after `pip install rust-deobf`. This file only runs in the second case (nothing imports `scripts`
8
+ as a package in a clone), and keeps the scripts' existing wiring safe there:
9
+
10
+ * The scripts import each other by bare name (`import recover_emulate`) after putting their own
11
+ directory on sys.path. `import rust_deobf.recover_emulate` / `from rust_deobf import recover`
12
+ therefore return *that same* bare module object (one copy, one `Image` class), not a second copy
13
+ under a dotted name.
14
+ * Library mode (a host program imports rust_deobf): the package directory is moved to the END of
15
+ sys.path before every top-level import, so its bare module names (recover, unpack, disasm, ...)
16
+ can never shadow the host program's own modules or another installed package. The rust-deobf
17
+ CLI process turns this off (cli.py), because there the toolkit's own modules must win.
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import importlib
22
+ import importlib.abc
23
+ import importlib.machinery
24
+ import os
25
+ import sys
26
+ from pathlib import Path
27
+
28
+ from ._version import __version__
29
+
30
+ __all__ = ["__version__"]
31
+
32
+ _HERE = str(Path(__file__).resolve().parent)
33
+
34
+ # module stems that are NOT bare-imported siblings: package plumbing (cli imports _version
35
+ # relatively; it is the console-script target, so it lives only under its dotted name)
36
+ _PLUMBING = {"cli"}
37
+
38
+
39
+ def _siblings(directory: str) -> frozenset:
40
+ try:
41
+ names = os.listdir(directory)
42
+ except OSError:
43
+ return frozenset()
44
+ return frozenset(n[:-3] for n in names
45
+ if n.endswith(".py") and not n.startswith("_") and n[:-3] not in _PLUMBING)
46
+
47
+
48
+ def _norm(p) -> str | None:
49
+ if not isinstance(p, str):
50
+ return None
51
+ try:
52
+ return os.path.normcase(os.path.abspath(p))
53
+ except (OSError, ValueError):
54
+ return None
55
+
56
+
57
+ class _PackageDirs(importlib.abc.MetaPathFinder):
58
+ """One finder for rust_deobf and (when installed) rust_deobf_premium.
59
+
60
+ - `<package>.<sibling>` resolves to the bare `<sibling>` module (see the module docstring).
61
+ - in library mode, every top-level import first moves the registered package directories to the
62
+ end of sys.path (the scripts re-insert them at index 0 whenever one of them is imported)."""
63
+
64
+ def __init__(self) -> None:
65
+ self.library_mode = True
66
+ self.packages: dict = {} # package name -> (dir, frozenset of sibling stems)
67
+ self._spellings: set = set() # sys.path strings that name a registered dir
68
+ self._norms: set = set()
69
+
70
+ def register(self, package: str, directory: str) -> None:
71
+ self.packages[package] = (directory, _siblings(directory))
72
+ self._spellings.add(directory)
73
+ n = _norm(directory)
74
+ if n:
75
+ self._norms.add(n)
76
+
77
+ def _ours(self, entry) -> bool:
78
+ if entry in self._spellings:
79
+ return True
80
+ n = _norm(entry)
81
+ if n in self._norms:
82
+ if isinstance(entry, str) and os.path.isabs(entry): # "" (cwd) may change
83
+ self._spellings.add(entry)
84
+ return True
85
+ return False
86
+
87
+ def demote(self) -> None:
88
+ """Move every registered package dir to the end of sys.path (each kept once)."""
89
+ path = sys.path
90
+ if not self._spellings or not path:
91
+ return
92
+ hits = [i for i, e in enumerate(path) if self._ours(e)]
93
+ if not hits:
94
+ return
95
+ n = len(hits)
96
+ if hits == list(range(len(path) - n, len(path))) and len({_norm(path[i]) for i in hits}) == n:
97
+ return # already at the end, no duplicates
98
+ keep, seen = [], set()
99
+ for i in hits:
100
+ k = _norm(path[i])
101
+ if k not in seen:
102
+ seen.add(k)
103
+ keep.append(path[i])
104
+ drop = set(hits)
105
+ path[:] = [e for i, e in enumerate(path) if i not in drop] + keep
106
+
107
+ def ensure_on_path(self, directory: str, front: bool) -> None:
108
+ present = any(_norm(e) == _norm(directory) for e in sys.path)
109
+ if front:
110
+ if not (sys.path and _norm(sys.path[0]) == _norm(directory)):
111
+ sys.path.insert(0, directory)
112
+ elif not present:
113
+ sys.path.append(directory)
114
+
115
+ def find_spec(self, fullname, path, target=None):
116
+ if path is None: # a top-level import
117
+ if self.library_mode:
118
+ self.demote()
119
+ return None
120
+ pkg, _, short = fullname.rpartition(".")
121
+ entry = self.packages.get(pkg)
122
+ if entry is None or short not in entry[1]:
123
+ return None
124
+ directory = entry[0]
125
+ return importlib.machinery.ModuleSpec(
126
+ fullname, _AliasLoader(self, short, directory),
127
+ origin=os.path.join(directory, short + ".py"))
128
+
129
+
130
+ class _AliasLoader(importlib.abc.Loader):
131
+ """Loads `<package>.<short>` as the bare module `<short>` (the one the scripts import)."""
132
+
133
+ def __init__(self, finder: _PackageDirs, short: str, directory: str) -> None:
134
+ self.finder, self.short, self.directory = finder, short, directory
135
+ self._spec = None
136
+
137
+ def create_module(self, spec):
138
+ self.finder.ensure_on_path(self.directory, front=not self.finder.library_mode)
139
+ mod = importlib.import_module(self.short)
140
+ origin = getattr(mod, "__file__", None) or ""
141
+ if _norm(os.path.dirname(origin)) != _norm(self.directory):
142
+ raise ImportError(
143
+ f"{spec.name}: the bare module name {self.short!r} resolves to {origin or mod!r}, "
144
+ f"not to {self.directory}: another top-level module of that name shadows it",
145
+ name=spec.name)
146
+ self._spec = getattr(mod, "__spec__", None)
147
+ return mod
148
+
149
+ def exec_module(self, module) -> None:
150
+ # module_from_spec() overwrote __spec__ with the alias spec: keep the bare module's own
151
+ if self._spec is not None:
152
+ module.__spec__ = self._spec
153
+ if self.finder.library_mode: # the module's own sys.path.insert(0, ...) undone now
154
+ self.finder.demote()
155
+
156
+
157
+ def _finder() -> _PackageDirs:
158
+ for f in sys.meta_path:
159
+ if type(f).__name__ == "_PackageDirs" and getattr(f, "_rust_deobf", False):
160
+ return f
161
+ f = _PackageDirs()
162
+ f._rust_deobf = True
163
+ sys.meta_path.insert(0, f)
164
+ return f
165
+
166
+
167
+ def register_package_dir(package: str, directory: str) -> None:
168
+ """Used by rust_deobf_premium: alias its modules and keep its dir in the same sys.path policy."""
169
+ _finder().register(package, str(directory))
170
+
171
+
172
+ def ensure_package_dirs_on_path() -> None:
173
+ """Every registered package dir on sys.path: appended (library mode) or first (CLI)."""
174
+ f = _finder()
175
+ for directory, _ in list(f.packages.values()):
176
+ f.ensure_on_path(directory, front=not f.library_mode)
177
+
178
+
179
+ def set_library_mode(on: bool) -> None:
180
+ """True (default on import): keep the package dirs last on sys.path. The CLI sets False and puts
181
+ the package dir first, like `python3 scripts/<tool>.py` does."""
182
+ f = _finder()
183
+ f.library_mode = bool(on)
184
+ if on:
185
+ f.demote()
186
+ else:
187
+ ensure_package_dirs_on_path()
188
+
189
+
190
+ register_package_dir(__name__, _HERE)
rust_deobf/__main__.py ADDED
@@ -0,0 +1,9 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Ve (VAHPEM) <ve0912084386@gmail.com>
3
+ """`python -m rust_deobf ...` == the `rust-deobf` console command."""
4
+ import sys
5
+
6
+ from .cli import main
7
+
8
+ if __name__ == "__main__":
9
+ sys.exit(main())
rust_deobf/_version.py ADDED
@@ -0,0 +1,10 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Ve (VAHPEM) <ve0912084386@gmail.com>
3
+ """The rust-deobf version: the single source of truth for both distributions.
4
+
5
+ Read by setup.py (core) and premium/setup.py at build time, by `rust-deobf --version` at run time,
6
+ and checked against the git tag by .github/workflows/release.yml. A plain file (not git-tag
7
+ derived) so a git clone, a shallow CI checkout, a GitHub zip download and an sdist all agree.
8
+ Bump it together with a new CHANGELOG.md section; see "Releasing" in CLAUDE.md.
9
+ """
10
+ __version__ = "0.1.0"