windows-exe-decompiler-mcp-server 0.1.0

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 (190) hide show
  1. package/CODEX_INSTALLATION.md +69 -0
  2. package/COPILOT_INSTALLATION.md +77 -0
  3. package/LICENSE +21 -0
  4. package/README.md +314 -0
  5. package/bin/windows-exe-decompiler-mcp-server.js +3 -0
  6. package/dist/analysis-provenance.d.ts +184 -0
  7. package/dist/analysis-provenance.js +74 -0
  8. package/dist/analysis-task-runner.d.ts +31 -0
  9. package/dist/analysis-task-runner.js +160 -0
  10. package/dist/artifact-inventory.d.ts +23 -0
  11. package/dist/artifact-inventory.js +175 -0
  12. package/dist/cache-manager.d.ts +128 -0
  13. package/dist/cache-manager.js +454 -0
  14. package/dist/confidence-semantics.d.ts +66 -0
  15. package/dist/confidence-semantics.js +122 -0
  16. package/dist/config.d.ts +335 -0
  17. package/dist/config.js +193 -0
  18. package/dist/database.d.ts +227 -0
  19. package/dist/database.js +601 -0
  20. package/dist/decompiler-worker.d.ts +441 -0
  21. package/dist/decompiler-worker.js +1962 -0
  22. package/dist/dynamic-trace.d.ts +95 -0
  23. package/dist/dynamic-trace.js +629 -0
  24. package/dist/env-validator.d.ts +15 -0
  25. package/dist/env-validator.js +249 -0
  26. package/dist/error-handler.d.ts +28 -0
  27. package/dist/error-handler.example.d.ts +22 -0
  28. package/dist/error-handler.example.js +141 -0
  29. package/dist/error-handler.js +139 -0
  30. package/dist/ghidra-analysis-status.d.ts +49 -0
  31. package/dist/ghidra-analysis-status.js +178 -0
  32. package/dist/ghidra-config.d.ts +134 -0
  33. package/dist/ghidra-config.js +464 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.js +200 -0
  36. package/dist/job-queue.d.ts +169 -0
  37. package/dist/job-queue.js +407 -0
  38. package/dist/logger.d.ts +106 -0
  39. package/dist/logger.js +176 -0
  40. package/dist/policy-guard.d.ts +115 -0
  41. package/dist/policy-guard.js +243 -0
  42. package/dist/process-output.d.ts +15 -0
  43. package/dist/process-output.js +90 -0
  44. package/dist/prompts/function-explanation-review.d.ts +5 -0
  45. package/dist/prompts/function-explanation-review.js +64 -0
  46. package/dist/prompts/semantic-name-review.d.ts +5 -0
  47. package/dist/prompts/semantic-name-review.js +63 -0
  48. package/dist/runtime-correlation.d.ts +34 -0
  49. package/dist/runtime-correlation.js +279 -0
  50. package/dist/runtime-paths.d.ts +3 -0
  51. package/dist/runtime-paths.js +11 -0
  52. package/dist/selection-diff.d.ts +667 -0
  53. package/dist/selection-diff.js +53 -0
  54. package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
  55. package/dist/semantic-name-suggestion-artifacts.js +314 -0
  56. package/dist/server.d.ts +129 -0
  57. package/dist/server.js +578 -0
  58. package/dist/tools/artifact-read.d.ts +235 -0
  59. package/dist/tools/artifact-read.js +317 -0
  60. package/dist/tools/artifacts-diff.d.ts +728 -0
  61. package/dist/tools/artifacts-diff.js +304 -0
  62. package/dist/tools/artifacts-list.d.ts +515 -0
  63. package/dist/tools/artifacts-list.js +389 -0
  64. package/dist/tools/attack-map.d.ts +290 -0
  65. package/dist/tools/attack-map.js +519 -0
  66. package/dist/tools/cache-observability.d.ts +4 -0
  67. package/dist/tools/cache-observability.js +36 -0
  68. package/dist/tools/code-function-cfg.d.ts +50 -0
  69. package/dist/tools/code-function-cfg.js +102 -0
  70. package/dist/tools/code-function-decompile.d.ts +55 -0
  71. package/dist/tools/code-function-decompile.js +103 -0
  72. package/dist/tools/code-function-disassemble.d.ts +43 -0
  73. package/dist/tools/code-function-disassemble.js +185 -0
  74. package/dist/tools/code-function-explain-apply.d.ts +255 -0
  75. package/dist/tools/code-function-explain-apply.js +225 -0
  76. package/dist/tools/code-function-explain-prepare.d.ts +535 -0
  77. package/dist/tools/code-function-explain-prepare.js +276 -0
  78. package/dist/tools/code-function-explain-review.d.ts +397 -0
  79. package/dist/tools/code-function-explain-review.js +589 -0
  80. package/dist/tools/code-function-rename-apply.d.ts +248 -0
  81. package/dist/tools/code-function-rename-apply.js +220 -0
  82. package/dist/tools/code-function-rename-prepare.d.ts +506 -0
  83. package/dist/tools/code-function-rename-prepare.js +279 -0
  84. package/dist/tools/code-function-rename-review.d.ts +574 -0
  85. package/dist/tools/code-function-rename-review.js +761 -0
  86. package/dist/tools/code-functions-list.d.ts +37 -0
  87. package/dist/tools/code-functions-list.js +91 -0
  88. package/dist/tools/code-functions-rank.d.ts +34 -0
  89. package/dist/tools/code-functions-rank.js +90 -0
  90. package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
  91. package/dist/tools/code-functions-reconstruct.js +2807 -0
  92. package/dist/tools/code-functions-search.d.ts +39 -0
  93. package/dist/tools/code-functions-search.js +90 -0
  94. package/dist/tools/code-reconstruct-export.d.ts +1212 -0
  95. package/dist/tools/code-reconstruct-export.js +4002 -0
  96. package/dist/tools/code-reconstruct-plan.d.ts +274 -0
  97. package/dist/tools/code-reconstruct-plan.js +342 -0
  98. package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
  99. package/dist/tools/dotnet-metadata-extract.js +355 -0
  100. package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
  101. package/dist/tools/dotnet-reconstruct-export.js +1151 -0
  102. package/dist/tools/dotnet-types-list.d.ts +325 -0
  103. package/dist/tools/dotnet-types-list.js +201 -0
  104. package/dist/tools/dynamic-dependencies.d.ts +115 -0
  105. package/dist/tools/dynamic-dependencies.js +213 -0
  106. package/dist/tools/dynamic-memory-import.d.ts +10 -0
  107. package/dist/tools/dynamic-memory-import.js +567 -0
  108. package/dist/tools/dynamic-trace-import.d.ts +10 -0
  109. package/dist/tools/dynamic-trace-import.js +235 -0
  110. package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
  111. package/dist/tools/entrypoint-fallback-disasm.js +89 -0
  112. package/dist/tools/ghidra-analyze.d.ts +88 -0
  113. package/dist/tools/ghidra-analyze.js +208 -0
  114. package/dist/tools/ghidra-health.d.ts +37 -0
  115. package/dist/tools/ghidra-health.js +212 -0
  116. package/dist/tools/ioc-export.d.ts +209 -0
  117. package/dist/tools/ioc-export.js +542 -0
  118. package/dist/tools/packer-detect.d.ts +165 -0
  119. package/dist/tools/packer-detect.js +284 -0
  120. package/dist/tools/pe-exports-extract.d.ts +175 -0
  121. package/dist/tools/pe-exports-extract.js +253 -0
  122. package/dist/tools/pe-fingerprint.d.ts +234 -0
  123. package/dist/tools/pe-fingerprint.js +269 -0
  124. package/dist/tools/pe-imports-extract.d.ts +105 -0
  125. package/dist/tools/pe-imports-extract.js +245 -0
  126. package/dist/tools/report-generate.d.ts +157 -0
  127. package/dist/tools/report-generate.js +457 -0
  128. package/dist/tools/report-summarize.d.ts +2131 -0
  129. package/dist/tools/report-summarize.js +596 -0
  130. package/dist/tools/runtime-detect.d.ts +135 -0
  131. package/dist/tools/runtime-detect.js +247 -0
  132. package/dist/tools/sample-ingest.d.ts +94 -0
  133. package/dist/tools/sample-ingest.js +327 -0
  134. package/dist/tools/sample-profile-get.d.ts +183 -0
  135. package/dist/tools/sample-profile-get.js +121 -0
  136. package/dist/tools/sandbox-execute.d.ts +441 -0
  137. package/dist/tools/sandbox-execute.js +392 -0
  138. package/dist/tools/strings-extract.d.ts +375 -0
  139. package/dist/tools/strings-extract.js +314 -0
  140. package/dist/tools/strings-floss-decode.d.ts +143 -0
  141. package/dist/tools/strings-floss-decode.js +259 -0
  142. package/dist/tools/system-health.d.ts +434 -0
  143. package/dist/tools/system-health.js +446 -0
  144. package/dist/tools/task-cancel.d.ts +21 -0
  145. package/dist/tools/task-cancel.js +70 -0
  146. package/dist/tools/task-status.d.ts +27 -0
  147. package/dist/tools/task-status.js +106 -0
  148. package/dist/tools/task-sweep.d.ts +22 -0
  149. package/dist/tools/task-sweep.js +77 -0
  150. package/dist/tools/tool-help.d.ts +340 -0
  151. package/dist/tools/tool-help.js +261 -0
  152. package/dist/tools/yara-scan.d.ts +554 -0
  153. package/dist/tools/yara-scan.js +313 -0
  154. package/dist/types.d.ts +266 -0
  155. package/dist/types.js +41 -0
  156. package/dist/worker-pool.d.ts +204 -0
  157. package/dist/worker-pool.js +650 -0
  158. package/dist/workflows/deep-static.d.ts +104 -0
  159. package/dist/workflows/deep-static.js +276 -0
  160. package/dist/workflows/function-explanation-review.d.ts +655 -0
  161. package/dist/workflows/function-explanation-review.js +440 -0
  162. package/dist/workflows/reconstruct.d.ts +2053 -0
  163. package/dist/workflows/reconstruct.js +666 -0
  164. package/dist/workflows/semantic-name-review.d.ts +2418 -0
  165. package/dist/workflows/semantic-name-review.js +521 -0
  166. package/dist/workflows/triage.d.ts +659 -0
  167. package/dist/workflows/triage.js +1374 -0
  168. package/dist/workspace-manager.d.ts +150 -0
  169. package/dist/workspace-manager.js +411 -0
  170. package/ghidra_scripts/DecompileFunction.java +487 -0
  171. package/ghidra_scripts/DecompileFunction.py +150 -0
  172. package/ghidra_scripts/ExtractCFG.java +256 -0
  173. package/ghidra_scripts/ExtractCFG.py +233 -0
  174. package/ghidra_scripts/ExtractFunctions.java +442 -0
  175. package/ghidra_scripts/ExtractFunctions.py +101 -0
  176. package/ghidra_scripts/README.md +125 -0
  177. package/ghidra_scripts/SearchFunctionReferences.java +380 -0
  178. package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
  179. package/helpers/DotNetMetadataProbe/Program.cs +566 -0
  180. package/install-to-codex.ps1 +178 -0
  181. package/install-to-copilot.ps1 +303 -0
  182. package/package.json +101 -0
  183. package/requirements.txt +9 -0
  184. package/workers/requirements-dynamic.txt +11 -0
  185. package/workers/requirements.txt +8 -0
  186. package/workers/speakeasy_compat.py +175 -0
  187. package/workers/static_worker.py +5183 -0
  188. package/workers/yara_rules/default.yar +33 -0
  189. package/workers/yara_rules/malware_families.yar +93 -0
  190. package/workers/yara_rules/packers.yar +80 -0
