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
@@ -20,10 +20,10 @@ import { ghidraConfig } from '../ghidra-config.js';
20
20
  import { loadDynamicTraceEvidence } from '../dynamic-trace.js';
21
21
  import { getPackageRoot } from '../runtime-paths.js';
22
22
  import { correlateFunctionWithRuntimeEvidence, modulesSuggestedByRuntimeStages, } from '../runtime-correlation.js';
23
- import { findSemanticFunctionExplanation, loadSemanticFunctionExplanationIndex, loadSemanticNameSuggestionIndex, SEMANTIC_FUNCTION_EXPLANATIONS_ARTIFACT_TYPE, SEMANTIC_NAME_SUGGESTIONS_ARTIFACT_TYPE, } from '../semantic-name-suggestion-artifacts.js';
23
+ import { findSemanticFunctionExplanation, findSemanticModuleReview, loadSemanticFunctionExplanationIndex, loadSemanticModuleReviewIndex, loadSemanticNameSuggestionIndex, SEMANTIC_FUNCTION_EXPLANATIONS_ARTIFACT_TYPE, SEMANTIC_NAME_SUGGESTIONS_ARTIFACT_TYPE, } from '../semantic-name-suggestion-artifacts.js';
24
24
  import { AnalysisProvenanceSchema, buildRuntimeArtifactProvenance, buildSemanticArtifactProvenance, } from '../analysis-provenance.js';
25
25
  const TOOL_NAME = 'code.reconstruct.export';
26
- const TOOL_VERSION = '0.2.13';
26
+ const TOOL_VERSION = '0.2.15';
27
27
  const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
