windows-exe-decompiler-mcp-server 0.1.0 → 0.1.3

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 (134) hide show
  1. package/CLAUDE_INSTALLATION.md +129 -0
  2. package/CODEX_INSTALLATION.md +25 -1
  3. package/COPILOT_INSTALLATION.md +29 -1
  4. package/LICENSE +21 -21
  5. package/README.md +232 -110
  6. package/dist/analysis-provenance.d.ts +50 -2
  7. package/dist/analysis-provenance.js +1 -0
  8. package/dist/analysis-task-runner.js +92 -0
  9. package/dist/config.d.ts +35 -16
  10. package/dist/config.js +37 -3
  11. package/dist/database.js +115 -115
  12. package/dist/decompiler-worker.d.ts +10 -5
  13. package/dist/decompiler-worker.js +240 -58
  14. package/dist/dynamic-trace.d.ts +6 -0
  15. package/dist/dynamic-trace.js +52 -1
  16. package/dist/ghidra-config.js +17 -1
  17. package/dist/index.js +38 -8
  18. package/dist/pe-runtime-functions.d.ts +90 -0
  19. package/dist/pe-runtime-functions.js +452 -0
  20. package/dist/prompts/module-reconstruction-review.d.ts +5 -0
  21. package/dist/prompts/module-reconstruction-review.js +65 -0
  22. package/dist/runtime-correlation.d.ts +5 -0
  23. package/dist/runtime-correlation.js +179 -2
  24. package/dist/sample-workspace.d.ts +28 -0
  25. package/dist/sample-workspace.js +186 -0
  26. package/dist/semantic-name-suggestion-artifacts.d.ts +56 -0
  27. package/dist/semantic-name-suggestion-artifacts.js +87 -0
  28. package/dist/server.js +1 -1
  29. package/dist/setup-guidance.d.ts +73 -0
  30. package/dist/setup-guidance.js +211 -0
  31. package/dist/tools/artifact-read.d.ts +10 -10
  32. package/dist/tools/artifacts-diff.d.ts +8 -8
  33. package/dist/tools/artifacts-list.d.ts +16 -16
  34. package/dist/tools/attack-map.d.ts +8 -8
  35. package/dist/tools/binary-role-profile.d.ts +1197 -0
  36. package/dist/tools/binary-role-profile.js +575 -0
  37. package/dist/tools/code-function-explain-apply.d.ts +12 -12
  38. package/dist/tools/code-function-explain-prepare.d.ts +12 -12
  39. package/dist/tools/code-function-explain-review.d.ts +58 -58
  40. package/dist/tools/code-function-rename-apply.d.ts +12 -12
  41. package/dist/tools/code-function-rename-prepare.d.ts +14 -14
  42. package/dist/tools/code-function-rename-review.d.ts +66 -66
  43. package/dist/tools/code-functions-define.d.ts +337 -0
  44. package/dist/tools/code-functions-define.js +337 -0
  45. package/dist/tools/code-functions-list.js +1 -1
  46. package/dist/tools/code-functions-rank.js +1 -1
  47. package/dist/tools/code-functions-reconstruct.d.ts +368 -58
  48. package/dist/tools/code-functions-reconstruct.js +130 -10
  49. package/dist/tools/code-functions-smart-recover.d.ts +352 -0
  50. package/dist/tools/code-functions-smart-recover.js +190 -0
  51. package/dist/tools/code-module-review-apply.d.ts +233 -0
  52. package/dist/tools/code-module-review-apply.js +209 -0
  53. package/dist/tools/code-module-review-prepare.d.ts +756 -0
  54. package/dist/tools/code-module-review-prepare.js +343 -0
  55. package/dist/tools/code-module-review.d.ts +474 -0
  56. package/dist/tools/code-module-review.js +566 -0
  57. package/dist/tools/code-reconstruct-export.d.ts +193 -21
  58. package/dist/tools/code-reconstruct-export.js +578 -21
  59. package/dist/tools/code-reconstruct-plan.d.ts +20 -20
  60. package/dist/tools/com-role-profile.d.ts +353 -0
  61. package/dist/tools/com-role-profile.js +186 -0
  62. package/dist/tools/dll-export-profile.d.ts +625 -0
  63. package/dist/tools/dll-export-profile.js +230 -0
  64. package/dist/tools/dotnet-metadata-extract.d.ts +122 -122
  65. package/dist/tools/dotnet-reconstruct-export.d.ts +49 -49
  66. package/dist/tools/dotnet-types-list.d.ts +66 -66
  67. package/dist/tools/dynamic-dependencies.d.ts +144 -8
  68. package/dist/tools/dynamic-dependencies.js +20 -1
  69. package/dist/tools/dynamic-memory-import.js +105 -2
  70. package/dist/tools/ghidra-analyze.d.ts +20 -0
  71. package/dist/tools/ghidra-analyze.js +10 -2
  72. package/dist/tools/ghidra-health.d.ts +177 -0
  73. package/dist/tools/ghidra-health.js +28 -0
  74. package/dist/tools/ioc-export.d.ts +8 -8
  75. package/dist/tools/packer-detect.d.ts +14 -14
  76. package/dist/tools/packer-detect.js +6 -9
  77. package/dist/tools/pe-exports-extract.d.ts +8 -8
  78. package/dist/tools/pe-exports-extract.js +6 -9
  79. package/dist/tools/pe-fingerprint.d.ts +36 -36
  80. package/dist/tools/pe-imports-extract.d.ts +8 -8
  81. package/dist/tools/pe-imports-extract.js +6 -9
  82. package/dist/tools/pe-pdata-extract.d.ts +448 -0
  83. package/dist/tools/pe-pdata-extract.js +219 -0
  84. package/dist/tools/pe-symbols-recover.d.ts +227 -0
  85. package/dist/tools/pe-symbols-recover.js +316 -0
  86. package/dist/tools/report-generate.d.ts +15 -1
  87. package/dist/tools/report-generate.js +139 -16
  88. package/dist/tools/report-summarize.d.ts +2242 -8
  89. package/dist/tools/report-summarize.js +246 -10
  90. package/dist/tools/runtime-detect.d.ts +8 -8
  91. package/dist/tools/runtime-detect.js +2 -12
  92. package/dist/tools/rust-binary-analyze.d.ts +2047 -0
  93. package/dist/tools/rust-binary-analyze.js +443 -0
  94. package/dist/tools/sample-profile-get.d.ts +135 -16
  95. package/dist/tools/sample-profile-get.js +35 -32
  96. package/dist/tools/sandbox-execute.d.ts +10 -10
  97. package/dist/tools/strings-extract.d.ts +8 -8
  98. package/dist/tools/strings-extract.js +2 -12
  99. package/dist/tools/strings-floss-decode.d.ts +8 -8
  100. package/dist/tools/system-health.d.ts +172 -36
  101. package/dist/tools/system-health.js +14 -0
  102. package/dist/tools/system-setup-guide.d.ts +179 -0
  103. package/dist/tools/system-setup-guide.js +62 -0
  104. package/dist/tools/tool-help.d.ts +16 -16
  105. package/dist/tools/tool-help.js +172 -0
  106. package/dist/tools/yara-scan.d.ts +8 -8
  107. package/dist/types.d.ts +2 -0
  108. package/dist/workflows/function-explanation-review.d.ts +3034 -76
  109. package/dist/workflows/function-explanation-review.js +172 -42
  110. package/dist/workflows/function-index-recover.d.ts +333 -0
  111. package/dist/workflows/function-index-recover.js +291 -0
  112. package/dist/workflows/module-reconstruction-review.d.ts +3537 -0
  113. package/dist/workflows/module-reconstruction-review.js +354 -0
  114. package/dist/workflows/reconstruct.d.ts +2182 -223
  115. package/dist/workflows/reconstruct.js +575 -32
  116. package/dist/workflows/semantic-name-review.d.ts +1436 -85
  117. package/dist/workflows/semantic-name-review.js +150 -58
  118. package/dist/workflows/triage.d.ts +8 -8
  119. package/ghidra_scripts/DecompileFunction.java +487 -487
  120. package/ghidra_scripts/ExtractCFG.java +256 -256
  121. package/ghidra_scripts/ExtractFunctions.java +442 -442
  122. package/ghidra_scripts/README.md +112 -112
  123. package/ghidra_scripts/SearchFunctionReferences.java +380 -380
  124. package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -9
  125. package/helpers/DotNetMetadataProbe/Program.cs +566 -566
  126. package/install-to-claude.ps1 +432 -0
  127. package/install-to-codex.ps1 +256 -178
  128. package/install-to-copilot.ps1 +389 -303
  129. package/package.json +4 -2
  130. package/requirements.txt +9 -9
  131. package/workers/requirements-dynamic.txt +11 -11
  132. package/workers/requirements.txt +8 -8
  133. package/workers/speakeasy_compat.py +175 -175
  134. package/workers/static_worker.py +3474 -3474
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windows-exe-decompiler-mcp-server",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "MCP server for Windows binary reverse engineering, PE triage, Ghidra-assisted analysis, and reconstruction workflows.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -28,8 +28,10 @@
28
28
  "requirements.txt",