@@ -0,0 +1,567 @@
1
+ /**
2
+ * dynamic.memory.import tool
3
+ * Import minidump or raw process-memory snapshots and normalize them into runtime evidence.
4
+ */
5
+ import fs from 'fs/promises';
6
+ import path from 'path';
7
+ import { createHash, randomUUID } from 'crypto';
8
+ import { z } from 'zod';
9
+ import { normalizeDynamicTrace, summarizeDynamicTrace, } from '../dynamic-trace.js';
10
+ const TOOL_NAME = 'dynamic.memory.import';
11
+ const DynamicMemoryImportInputSchema = z.object({
12
+ sample_id: z.string().describe('Sample ID (format: sha256:<hex>)'),
13
+ path: z.string().describe('Path to a minidump or raw process-memory snapshot'),
14
+ format: z
15
+ .enum(['auto', 'minidump', 'process_memory', 'raw_dump'])
16
+ .optional()
17
+ .default('auto')
18
+ .describe('Format hint for the memory snapshot'),
19
+ trace_name: z.string().optional().describe('Optional source name used in persisted artifact naming'),
20
+ persist_artifact: z
21
+ .boolean()
22
+ .optional()
23
+ .default(true)
24
+ .describe('Persist copied raw dump plus normalized runtime-evidence artifact'),
25
+ register_analysis: z
26
+ .boolean()
27
+ .optional()
28
+ .default(true)
29
+ .describe('Insert a completed analysis row for the imported memory evidence'),
30
+ min_string_length: z
31
+ .number()
32
+ .int()
33
+ .min(4)
34
+ .max(32)
35
+ .optional()
36
+ .default(5)
37
+ .describe('Minimum extracted string length'),
38
+ max_strings: z
39
+ .number()
40
+ .int()
41
+ .min(50)
42
+ .max(5000)
43
+ .optional()
44
+ .default(800)
45
+ .describe('Maximum number of strings retained from the snapshot'),
46
+ context_window_bytes: z
47
+ .number()
48
+ .int()
49
+ .min(32)
50
+ .max(4096)
51
+ .optional()
52
+ .default(192)
53
+ .describe('Maximum offset gap used to group nearby strings into memory windows'),
54
+ });
55
+ const DynamicMemoryImportOutputSchema = z.object({
56
+ ok: z.boolean(),
57
+ data: z
58
+ .object({
59
+ ingest_id: z.string(),
60
+ format: z.string(),
61
+ evidence_kind: z.literal('memory_snapshot'),
62
+ executed: z.boolean(),
63
+ extracted_string_count: z.number().int().nonnegative(),
64
+ context_window_count: z.number().int().nonnegative(),
65
+ summary: z.object({
66
+ artifact_count: z.number(),
67
+ executed: z.boolean(),
68
+ executed_artifact_count: z.number().optional(),
69
+ api_count: z.number(),
70
+ memory_region_count: z.number(),
71
+ stage_count: z.number(),
72
+ observed_apis: z.array(z.string()),
73
+ high_signal_apis: z.array(z.string()),
74
+ memory_regions: z.array(z.string()),
75
+ stages: z.array(z.string()),
76
+ risk_hints: z.array(z.string()),
77
+ source_formats: z.array(z.string()).optional(),
78
+ evidence_kinds: z.array(z.string()).optional(),
79
+ source_names: z.array(z.string()).optional(),
80
+ evidence: z.array(z.string()),
81
+ summary: z.string(),
82
+ }),
83
+ normalized_trace: z.any(),
84
+ analysis_id: z.string().optional(),
85
+ raw_artifact: z.any().optional(),
86
+ trace_artifact: z.any().optional(),
87
+ })
88
+ .optional(),
89
+ warnings: z.array(z.string()).optional(),
90
+ errors: z.array(z.string()).optional(),
91
+ artifacts: z.array(z.any()).optional(),
92
+ metrics: z
93
+ .object({
94
+ elapsed_ms: z.number(),
95
+ tool: z.string(),
96
+ })
97
+ .optional(),
98
+ });
99
+ export const dynamicMemoryImportToolDefinition = {
100
+ name: TOOL_NAME,
101
+ description: 'Import a minidump or raw process-memory snapshot, extract runtime-relevant strings/API evidence, and persist normalized memory-snapshot runtime artifacts.',
102
+ inputSchema: DynamicMemoryImportInputSchema,
103
+ outputSchema: DynamicMemoryImportOutputSchema,
104
+ };
105
+ const API_CANDIDATES = [
106
+ 'GetProcAddress',
107
+ 'LoadLibraryA',
108
+ 'LoadLibraryW',
109
+ 'LoadLibraryExA',
110
+ 'LoadLibraryExW',
111
+ 'GetModuleHandleA',
112
+ 'GetModuleHandleW',
113
+ 'OpenProcess',
114
+ 'OpenProcessToken',
115
+ 'CreateProcessA',
116
+ 'CreateProcessW',
117
+ 'ReadProcessMemory',
118
+ 'WriteProcessMemory',
119
+ 'VirtualAllocEx',
120
+ 'VirtualProtectEx',
121
+ 'CreateRemoteThread',
122
+ 'ResumeThread',
123
+ 'SetThreadContext',
124
+ 'NtQueryInformationProcess',
125
+ 'NtQuerySystemInformation',
126
+ 'CreateFileA',
127
+ 'CreateFileW',
128
+ 'ReadFile',
129
+ 'WriteFile',
130
+ 'DeleteFileA',
131
+ 'DeleteFileW',
132
+ 'CopyFileA',
133
+ 'CopyFileW',
134
+ 'FindFirstFileA',
135
+ 'FindFirstFileW',
136
+ 'FindNextFileA',
137
+ 'FindNextFileW',
138
+ 'RegOpenKeyExA',
139
+ 'RegOpenKeyExW',
140
+ 'RegCreateKeyExA',
141
+ 'RegCreateKeyExW',
142
+ 'RegSetValueExA',
143
+ 'RegSetValueExW',
144
+ 'InternetOpenA',
145
+ 'InternetOpenW',
146
+ 'InternetConnectA',
147
+ 'InternetConnectW',
148
+ 'HttpSendRequestA',
149
+ 'HttpSendRequestW',
150
+ 'WinHttpSendRequest',
151
+ 'socket',
152
+ 'connect',
153
+ 'send',
154
+ 'recv',
155
+ ];
156
+ function sanitizeName(value) {
157
+ const base = (value || 'memory').trim().toLowerCase();
158
+ const normalized = base.replace(/[^a-z0-9._-]+/g, '_').replace(/^_+|_+$/g, '');
159
+ return normalized.length > 0 ? normalized.slice(0, 48) : 'memory';
160
+ }
161
+ function isPrintableAscii(byte) {
162
+ return byte >= 0x20 && byte <= 0x7e;
163
+ }
164
+ function dedupeStrings(values, limit) {
165
+ const unique = Array.from(new Set(values.map((item) => item.trim()).filter((item) => item.length > 0)));
166
+ return typeof limit === 'number' ? unique.slice(0, limit) : unique;
167
+ }
168
+ function detectMemoryFormat(filePath, data, hint) {
169
+ if (hint && hint !== 'auto') {
170
+ return hint;
171
+ }
172
+ if (data.length >= 4 && data.subarray(0, 4).toString('ascii') === 'MDMP') {
173
+ return 'minidump';
174
+ }
175
+ const ext = path.extname(filePath).toLowerCase();
176
+ if (ext === '.dmp' || ext === '.mdmp' || ext === '.dump') {
177
+ return 'minidump';
178
+ }
179
+ if (ext === '.mem' || ext === '.pmem' || ext === '.vmem') {
180
+ return 'process_memory';
181
+ }
182
+ return 'raw_dump';
183
+ }
184
+ function extractAsciiStrings(data, minLen, maxItems) {
185
+ const results = [];
186
+ let start = -1;
187
+ for (let index = 0; index <= data.length; index += 1) {
188
+ const current = index < data.length ? data[index] : 0;
189
+ if (index < data.length && isPrintableAscii(current)) {
190
+ if (start === -1) {
191
+ start = index;
192
+ }
193
+ continue;
194
+ }
195
+ if (start !== -1) {
196
+ const length = index - start;
197
+ if (length >= minLen) {
198
+ results.push({
199
+ offset: start,
200
+ value: data.subarray(start, index).toString('ascii'),
201
+ encoding: 'ascii',
202
+ });
203
+ if (results.length >= maxItems) {
204
+ return results;
205
+ }
206
+ }
207
+ start = -1;
208
+ }
209
+ }
210
+ return results;
211
+ }
212
+ function extractUtf16Strings(data, minLen, maxItems) {
213
+ const results = [];
214
+ let start = -1;
215
+ let charCount = 0;
216
+ let index = 0;
217
+ while (index + 1 < data.length) {
218
+ const low = data[index];
219
+ const high = data[index + 1];
220
+ if (isPrintableAscii(low) && high === 0x00) {
221
+ if (start === -1) {
222
+ start = index;
223
+ }
224
+ charCount += 1;
225
+ index += 2;
226
+ continue;
227
+ }
228
+ if (start !== -1 && charCount >= minLen) {
229
+ results.push({
230
+ offset: start,
231
+ value: data.subarray(start, index).toString('utf16le'),
232
+ encoding: 'utf16le',
233
+ });
234
+ if (results.length >= maxItems) {
235
+ return results;
236
+ }
237
+ }
238
+ start = -1;
239
+ charCount = 0;
240
+ index += 1;
241
+ }
242
+ if (start !== -1 && charCount >= minLen && results.length < maxItems) {
243
+ results.push({
244
+ offset: start,
245
+ value: data.subarray(start, index).toString('utf16le'),
246
+ encoding: 'utf16le',
247
+ });
248
+ }
249
+ return results;
250
+ }
251
+ function extractStrings(data, minLen, maxItems) {
252
+ const half = Math.max(50, Math.floor(maxItems / 2));
253
+ const entries = [...extractAsciiStrings(data, minLen, half), ...extractUtf16Strings(data, minLen, half)];
254
+ return entries
255
+ .sort((left, right) => left.offset - right.offset)
256
+ .slice(0, maxItems);
257
+ }
258
+ function extractApisFromString(value) {
259
+ const hits = [];
260
+ for (const api of API_CANDIDATES) {
261
+ const pattern = new RegExp(`\\b${api.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\$&')}\\b`, 'i');
262
+ if (pattern.test(value)) {
263
+ hits.push(api);
264
+ }
265
+ }
266
+ return dedupeStrings(hits);
267
+ }
268
+ function aggregateApiStrings(strings) {
269
+ const aggregates = new Map();
270
+ for (const entry of strings) {
271
+ const apis = extractApisFromString(entry.value);
272
+ for (const api of apis) {
273
+ const existing = aggregates.get(api);
274
+ const source = `offset:0x${entry.offset.toString(16)}:${entry.encoding}`;
275
+ if (existing) {
276
+ existing.count += 1;
277
+ existing.sources = dedupeStrings([...existing.sources, source], 6);
278
+ existing.confidence = Math.min(0.92, existing.confidence + 0.03);
279
+ }
280
+ else {
281
+ aggregates.set(api, {
282
+ api,
283
+ count: 1,
284
+ confidence: /Process|Thread|GetProcAddress|LoadLibrary|NtQuery/i.test(api) ? 0.82 : 0.72,
285
+ sources: [source],
286
+ });
287
+ }
288
+ }
289
+ }
290
+ return Array.from(aggregates.values()).sort((left, right) => {
291
+ if (right.count !== left.count) {
292
+ return right.count - left.count;
293
+ }
294
+ return right.confidence - left.confidence;
295
+ });
296
+ }
297
+ function classifyWindow(apis) {
298
+ const corpus = apis.join(' ');
299
+ if (/\b(GetProcAddress|LoadLibrary|GetModuleHandle)\b/i.test(corpus)) {
300
+ return {
301
+ regionType: 'api_resolution_table',
302
+ purpose: 'dynamic_api_table',
303
+ };
304
+ }
305
+ if (/\b(OpenProcess|ReadProcessMemory|WriteProcessMemory|SetThreadContext|ResumeThread|CreateRemoteThread|VirtualAllocEx|CreateProcess)\b/i.test(corpus)) {
306
+ return {
307
+ regionType: 'process_operation_plan',
308
+ purpose: 'remote_process_access',
309
+ };
310
+ }
311
+ if (/\b(CreateFile|ReadFile|WriteFile|DeleteFile|CopyFile|FindFirstFile|FindNextFile)\b/i.test(corpus)) {
312
+ return {
313
+ regionType: 'file_operation_plan',
314
+ purpose: 'file_dispatch_table',
315
+ };
316
+ }
317
+ if (/\b(RegOpenKey|RegCreateKey|RegSetValue)\b/i.test(corpus)) {
318
+ return {
319
+ regionType: 'registry_operation_plan',
320
+ purpose: 'registry_staging',
321
+ };
322
+ }
323
+ if (/\b(InternetOpen|InternetConnect|HttpSendRequest|WinHttp|socket|connect|send|recv)\b/i.test(corpus)) {
324
+ return {
325
+ regionType: 'network_operation_plan',
326
+ purpose: 'network_dispatch',
327
+ };
328
+ }
329
+ if (/\b(NtQueryInformationProcess|NtQuerySystemInformation)\b/i.test(corpus)) {
330
+ return {
331
+ regionType: 'environment_probe',
332
+ purpose: 'anti_analysis_observations',
333
+ };
334
+ }
335
+ return {
336
+ regionType: 'memory_string_window',
337
+ purpose: 'snapshot_context_window',
338
+ };
339
+ }
340
+ function buildContextRegions(strings, contextWindowBytes) {
341
+ if (strings.length === 0) {
342
+ return [];
343
+ }
344
+ const windows = [];
345
+ let current = [strings[0]];
346
+ for (const entry of strings.slice(1)) {
347
+ const previous = current[current.length - 1];
348
+ if (entry.offset - previous.offset <= contextWindowBytes) {
349
+ current.push(entry);
350
+ continue;
351
+ }
352
+ windows.push(current);
353
+ current = [entry];
354
+ }
355
+ windows.push(current);
356
+ return windows
357
+ .map((windowEntries) => {
358
+ const apis = dedupeStrings(windowEntries.flatMap((entry) => extractApisFromString(entry.value)), 12);
359
+ const indicators = dedupeStrings([...apis, ...windowEntries.slice(0, 4).map((entry) => entry.value)], 10);
360
+ const classification = classifyWindow(apis);
361
+ const start = windowEntries[0].offset;
362
+ const end = windowEntries[windowEntries.length - 1].offset;
363
+ return {
364
+ region_type: classification.regionType,
365
+ purpose: classification.purpose,
366
+ source: 'memory_snapshot_window',
367
+ confidence: Number(Math.min(0.9, 0.54 + Math.min(0.18, apis.length * 0.06) + Math.min(0.12, windowEntries.length * 0.02)).toFixed(2)),
368
+ base_address: `0x${start.toString(16)}`,
369
+ size: Math.max(1, end - start),
370
+ indicators,
371
+ };
372
+ })
373
+ .filter((item) => item.indicators.length > 0)
374
+ .slice(0, 16);
375
+ }
376
+ function buildRiskHints(format, apis) {
377
+ const apiNames = apis.map((item) => item.api);
378
+ const hints = [];
379
+ if (format === 'minidump') {
380
+ hints.push('Memory evidence originated from a minidump; execution was not directly replayed.');
381
+ }
382
+ else {
383
+ hints.push('Memory evidence originated from a process-memory snapshot; execution was not directly replayed.');
384
+ }
385
+ if (apiNames.some((item) => /WriteProcessMemory|ReadProcessMemory|VirtualAllocEx|CreateRemoteThread/i.test(item))) {
386
+ hints.push('Process-memory manipulation indicators were recovered from memory strings.');
387
+ }
388
+ if (apiNames.some((item) => /GetProcAddress|LoadLibrary/i.test(item))) {
389
+ hints.push('Dynamic API resolution indicators were recovered from memory strings.');
390
+ }
391
+ if (apiNames.some((item) => /NtQueryInformationProcess|NtQuerySystemInformation/i.test(item))) {
392
+ hints.push('Anti-analysis or environment-query indicators were recovered from memory strings.');
393
+ }
394
+ return dedupeStrings(hints, 8);
395
+ }
396
+ function buildNormalizedMemoryTrace(input, effectiveFormat, strings, apiAggregates, memoryRegions) {
397
+ const rawRecord = {
398
+ executed: false,
399
+ api_calls: apiAggregates.map((item) => ({
400
+ api: item.api,
401
+ count: item.count,
402
+ confidence: item.confidence,
403
+ sources: item.sources,
404
+ })),
405
+ memory_regions: memoryRegions,
406
+ strings: strings.slice(0, 120).map((item) => item.value),
407
+ risk_hints: buildRiskHints(effectiveFormat, apiAggregates),
408
+ notes: [
409
+ `Imported ${effectiveFormat} memory snapshot from ${path.basename(input.path)}`,
410
+ `Recovered ${apiAggregates.length} API indicator(s) from ${strings.length} extracted string(s).`,
411
+ ],
412
+ };
413
+ return normalizeDynamicTrace(rawRecord, {
414
+ sourceFormat: effectiveFormat,
415
+ evidenceKind: 'memory_snapshot',
416
+ sourceName: input.trace_name || path.basename(input.path),
417
+ });
418
+ }
419
+ export function createDynamicMemoryImportHandler(workspaceManager, database) {
420
+ return async (args) => {
421
+ const startTime = Date.now();
422
+ try {
423
+ const input = DynamicMemoryImportInputSchema.parse(args);
424
+ const sample = database.findSample(input.sample_id);
425
+ if (!sample) {
426
+ return {
427
+ ok: false,
428
+ errors: [`Sample not found: ${input.sample_id}`],
429
+ metrics: {
430
+ elapsed_ms: Date.now() - startTime,
431
+ tool: TOOL_NAME,
432
+ },
433
+ };
434
+ }
435
+ const rawData = await fs.readFile(input.path);
436
+ const effectiveFormat = detectMemoryFormat(input.path, rawData, input.format);
437
+ const strings = extractStrings(rawData, input.min_string_length, input.max_strings);
438
+ const apiAggregates = aggregateApiStrings(strings);
439
+ const memoryRegions = buildContextRegions(strings, input.context_window_bytes);
440
+ const normalizedTrace = buildNormalizedMemoryTrace(input, effectiveFormat, strings, apiAggregates, memoryRegions);
441
+ const summary = summarizeDynamicTrace(normalizedTrace);
442
+ const warnings = [
443
+ 'Imported evidence does not prove full execution; treat it as memory-snapshot evidence until corroborated.',
444
+ ];
445
+ if (apiAggregates.length === 0) {
446
+ warnings.push('No strong API indicators were recovered from the memory snapshot; confidence is string-window limited.');
447
+ }
448
+ const artifacts = [];
449
+ let rawArtifact;
450
+ let traceArtifact;
451
+ let analysisId;
452
+ if (input.persist_artifact) {
453
+ const workspace = await workspaceManager.createWorkspace(input.sample_id);
454
+ const reportDir = path.join(workspace.reports, 'dynamic');
455
+ await fs.mkdir(reportDir, { recursive: true });
456
+ const timestamp = Date.now();
457
+ const sourceExt = path.extname(input.path) || '.bin';
458
+ const rawFileName = `memory_snapshot_${sanitizeName(input.trace_name || path.basename(input.path))}_${timestamp}${sourceExt}`;
459
+ const rawAbsPath = path.join(reportDir, rawFileName);
460
+ await fs.writeFile(rawAbsPath, rawData);
461
+ const rawArtifactId = randomUUID();
462
+ const rawSha256 = createHash('sha256').update(rawData).digest('hex');
463
+ const rawRelativePath = `reports/dynamic/${rawFileName}`;
464
+ database.insertArtifact({
465
+ id: rawArtifactId,
466
+ sample_id: input.sample_id,
467
+ type: 'raw_dump',
468
+ path: rawRelativePath,
469
+ sha256: rawSha256,
470
+ mime: 'application/octet-stream',
471
+ created_at: new Date().toISOString(),
472
+ });
473
+ rawArtifact = {
474
+ id: rawArtifactId,
475
+ type: 'raw_dump',
476
+ path: rawRelativePath,
477
+ sha256: rawSha256,
478
+ mime: 'application/octet-stream',
479
+ };
480
+ artifacts.push(rawArtifact);
481
+ const traceFileName = `memory_trace_${sanitizeName(input.trace_name || path.basename(input.path))}_${timestamp}.json`;
482
+ const traceAbsPath = path.join(reportDir, traceFileName);
483
+ const traceSerialized = JSON.stringify(normalizedTrace, null, 2);
484
+ await fs.writeFile(traceAbsPath, traceSerialized, 'utf-8');
485
+ const traceArtifactId = randomUUID();
486
+ const traceSha256 = createHash('sha256').update(traceSerialized).digest('hex');
487
+ const traceRelativePath = `reports/dynamic/${traceFileName}`;
488
+ database.insertArtifact({
489
+ id: traceArtifactId,
490
+ sample_id: input.sample_id,
491
+ type: 'dynamic_trace_json',
492
+ path: traceRelativePath,
493
+ sha256: traceSha256,
494
+ mime: 'application/json',
495
+ created_at: new Date().toISOString(),
496
+ });
497
+ traceArtifact = {
498
+ id: traceArtifactId,
499
+ type: 'dynamic_trace_json',
500
+ path: traceRelativePath,
501
+ sha256: traceSha256,
502
+ mime: 'application/json',
503
+ };
504
+ artifacts.push(traceArtifact);
505
+ }
506
+ if (input.register_analysis) {
507
+ analysisId = randomUUID();
508
+ database.insertAnalysis({
509
+ id: analysisId,
510
+ sample_id: input.sample_id,
511
+ stage: 'memory_snapshot_import',
512
+ backend: 'memory_snapshot_import',
513
+ status: 'done',
514
+ started_at: new Date(startTime).toISOString(),
515
+ finished_at: new Date().toISOString(),
516
+ output_json: JSON.stringify({
517
+ format: effectiveFormat,
518
+ evidence_kind: normalizedTrace.evidence_kind,
519
+ executed: normalizedTrace.executed,
520
+ summary,
521
+ raw_artifact_id: rawArtifact?.id,
522
+ trace_artifact_id: traceArtifact?.id,
523
+ }),
524
+ metrics_json: JSON.stringify({
525
+ extracted_string_count: strings.length,
526
+ api_count: summary.api_count,
527
+ memory_region_count: summary.memory_region_count,
528
+ stage_count: summary.stage_count,
529
+ }),
530
+ });
531
+ }
532
+ return {
533
+ ok: true,
534
+ data: {
535
+ ingest_id: randomUUID(),
536
+ format: effectiveFormat,
537
+ evidence_kind: 'memory_snapshot',
538
+ executed: false,
539
+ extracted_string_count: strings.length,
540
+ context_window_count: memoryRegions.length,
541
+ summary,
542
+ normalized_trace: normalizedTrace,
543
+ analysis_id: analysisId,
544
+ raw_artifact: rawArtifact,
545
+ trace_artifact: traceArtifact,
546
+ },
547
+ warnings,
548
+ artifacts: artifacts.length > 0 ? artifacts : undefined,
549
+ metrics: {
550
+ elapsed_ms: Date.now() - startTime,
551
+ tool: TOOL_NAME,
552
+ },
553
+ };
554
+ }
555
+ catch (error) {
556
+ return {
557
+ ok: false,
558
+ errors: [error.message],
559
+ metrics: {
560
+ elapsed_ms: Date.now() - startTime,
561
+ tool: TOOL_NAME,
562
+ },
563
+ };
564
+ }
565
+ };
566
+ }
567
+ //# sourceMappingURL=dynamic-memory-import.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * dynamic.trace.import tool
3
+ * Import external runtime API traces or memory-snapshot summaries into the MCP workspace.
4
+ */
5
+ import type { ToolDefinition, ToolArgs, WorkerResult } from '../types.js';
6
+ import type { WorkspaceManager } from '../workspace-manager.js';
7
+ import type { DatabaseManager } from '../database.js';
8
+ export declare const dynamicTraceImportToolDefinition: ToolDefinition;
9
+ export declare function createDynamicTraceImportHandler(workspaceManager: WorkspaceManager, database: DatabaseManager): (args: ToolArgs) => Promise<WorkerResult>;
10
+ //# sourceMappingURL=dynamic-trace-import.d.ts.map