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.
- package/CODEX_INSTALLATION.md +69 -0
- package/COPILOT_INSTALLATION.md +77 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/bin/windows-exe-decompiler-mcp-server.js +3 -0
- package/dist/analysis-provenance.d.ts +184 -0
- package/dist/analysis-provenance.js +74 -0
- package/dist/analysis-task-runner.d.ts +31 -0
- package/dist/analysis-task-runner.js +160 -0
- package/dist/artifact-inventory.d.ts +23 -0
- package/dist/artifact-inventory.js +175 -0
- package/dist/cache-manager.d.ts +128 -0
- package/dist/cache-manager.js +454 -0
- package/dist/confidence-semantics.d.ts +66 -0
- package/dist/confidence-semantics.js +122 -0
- package/dist/config.d.ts +335 -0
- package/dist/config.js +193 -0
- package/dist/database.d.ts +227 -0
- package/dist/database.js +601 -0
- package/dist/decompiler-worker.d.ts +441 -0
- package/dist/decompiler-worker.js +1962 -0
- package/dist/dynamic-trace.d.ts +95 -0
- package/dist/dynamic-trace.js +629 -0
- package/dist/env-validator.d.ts +15 -0
- package/dist/env-validator.js +249 -0
- package/dist/error-handler.d.ts +28 -0
- package/dist/error-handler.example.d.ts +22 -0
- package/dist/error-handler.example.js +141 -0
- package/dist/error-handler.js +139 -0
- package/dist/ghidra-analysis-status.d.ts +49 -0
- package/dist/ghidra-analysis-status.js +178 -0
- package/dist/ghidra-config.d.ts +134 -0
- package/dist/ghidra-config.js +464 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +200 -0
- package/dist/job-queue.d.ts +169 -0
- package/dist/job-queue.js +407 -0
- package/dist/logger.d.ts +106 -0
- package/dist/logger.js +176 -0
- package/dist/policy-guard.d.ts +115 -0
- package/dist/policy-guard.js +243 -0
- package/dist/process-output.d.ts +15 -0
- package/dist/process-output.js +90 -0
- package/dist/prompts/function-explanation-review.d.ts +5 -0
- package/dist/prompts/function-explanation-review.js +64 -0
- package/dist/prompts/semantic-name-review.d.ts +5 -0
- package/dist/prompts/semantic-name-review.js +63 -0
- package/dist/runtime-correlation.d.ts +34 -0
- package/dist/runtime-correlation.js +279 -0
- package/dist/runtime-paths.d.ts +3 -0
- package/dist/runtime-paths.js +11 -0
- package/dist/selection-diff.d.ts +667 -0
- package/dist/selection-diff.js +53 -0
- package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
- package/dist/semantic-name-suggestion-artifacts.js +314 -0
- package/dist/server.d.ts +129 -0
- package/dist/server.js +578 -0
- package/dist/tools/artifact-read.d.ts +235 -0
- package/dist/tools/artifact-read.js +317 -0
- package/dist/tools/artifacts-diff.d.ts +728 -0
- package/dist/tools/artifacts-diff.js +304 -0
- package/dist/tools/artifacts-list.d.ts +515 -0
- package/dist/tools/artifacts-list.js +389 -0
- package/dist/tools/attack-map.d.ts +290 -0
- package/dist/tools/attack-map.js +519 -0
- package/dist/tools/cache-observability.d.ts +4 -0
- package/dist/tools/cache-observability.js +36 -0
- package/dist/tools/code-function-cfg.d.ts +50 -0
- package/dist/tools/code-function-cfg.js +102 -0
- package/dist/tools/code-function-decompile.d.ts +55 -0
- package/dist/tools/code-function-decompile.js +103 -0
- package/dist/tools/code-function-disassemble.d.ts +43 -0
- package/dist/tools/code-function-disassemble.js +185 -0
- package/dist/tools/code-function-explain-apply.d.ts +255 -0
- package/dist/tools/code-function-explain-apply.js +225 -0
- package/dist/tools/code-function-explain-prepare.d.ts +535 -0
- package/dist/tools/code-function-explain-prepare.js +276 -0
- package/dist/tools/code-function-explain-review.d.ts +397 -0
- package/dist/tools/code-function-explain-review.js +589 -0
- package/dist/tools/code-function-rename-apply.d.ts +248 -0
- package/dist/tools/code-function-rename-apply.js +220 -0
- package/dist/tools/code-function-rename-prepare.d.ts +506 -0
- package/dist/tools/code-function-rename-prepare.js +279 -0
- package/dist/tools/code-function-rename-review.d.ts +574 -0
- package/dist/tools/code-function-rename-review.js +761 -0
- package/dist/tools/code-functions-list.d.ts +37 -0
- package/dist/tools/code-functions-list.js +91 -0
- package/dist/tools/code-functions-rank.d.ts +34 -0
- package/dist/tools/code-functions-rank.js +90 -0
- package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
- package/dist/tools/code-functions-reconstruct.js +2807 -0
- package/dist/tools/code-functions-search.d.ts +39 -0
- package/dist/tools/code-functions-search.js +90 -0
- package/dist/tools/code-reconstruct-export.d.ts +1212 -0
- package/dist/tools/code-reconstruct-export.js +4002 -0
- package/dist/tools/code-reconstruct-plan.d.ts +274 -0
- package/dist/tools/code-reconstruct-plan.js +342 -0
- package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
- package/dist/tools/dotnet-metadata-extract.js +355 -0
- package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
- package/dist/tools/dotnet-reconstruct-export.js +1151 -0
- package/dist/tools/dotnet-types-list.d.ts +325 -0
- package/dist/tools/dotnet-types-list.js +201 -0
- package/dist/tools/dynamic-dependencies.d.ts +115 -0
- package/dist/tools/dynamic-dependencies.js +213 -0
- package/dist/tools/dynamic-memory-import.d.ts +10 -0
- package/dist/tools/dynamic-memory-import.js +567 -0
- package/dist/tools/dynamic-trace-import.d.ts +10 -0
- package/dist/tools/dynamic-trace-import.js +235 -0
- package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
- package/dist/tools/entrypoint-fallback-disasm.js +89 -0
- package/dist/tools/ghidra-analyze.d.ts +88 -0
- package/dist/tools/ghidra-analyze.js +208 -0
- package/dist/tools/ghidra-health.d.ts +37 -0
- package/dist/tools/ghidra-health.js +212 -0
- package/dist/tools/ioc-export.d.ts +209 -0
- package/dist/tools/ioc-export.js +542 -0
- package/dist/tools/packer-detect.d.ts +165 -0
- package/dist/tools/packer-detect.js +284 -0
- package/dist/tools/pe-exports-extract.d.ts +175 -0
- package/dist/tools/pe-exports-extract.js +253 -0
- package/dist/tools/pe-fingerprint.d.ts +234 -0
- package/dist/tools/pe-fingerprint.js +269 -0
- package/dist/tools/pe-imports-extract.d.ts +105 -0
- package/dist/tools/pe-imports-extract.js +245 -0
- package/dist/tools/report-generate.d.ts +157 -0
- package/dist/tools/report-generate.js +457 -0
- package/dist/tools/report-summarize.d.ts +2131 -0
- package/dist/tools/report-summarize.js +596 -0
- package/dist/tools/runtime-detect.d.ts +135 -0
- package/dist/tools/runtime-detect.js +247 -0
- package/dist/tools/sample-ingest.d.ts +94 -0
- package/dist/tools/sample-ingest.js +327 -0
- package/dist/tools/sample-profile-get.d.ts +183 -0
- package/dist/tools/sample-profile-get.js +121 -0
- package/dist/tools/sandbox-execute.d.ts +441 -0
- package/dist/tools/sandbox-execute.js +392 -0
- package/dist/tools/strings-extract.d.ts +375 -0
- package/dist/tools/strings-extract.js +314 -0
- package/dist/tools/strings-floss-decode.d.ts +143 -0
- package/dist/tools/strings-floss-decode.js +259 -0
- package/dist/tools/system-health.d.ts +434 -0
- package/dist/tools/system-health.js +446 -0
- package/dist/tools/task-cancel.d.ts +21 -0
- package/dist/tools/task-cancel.js +70 -0
- package/dist/tools/task-status.d.ts +27 -0
- package/dist/tools/task-status.js +106 -0
- package/dist/tools/task-sweep.d.ts +22 -0
- package/dist/tools/task-sweep.js +77 -0
- package/dist/tools/tool-help.d.ts +340 -0
- package/dist/tools/tool-help.js +261 -0
- package/dist/tools/yara-scan.d.ts +554 -0
- package/dist/tools/yara-scan.js +313 -0
- package/dist/types.d.ts +266 -0
- package/dist/types.js +41 -0
- package/dist/worker-pool.d.ts +204 -0
- package/dist/worker-pool.js +650 -0
- package/dist/workflows/deep-static.d.ts +104 -0
- package/dist/workflows/deep-static.js +276 -0
- package/dist/workflows/function-explanation-review.d.ts +655 -0
- package/dist/workflows/function-explanation-review.js +440 -0
- package/dist/workflows/reconstruct.d.ts +2053 -0
- package/dist/workflows/reconstruct.js +666 -0
- package/dist/workflows/semantic-name-review.d.ts +2418 -0
- package/dist/workflows/semantic-name-review.js +521 -0
- package/dist/workflows/triage.d.ts +659 -0
- package/dist/workflows/triage.js +1374 -0
- package/dist/workspace-manager.d.ts +150 -0
- package/dist/workspace-manager.js +411 -0
- package/ghidra_scripts/DecompileFunction.java +487 -0
- package/ghidra_scripts/DecompileFunction.py +150 -0
- package/ghidra_scripts/ExtractCFG.java +256 -0
- package/ghidra_scripts/ExtractCFG.py +233 -0
- package/ghidra_scripts/ExtractFunctions.java +442 -0
- package/ghidra_scripts/ExtractFunctions.py +101 -0
- package/ghidra_scripts/README.md +125 -0
- package/ghidra_scripts/SearchFunctionReferences.java +380 -0
- package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
- package/helpers/DotNetMetadataProbe/Program.cs +566 -0
- package/install-to-codex.ps1 +178 -0
- package/install-to-copilot.ps1 +303 -0
- package/package.json +101 -0
- package/requirements.txt +9 -0
- package/workers/requirements-dynamic.txt +11 -0
- package/workers/requirements.txt +8 -0
- package/workers/speakeasy_compat.py +175 -0
- package/workers/static_worker.py +5183 -0
- package/workers/yara_rules/default.yar +33 -0
- package/workers/yara_rules/malware_families.yar +93 -0
- 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
|