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,566 @@
1
+ using System.Reflection;
2
+ using System.Reflection.Metadata;
3
+ using System.Reflection.Metadata.Ecma335;
4
+ using System.Reflection.PortableExecutable;
5
+ using System.Text.Json;
6
+
7
+ internal sealed record ProbeOptions(
8
+ string SamplePath,
9
+ bool IncludeTypes,
10
+ bool IncludeMethods,
11
+ int MaxTypes,
12
+ int MaxMethodsPerType
13
+ );
14
+
15
+ internal static class Program
16
+ {
17
+ private static readonly JsonSerializerOptions JsonOptions = new()
18
+ {
19
+ WriteIndented = false
20
+ };
21
+
22
+ public static int Main(string[] args)
23
+ {
24
+ var result = Run(args);
25
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonOptions));
26
+ return 0;
27
+ }
28
+
29
+ private static object Run(string[] args)
30
+ {
31
+ try
32
+ {
33
+ var options = ParseArgs(args);
34
+ if (!File.Exists(options.SamplePath))
35
+ {
36
+ return Fail($"Sample path does not exist: {options.SamplePath}");
37
+ }
38
+
39
+ using var stream = File.OpenRead(options.SamplePath);
40
+ using var peReader = new PEReader(stream);
41
+
42
+ if (!peReader.HasMetadata)
43
+ {
44
+ return Fail("PE image does not expose CLR metadata.");
45
+ }
46
+
47
+ var mdReader = peReader.GetMetadataReader();
48
+ var warnings = new List<string>();
49
+
50
+ var assemblyDefinition = mdReader.IsAssembly
51
+ ? mdReader.GetAssemblyDefinition()
52
+ : default(AssemblyDefinition);
53
+ var assemblyName = mdReader.IsAssembly ? SafeString(mdReader, assemblyDefinition.Name) : null;
54
+ var assemblyVersion = mdReader.IsAssembly ? assemblyDefinition.Version.ToString() : null;
55
+ var moduleDefinition = mdReader.GetModuleDefinition();
56
+ var moduleName = SafeString(mdReader, moduleDefinition.Name);
57
+ var targetFramework = TryReadTargetFramework(mdReader);
58
+
59
+ var typeRows = new List<object>();
60
+ var namespaceCounts = new Dictionary<string, (int Types, int Methods)>(StringComparer.OrdinalIgnoreCase);
61
+
62
+ var allTypeDefinitions = mdReader.TypeDefinitions
63
+ .Select(handle => BuildTypeInfo(mdReader, handle, options.IncludeMethods, options.MaxMethodsPerType))
64
+ .Where(item => !item.IsCompilerGenerated)
65
+ .ToList();
66
+
67
+ foreach (var type in allTypeDefinitions)
68
+ {
69
+ var key = string.IsNullOrWhiteSpace(type.Namespace) ? "<global>" : type.Namespace;
70
+ if (!namespaceCounts.TryGetValue(key, out var counts))
71
+ {
72
+ counts = (0, 0);
73
+ }
74
+ namespaceCounts[key] = (counts.Types + 1, counts.Methods + type.MethodCount);
75
+ }
76
+
77
+ var typeLimit = Math.Max(1, options.MaxTypes);
78
+ if (allTypeDefinitions.Count > typeLimit)
79
+ {
80
+ warnings.Add($"Type list truncated from {allTypeDefinitions.Count} to {typeLimit}.");
81
+ }
82
+
83
+ foreach (var type in allTypeDefinitions
84
+ .OrderByDescending(item => item.MethodCount)
85
+ .ThenBy(item => item.FullName, StringComparer.OrdinalIgnoreCase)
86
+ .Take(typeLimit))
87
+ {
88
+ var methods = options.IncludeMethods
89
+ ? type.Methods.Select(method => new
90
+ {
91
+ name = method.Name,
92
+ token = method.Token,
93
+ rva = method.Rva,
94
+ attributes = method.Attributes,
95
+ is_constructor = method.IsConstructor,
96
+ is_static = method.IsStatic,
97
+ }).ToArray()
98
+ : Array.Empty<object>();
99
+
100
+ typeRows.Add(new
101
+ {
102
+ token = type.Token,
103
+ @namespace = type.Namespace,
104
+ name = type.Name,
105
+ full_name = type.FullName,
106
+ kind = type.Kind,
107
+ visibility = type.Visibility,
108
+ base_type = type.BaseType,
109
+ method_count = type.MethodCount,
110
+ field_count = type.FieldCount,
111
+ nested_type_count = type.NestedTypeCount,
112
+ flags = type.Flags,
113
+ methods,
114
+ });
115
+ }
116
+
117
+ var references = mdReader.AssemblyReferences
118
+ .Select(handle =>
119
+ {
120
+ var reference = mdReader.GetAssemblyReference(handle);
121
+ return new
122
+ {
123
+ name = SafeString(mdReader, reference.Name),
124
+ version = reference.Version.ToString(),
125
+ culture = reference.Culture.IsNil ? null : SafeString(mdReader, reference.Culture),
126
+ };
127
+ })
128
+ .OrderBy(item => item.name, StringComparer.OrdinalIgnoreCase)
129
+ .ToArray();
130
+
131
+ var resources = mdReader.ManifestResources
132
+ .Select(handle =>
133
+ {
134
+ var resource = mdReader.GetManifestResource(handle);
135
+ return new
136
+ {
137
+ name = SafeString(mdReader, resource.Name),
138
+ attributes = resource.Attributes.ToString(),
139
+ implementation = resource.Implementation.IsNil
140
+ ? "embedded"
141
+ : resource.Implementation.Kind.ToString(),
142
+ };
143
+ })
144
+ .OrderBy(item => item.name, StringComparer.OrdinalIgnoreCase)
145
+ .ToArray();
146
+
147
+ var namespaceRows = namespaceCounts
148
+ .OrderByDescending(item => item.Value.Methods)
149
+ .ThenBy(item => item.Key, StringComparer.OrdinalIgnoreCase)
150
+ .Select(item => new
151
+ {
152
+ name = item.Key,
153
+ type_count = item.Value.Types,
154
+ method_count = item.Value.Methods,
155
+ })
156
+ .ToArray();
157
+
158
+ var corHeader = peReader.PEHeaders.CorHeader;
159
+ var entryPointToken = corHeader is null || corHeader.EntryPointTokenOrRelativeVirtualAddress == 0
160
+ ? null
161
+ : $"0x{corHeader.EntryPointTokenOrRelativeVirtualAddress:X8}";
162
+ var isLibrary = (peReader.PEHeaders.CoffHeader.Characteristics & Characteristics.Dll) != 0;
163
+
164
+ return new
165
+ {
166
+ ok = true,
167
+ warnings,
168
+ data = new
169
+ {
170
+ is_dotnet = true,
171
+ assembly_name = assemblyName,
172
+ assembly_version = assemblyVersion,
173
+ module_name = moduleName,
174
+ metadata_version = mdReader.MetadataVersion,
175
+ target_framework = targetFramework,
176
+ is_library = isLibrary,
177
+ entry_point_token = entryPointToken,
178
+ assembly_references = references,
179
+ resources,
180
+ namespaces = namespaceRows,
181
+ types = options.IncludeTypes ? typeRows.ToArray() : Array.Empty<object>(),
182
+ summary = new
183
+ {
184
+ type_count = allTypeDefinitions.Count,
185
+ method_count = allTypeDefinitions.Sum(item => item.MethodCount),
186
+ namespace_count = namespaceRows.Length,
187
+ assembly_reference_count = references.Length,
188
+ resource_count = resources.Length,
189
+ }
190
+ }
191
+ };
192
+ }
193
+ catch (Exception ex)
194
+ {
195
+ return Fail(ex.Message);
196
+ }
197
+ }
198
+
199
+ private static ProbeOptions ParseArgs(string[] args)
200
+ {
201
+ if (args.Length == 0 || string.IsNullOrWhiteSpace(args[0]))
202
+ {
203
+ throw new InvalidOperationException("Usage: DotNetMetadataProbe <sample-path> [--include-types=true|false] [--include-methods=true|false] [--max-types=N] [--max-methods-per-type=N]");
204
+ }
205
+
206
+ var samplePath = args[0];
207
+ var includeTypes = true;
208
+ var includeMethods = true;
209
+ var maxTypes = 80;
210
+ var maxMethodsPerType = 24;
211
+
212
+ foreach (var raw in args.Skip(1))
213
+ {
214
+ if (!raw.StartsWith("--", StringComparison.Ordinal))
215
+ {
216
+ continue;
217
+ }
218
+
219
+ var parts = raw.Substring(2).Split('=', 2);
220
+ var key = parts[0].Trim();
221
+ var value = parts.Length > 1 ? parts[1].Trim() : string.Empty;
222
+
223
+ switch (key)
224
+ {
225
+ case "include-types":
226
+ includeTypes = ParseBool(value, includeTypes);
227
+ break;
228
+ case "include-methods":
229
+ includeMethods = ParseBool(value, includeMethods);
230
+ break;
231
+ case "max-types":
232
+ maxTypes = ParsePositiveInt(value, maxTypes);
233
+ break;
234
+ case "max-methods-per-type":
235
+ maxMethodsPerType = ParsePositiveInt(value, maxMethodsPerType);
236
+ break;
237
+ }
238
+ }
239
+
240
+ return new ProbeOptions(samplePath, includeTypes, includeMethods, maxTypes, maxMethodsPerType);
241
+ }
242
+
243
+ private static bool ParseBool(string value, bool fallback)
244
+ {
245
+ return bool.TryParse(value, out var parsed) ? parsed : fallback;
246
+ }
247
+
248
+ private static int ParsePositiveInt(string value, int fallback)
249
+ {
250
+ return int.TryParse(value, out var parsed) && parsed > 0 ? parsed : fallback;
251
+ }
252
+
253
+ private static object Fail(string error) => new
254
+ {
255
+ ok = false,
256
+ errors = new[] { error },
257
+ };
258
+
259
+ private static string? SafeString(MetadataReader reader, StringHandle handle)
260
+ {
261
+ return handle.IsNil ? null : reader.GetString(handle);
262
+ }
263
+
264
+ private static string? ResolveTypeName(MetadataReader reader, EntityHandle handle)
265
+ {
266
+ if (handle.IsNil)
267
+ {
268
+ return null;
269
+ }
270
+
271
+ return handle.Kind switch
272
+ {
273
+ HandleKind.TypeDefinition => ResolveTypeDefinitionName(reader, (TypeDefinitionHandle)handle),
274
+ HandleKind.TypeReference => ResolveTypeReferenceName(reader, (TypeReferenceHandle)handle),
275
+ HandleKind.TypeSpecification => "TypeSpecification",
276
+ _ => handle.Kind.ToString(),
277
+ };
278
+ }
279
+
280
+ private static string ResolveTypeDefinitionName(MetadataReader reader, TypeDefinitionHandle handle)
281
+ {
282
+ var definition = reader.GetTypeDefinition(handle);
283
+ var ns = SafeString(reader, definition.Namespace);
284
+ var name = SafeString(reader, definition.Name) ?? "<unnamed>";
285
+ return string.IsNullOrWhiteSpace(ns) ? name : $"{ns}.{name}";
286
+ }
287
+
288
+ private static string ResolveTypeReferenceName(MetadataReader reader, TypeReferenceHandle handle)
289
+ {
290
+ var reference = reader.GetTypeReference(handle);
291
+ var ns = SafeString(reader, reference.Namespace);
292
+ var name = SafeString(reader, reference.Name) ?? "<unnamed>";
293
+ return string.IsNullOrWhiteSpace(ns) ? name : $"{ns}.{name}";
294
+ }
295
+
296
+ private static string GetTypeVisibility(TypeAttributes attributes)
297
+ {
298
+ return (attributes & TypeAttributes.VisibilityMask) switch
299
+ {
300
+ TypeAttributes.Public => "public",
301
+ TypeAttributes.NotPublic => "internal",
302
+ TypeAttributes.NestedPublic => "nested_public",
303
+ TypeAttributes.NestedPrivate => "nested_private",
304
+ TypeAttributes.NestedFamily => "nested_family",
305
+ TypeAttributes.NestedAssembly => "nested_internal",
306
+ TypeAttributes.NestedFamORAssem => "nested_protected_internal",
307
+ TypeAttributes.NestedFamANDAssem => "nested_private_protected",
308
+ _ => "unknown",
309
+ };
310
+ }
311
+
312
+ private static string GetTypeKind(MetadataReader reader, TypeDefinition definition)
313
+ {
314
+ var attributes = definition.Attributes;
315
+ var baseType = ResolveTypeName(reader, definition.BaseType);
316
+
317
+ if ((attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface)
318
+ {
319
+ return "interface";
320
+ }
321
+
322
+ if (string.Equals(baseType, "System.Enum", StringComparison.Ordinal))
323
+ {
324
+ return "enum";
325
+ }
326
+
327
+ if (string.Equals(baseType, "System.ValueType", StringComparison.Ordinal))
328
+ {
329
+ return "struct";
330
+ }
331
+
332
+ if (string.Equals(baseType, "System.MulticastDelegate", StringComparison.Ordinal))
333
+ {
334
+ return "delegate";
335
+ }
336
+
337
+ return "class";
338
+ }
339
+
340
+ private static bool IsCompilerGenerated(string? namespaceName, string? typeName)
341
+ {
342
+ var ns = namespaceName ?? string.Empty;
343
+ var name = typeName ?? string.Empty;
344
+ return name.StartsWith("<", StringComparison.Ordinal)
345
+ || ns.StartsWith("<", StringComparison.Ordinal)
346
+ || name.Contains("AnonymousType", StringComparison.Ordinal)
347
+ || name.Contains("DisplayClass", StringComparison.Ordinal);
348
+ }
349
+
350
+ private static string[] CollectTypeFlags(TypeDefinition definition)
351
+ {
352
+ var flags = new List<string>();
353
+ var attributes = definition.Attributes;
354
+
355
+ if ((attributes & TypeAttributes.Abstract) != 0)
356
+ {
357
+ flags.Add("abstract");
358
+ }
359
+ if ((attributes & TypeAttributes.Sealed) != 0)
360
+ {
361
+ flags.Add("sealed");
362
+ }
363
+ if ((attributes & TypeAttributes.SpecialName) != 0)
364
+ {
365
+ flags.Add("special_name");
366
+ }
367
+
368
+ return flags.ToArray();
369
+ }
370
+
371
+ private static string[] CollectMethodFlags(MethodDefinition definition)
372
+ {
373
+ var flags = new List<string>();
374
+ var attributes = definition.Attributes;
375
+
376
+ switch (attributes & MethodAttributes.MemberAccessMask)
377
+ {
378
+ case MethodAttributes.Public:
379
+ flags.Add("public");
380
+ break;
381
+ case MethodAttributes.Private:
382
+ flags.Add("private");
383
+ break;
384
+ case MethodAttributes.Family:
385
+ flags.Add("protected");
386
+ break;
387
+ case MethodAttributes.Assembly:
388
+ flags.Add("internal");
389
+ break;
390
+ }
391
+
392
+ if ((attributes & MethodAttributes.Static) != 0)
393
+ {
394
+ flags.Add("static");
395
+ }
396
+ if ((attributes & MethodAttributes.Abstract) != 0)
397
+ {
398
+ flags.Add("abstract");
399
+ }
400
+ if ((attributes & MethodAttributes.Virtual) != 0)
401
+ {
402
+ flags.Add("virtual");
403
+ }
404
+ if ((attributes & MethodAttributes.PinvokeImpl) != 0)
405
+ {
406
+ flags.Add("pinvoke");
407
+ }
408
+ if ((attributes & MethodAttributes.SpecialName) != 0)
409
+ {
410
+ flags.Add("special_name");
411
+ }
412
+
413
+ return flags.ToArray();
414
+ }
415
+
416
+ private static string? ResolveCustomAttributeType(MetadataReader reader, CustomAttribute attribute)
417
+ {
418
+ var constructor = attribute.Constructor;
419
+ return constructor.Kind switch
420
+ {
421
+ HandleKind.MemberReference => ResolveMemberReferenceParentType(reader, (MemberReferenceHandle)constructor),
422
+ HandleKind.MethodDefinition => ResolveMethodDefinitionParentType(reader, (MethodDefinitionHandle)constructor),
423
+ _ => null,
424
+ };
425
+ }
426
+
427
+ private static string? ResolveMemberReferenceParentType(MetadataReader reader, MemberReferenceHandle handle)
428
+ {
429
+ var reference = reader.GetMemberReference(handle);
430
+ return ResolveTypeName(reader, reference.Parent);
431
+ }
432
+
433
+ private static string? ResolveMethodDefinitionParentType(MetadataReader reader, MethodDefinitionHandle handle)
434
+ {
435
+ var method = reader.GetMethodDefinition(handle);
436
+ return ResolveTypeName(reader, method.GetDeclaringType());
437
+ }
438
+
439
+ private static string? ReadSingleStringCustomAttribute(MetadataReader reader, CustomAttribute attribute)
440
+ {
441
+ try
442
+ {
443
+ var blobReader = reader.GetBlobReader(attribute.Value);
444
+ if (blobReader.Length < 2 || blobReader.ReadUInt16() != 1)
445
+ {
446
+ return null;
447
+ }
448
+ return blobReader.ReadSerializedString();
449
+ }
450
+ catch
451
+ {
452
+ return null;
453
+ }
454
+ }
455
+
456
+ private static string? TryReadTargetFramework(MetadataReader reader)
457
+ {
458
+ if (!reader.IsAssembly)
459
+ {
460
+ return null;
461
+ }
462
+
463
+ var assemblyDefinition = reader.GetAssemblyDefinition();
464
+ foreach (var attributeHandle in assemblyDefinition.GetCustomAttributes())
465
+ {
466
+ var attribute = reader.GetCustomAttribute(attributeHandle);
467
+ var attributeType = ResolveCustomAttributeType(reader, attribute);
468
+ if (!string.Equals(
469
+ attributeType,
470
+ "System.Runtime.Versioning.TargetFrameworkAttribute",
471
+ StringComparison.Ordinal))
472
+ {
473
+ continue;
474
+ }
475
+
476
+ var value = ReadSingleStringCustomAttribute(reader, attribute);
477
+ if (!string.IsNullOrWhiteSpace(value))
478
+ {
479
+ return value;
480
+ }
481
+ }
482
+
483
+ return null;
484
+ }
485
+
486
+ private static TypeRow BuildTypeInfo(
487
+ MetadataReader reader,
488
+ TypeDefinitionHandle handle,
489
+ bool includeMethods,
490
+ int maxMethodsPerType
491
+ )
492
+ {
493
+ var definition = reader.GetTypeDefinition(handle);
494
+ var namespaceName = SafeString(reader, definition.Namespace);
495
+ var typeName = SafeString(reader, definition.Name) ?? "<unnamed>";
496
+ var fullName = string.IsNullOrWhiteSpace(namespaceName) ? typeName : $"{namespaceName}.{typeName}";
497
+ var methods = new List<MethodRow>();
498
+
499
+ if (includeMethods)
500
+ {
501
+ foreach (var methodHandle in definition.GetMethods())
502
+ {
503
+ var method = reader.GetMethodDefinition(methodHandle);
504
+ methods.Add(new MethodRow(
505
+ SafeString(reader, method.Name) ?? "<unnamed>",
506
+ $"0x{MetadataTokens.GetToken(methodHandle):X8}",
507
+ method.RelativeVirtualAddress,
508
+ CollectMethodFlags(method),
509
+ string.Equals(SafeString(reader, method.Name), ".ctor", StringComparison.Ordinal)
510
+ || string.Equals(SafeString(reader, method.Name), ".cctor", StringComparison.Ordinal),
511
+ method.Attributes.HasFlag(MethodAttributes.Static)
512
+ ));
513
+ }
514
+ }
515
+
516
+ if (methods.Count > maxMethodsPerType)
517
+ {
518
+ methods = methods
519
+ .OrderByDescending(item => item.Rva)
520
+ .ThenBy(item => item.Name, StringComparer.OrdinalIgnoreCase)
521
+ .Take(maxMethodsPerType)
522
+ .ToList();
523
+ }
524
+
525
+ return new TypeRow(
526
+ $"0x{MetadataTokens.GetToken(handle):X8}",
527
+ namespaceName ?? string.Empty,
528
+ typeName,
529
+ fullName,
530
+ GetTypeKind(reader, definition),
531
+ GetTypeVisibility(definition.Attributes),
532
+ ResolveTypeName(reader, definition.BaseType),
533
+ definition.GetMethods().Count(),
534
+ definition.GetFields().Count(),
535
+ definition.GetNestedTypes().Count(),
536
+ CollectTypeFlags(definition),
537
+ methods,
538
+ IsCompilerGenerated(namespaceName, typeName)
539
+ );
540
+ }
541
+ }
542
+
543
+ internal sealed record MethodRow(
544
+ string Name,
545
+ string Token,
546
+ int Rva,
547
+ string[] Attributes,
548
+ bool IsConstructor,
549
+ bool IsStatic
550
+ );
551
+
552
+ internal sealed record TypeRow(
553
+ string Token,
554
+ string Namespace,
555
+ string Name,
556
+ string FullName,
557
+ string Kind,
558
+ string Visibility,
559
+ string? BaseType,
560
+ int MethodCount,
561
+ int FieldCount,
562
+ int NestedTypeCount,
563
+ string[] Flags,
564
+ List<MethodRow> Methods,
565
+ bool IsCompilerGenerated
566
+ );