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,102 @@
1
+ /**
2
+ * code.function.cfg MCP Tool
3
+ *
4
+ * Requirements: 11.1, 11.2, 11.3, 11.4
5
+ *
6
+ * Extracts control flow graph for a function
7
+ */
8
+ import { z } from 'zod';
9
+ import { DecompilerWorker, getGhidraDiagnostics, normalizeGhidraError } from '../decompiler-worker.js';
10
+ import { logger } from '../logger.js';
11
+ /**
12
+ * Input schema for code.function.cfg tool
13
+ */
14
+ export const codeFunctionCFGInputSchema = z.object({
15
+ sample_id: z.string().describe('Sample identifier (sha256:<hex>)'),
16
+ address: z.string().optional().describe('Function address (hex string)'),
17
+ symbol: z.string().optional().describe('Function symbol name'),
18
+ timeout: z.number().optional().describe('Timeout in seconds (default: 30)')
19
+ }).refine(data => data.address || data.symbol, {
20
+ message: 'Either address or symbol must be provided'
21
+ });
22
+ /**
23
+ * Tool definition for code.function.cfg
24
+ */
25
+ export const codeFunctionCFGToolDefinition = {
26
+ name: 'code.function.cfg',
27
+ description: 'Extract control flow graph (CFG) for a function. Returns nodes (basic blocks) and edges (control flow). Requires prior Ghidra analysis.',
28
+ inputSchema: codeFunctionCFGInputSchema
29
+ };
30
+ /**
31
+ * Create handler for code.function.cfg tool
32
+ */
33
+ export function createCodeFunctionCFGHandler(workspaceManager, database) {
34
+ return async (args) => {
35
+ try {
36
+ const input = codeFunctionCFGInputSchema.parse(args);
37
+ const addressOrSymbol = input.address || input.symbol;
38
+ logger.info({
39
+ sample_id: input.sample_id,
40
+ address_or_symbol: addressOrSymbol
41
+ }, 'code.function.cfg tool called');
42
+ // Check if sample exists
43
+ const sample = database.findSample(input.sample_id);
44
+ if (!sample) {
45
+ return {
46
+ content: [{
47
+ type: 'text',
48
+ text: JSON.stringify({
49
+ ok: false,
50
+ errors: [`Sample not found: ${input.sample_id}`]
51
+ }, null, 2)
52
+ }],
53
+ isError: true
54
+ };
55
+ }
56
+ // Create decompiler worker
57
+ const decompilerWorker = new DecompilerWorker(database, workspaceManager);
58
+ // Convert timeout from seconds to milliseconds
59
+ const timeoutMs = (input.timeout || 30) * 1000;
60
+ // Extract CFG
61
+ const cfg = await decompilerWorker.getFunctionCFG(input.sample_id, addressOrSymbol, timeoutMs);
62
+ logger.info({
63
+ sample_id: input.sample_id,
64
+ function: cfg.function,
65
+ node_count: cfg.nodes.length,
66
+ edge_count: cfg.edges.length
67
+ }, 'Function CFG extracted successfully');
68
+ return {
69
+ content: [{
70
+ type: 'text',
71
+ text: JSON.stringify({
72
+ ok: true,
73
+ data: cfg
74
+ }, null, 2)
75
+ }]
76
+ };
77
+ }
78
+ catch (error) {
79
+ const errorMessage = error instanceof Error ? error.message : String(error);
80
+ const diagnostics = getGhidraDiagnostics(error);
81
+ const normalizedError = normalizeGhidraError(error, 'code.function.cfg');
82
+ logger.error({
83
+ error: errorMessage,
84
+ ghidra_diagnostics: diagnostics,
85
+ normalized_error: normalizedError,
86
+ }, 'code.function.cfg tool failed');
87
+ return {
88
+ content: [{
89
+ type: 'text',
90
+ text: JSON.stringify({
91
+ ok: false,
92
+ errors: [errorMessage],
93
+ diagnostics,
94
+ normalized_error: normalizedError,
95
+ }, null, 2)
96
+ }],
97
+ isError: true
98
+ };
99
+ }
100
+ };
101
+ }
102
+ //# sourceMappingURL=code-function-cfg.js.map
@@ -0,0 +1,55 @@
1
+ /**
2
+ * code.function.decompile MCP Tool
3
+ *
4
+ * Requirements: 10.1, 10.2, 10.3, 10.4
5
+ *
6
+ * Decompiles a specific function to pseudocode
7
+ */
8
+ import { z } from 'zod';
9
+ import type { ToolDefinition, ToolHandler } from '../types.js';
10
+ import type { DatabaseManager } from '../database.js';
11
+ import type { WorkspaceManager } from '../workspace-manager.js';
12
+ /**
13
+ * Input schema for code.function.decompile tool
14
+ */
15
+ export declare const codeFunctionDecompileInputSchema: z.ZodEffects<z.ZodObject<{
16
+ sample_id: z.ZodString;
17
+ address: z.ZodOptional<z.ZodString>;
18
+ symbol: z.ZodOptional<z.ZodString>;
19
+ include_xrefs: z.ZodOptional<z.ZodBoolean>;
20
+ timeout: z.ZodOptional<z.ZodNumber>;
21
+ }, "strip", z.ZodTypeAny, {
22
+ sample_id: string;
23
+ symbol?: string | undefined;
24
+ timeout?: number | undefined;
25
+ address?: string | undefined;
26
+ include_xrefs?: boolean | undefined;
27
+ }, {
28
+ sample_id: string;
29
+ symbol?: string | undefined;
30
+ timeout?: number | undefined;
31
+ address?: string | undefined;
32
+ include_xrefs?: boolean | undefined;
33
+ }>, {
34
+ sample_id: string;
35
+ symbol?: string | undefined;
36
+ timeout?: number | undefined;
37
+ address?: string | undefined;
38
+ include_xrefs?: boolean | undefined;
39
+ }, {
40
+ sample_id: string;
41
+ symbol?: string | undefined;
42
+ timeout?: number | undefined;
43
+ address?: string | undefined;
44
+ include_xrefs?: boolean | undefined;
45
+ }>;
46
+ export type CodeFunctionDecompileInput = z.infer<typeof codeFunctionDecompileInputSchema>;
47
+ /**
48
+ * Tool definition for code.function.decompile
49
+ */
50
+ export declare const codeFunctionDecompileToolDefinition: ToolDefinition;
51
+ /**
52
+ * Create handler for code.function.decompile tool
53
+ */
54
+ export declare function createCodeFunctionDecompileHandler(workspaceManager: WorkspaceManager, database: DatabaseManager): ToolHandler;
55
+ //# sourceMappingURL=code-function-decompile.d.ts.map
@@ -0,0 +1,103 @@
1
+ /**
2
+ * code.function.decompile MCP Tool
3
+ *
4
+ * Requirements: 10.1, 10.2, 10.3, 10.4
5
+ *
6
+ * Decompiles a specific function to pseudocode
7
+ */
8
+ import { z } from 'zod';
9
+ import { DecompilerWorker, getGhidraDiagnostics, normalizeGhidraError } from '../decompiler-worker.js';
10
+ import { logger } from '../logger.js';
11
+ /**
12
+ * Input schema for code.function.decompile tool
13
+ */
14
+ export const codeFunctionDecompileInputSchema = z.object({
15
+ sample_id: z.string().describe('Sample identifier (sha256:<hex>)'),
16
+ address: z.string().optional().describe('Function address (hex string, e.g., "0x00401000")'),
17
+ symbol: z.string().optional().describe('Function symbol name'),
18
+ include_xrefs: z.boolean().optional().describe('Include cross-references (default: false)'),
19
+ timeout: z.number().optional().describe('Timeout in seconds (default: 30)')
20
+ }).refine(data => data.address || data.symbol, {
21
+ message: 'Either address or symbol must be provided'
22
+ });
23
+ /**
24
+ * Tool definition for code.function.decompile
25
+ */
26
+ export const codeFunctionDecompileToolDefinition = {
27
+ name: 'code.function.decompile',
28
+ description: 'Decompile a specific function to pseudocode. Requires prior Ghidra analysis. Provide either address or symbol name.',
29
+ inputSchema: codeFunctionDecompileInputSchema
30
+ };
31
+ /**
32
+ * Create handler for code.function.decompile tool
33
+ */
34
+ export function createCodeFunctionDecompileHandler(workspaceManager, database) {
35
+ return async (args) => {
36
+ try {
37
+ const input = codeFunctionDecompileInputSchema.parse(args);
38
+ const addressOrSymbol = input.address || input.symbol;
39
+ logger.info({
40
+ sample_id: input.sample_id,
41
+ address_or_symbol: addressOrSymbol,
42
+ include_xrefs: input.include_xrefs
43
+ }, 'code.function.decompile tool called');
44
+ // Check if sample exists
45
+ const sample = database.findSample(input.sample_id);
46
+ if (!sample) {
47
+ return {
48
+ content: [{
49
+ type: 'text',
50
+ text: JSON.stringify({
51
+ ok: false,
52
+ errors: [`Sample not found: ${input.sample_id}`]
53
+ }, null, 2)
54
+ }],
55
+ isError: true
56
+ };
57
+ }
58
+ // Create decompiler worker
59
+ const decompilerWorker = new DecompilerWorker(database, workspaceManager);
60
+ // Convert timeout from seconds to milliseconds
61
+ const timeoutMs = (input.timeout || 30) * 1000;
62
+ // Decompile function
63
+ const result = await decompilerWorker.decompileFunction(input.sample_id, addressOrSymbol, input.include_xrefs || false, timeoutMs);
64
+ logger.info({
65
+ sample_id: input.sample_id,
66
+ function: result.function,
67
+ address: result.address
68
+ }, 'Function decompiled successfully');
69
+ return {
70
+ content: [{
71
+ type: 'text',
72
+ text: JSON.stringify({
73
+ ok: true,
74
+ data: result
75
+ }, null, 2)
76
+ }]
77
+ };
78
+ }
79
+ catch (error) {
80
+ const errorMessage = error instanceof Error ? error.message : String(error);
81
+ const diagnostics = getGhidraDiagnostics(error);
82
+ const normalizedError = normalizeGhidraError(error, 'code.function.decompile');
83
+ logger.error({
84
+ error: errorMessage,
85
+ ghidra_diagnostics: diagnostics,
86
+ normalized_error: normalizedError,
87
+ }, 'code.function.decompile tool failed');
88
+ return {
89
+ content: [{
90
+ type: 'text',
91
+ text: JSON.stringify({
92
+ ok: false,
93
+ errors: [errorMessage],
94
+ diagnostics,
95
+ normalized_error: normalizedError,
96
+ }, null, 2)
97
+ }],
98
+ isError: true
99
+ };
100
+ }
101
+ };
102
+ }
103
+ //# sourceMappingURL=code-function-decompile.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * code.function.disassemble MCP Tool
3
+ *
4
+ * Returns assembly code for a function
5
+ */
6
+ import { z } from 'zod';
7
+ import type { ToolDefinition, ToolHandler } from '../types.js';
8
+ import type { DatabaseManager } from '../database.js';
9
+ import type { WorkspaceManager } from '../workspace-manager.js';
10
+ /**
11
+ * Input schema for code.function.disassemble tool
12
+ */
13
+ export declare const codeFunctionDisassembleInputSchema: z.ZodEffects<z.ZodObject<{
14
+ sample_id: z.ZodString;
15
+ address: z.ZodOptional<z.ZodString>;
16
+ symbol: z.ZodOptional<z.ZodString>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ sample_id: string;
19
+ symbol?: string | undefined;
20
+ address?: string | undefined;
21
+ }, {
22
+ sample_id: string;
23
+ symbol?: string | undefined;
24
+ address?: string | undefined;
25
+ }>, {
26
+ sample_id: string;
27
+ symbol?: string | undefined;
28
+ address?: string | undefined;
29
+ }, {
30
+ sample_id: string;
31
+ symbol?: string | undefined;
32
+ address?: string | undefined;
33
+ }>;
34
+ export type CodeFunctionDisassembleInput = z.infer<typeof codeFunctionDisassembleInputSchema>;
35
+ /**
36
+ * Tool definition for code.function.disassemble
37
+ */
38
+ export declare const codeFunctionDisassembleToolDefinition: ToolDefinition;
39
+ /**
40
+ * Create handler for code.function.disassemble tool
41
+ */
42
+ export declare function createCodeFunctionDisassembleHandler(workspaceManager: WorkspaceManager, database: DatabaseManager): ToolHandler;
43
+ //# sourceMappingURL=code-function-disassemble.d.ts.map
@@ -0,0 +1,185 @@
1
+ /**
2
+ * code.function.disassemble MCP Tool
3
+ *
4
+ * Returns assembly code for a function
5
+ */
6
+ import { z } from 'zod';
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+ import { DecompilerWorker, getGhidraDiagnostics, normalizeGhidraError } from '../decompiler-worker.js';
10
+ import { runEntrypointFallbackDisasm } from './entrypoint-fallback-disasm.js';
11
+ import { logger } from '../logger.js';
12
+ /**
13
+ * Input schema for code.function.disassemble tool
14
+ */
15
+ export const codeFunctionDisassembleInputSchema = z.object({
16
+ sample_id: z.string().describe('Sample identifier (sha256:<hex>)'),
17
+ address: z.string().optional().describe('Function address (hex string)'),
18
+ symbol: z.string().optional().describe('Function symbol name')
19
+ }).refine(data => data.address || data.symbol, {
20
+ message: 'Either address or symbol must be provided'
21
+ });
22
+ /**
23
+ * Tool definition for code.function.disassemble
24
+ */
25
+ export const codeFunctionDisassembleToolDefinition = {
26
+ name: 'code.function.disassemble',
27
+ description: 'Get assembly code for a function. Requires prior Ghidra analysis. Provide either address or symbol name.',
28
+ inputSchema: codeFunctionDisassembleInputSchema
29
+ };
30
+ function normalizeAddress(address) {
31
+ if (!address) {
32
+ return undefined;
33
+ }
34
+ const trimmed = address.trim();
35
+ if (!trimmed) {
36
+ return undefined;
37
+ }
38
+ if (/^0x[0-9a-fA-F]+$/.test(trimmed)) {
39
+ return `0x${trimmed.slice(2).toLowerCase()}`;
40
+ }
41
+ if (/^[0-9a-fA-F]+$/.test(trimmed)) {
42
+ return `0x${trimmed.toLowerCase()}`;
43
+ }
44
+ return undefined;
45
+ }
46
+ async function resolveSamplePath(originalDir) {
47
+ const entries = await fs.readdir(originalDir, { withFileTypes: true });
48
+ const files = entries
49
+ .filter((entry) => entry.isFile())
50
+ .map((entry) => entry.name)
51
+ .sort((a, b) => a.localeCompare(b));
52
+ if (files.length === 0) {
53
+ throw new Error(`Sample file not found in workspace: ${originalDir}`);
54
+ }
55
+ return path.join(originalDir, files[0]);
56
+ }
57
+ /**
58
+ * Create handler for code.function.disassemble tool
59
+ */
60
+ export function createCodeFunctionDisassembleHandler(workspaceManager, database) {
61
+ return async (args) => {
62
+ try {
63
+ const input = codeFunctionDisassembleInputSchema.parse(args);
64
+ const addressOrSymbol = input.address || input.symbol;
65
+ logger.info({
66
+ sample_id: input.sample_id,
67
+ address_or_symbol: addressOrSymbol
68
+ }, 'code.function.disassemble tool called');
69
+ // Check if sample exists
70
+ const sample = database.findSample(input.sample_id);
71
+ if (!sample) {
72
+ return {
73
+ content: [{
74
+ type: 'text',
75
+ text: JSON.stringify({
76
+ ok: false,
77
+ errors: [`Sample not found: ${input.sample_id}`]
78
+ }, null, 2)
79
+ }],
80
+ isError: true
81
+ };
82
+ }
83
+ // For now, return a placeholder indicating this feature uses CFG
84
+ // In a full implementation, this would extract assembly from Ghidra
85
+ const decompilerWorker = new DecompilerWorker(database, workspaceManager);
86
+ let functionName = '';
87
+ let functionAddress = '';
88
+ let assemblyText = '';
89
+ let warnings;
90
+ let requestedAddress = normalizeAddress(input.address);
91
+ if (!requestedAddress && input.symbol) {
92
+ const matched = database
93
+ .findFunctions(input.sample_id)
94
+ .find((item) => item.name?.toLowerCase() === input.symbol?.toLowerCase());
95
+ if (matched?.address) {
96
+ requestedAddress = normalizeAddress(matched.address) || matched.address;
97
+ }
98
+ }
99
+ let fallbackMetadata;
100
+ try {
101
+ // Primary path: use CFG generated by Ghidra
102
+ const cfg = await decompilerWorker.getFunctionCFG(input.sample_id, addressOrSymbol);
103
+ const assembly = [];
104
+ for (const node of cfg.nodes) {
105
+ assembly.push(`; Block ${node.id} (${node.type})`);
106
+ assembly.push(...node.instructions);
107
+ assembly.push('');
108
+ }
109
+ functionName = cfg.function;
110
+ functionAddress = cfg.address;
111
+ assemblyText = assembly.join('\n');
112
+ }
113
+ catch (primaryError) {
114
+ // Secondary path: fallback disassembly around PE entrypoint
115
+ const workspace = await workspaceManager.getWorkspace(input.sample_id);
116
+ const samplePath = await resolveSamplePath(workspace.original);
117
+ const fallback = await runEntrypointFallbackDisasm(samplePath, {
118
+ max_instructions: 140,
119
+ max_bytes: 1536,
120
+ target_address: requestedAddress,
121
+ target_symbol: input.symbol,
122
+ });
123
+ functionName = fallback.result.function;
124
+ functionAddress = fallback.result.address;
125
+ assemblyText = fallback.result.assembly;
126
+ fallbackMetadata = {
127
+ backend: fallback.result.backend,
128
+ parser: fallback.result.parser,
129
+ entry_section: fallback.result.entry_section,
130
+ resolved_from: fallback.result.resolved_from,
131
+ requested_address: fallback.result.requested_address,
132
+ };
133
+ warnings = [
134
+ `Ghidra disassembly unavailable, fallback used: ${primaryError instanceof Error ? primaryError.message : String(primaryError)}`,
135
+ ...(fallback.warnings || []),
136
+ ];
137
+ }
138
+ logger.info({
139
+ sample_id: input.sample_id,
140
+ function: functionName,
141
+ instruction_count: assemblyText.split('\n').filter((line) => line.length > 0).length,
142
+ fallback: Boolean(fallbackMetadata),
143
+ }, 'Function disassembled successfully');
144
+ return {
145
+ content: [{
146
+ type: 'text',
147
+ text: JSON.stringify({
148
+ ok: true,
149
+ data: {
150
+ function: functionName,
151
+ address: functionAddress,
152
+ assembly: assemblyText,
153
+ fallback: Boolean(fallbackMetadata),
154
+ fallback_metadata: fallbackMetadata,
155
+ },
156
+ warnings,
157
+ }, null, 2)
158
+ }]
159
+ };
160
+ }
161
+ catch (error) {
162
+ const errorMessage = error instanceof Error ? error.message : String(error);
163
+ const diagnostics = getGhidraDiagnostics(error);
164
+ const normalizedError = normalizeGhidraError(error, 'code.function.disassemble');
165
+ logger.error({
166
+ error: errorMessage,
167
+ ghidra_diagnostics: diagnostics,
168
+ normalized_error: normalizedError,
169
+ }, 'code.function.disassemble tool failed');
170
+ return {
171
+ content: [{
172
+ type: 'text',
173
+ text: JSON.stringify({
174
+ ok: false,
175
+ errors: [errorMessage],
176
+ diagnostics,
177
+ normalized_error: normalizedError,
178
+ }, null, 2)
179
+ }],
180
+ isError: true
181
+ };
182
+ }
183
+ };
184
+ }
185
+ //# sourceMappingURL=code-function-disassemble.js.map