29
29
  "install-to-codex.ps1",
30
30
  "install-to-copilot.ps1",
31
+ "install-to-claude.ps1",
31
32
  "CODEX_INSTALLATION.md",
32
- "COPILOT_INSTALLATION.md"
33
+ "COPILOT_INSTALLATION.md",
34
+ "CLAUDE_INSTALLATION.md"
33
35
  ],
34
36
  "scripts": {
35
37
  "build": "tsc",
package/requirements.txt CHANGED
@@ -1,9 +1,9 @@
1
- # Root Python dependency baseline for the reverse-engineering workers.
2
- # Latest versions verified on PyPI 2026-03-10.
3
- # For optional dynamic-analysis tooling, install workers/requirements-dynamic.txt as an extra step.
4
-
5
- pefile==2024.8.26
6
- lief==0.17.5
7
- yara-python==4.5.4
8
- flare-floss==3.1.1
9
- dnfile==0.18.0
1
+ # Root Python dependency baseline for the reverse-engineering workers.
2
+ # Latest versions verified on PyPI 2026-03-10.
3
+ # For optional dynamic-analysis tooling, install workers/requirements-dynamic.txt as an extra step.
4
+
5
+ pefile==2024.8.26
6
+ lief==0.17.5
7
+ yara-python==4.5.4
8
+ flare-floss==3.1.1
9
+ dnfile==0.18.0
@@ -1,11 +1,11 @@
1
- # Optional dynamic-analysis dependencies (install only in isolated lab environments)
2
- # These are intentionally separated from the static baseline requirements.
3
- # Latest verified on PyPI 2026-03-10.
4
- # Note: the PyPI package named `speakeasy` is an unrelated metrics server.
5
- # Use `speakeasy-emulator`, which provides the `speakeasy` malware-emulation module.
6
- # Unicorn still imports `pkg_resources`, so keep setuptools below 81 for now.
7
-
8
- speakeasy-emulator==1.5.11
9
- setuptools<81
10
- frida==17.8.0
11
- psutil==7.2.2
1
+ # Optional dynamic-analysis dependencies (install only in isolated lab environments)
2
+ # These are intentionally separated from the static baseline requirements.
3
+ # Latest verified on PyPI 2026-03-10.
4
+ # Note: the PyPI package named `speakeasy` is an unrelated metrics server.
5
+ # Use `speakeasy-emulator`, which provides the `speakeasy` malware-emulation module.
6
+ # Unicorn still imports `pkg_resources`, so keep setuptools below 81 for now.
7
+
8
+ speakeasy-emulator==1.5.11
9
+ setuptools<81
10
+ frida==17.8.0
11
+ psutil==7.2.2
@@ -1,8 +1,8 @@
1
- # Python worker dependency baseline (root-synced, latest verified on PyPI 2026-03-10)
2
- pefile==2024.8.26
3
- lief==0.17.5
4
- yara-python==4.5.4
5
- flare-floss==3.1.1
6
- dnfile==0.18.0
7
-
8
- # Optional dynamic-analysis stack is maintained in requirements-dynamic.txt
1
+ # Python worker dependency baseline (root-synced, latest verified on PyPI 2026-03-10)
2
+ pefile==2024.8.26
3
+ lief==0.17.5
4
+ yara-python==4.5.4
5
+ flare-floss==3.1.1
6
+ dnfile==0.18.0
7
+
8
+ # Optional dynamic-analysis stack is maintained in requirements-dynamic.txt
@@ -1,175 +1,175 @@
1
- """
2
- Compatibility helpers for environments where the installed speakeasy-emulator
3
- distribution exposes a namespace package without the top-level Speakeasy class.
4
- """
5
-
6
- from __future__ import annotations
7
-
8
- import importlib
9
- import json
10
- from pathlib import Path
11
- from types import ModuleType
12
- from typing import Any, Dict, Optional, Tuple
13
-
14
-
15
- def _resolve_package_root(module: ModuleType) -> Path:
16
- module_path = getattr(module, "__file__", None)
17
- if module_path:
18
- return Path(module_path).resolve().parent
19
-
20
- module_search_paths = list(getattr(module, "__path__", []))
21
- if module_search_paths:
22
- return Path(module_search_paths[0]).resolve()
23
-
24
- raise RuntimeError("Unable to resolve speakeasy package root")
25
-
26
-
27
- def _load_default_config(package_root: Path) -> Dict[str, Any]:
28
- config_path = package_root / "configs" / "default.json"
29
- if not config_path.is_file():
30
- raise RuntimeError(f"Speakeasy default config not found: {config_path}")
31
- return json.loads(config_path.read_text(encoding="utf-8"))
32
-
33
-
34
- def load_speakeasy_module() -> Tuple[ModuleType, Dict[str, Any]]:
35
- """
36
- Return an importable speakeasy module plus diagnostics.
37
-
38
- On some environments the wheel installs `speakeasy` as a namespace package
39
- without exporting `Speakeasy` from the top level. In that case we inject a
40
- small compatibility wrapper that recreates the public API used by this
41
- project.
42
- """
43
-
44
- module = importlib.import_module("speakeasy")
45
- package_root = _resolve_package_root(module)
46
- module_path = getattr(module, "__file__", None) or str(package_root)
47
-
48
- if hasattr(module, "Speakeasy"):
49
- return module, {
50
- "import_mode": "direct",
51
- "module_path": module_path,
52
- "package_root": str(package_root),
53
- "warnings": [],
54
- }
55
-
56
- from speakeasy.windows.common import PeFile
57
- from speakeasy.windows.kernel import WinKernelEmulator
58
- from speakeasy.windows.win32 import Win32Emulator
59
-
60
- default_config = _load_default_config(package_root)
61
-
62
- class SpeakeasyCompat:
63
- """Small adapter that recreates the top-level Speakeasy API."""
64
-
65
- def __init__(
66
- self,
67
- config: Optional[Dict[str, Any]] = None,
68
- logger: Any = None,
69
- argv: Optional[list[str]] = None,
70
- exit_event: Any = None,
71
- debug: bool = False,
72
- ) -> None:
73
- if config is None:
74
- config_obj = dict(default_config)
75
- elif isinstance(config, str):
76
- config_obj = json.loads(config)
77
- else:
78
- config_obj = config
79
-
80
- self.config = config_obj
81
- self.logger = logger
82
- self.argv = list(argv or [])
83
- self.exit_event = exit_event
84
- self.debug = debug
85
- self._emu: Any = None
86
- self._loaded_module: Any = None
87
-
88
- def _ensure_emu(self) -> Any:
89
- if self._emu is None:
90
- raise RuntimeError("No module or shellcode has been loaded into SpeakeasyCompat")
91
- return self._emu
92
-
93
- def load_module(self, path: Optional[str] = None, data: Optional[bytes] = None) -> Any:
94
- pe = PeFile(path=path, data=data)
95
- if pe.is_driver():
96
- emu = WinKernelEmulator(
97
- self.config,
98
- debug=self.debug,
99
- logger=self.logger,
100
- exit_event=self.exit_event,
101
- )
102
- else:
103
- emu = Win32Emulator(
104
- self.config,
105
- argv=self.argv,
106
- debug=self.debug,
107
- logger=self.logger,
108
- exit_event=self.exit_event,
109
- )
110
- module_obj = emu.load_module(path=path, data=data)
111
- self._emu = emu
112
- self._loaded_module = module_obj
113
- return module_obj
114
-
115
- def run_module(
116
- self,
117
- module_obj: Any = None,
118
- all_entrypoints: bool = False,
119
- emulate_children: bool = False,
120
- ) -> Any:
121
- emu = self._ensure_emu()
122
- target = module_obj if module_obj is not None else self._loaded_module
123
- if isinstance(emu, WinKernelEmulator):
124
- return emu.run_module(target, all_entrypoints=all_entrypoints)
125
- return emu.run_module(
126
- target,
127
- all_entrypoints=all_entrypoints,
128
- emulate_children=emulate_children,
129
- )
130
-
131
- def load_shellcode(self, path: str, arch: str, data: Optional[bytes] = None) -> Any:
132
- emu = Win32Emulator(
133
- self.config,
134
- argv=self.argv,
135
- debug=self.debug,
136
- logger=self.logger,
137
- exit_event=self.exit_event,
138
- )
139
- shellcode_addr = emu.load_shellcode(path, arch, data=data)
140
- self._emu = emu
141
- self._loaded_module = None
142
- return shellcode_addr
143
-
144
- def run_shellcode(self, shellcode_addr: int, offset: int = 0) -> Any:
145
- emu = self._ensure_emu()
146
- return emu.run_shellcode(shellcode_addr, offset=offset)
147
-
148
- def get_report(self) -> Any:
149
- emu = self._ensure_emu()
150
- return emu.get_report()
151
-
152
- def get_json_report(self) -> Any:
153
- emu = self._ensure_emu()
154
- return emu.get_json_report()
155
-
156
- def create_memdump_archive(self) -> Optional[bytes]:
157
- return None
158
-
159
- def create_file_archive(self) -> Optional[bytes]:
160
- return None
161
-
162
- module.Speakeasy = SpeakeasyCompat
163
- module.Win32Emulator = Win32Emulator
164
- module.WinKernelEmulator = WinKernelEmulator
165
- module.PeFile = PeFile
166
-
167
- return module, {
168
- "import_mode": "compat_shim",
169
- "module_path": module_path,
170
- "package_root": str(package_root),
171
- "warnings": [
172
- "Installed speakeasy-emulator exposes a namespace package without top-level Speakeasy; activated local compatibility shim."
173
- ],
174
- }
175
-
1
+ """
2
+ Compatibility helpers for environments where the installed speakeasy-emulator
3
+ distribution exposes a namespace package without the top-level Speakeasy class.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import importlib
9
+ import json
10
+ from pathlib import Path
11
+ from types import ModuleType
12
+ from typing import Any, Dict, Optional, Tuple
13
+
14
+
15
+ def _resolve_package_root(module: ModuleType) -> Path:
16
+ module_path = getattr(module, "__file__", None)
17
+ if module_path:
18
+ return Path(module_path).resolve().parent
19
+
20
+ module_search_paths = list(getattr(module, "__path__", []))
21
+ if module_search_paths:
22
+ return Path(module_search_paths[0]).resolve()
23
+
24
+ raise RuntimeError("Unable to resolve speakeasy package root")
25
+
26
+
27
+ def _load_default_config(package_root: Path) -> Dict[str, Any]:
28
+ config_path = package_root / "configs" / "default.json"
29
+ if not config_path.is_file():
30
+ raise RuntimeError(f"Speakeasy default config not found: {config_path}")
31
+ return json.loads(config_path.read_text(encoding="utf-8"))
32
+
33
+
34
+ def load_speakeasy_module() -> Tuple[ModuleType, Dict[str, Any]]:
35
+ """
36
+ Return an importable speakeasy module plus diagnostics.
37
+
38
+ On some environments the wheel installs `speakeasy` as a namespace package
39
+ without exporting `Speakeasy` from the top level. In that case we inject a
40
+ small compatibility wrapper that recreates the public API used by this
41
+ project.
42
+ """
43
+
44
+ module = importlib.import_module("speakeasy")
45
+ package_root = _resolve_package_root(module)
46
+ module_path = getattr(module, "__file__", None) or str(package_root)
47
+
48
+ if hasattr(module, "Speakeasy"):
49
+ return module, {
50
+ "import_mode": "direct",
51
+ "module_path": module_path,
52
+ "package_root": str(package_root),
53
+ "warnings": [],
54
+ }
55
+
56
+ from speakeasy.windows.common import PeFile
57
+ from speakeasy.windows.kernel import WinKernelEmulator
58
+ from speakeasy.windows.win32 import Win32Emulator
59
+
60
+ default_config = _load_default_config(package_root)
61
+
62
+ class SpeakeasyCompat:
63
+ """Small adapter that recreates the top-level Speakeasy API."""
64
+
65
+ def __init__(
66
+ self,
67
+ config: Optional[Dict[str, Any]] = None,
68
+ logger: Any = None,
69
+ argv: Optional[list[str]] = None,
70
+ exit_event: Any = None,
71
+ debug: bool = False,
72
+ ) -> None:
73
+ if config is None:
74
+ config_obj = dict(default_config)
75
+ elif isinstance(config, str):
76
+ config_obj = json.loads(config)
77
+ else:
78
+ config_obj = config
79
+
80
+ self.config = config_obj
81
+ self.logger = logger
82
+ self.argv = list(argv or [])
83
+ self.exit_event = exit_event
84
+ self.debug = debug
85
+ self._emu: Any = None
86
+ self._loaded_module: Any = None
87
+
88
+ def _ensure_emu(self) -> Any:
89
+ if self._emu is None:
90
+ raise RuntimeError("No module or shellcode has been loaded into SpeakeasyCompat")
91
+ return self._emu
92
+
93
+ def load_module(self, path: Optional[str] = None, data: Optional[bytes] = None) -> Any:
94
+ pe = PeFile(path=path, data=data)
95
+ if pe.is_driver():
96
+ emu = WinKernelEmulator(
97
+ self.config,
98
+ debug=self.debug,
99
+ logger=self.logger,
100
+ exit_event=self.exit_event,
101
+ )
102
+ else:
103
+ emu = Win32Emulator(
104
+ self.config,
105
+ argv=self.argv,
106
+ debug=self.debug,
107
+ logger=self.logger,
108
+ exit_event=self.exit_event,
109
+ )
110
+ module_obj = emu.load_module(path=path, data=data)
111
+ self._emu = emu
112
+ self._loaded_module = module_obj
113
+ return module_obj
114
+
115
+ def run_module(
116
+ self,
117
+ module_obj: Any = None,
118
+ all_entrypoints: bool = False,
119
+ emulate_children: bool = False,
120
+ ) -> Any:
121
+ emu = self._ensure_emu()
122
+ target = module_obj if module_obj is not None else self._loaded_module
123
+ if isinstance(emu, WinKernelEmulator):
124
+ return emu.run_module(target, all_entrypoints=all_entrypoints)
125
+ return emu.run_module(
126
+ target,
127
+ all_entrypoints=all_entrypoints,
128
+ emulate_children=emulate_children,
129
+ )
130
+
131
+ def load_shellcode(self, path: str, arch: str, data: Optional[bytes] = None) -> Any:
132
+ emu = Win32Emulator(
133
+ self.config,
134
+ argv=self.argv,
135
+ debug=self.debug,
136
+ logger=self.logger,
137
+ exit_event=self.exit_event,
138
+ )
139
+ shellcode_addr = emu.load_shellcode(path, arch, data=data)
140
+ self._emu = emu
141
+ self._loaded_module = None
142
+ return shellcode_addr
143
+
144
+ def run_shellcode(self, shellcode_addr: int, offset: int = 0) -> Any:
145
+ emu = self._ensure_emu()
146
+ return emu.run_shellcode(shellcode_addr, offset=offset)
147
+
148
+ def get_report(self) -> Any:
149
+ emu = self._ensure_emu()
150
+ return emu.get_report()
151
+
152
+ def get_json_report(self) -> Any:
153
+ emu = self._ensure_emu()
154
+ return emu.get_json_report()
155
+
156
+ def create_memdump_archive(self) -> Optional[bytes]:
157
+ return None
158
+
159
+ def create_file_archive(self) -> Optional[bytes]:
160
+ return None
161
+
162
+ module.Speakeasy = SpeakeasyCompat
163
+ module.Win32Emulator = Win32Emulator
164
+ module.WinKernelEmulator = WinKernelEmulator
165
+ module.PeFile = PeFile
166
+
167
+ return module, {
168
+ "import_mode": "compat_shim",
169
+ "module_path": module_path,
170
+ "package_root": str(package_root),
171
+ "warnings": [
172
+ "Installed speakeasy-emulator exposes a namespace package without top-level Speakeasy; activated local compatibility shim."
173
+ ],
174
+ }
175
+