28
28
  export const CodeReconstructExportInputSchema = z.object({
29
29
  sample_id: z.string().describe('Sample ID (format: sha256:<hex>)'),
@@ -106,6 +106,22 @@ export const CodeReconstructExportInputSchema = z.object({
106
106
  .string()
107
107
  .optional()
108
108
  .describe('Optional semantic review session selector used when semantic_scope=session or to narrow all/latest results'),
109
+ role_target: z
110
+ .string()
111
+ .min(1)
112
+ .max(64)
113
+ .optional()
114
+ .describe('Optional high-level binary role hint from workflow preflight, such as native_rust_executable, dll_library, or com_server'),
115
+ role_focus_areas: z
116
+ .array(z.string().min(1).max(96))
117
+ .max(16)
118
+ .default([])
119
+ .describe('Optional role-aware focus areas that bias module grouping and rewrite prioritization'),
120
+ role_priority_order: z
121
+ .array(z.string().min(1).max(96))
122
+ .max(24)
123
+ .default([])
124
+ .describe('Optional priority-order hints from role-aware planning that influence module ordering and preservation'),
109
125
  reuse_cached: z
110
126
  .boolean()
111
127
  .default(true)
@@ -139,6 +155,11 @@ const ModuleSchema = z.object({
139
155
  name: z.string(),
140
156
  confidence: z.number().min(0).max(1),
141
157
  function_count: z.number().int().nonnegative(),
158
+ role_hint: z.string().nullable().optional(),
159
+ focus_matches: z.array(z.string()).optional(),
160
+ refined_name: z.string().nullable().optional(),
161
+ review_summary: z.string().nullable().optional(),
162
+ review_confidence: z.number().min(0).max(1).nullable().optional(),
142
163
  import_hints: z.array(z.string()),
143
164
  string_hints: z.array(z.string()),
144
165
  runtime_apis: z.array(z.string()),
@@ -216,7 +237,11 @@ export const CodeReconstructExportOutputSchema = z.object({
216
237
  stage_count: z.number().int().nonnegative(),
217
238
  observed_apis: z.array(z.string()),
218
239
  region_types: z.array(z.string()).optional(),
240
+ protections: z.array(z.string()).optional(),
241
+ address_ranges: z.array(z.string()).optional(),
242
+ region_owners: z.array(z.string()).optional(),
219
243
  observed_modules: z.array(z.string()).optional(),
244
+ segment_names: z.array(z.string()).optional(),
220
245
  observed_strings: z.array(z.string()).optional(),
221
246
  stages: z.array(z.string()),
222
247
  summary: z.string(),
@@ -248,6 +273,54 @@ export const codeReconstructExportToolDefinition = {
248
273
  inputSchema: CodeReconstructExportInputSchema,
249
274
  outputSchema: CodeReconstructExportOutputSchema,
250
275
  };
276
+ function collectRoleFocusMatchesForModule(moduleName, roleOptions) {
277
+ if (!roleOptions) {
278
+ return [];
279
+ }
280
+ const normalized = sanitizeModuleName(moduleName);
281
+ const matches = new Set();
282
+ if (roleOptions.targetRole &&
283
+ mapRoleSignalToModules(roleOptions.targetRole).some((item) => sanitizeModuleName(item) === normalized)) {
284
+ matches.add(`target:${roleOptions.targetRole}`);
285
+ }
286
+ for (const focus of roleOptions.focusAreas) {
287
+ if (mapRoleSignalToModules(focus).some((item) => sanitizeModuleName(item) === normalized)) {
288
+ matches.add(`focus:${focus}`);
289
+ }
290
+ }
291
+ for (const priority of roleOptions.priorityOrder) {
292
+ if (mapRoleSignalToModules(priority).some((item) => sanitizeModuleName(item) === normalized)) {
293
+ matches.add(`priority:${priority}`);
294
+ }
295
+ }
296
+ return Array.from(matches);
297
+ }
298
+ function inferRoleHintForModule(moduleName, roleOptions) {
299
+ const normalized = sanitizeModuleName(moduleName);
300
+ const focusMatches = collectRoleFocusMatchesForModule(normalized, roleOptions);
301
+ if (focusMatches.length === 0) {
302
+ return null;
303
+ }
304
+ if (normalized === 'dll_lifecycle') {
305
+ return 'Role-aware focus on DLL entry, attach/detach, and lifecycle side effects.';
306
+ }
307
+ if (normalized === 'com_activation') {
308
+ return 'Role-aware focus on COM activation, registration, and class factory flow.';
309
+ }
310
+ if (normalized === 'export_dispatch') {
311
+ return 'Role-aware focus on exported command dispatch and externally reachable entrypoints.';
312
+ }
313
+ if (normalized === 'callback_surface') {
314
+ return 'Role-aware focus on callback, plugin, and host interaction surfaces.';
315
+ }
316
+ if (normalized === 'service_ops') {
317
+ return 'Role-aware focus on service control, hook, and hosted callback paths.';
318
+ }
319
+ if (normalized === 'process_ops') {
320
+ return 'Role-aware focus on runtime wrappers, process manipulation, and execution-transfer paths.';
321
+ }
322
+ return `Role-aware focus derived from ${focusMatches.join(', ')}.`;
323
+ }
251
324
  const BEHAVIOR_TO_MODULE = {
252
325
  process_injection: 'process_ops',
253
326
  process_spawn: 'process_ops',
@@ -257,6 +330,10 @@ const BEHAVIOR_TO_MODULE = {
257
330
  crypto: 'crypto_ops',
258
331
  anti_debug: 'anti_analysis',
259
332
  service_control: 'service_ops',
333
+ dll_lifecycle: 'dll_lifecycle',
334
+ export_dispatch: 'export_dispatch',
335
+ com_activation: 'com_activation',
336
+ callback_surface: 'callback_surface',
260
337
  };
261
338
  const IMPORT_TO_MODULE_HINT = [
262
339
  { module: 'network_ops', matcher: /^(ws2_32|wininet|winhttp|dnsapi)\.dll$/i },
@@ -290,6 +367,22 @@ const API_TO_MODULE_HINT = [
290
367
  module: 'service_ops',
291
368
  matcher: /\b(OpenSCManager|CreateService|StartService|ControlService|DeleteService)\b/i,
292
369
  },
370
+ {
371
+ module: 'dll_lifecycle',
372
+ matcher: /\b(DllMain|DisableThreadLibraryCalls|LdrRegisterDllNotification|DLL_(PROCESS|THREAD)_(ATTACH|DETACH)|ProcessAttach|ThreadAttach|ProcessDetach|ThreadDetach)\b/i,
373
+ },
374
+ {
375
+ module: 'com_activation',
376
+ matcher: /\b(DllGetClassObject|DllCanUnloadNow|CoCreateInstance|CLSIDFromProgID|ProgIDFromCLSID|IClassFactory|IUnknown|IDispatch)\b/i,
377
+ },
378
+ {
379
+ module: 'export_dispatch',
380
+ matcher: /\b(InvokeCommand|Dispatch(Command|Export)?|HandleCommand|ExecuteCommand|RunCommand|DispatchTable|ForwardedExport)\b/i,
381
+ },
382
+ {
383
+ module: 'callback_surface',
384
+ matcher: /\b(InitializePlugin|RegisterCallback|SetCallback|Notify|OnEvent|EventSink|HookProc|Observer|Callback)\b/i,
385
+ },
293
386
  {
294
387
  module: 'anti_analysis',
295
388
  matcher: /\b(IsDebuggerPresent|CheckRemoteDebuggerPresent|NtQuerySystemInformation|NtQueryInformationProcess|OutputDebugString)\b/i,
@@ -342,6 +435,96 @@ function sanitizeModuleName(name) {
342
435
  const cleaned = name.toLowerCase().replace(/[^a-z0-9_]+/g, '_').replace(/^_+|_+$/g, '');
343
436
  return cleaned.length > 0 ? cleaned : 'core';
344
437
  }
438
+ function dedupeLower(values) {
439
+ return Array.from(new Set(values.map((item) => item.trim()).filter((item) => item.length > 0)));
440
+ }
441
+ function addRoleModules(target, values) {
442
+ for (const value of values) {
443
+ target.add(sanitizeModuleName(value));
444
+ }
445
+ }
446
+ function mapRoleSignalToModules(signal) {
447
+ const lowered = signal.toLowerCase();
448
+ if (/class_factory|registration|com_activation|com_server|dllgetclassobject|dllcanunloadnow|inprocserver32|localserver32/.test(lowered)) {
449
+ return ['com_activation', 'export_dispatch', 'dll_lifecycle'];
450
+ }
451
+ if (/dllmain|attach_detach|dll_entry|dllmain_and_export_surface|lifecycle/.test(lowered)) {
452
+ return ['dll_lifecycle'];
453
+ }
454
+ if (/export_dispatch|dispatch_model|review_exported_command_dispatch_surface/.test(lowered)) {
455
+ return ['export_dispatch'];
456
+ }
457
+ if (/host_callback|plugin|extension_contract|callback/.test(lowered)) {
458
+ return ['callback_surface'];
459
+ }
460
+ if (/service/.test(lowered)) {
461
+ return ['service_ops', 'callback_surface'];
462
+ }
463
+ if (/runtime_wrappers|process_manipulation/.test(lowered)) {
464
+ return ['process_ops'];
465
+ }
466
+ if (/network/.test(lowered)) {
467
+ return ['network_ops'];
468
+ }
469
+ return [];
470
+ }
471
+ function buildRoleAwareModuleOptions(input) {
472
+ const preferredModules = new Set();
473
+ const stickyModules = new Set();
474
+ const moduleOrder = new Map();
475
+ const targetRole = input.role_target?.trim() || null;
476
+ const focusAreas = dedupeLower(input.role_focus_areas || []);
477
+ const priorityOrder = dedupeLower(input.role_priority_order || []);
478
+ if (targetRole) {
479
+ addRoleModules(preferredModules, mapRoleSignalToModules(targetRole));
480
+ }
481
+ for (const focus of focusAreas) {
482
+ addRoleModules(preferredModules, mapRoleSignalToModules(focus));
483
+ if (/class_factory|registration|com_activation|dllmain|lifecycle|host_callback|plugin|callback/.test(focus)) {
484
+ addRoleModules(stickyModules, mapRoleSignalToModules(focus));
485
+ }
486
+ }
487
+ for (const priority of priorityOrder) {
488
+ addRoleModules(preferredModules, mapRoleSignalToModules(priority));
489
+ if (/trace_com_activation|review_dllmain|identify_host_callbacks|extension_contract|exported_command_dispatch_surface/.test(priority)) {
490
+ addRoleModules(stickyModules, mapRoleSignalToModules(priority));
491
+ }
492
+ }
493
+ if (targetRole === 'com_server') {
494
+ addRoleModules(stickyModules, ['com_activation', 'dll_lifecycle', 'export_dispatch']);
495
+ }
496
+ else if (targetRole === 'export_dispatch_dll') {
497
+ addRoleModules(stickyModules, ['export_dispatch', 'dll_lifecycle']);
498
+ }
499
+ else if (targetRole === 'hosted_plugin_or_service_dll') {
500
+ addRoleModules(stickyModules, ['callback_surface', 'dll_lifecycle', 'service_ops']);
501
+ }
502
+ else if (targetRole === 'dll_library') {
503
+ addRoleModules(stickyModules, ['dll_lifecycle']);
504
+ }
505
+ let order = 0;
506
+ for (const priority of priorityOrder) {
507
+ for (const moduleName of mapRoleSignalToModules(priority)) {
508
+ const normalized = sanitizeModuleName(moduleName);
509
+ if (!moduleOrder.has(normalized)) {
510
+ moduleOrder.set(normalized, order++);
511
+ }
512
+ }
513
+ }
514
+ for (const moduleName of preferredModules) {
515
+ if (!moduleOrder.has(moduleName)) {
516
+ moduleOrder.set(moduleName, order++);
517
+ }
518
+ }
519
+ return {
520
+ targetRole,
521
+ focusAreas,
522
+ priorityOrder,
523
+ preferredModules,
524
+ stickyModules,
525
+ moduleOrder,
526
+ };
527
+ }
345
528
  function sanitizeSymbolForHeader(symbol) {
346
529
  const cleaned = symbol.replace(/[^a-zA-Z0-9_]/g, '_');
347
530
  if (cleaned.length === 0) {
@@ -374,6 +557,37 @@ async function attachFunctionExplanations(workspaceManager, database, sampleId,
374
557
  }
375
558
  return explanationIndex;
376
559
  }
560
+ async function attachModuleReviews(workspaceManager, database, sampleId, modules, options) {
561
+ const reviewIndex = await loadSemanticModuleReviewIndex(workspaceManager, database, sampleId, {
562
+ scope: options?.scope,
563
+ sessionTag: options?.sessionTag,
564
+ });
565
+ for (const module of modules) {
566
+ const review = findSemanticModuleReview(reviewIndex, module.name);
567
+ if (!review) {
568
+ continue;
569
+ }
570
+ module.reviewResolution = {
571
+ refined_name: review.refined_name,
572
+ summary: review.summary,
573
+ role_hint: review.role_hint,
574
+ confidence: review.confidence,
575
+ assumptions: review.assumptions,
576
+ evidence_used: review.evidence_used,
577
+ rewrite_guidance: review.rewrite_guidance,
578
+ focus_areas: review.focus_areas,
579
+ priority_functions: review.priority_functions,
580
+ source: 'llm',
581
+ };
582
+ if (review.role_hint && (!module.roleHint || module.roleHint.trim().length === 0)) {
583
+ module.roleHint = review.role_hint || module.roleHint;
584
+ }
585
+ for (const focus of review.focus_areas || []) {
586
+ module.focusMatches.add(`review:${focus}`);
587
+ }
588
+ }
589
+ return reviewIndex;
590
+ }
377
591
  function addHint(hints, module, value) {
378
592
  const normalized = normalizeReadableHint(value);
379
593
  if (!isReadableTextCandidate(normalized)) {
@@ -385,7 +599,7 @@ function addHint(hints, module, value) {
385
599
  hints.set(module, current);
386
600
  }
387
601
  }
388
- function inferModulesFromText(text, categories = []) {
602
+ function inferModulesFromText(text, categories = [], roleOptions) {
389
603
  const modules = new Set();
390
604
  const lowered = text.toLowerCase();
391
605
  if (categories.includes('network') || categories.includes('url')) {
@@ -414,9 +628,35 @@ function inferModulesFromText(text, categories = []) {
414
628
  if (/\b(pack(er)? detection|protection|entropy|section|signature)\b/i.test(lowered)) {
415
629
  modules.add('packer_analysis');
416
630
  }
631
+ if (/\b(dllmain|disablethreadlibrarycalls|dll_process_attach|dll_thread_attach|processattach|threadattach|processdetach|threaddetach|attach\/detach)\b/i.test(lowered)) {
632
+ modules.add('dll_lifecycle');
633
+ }
634
+ if (/\b(dllgetclassobject|dllcanunloadnow|iclassfactory|cocreateinstance|clsid|progid|inprocserver32|localserver32|class factory)\b/i.test(lowered)) {
635
+ modules.add('com_activation');
636
+ }
637
+ if (/\b(export dispatch|dispatch table|invokecommand|handlecommand|executecommand|runcommand|forwarded export|ordinal export|export surface)\b/i.test(lowered)) {
638
+ modules.add('export_dispatch');
639
+ }
640
+ if (/\b(callback|event sink|observer|notify|plugin|addin|extension point|initializeplugin|registercallback|setcallback|hook proc)\b/i.test(lowered)) {
641
+ modules.add('callback_surface');
642
+ }
643
+ if (roleOptions) {
644
+ if (roleOptions.preferredModules.has('dll_lifecycle') && /\b(dll|attach|detach|module handle|dllmain)\b/i.test(lowered)) {
645
+ modules.add('dll_lifecycle');
646
+ }
647
+ if (roleOptions.preferredModules.has('com_activation') && /\b(class factory|registerserver|clsid|progid|activation)\b/i.test(lowered)) {
648
+ modules.add('com_activation');
649
+ }
650
+ if (roleOptions.preferredModules.has('export_dispatch') && /\b(dispatch|export|command|invoke)\b/i.test(lowered)) {
651
+ modules.add('export_dispatch');
652
+ }
653
+ if (roleOptions.preferredModules.has('callback_surface') && /\b(callback|plugin|notify|hook|host)\b/i.test(lowered)) {
654
+ modules.add('callback_surface');
655
+ }
656
+ }
417
657
  return Array.from(modules);
418
658
  }
419
- function detectModuleByNameOrReasons(func) {
659
+ function detectModuleByNameOrReasons(func, roleOptions) {
420
660
  const xrefApis = (func.xref_signals || []).map((item) => item.api).join(' ');
421
661
  const callContext = [
422
662
  ...(func.call_context?.callers || []),
@@ -431,6 +671,18 @@ function detectModuleByNameOrReasons(func) {
431
671
  callContext,
432
672
  ].join(' ');
433
673
  const lowered = corpus.toLowerCase();
674
+ if (/dllmain|disablethreadlibrarycalls|dll_process_attach|dll_thread_attach|processattach|threadattach|processdetach|threaddetach/.test(lowered)) {
675
+ return 'dll_lifecycle';
676
+ }
677
+ if (/dllgetclassobject|dllcanunloadnow|iclassfactory|cocreateinstance|clsid|progid|inprocserver32|localserver32/.test(lowered)) {
678
+ return 'com_activation';
679
+ }
680
+ if (/initializeplugin|registercallback|setcallback|event sink|notify|hook proc|observer|plugin host/.test(lowered)) {
681
+ return 'callback_surface';
682
+ }
683
+ if (/export dispatch|dispatch table|invokecommand|handlecommand|executecommand|runcommand|forwarded export/.test(lowered)) {
684
+ return 'export_dispatch';
685
+ }
434
686
  if (/socket|http|internet|dns|connect|send|recv/.test(lowered)) {
435
687
  return 'network_ops';
436
688
  }
@@ -455,6 +707,18 @@ function detectModuleByNameOrReasons(func) {
455
707
  if (/packer|entropy|section|signature|goblin|iced-x86|recon|analysis/.test(lowered)) {
456
708
  return 'packer_analysis';
457
709
  }
710
+ if (roleOptions?.preferredModules.has('dll_lifecycle') && /\bdll\b/.test(lowered)) {
711
+ return 'dll_lifecycle';
712
+ }
713
+ if (roleOptions?.preferredModules.has('com_activation') && /\b(class factory|registration|activation)\b/.test(lowered)) {
714
+ return 'com_activation';
715
+ }
716
+ if (roleOptions?.preferredModules.has('export_dispatch') && /\b(dispatch|export|command)\b/.test(lowered)) {
717
+ return 'export_dispatch';
718
+ }
719
+ if (roleOptions?.preferredModules.has('callback_surface') && /\b(callback|plugin|host)\b/.test(lowered)) {
720
+ return 'callback_surface';
721
+ }
458
722
  return 'core';
459
723
  }
460
724
  function computeImportModuleHints(importsData) {
@@ -658,6 +922,26 @@ function enrichFunctionsWithRuntimeContext(functions, dynamicEvidence) {
658
922
  ...(func.runtime_context.matched_memory_regions || []),
659
923
  ...(runtimeContext.matched_memory_regions || []),
660
924
  ]).slice(0, 6),
925
+ matched_protections: dedupe([
926
+ ...(func.runtime_context.matched_protections || []),
927
+ ...(runtimeContext.matched_protections || []),
928
+ ]).slice(0, 6),
929
+ matched_address_ranges: dedupe([
930
+ ...(func.runtime_context.matched_address_ranges || []),
931
+ ...(runtimeContext.matched_address_ranges || []),
932
+ ]).slice(0, 6),
933
+ matched_region_owners: dedupe([
934
+ ...(func.runtime_context.matched_region_owners || []),
935
+ ...(runtimeContext.matched_region_owners || []),
936
+ ]).slice(0, 6),
937
+ matched_observed_modules: dedupe([
938
+ ...(func.runtime_context.matched_observed_modules || []),
939
+ ...(runtimeContext.matched_observed_modules || []),
940
+ ]).slice(0, 6),
941
+ matched_segment_names: dedupe([
942
+ ...(func.runtime_context.matched_segment_names || []),
943
+ ...(runtimeContext.matched_segment_names || []),
944
+ ]).slice(0, 6),
661
945
  suggested_modules: dedupe([
662
946
  ...(func.runtime_context.suggested_modules || []),
663
947
  ...(runtimeContext.suggested_modules || []),
@@ -670,7 +954,7 @@ function enrichFunctionsWithRuntimeContext(functions, dynamicEvidence) {
670
954
  };
671
955
  });
672
956
  }
673
- function chooseModuleForFunctionWithScoring(func, functionStringHints) {
957
+ function chooseModuleForFunctionWithScoring(func, functionStringHints, roleOptions) {
674
958
  const scores = new Map();
675
959
  const importHints = new Map();
676
960
  const stringHints = new Map();
@@ -696,7 +980,7 @@ function chooseModuleForFunctionWithScoring(func, functionStringHints) {
696
980
  ...((func.call_context?.callers || []).slice(0, 3)),
697
981
  ...((func.call_context?.callees || []).slice(0, 5)),
698
982
  ].join(' ');
699
- for (const module of inferModulesFromText(textCorpus)) {
983
+ for (const module of inferModulesFromText(textCorpus, [], roleOptions)) {
700
984
  addScore(module, 3);
701
985
  }
702
986
  for (const signal of func.xref_signals || []) {
@@ -765,26 +1049,95 @@ function chooseModuleForFunctionWithScoring(func, functionStringHints) {
765
1049
  addScore('network_ops', 3, region, stringHints);
766
1050
  }
767
1051
  }
1052
+ for (const protection of runtimeContext.matched_protections || []) {
1053
+ if (/read_write|write|execute|rwx/i.test(protection)) {
1054
+ addScore('process_ops', 2, protection, stringHints);
1055
+ }
1056
+ if (/file|container/i.test(protection)) {
1057
+ addScore('file_ops', 1, protection, stringHints);
1058
+ }
1059
+ if (/image|r-x|read/i.test(protection) && roleOptions?.preferredModules.has('export_dispatch')) {
1060
+ addScore('export_dispatch', 2, protection, stringHints);
1061
+ }
1062
+ if (/image|r-x|read/i.test(protection) && roleOptions?.preferredModules.has('dll_lifecycle')) {
1063
+ addScore('dll_lifecycle', 2, protection, stringHints);
1064
+ }
1065
+ }
1066
+ for (const owner of [
1067
+ ...(runtimeContext.matched_region_owners || []),
1068
+ ...(runtimeContext.matched_observed_modules || []),
1069
+ ]) {
1070
+ if (/ole32|oleaut32|combase|rpcrt4/i.test(owner)) {
1071
+ addScore('com_activation', 4, owner, stringHints);
1072
+ }
1073
+ if (/plugin|host|extension|addin/i.test(owner)) {
1074
+ addScore('callback_surface', 3, owner, stringHints);
1075
+ }
1076
+ if (/\.dll$|\.ocx$|\.cpl$/i.test(owner) && roleOptions?.preferredModules.has('dll_lifecycle')) {
1077
+ addScore('dll_lifecycle', 2, owner, stringHints);
1078
+ }
1079
+ }
1080
+ for (const segment of runtimeContext.matched_segment_names || []) {
1081
+ if (/\.edata|export|dispatch/i.test(segment)) {
1082
+ addScore('export_dispatch', 4, segment, stringHints);
1083
+ }
1084
+ if (/\.tls|\.crt|init/i.test(segment)) {
1085
+ addScore('dll_lifecycle', 4, segment, stringHints);
1086
+ }
1087
+ if (/class|factory|\.idata/i.test(segment)) {
1088
+ addScore('com_activation', 3, segment, stringHints);
1089
+ }
1090
+ if (/callback|hook|event|notify/i.test(segment)) {
1091
+ addScore('callback_surface', 3, segment, stringHints);
1092
+ }
1093
+ }
1094
+ }
1095
+ if (roleOptions) {
1096
+ for (const module of roleOptions.preferredModules) {
1097
+ if (scores.has(module)) {
1098
+ addScore(module, roleOptions.stickyModules.has(module) ? 4 : 2);
1099
+ }
1100
+ }
1101
+ if (roleOptions.preferredModules.has('export_dispatch') &&
1102
+ /\b(export|dispatch|invoke|command)\b/i.test(textCorpus)) {
1103
+ addScore('export_dispatch', 3);
1104
+ }
1105
+ if (roleOptions.preferredModules.has('com_activation') &&
1106
+ /\b(class factory|dllgetclassobject|registerserver|activation|clsid|progid)\b/i.test(textCorpus)) {
1107
+ addScore('com_activation', 4);
1108
+ }
1109
+ if (roleOptions.preferredModules.has('dll_lifecycle') &&
1110
+ /\b(dllmain|attach|detach|disablethreadlibrarycalls|module handle)\b/i.test(textCorpus)) {
1111
+ addScore('dll_lifecycle', 4);
1112
+ }
1113
+ if (roleOptions.preferredModules.has('callback_surface') &&
1114
+ /\b(callback|notify|plugin|hook|host)\b/i.test(textCorpus)) {
1115
+ addScore('callback_surface', 3);
1116
+ }
768
1117
  }
769
1118
  const top = Array.from(scores.entries()).sort((a, b) => b[1] - a[1])[0];
770
- const moduleName = top && top[1] >= 3 ? sanitizeModuleName(top[0]) : sanitizeModuleName(detectModuleByNameOrReasons(func));
1119
+ const moduleName = top && top[1] >= 3
1120
+ ? sanitizeModuleName(top[0])
1121
+ : sanitizeModuleName(detectModuleByNameOrReasons(func, roleOptions));
771
1122
  return {
772
1123
  moduleName,
773
1124
  importHints: Array.from(importHints.get(moduleName) || []).slice(0, 8),
774
1125
  stringHints: Array.from(stringHints.get(moduleName) || []).slice(0, 8),
775
1126
  };
776
1127
  }
777
- function regroupModules(functions, moduleLimit, minModuleSize, importsData, stringsData, functionStringHints) {
1128
+ function regroupModules(functions, moduleLimit, minModuleSize, importsData, stringsData, functionStringHints, roleOptions) {
778
1129
  const moduleMap = new Map();
779
1130
  const importHints = computeImportModuleHints(importsData);
780
1131
  const stringHints = computeStringModuleHints(stringsData);
781
1132
  for (const func of functions) {
782
- const decision = chooseModuleForFunctionWithScoring(func, functionStringHints || new Map());
1133
+ const decision = chooseModuleForFunctionWithScoring(func, functionStringHints || new Map(), roleOptions);
783
1134
  const moduleName = sanitizeModuleName(decision.moduleName);
784
1135
  if (!moduleMap.has(moduleName)) {
785
1136
  moduleMap.set(moduleName, {
786
1137
  name: moduleName,
787
1138
  functions: [],
1139
+ roleHint: inferRoleHintForModule(moduleName, roleOptions),
1140
+ focusMatches: new Set(collectRoleFocusMatchesForModule(moduleName, roleOptions)),
788
1141
  importHints: new Set(importHints.get(moduleName) || []),
789
1142
  stringHints: new Set((stringHints.get(moduleName) || []).slice(0, 8)),
790
1143
  runtimeApis: new Set(),
@@ -810,13 +1163,39 @@ function regroupModules(functions, moduleLimit, minModuleSize, importsData, stri
810
1163
  bucket.runtimeNotes.add(note);
811
1164
  }
812
1165
  }
813
- let modules = Array.from(moduleMap.values()).sort((a, b) => b.functions.length - a.functions.length);
1166
+ const modulePriority = (module) => {
1167
+ if (!roleOptions) {
1168
+ return 0;
1169
+ }
1170
+ return roleOptions.moduleOrder.has(module.name)
1171
+ ? 100 - (roleOptions.moduleOrder.get(module.name) || 0)
1172
+ : 0;
1173
+ };
1174
+ let modules = Array.from(moduleMap.values()).sort((a, b) => {
1175
+ const priorityDelta = modulePriority(b) - modulePriority(a);
1176
+ if (priorityDelta !== 0) {
1177
+ return priorityDelta;
1178
+ }
1179
+ return b.functions.length - a.functions.length;
1180
+ });
814
1181
  if (modules.length > moduleLimit) {
815
- const kept = modules.slice(0, moduleLimit - 1);
816
- const overflow = modules.slice(moduleLimit - 1);
1182
+ const sticky = roleOptions
1183
+ ? modules.filter((module) => roleOptions.stickyModules.has(module.name))
1184
+ : [];
1185
+ const nonSticky = roleOptions
1186
+ ? modules.filter((module) => !roleOptions.stickyModules.has(module.name))
1187
+ : modules;
1188
+ const keepCount = Math.max(1, moduleLimit - 1);
1189
+ const stickyKept = sticky.slice(0, Math.max(0, keepCount));
1190
+ const remainingSlots = Math.max(0, keepCount - stickyKept.length);
1191
+ const kept = [...stickyKept, ...nonSticky.slice(0, remainingSlots)];
1192
+ const keptNames = new Set(kept.map((module) => module.name));
1193
+ const overflow = modules.filter((module) => !keptNames.has(module.name));
817
1194
  const mergedCore = {
818
1195
  name: 'core',
819
1196
  functions: overflow.flatMap((module) => module.functions),
1197
+ roleHint: inferRoleHintForModule('core', roleOptions),
1198
+ focusMatches: new Set(overflow.flatMap((module) => Array.from(module.focusMatches || []))),
820
1199
  importHints: new Set(overflow.flatMap((module) => Array.from(module.importHints))),
821
1200
  stringHints: new Set(overflow.flatMap((module) => Array.from(module.stringHints))),
822
1201
  runtimeApis: new Set(overflow.flatMap((module) => Array.from(module.runtimeApis))),
@@ -827,17 +1206,23 @@ function regroupModules(functions, moduleLimit, minModuleSize, importsData, stri
827
1206
  }
828
1207
  const small = modules.filter((module) => module.functions.length < minModuleSize);
829
1208
  if (small.length > 0 && modules.length > 1) {
830
- const large = modules.filter((module) => module.functions.length >= minModuleSize);
1209
+ const smallMergeable = roleOptions
1210
+ ? small.filter((module) => !roleOptions.stickyModules.has(module.name))
1211
+ : small;
1212
+ const large = modules.filter((module) => module.functions.length >= minModuleSize ||
1213
+ Boolean(roleOptions?.stickyModules.has(module.name)));
831
1214
  const core = large.find((module) => module.name === 'core') || {
832
1215
  name: 'core',
833
1216
  functions: [],
1217
+ roleHint: inferRoleHintForModule('core', roleOptions),
1218
+ focusMatches: new Set(),
834
1219
  importHints: new Set(),
835
1220
  stringHints: new Set(),
836
1221
  runtimeApis: new Set(),
837
1222
  runtimeStages: new Set(),
838
1223
  runtimeNotes: new Set(),
839
1224
  };
840
- for (const module of small) {
1225
+ for (const module of smallMergeable) {
841
1226
  core.functions.push(...module.functions);
842
1227
  for (const hint of module.importHints) {
843
1228
  core.importHints.add(hint);
@@ -854,13 +1239,26 @@ function regroupModules(functions, moduleLimit, minModuleSize, importsData, stri
854
1239
  for (const note of module.runtimeNotes) {
855
1240
  core.runtimeNotes.add(note);
856
1241
  }
1242
+ for (const match of module.focusMatches) {
1243
+ core.focusMatches.add(match);
1244
+ }
1245
+ if (!core.roleHint && module.roleHint) {
1246
+ core.roleHint = module.roleHint;
1247
+ }
857
1248
  }
858
- modules = large.filter((module) => module.functions.length >= minModuleSize);
1249
+ modules = large.filter((module) => module.functions.length >= minModuleSize ||
1250
+ Boolean(roleOptions?.stickyModules.has(module.name)));
859
1251
  if (!modules.find((module) => module.name === 'core')) {
860
1252
  modules.push(core);
861
1253
  }
862
1254
  }
863
- return modules.sort((a, b) => b.functions.length - a.functions.length);
1255
+ return modules.sort((a, b) => {
1256
+ const priorityDelta = modulePriority(b) - modulePriority(a);
1257
+ if (priorityDelta !== 0) {
1258
+ return priorityDelta;
1259
+ }
1260
+ return b.functions.length - a.functions.length;
1261
+ });
864
1262
  }
865
1263
  const RESERVED_C_WRAPPER_NAMES = new Set([
866
1264
  'main',
@@ -1062,6 +1460,30 @@ function humanizeModuleName(value) {
1062
1460
  }
1063
1461
  function deriveSemanticCliDefaults(module) {
1064
1462
  const features = collectModuleFeatureSnapshot(module);
1463
+ if (module.name === 'dll_lifecycle') {
1464
+ return {
1465
+ toolName: 'DLL Lifecycle Surface',
1466
+ helpBanner: 'Review DllMain attach/detach behavior, library initialization, and module-lifetime side effects.',
1467
+ };
1468
+ }
1469
+ if (module.name === 'com_activation') {
1470
+ return {
1471
+ toolName: 'COM Activation Surface',
1472
+ helpBanner: 'Trace class-factory exports, registration paths, and COM activation routines.',
1473
+ };
1474
+ }
1475
+ if (module.name === 'export_dispatch') {
1476
+ return {
1477
+ toolName: 'Export Dispatch Surface',
1478
+ helpBanner: 'Recover exported command handlers, dispatch tables, and forwarded-export routing.',
1479
+ };
1480
+ }
1481
+ if (module.name === 'callback_surface') {
1482
+ return {
1483
+ toolName: 'Host Callback Surface',
1484
+ helpBanner: 'Recover host-driven callbacks, plugin entrypoints, and extension notification paths.',
1485
+ };
1486
+ }
1065
1487
  if (module.name === 'packer_analysis' || features.hasPackerScan) {
1066
1488
  return {
1067
1489
  toolName: 'Packer/Protector Detection',
@@ -1157,6 +1579,12 @@ function collectDisplayStringHints(module, limit = 8) {
1157
1579
  return [semanticDefaults.helpBanner].filter(Boolean).slice(0, limit);
1158
1580
  }
1159
1581
  function describeModuleRole(module) {
1582
+ if (module.reviewResolution?.role_hint) {
1583
+ return module.reviewResolution.role_hint;
1584
+ }
1585
+ if (module.roleHint) {
1586
+ return module.roleHint;
1587
+ }
1160
1588
  const defaults = deriveSemanticCliDefaults(module);
1161
1589
  return defaults.helpBanner;
1162
1590
  }
@@ -1692,6 +2120,13 @@ function summarizeRewriteParameterRoles(func) {
1692
2120
  .map((item) => `${item.slot}=>${item.role}<${item.inferred_type}>`)
1693
2121
  .join('; ');
1694
2122
  }
2123
+ function summarizeRewriteReturnRole(func) {
2124
+ const role = func.return_role || func.semantic_evidence?.return_role || null;
2125
+ if (!role) {
2126
+ return 'none';
2127
+ }
2128
+ return `${role.role}<${role.inferred_type}>`;
2129
+ }
1695
2130
  function summarizeRewriteStateRoles(func) {
1696
2131
  const roles = func.state_roles || func.semantic_evidence?.state_roles || [];
1697
2132
  if (roles.length === 0) {
@@ -2446,6 +2881,7 @@ function buildRewriteSteps(func) {
2446
2881
  }
2447
2882
  function buildAnnotatedRewriteContent(module) {
2448
2883
  const lines = [];
2884
+ const orderedFunctions = orderModuleFunctionsForPresentation(module);
2449
2885
  const displayStringHints = collectDisplayStringHints(module);
2450
2886
  lines.push(`/* module: ${module.name} | annotated rewrite */`);
2451
2887
  lines.push(`#include "${sanitizeModuleName(module.name)}.interface.h"`);
@@ -2454,16 +2890,33 @@ function buildAnnotatedRewriteContent(module) {
2454
2890
  lines.push(` * Analyst summary:`);
2455
2891
  lines.push(` * - function_count: ${module.functions.length}`);
2456
2892
  lines.push(` * - recovered_role: ${describeModuleRole(module)}`);
2893
+ lines.push(` * - role_focus: ${Array.from(module.focusMatches).join(', ') || 'none'}`);
2894
+ if (module.reviewResolution?.refined_name) {
2895
+ lines.push(` * - module_review_name: ${module.reviewResolution.refined_name}`);
2896
+ }
2897
+ if (module.reviewResolution?.summary) {
2898
+ lines.push(` * - module_review_summary: ${normalizeExplanationText(module.reviewResolution.summary) || 'none'}`);
2899
+ }
2900
+ if (typeof module.reviewResolution?.confidence === 'number') {
2901
+ lines.push(` * - module_review_confidence: ${Number(module.reviewResolution.confidence).toFixed(2)}`);
2902
+ }
2903
+ lines.push(` * - prioritized_functions: ${orderedFunctions
2904
+ .slice(0, 3)
2905
+ .map((func) => getValidatedSemanticName(func) || func.function)
2906
+ .join(', ') || 'none'}`);
2457
2907
  lines.push(` * - import_hints: ${Array.from(module.importHints).slice(0, 8).join(', ') || 'none'}`);
2458
2908
  lines.push(` * - string_hints: ${displayStringHints.join(' | ') || 'none'}`);
2459
2909
  lines.push(` * - runtime_apis: ${Array.from(module.runtimeApis).slice(0, 8).join(', ') || 'none'}`);
2460
2910
  lines.push(` * - runtime_stages: ${Array.from(module.runtimeStages).slice(0, 6).join(', ') || 'none'}`);
2461
2911
  lines.push(` * - runtime_notes: ${Array.from(module.runtimeNotes).slice(0, 4).join(' | ') || 'none'}`);
2912
+ if ((module.reviewResolution?.rewrite_guidance || []).length > 0) {
2913
+ lines.push(` * - module_rewrite_guidance: ${(module.reviewResolution?.rewrite_guidance || []).join(' | ')}`);
2914
+ }
2462
2915
  lines.push(' * - This file is a human-readable rewrite scaffold, not original source.');
2463
2916
  lines.push(' */');
2464
2917
  lines.push('');
2465
2918
  lines.push(...buildModuleRewritePrelude(module));
2466
- for (const func of module.functions) {
2919
+ for (const func of orderedFunctions) {
2467
2920
  const names = deriveRewriteEntryNames(func, module);
2468
2921
  const validatedSemanticName = getValidatedSemanticName(func);
2469
2922
  lines.push(`int ${names.implementationName}(AkRuntimeContext *runtime_ctx, const AkSemanticInputs *inputs, AkSemanticOutputs *outputs)`);
@@ -2502,12 +2955,17 @@ function buildAnnotatedRewriteContent(module) {
2502
2955
  lines.push(` /* call_context: callers=${func.call_context?.callers?.slice(0, 4).join(', ') || 'none'} callees=${func.call_context?.callees?.slice(0, 6).join(', ') || 'none'} */`);
2503
2956
  lines.push(` /* relation_hints: callers=${summarizeRelationshipEntries(func.call_relationships?.callers)} callees=${summarizeRelationshipEntries(func.call_relationships?.callees)} */`);
2504
2957
  lines.push(` /* parameter_roles: ${summarizeRewriteParameterRoles(func)} */`);
2958
+ lines.push(` /* return_role: ${summarizeRewriteReturnRole(func)} */`);
2505
2959
  lines.push(` /* state_roles: ${summarizeRewriteStateRoles(func)} */`);
2506
2960
  lines.push(` /* struct_inference: ${summarizeRewriteStructInference(func)} */`);
2507
2961
  if ((func.runtime_context?.corroborated_apis || []).length > 0 ||
2508
2962
  (func.runtime_context?.corroborated_stages || []).length > 0 ||
2509
- (func.runtime_context?.matched_memory_regions || []).length > 0) {
2510
- lines.push(` /* runtime_context: apis=${(func.runtime_context?.corroborated_apis || []).join(', ') || 'none'} stages=${(func.runtime_context?.corroborated_stages || []).join(', ') || 'none'} regions=${(func.runtime_context?.matched_memory_regions || []).join(', ') || 'none'} modules=${(func.runtime_context?.suggested_modules || []).join(', ') || 'none'} confidence=${Number(func.runtime_context?.confidence || 0).toFixed(2)} executed=${func.runtime_context?.executed ? 'yes' : 'no'} sources=${(func.runtime_context?.evidence_sources || []).join(', ') || 'unknown'} names=${(func.runtime_context?.source_names || []).join(', ') || 'unknown'} matched_by=${(func.runtime_context?.matched_by || []).join(', ') || 'unknown'} artifacts=${func.runtime_context?.executed_artifact_count || 0}/${func.runtime_context?.artifact_count || 0} */`);
2963
+ (func.runtime_context?.matched_memory_regions || []).length > 0 ||
2964
+ (func.runtime_context?.matched_protections || []).length > 0 ||
2965
+ (func.runtime_context?.matched_region_owners || []).length > 0 ||
2966
+ (func.runtime_context?.matched_observed_modules || []).length > 0 ||
2967
+ (func.runtime_context?.matched_segment_names || []).length > 0) {
2968
+ lines.push(` /* runtime_context: apis=${(func.runtime_context?.corroborated_apis || []).join(', ') || 'none'} stages=${(func.runtime_context?.corroborated_stages || []).join(', ') || 'none'} regions=${(func.runtime_context?.matched_memory_regions || []).join(', ') || 'none'} protections=${(func.runtime_context?.matched_protections || []).join(', ') || 'none'} owners=${(func.runtime_context?.matched_region_owners || []).join(', ') || 'none'} observed_modules=${(func.runtime_context?.matched_observed_modules || []).join(', ') || 'none'} segments=${(func.runtime_context?.matched_segment_names || []).join(', ') || 'none'} ranges=${(func.runtime_context?.matched_address_ranges || []).join(', ') || 'none'} modules=${(func.runtime_context?.suggested_modules || []).join(', ') || 'none'} confidence=${Number(func.runtime_context?.confidence || 0).toFixed(2)} executed=${func.runtime_context?.executed ? 'yes' : 'no'} sources=${(func.runtime_context?.evidence_sources || []).join(', ') || 'unknown'} names=${(func.runtime_context?.source_names || []).join(', ') || 'unknown'} matched_by=${(func.runtime_context?.matched_by || []).join(', ') || 'unknown'} artifacts=${func.runtime_context?.executed_artifact_count || 0}/${func.runtime_context?.artifact_count || 0} */`);
2511
2969
  }
2512
2970
  if ((func.runtime_context?.notes || []).length > 0) {
2513
2971
  lines.push(` /* runtime_notes: ${(func.runtime_context?.notes || []).join(' || ')} */`);
@@ -2632,6 +3090,58 @@ function scoreFunctionForDedup(func) {
2632
3090
  (func.runtime_context?.matched_memory_regions?.length || 0) * 2 -
2633
3091
  (func.gaps?.length || 0) * 3);
2634
3092
  }
3093
+ function scoreFunctionForRewritePresentation(func, module) {
3094
+ const corpus = [
3095
+ func.function,
3096
+ func.semantic_summary || '',
3097
+ func.source_like_snippet || '',
3098
+ ...(func.behavior_tags || []),
3099
+ ...(func.rank_reasons || []),
3100
+ ...(func.runtime_context?.corroborated_stages || []),
3101
+ ...(func.runtime_context?.matched_memory_regions || []),
3102
+ ].join(' ');
3103
+ let score = func.confidence * 100 +
3104
+ (func.xref_signals?.length || 0) * 6 +
3105
+ (func.runtime_context?.corroborated_apis?.length || 0) * 5 +
3106
+ (func.runtime_context?.matched_memory_regions?.length || 0) * 4 +
3107
+ (func.parameter_roles?.length || 0) * 2 +
3108
+ (func.struct_inference?.length || 0) * 3;
3109
+ if (func.name_resolution?.validated_name) {
3110
+ score += 10;
3111
+ }
3112
+ if (func.explanation_resolution?.summary) {
3113
+ score += 8;
3114
+ }
3115
+ const lowered = corpus.toLowerCase();
3116
+ if (module.name === 'com_activation' && /\b(dllgetclassobject|class factory|iclassfactory|cocreateinstance|inprocserver32)\b/.test(lowered)) {
3117
+ score += 30;
3118
+ }
3119
+ if (module.name === 'dll_lifecycle' && /\b(dllmain|disablethreadlibrarycalls|attach|detach)\b/.test(lowered)) {
3120
+ score += 30;
3121
+ }
3122
+ if (module.name === 'export_dispatch' && /\b(dispatch|invokecommand|handlecommand|runcommand|export)\b/.test(lowered)) {
3123
+ score += 24;
3124
+ }
3125
+ if (module.name === 'callback_surface' && /\b(callback|plugin|notify|hook|host)\b/.test(lowered)) {
3126
+ score += 24;
3127
+ }
3128
+ if (module.name === 'process_ops' && /\b(writeprocessmemory|openprocess|createprocess|setthreadcontext|resumethread)\b/.test(lowered)) {
3129
+ score += 20;
3130
+ }
3131
+ if (module.name === 'packer_analysis' && /\b(packer|protector|entropy|section|signature|layout)\b/.test(lowered)) {
3132
+ score += 20;
3133
+ }
3134
+ return score;
3135
+ }
3136
+ function orderModuleFunctionsForPresentation(module) {
3137
+ return [...module.functions].sort((left, right) => {
3138
+ const scoreDelta = scoreFunctionForRewritePresentation(right, module) - scoreFunctionForRewritePresentation(left, module);
3139
+ if (scoreDelta !== 0) {
3140
+ return scoreDelta;
3141
+ }
3142
+ return left.address.localeCompare(right.address);
3143
+ });
3144
+ }
2635
3145
  function dedupeReconstructedFunctions(functions) {
2636
3146
  const orderedKeys = [];
2637
3147
  const byKey = new Map();
@@ -3067,6 +3577,8 @@ function buildBinaryProfile(sampleFileType, originalFilename, exportsData, packe
3067
3577
  buildReconstructCliProfile(modules.map((module) => ({
3068
3578
  name: module.name,
3069
3579
  functions: [],
3580
+ roleHint: module.role_hint || null,
3581
+ focusMatches: new Set(module.focus_matches || []),
3070
3582
  importHints: new Set(module.import_hints || []),
3071
3583
  stringHints: new Set(module.string_hints || []),
3072
3584
  runtimeApis: new Set(module.runtime_apis || []),
@@ -3155,6 +3667,22 @@ function buildReverseNotesMarkdown(profile, modules, warnings, runtimeEvidence,
3155
3667
  const moduleBucket = {
3156
3668
  name: module.name,
3157
3669
  functions: [],
3670
+ roleHint: module.role_hint || null,
3671
+ focusMatches: new Set(module.focus_matches || []),
3672
+ reviewResolution: module.review_summary || module.refined_name || module.role_hint
3673
+ ? {
3674
+ refined_name: module.refined_name || null,
3675
+ summary: module.review_summary || null,
3676
+ role_hint: module.role_hint || null,
3677
+ confidence: typeof module.review_confidence === 'number' ? module.review_confidence : null,
3678
+ assumptions: [],
3679
+ evidence_used: [],
3680
+ rewrite_guidance: [],
3681
+ focus_areas: module.focus_matches || [],
3682
+ priority_functions: [],
3683
+ source: 'llm',
3684
+ }
3685
+ : undefined,
3158
3686
  importHints: new Set(module.import_hints || []),
3159
3687
  stringHints: new Set(module.string_hints || []),
3160
3688
  runtimeApis: new Set(module.runtime_apis || []),
@@ -3163,7 +3691,7 @@ function buildReverseNotesMarkdown(profile, modules, warnings, runtimeEvidence,
3163
3691
  };
3164
3692
  const displayHints = collectDisplayStringHints(moduleBucket).slice(0, 3);
3165
3693
  const semanticRole = describeModuleRole(moduleBucket);
3166
- lines.push(`- ${module.name}: role=${semanticRole} confidence=${module.confidence.toFixed(2)}, functions=${module.function_count}, strings=${displayHints.join(' | ') || 'none'}, runtime=${module.runtime_stages.slice(0, 2).join(', ') || 'none'}`);
3694
+ lines.push(`- ${module.name}${module.refined_name ? ` [${module.refined_name}]` : ''}: role=${semanticRole} confidence=${module.confidence.toFixed(2)}, focus=${(module.focus_matches || []).join(', ') || 'none'}, functions=${module.function_count}, strings=${displayHints.join(' | ') || 'none'}, runtime=${module.runtime_stages.slice(0, 2).join(', ') || 'none'}${module.review_summary ? `, review=${normalizeExplanationText(module.review_summary)}` : ''}`);
3167
3695
  }
3168
3696
  lines.push('');
3169
3697
  if (runtimeEvidence) {
@@ -3288,6 +3816,9 @@ export function createCodeReconstructExportHandler(workspaceManager, database, c
3288
3816
  evidence_session_tag: input.evidence_session_tag || null,
3289
3817
  semantic_scope: input.semantic_scope,
3290
3818
  semantic_session_tag: input.semantic_session_tag || null,
3819
+ role_target: input.role_target || null,
3820
+ role_focus_areas: input.role_focus_areas,
3821
+ role_priority_order: input.role_priority_order,
3291
3822
  analysis_marker: analysisMarker,
3292
3823
  runtime_marker: runtimeMarker,
3293
3824
  semantic_name_marker: semanticNameMarker,
@@ -3453,7 +3984,13 @@ export function createCodeReconstructExportHandler(workspaceManager, database, c
3453
3984
  semantic_names: buildSemanticArtifactProvenance('semantic naming artifacts', semanticNameIndex, input.semantic_scope, input.semantic_session_tag),
3454
3985
  semantic_explanations: buildSemanticArtifactProvenance('semantic explanation artifacts', explanationIndex, input.semantic_scope, input.semantic_session_tag),
3455
3986
  };
3456
- const modules = regroupModules(normalizedFunctions, input.module_limit, input.min_module_size, importsData, stringsData, functionStringHints);
3987
+ const roleAwareModules = buildRoleAwareModuleOptions(input);
3988
+ const modules = regroupModules(normalizedFunctions, input.module_limit, input.min_module_size, importsData, stringsData, functionStringHints, roleAwareModules);
3989
+ const moduleReviewIndex = await attachModuleReviews(workspaceManager, database, input.sample_id, modules, {
3990
+ scope: input.semantic_scope,
3991
+ sessionTag: input.semantic_session_tag,
3992
+ });
3993
+ provenance.semantic_module_reviews = buildSemanticArtifactProvenance('semantic module review artifacts', moduleReviewIndex, input.semantic_scope, input.semantic_session_tag);
3457
3994
  const workspace = await workspaceManager.getWorkspace(input.sample_id);
3458
3995
  const originalEntries = await fs.readdir(workspace.original);
3459
3996
  const originalFilename = originalEntries.length > 0 ? originalEntries[0] : null;
@@ -3483,6 +4020,13 @@ export function createCodeReconstructExportHandler(workspaceManager, database, c
3483
4020
  name: safeName,
3484
4021
  confidence: averageConfidence(module.functions),
3485
4022
  function_count: module.functions.length,
4023
+ role_hint: module.reviewResolution?.role_hint || module.roleHint,
4024
+ focus_matches: Array.from(module.focusMatches),
4025
+ refined_name: module.reviewResolution?.refined_name || null,
4026
+ review_summary: module.reviewResolution?.summary || null,
4027
+ review_confidence: typeof module.reviewResolution?.confidence === 'number'
4028
+ ? module.reviewResolution.confidence
4029
+ : null,
3486
4030
  import_hints: Array.from(module.importHints).slice(0, 10),
3487
4031
  string_hints: Array.from(module.stringHints).slice(0, 10),
3488
4032
  runtime_apis: Array.from(module.runtimeApis).slice(0, 10),
@@ -3658,7 +4202,11 @@ export function createCodeReconstructExportHandler(workspaceManager, database, c
3658
4202
  stage_count: dynamicEvidence.stage_count,
3659
4203
  observed_apis: dynamicEvidence.observed_apis.slice(0, 12),
3660
4204
  region_types: (dynamicEvidence.region_types || []).slice(0, 12),
4205
+ protections: (dynamicEvidence.protections || []).slice(0, 12),
4206
+ address_ranges: (dynamicEvidence.address_ranges || []).slice(0, 8),
4207
+ region_owners: (dynamicEvidence.region_owners || []).slice(0, 8),
3661
4208
  observed_modules: (dynamicEvidence.observed_modules || []).slice(0, 8),
4209
+ segment_names: (dynamicEvidence.segment_names || []).slice(0, 8),
3662
4210
  observed_strings: (dynamicEvidence.observed_strings || []).slice(0, 8),
3663
4211
  stages: dynamicEvidence.stages.slice(0, 12),
3664
4212
  summary: dynamicEvidence.summary,
@@ -3669,6 +4217,11 @@ export function createCodeReconstructExportHandler(workspaceManager, database, c
3669
4217
  name: module.name,
3670
4218
  confidence: module.confidence,
3671
4219
  function_count: module.function_count,
4220
+ role_hint: module.role_hint || null,
4221
+ focus_matches: module.focus_matches || [],
4222
+ refined_name: module.refined_name || null,
4223
+ review_summary: module.review_summary || null,
4224
+ review_confidence: typeof module.review_confidence === 'number' ? module.review_confidence : null,
3672
4225
  interface_path: module.interface_path,
3673
4226
  pseudocode_path: module.pseudocode_path,
3674
4227
  rewrite_path: module.rewrite_path,
@@ -3966,7 +4519,11 @@ export function createCodeReconstructExportHandler(workspaceManager, database, c
3966
4519
  stage_count: dynamicEvidence.stage_count,
3967
4520
  observed_apis: dynamicEvidence.observed_apis.slice(0, 12),
3968
4521
  region_types: (dynamicEvidence.region_types || []).slice(0, 12),
4522
+ protections: (dynamicEvidence.protections || []).slice(0, 12),
4523
+ address_ranges: (dynamicEvidence.address_ranges || []).slice(0, 8),
4524
+ region_owners: (dynamicEvidence.region_owners || []).slice(0, 8),
3969
4525
  observed_modules: (dynamicEvidence.observed_modules || []).slice(0, 8),
4526
+ segment_names: (dynamicEvidence.segment_names || []).slice(0, 8),
3970
4527
  observed_strings: (dynamicEvidence.observed_strings || []).slice(0, 8),
3971
4528
  stages: dynamicEvidence.stages.slice(0, 12),
3972
4529
  summary: dynamicEvidence.summary,