dotscope 0.7.0

A high-performance, cross-platform framework for analyzing and reverse engineering .NET PE executables
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
//! # Type System Deep Dive
//!
//! **What this example teaches:**
//! - Advanced type system analysis capabilities
//! - Generic type instantiation and constraints
//! - Interface implementation analysis
//! - Inheritance hierarchy exploration
//! - Signature parsing and method resolution
//!
//! **When to use this pattern:**
//! - Building type analysis tools
//! - Investigating inheritance relationships
//! - Generic type constraint analysis
//! - Interface implementation discovery
//!
//! **Prerequisites:**
//! - Strong understanding of .NET type system
//! - Familiarity with generics and inheritance
//! - Experience with metadata analysis

use dotscope::prelude::*;
use std::{collections::HashMap, env, path::Path};

fn main() -> Result<()> {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        eprintln!("Usage: {} <path-to-dotnet-assembly>", args[0]);
        eprintln!();
        eprintln!("This example explores the .NET type system in detail:");
        eprintln!("  • Type categorization and analysis");
        eprintln!("  • Generic type parameter examination");
        eprintln!("  • Inheritance hierarchy mapping");
        eprintln!("  • Interface implementation tracking");
        return Ok(());
    }

    let path = Path::new(&args[1]);
    println!("🏗️  Type system analysis of: {}", path.display());

    let assembly = CilObject::from_path(path)?;

    // === Type Registry Analysis ===
    print_type_registry_analysis(&assembly);

    // === Generic Type Analysis ===
    print_generic_analysis(&assembly);

    // === Inheritance Analysis ===
    print_inheritance_analysis(&assembly);

    // === Interface Analysis ===
    print_interface_analysis(&assembly);

    // === Signature Analysis ===
    print_signature_analysis(&assembly);

    println!("\n✅ Type system analysis completed!");

    Ok(())
}

fn print_type_registry_analysis(assembly: &CilObject) {
    println!("\n📚 Type Registry Analysis:");

    let types = assembly.types();
    let mut type_categories = TypeCategories::default();

    // Analyze type definitions
    for type_def in &types.all_types() {
        type_categories.total_types += 1;

        // Categorize by visibility
        match type_def.flags.bits() & 0x07 {
            // TypeAttributes.VisibilityMask
            0 => type_categories.not_public += 1, // NotPublic
            1 => type_categories.public += 1,     // Public
            2 => type_categories.nested_public += 1, // NestedPublic
            3 => type_categories.nested_private += 1, // NestedPrivate
            4 => type_categories.nested_family += 1, // NestedFamily
            5 => type_categories.nested_assembly += 1, // NestedAssembly
            6 => type_categories.nested_fam_and_assem += 1, // NestedFamANDAssem
            7 => type_categories.nested_fam_or_assem += 1, // NestedFamORAssem
            _ => {}
        }

        // Categorize by layout
        match type_def.flags.bits() & 0x18 {
            // TypeAttributes.LayoutMask
            0x00 => type_categories.auto_layout += 1, // AutoLayout
            0x08 => type_categories.sequential_layout += 1, // SequentialLayout
            0x10 => type_categories.explicit_layout += 1, // ExplicitLayout
            _ => {}
        }

        // Categorize by semantics
        match type_def.flags.bits() & 0x20 {
            // TypeAttributes.ClassSemanticsMask
            0x00 => type_categories.class_types += 1, // Class
            0x20 => type_categories.interface_types += 1, // Interface
            _ => {}
        }

        // Check for special types
        if type_def.flags.bits() & 0x80 != 0 {
            // Abstract
            type_categories.abstract_types += 1;
        }
        if type_def.flags.bits() & 0x100 != 0 {
            // Sealed
            type_categories.sealed_types += 1;
        }
        if type_def.flags.bits() & 0x400 != 0 {
            // Serializable
            type_categories.serializable_types += 1;
        }

        // Analyze by naming patterns
        if type_def.name.ends_with("Attribute") {
            type_categories.attribute_types += 1;
        } else if type_def.name.ends_with("Exception") {
            type_categories.exception_types += 1;
        } else if type_def.name.ends_with("EventArgs") {
            type_categories.event_arg_types += 1;
        } else if type_def.name.starts_with('I')
            && type_def.name.len() > 1
            && type_def.name.chars().nth(1).unwrap().is_uppercase()
        {
            type_categories.interface_named_types += 1;
        }
    }

    print_type_categories(&type_categories);
}

fn print_type_categories(categories: &TypeCategories) {
    println!("  Type visibility distribution:");
    println!("    Public: {}", categories.public);
    println!("    Not public: {}", categories.not_public);
    println!("    Nested public: {}", categories.nested_public);
    println!("    Nested private: {}", categories.nested_private);
    println!("    Nested family: {}", categories.nested_family);

    println!("  Type layout distribution:");
    println!("    Auto layout: {}", categories.auto_layout);
    println!("    Sequential layout: {}", categories.sequential_layout);
    println!("    Explicit layout: {}", categories.explicit_layout);

    println!("  Type semantics:");
    println!("    Classes: {}", categories.class_types);
    println!("    Interfaces: {}", categories.interface_types);
    println!("    Abstract types: {}", categories.abstract_types);
    println!("    Sealed types: {}", categories.sealed_types);
    println!("    Serializable types: {}", categories.serializable_types);

    println!("  Special type patterns:");
    println!("    Attributes: {}", categories.attribute_types);
    println!("    Exceptions: {}", categories.exception_types);
    println!("    Event args: {}", categories.event_arg_types);
    println!(
        "    Interface-named types: {}",
        categories.interface_named_types
    );
}

fn print_generic_analysis(assembly: &CilObject) {
    println!("\n🧬 Generic Type Analysis:");

    let types = assembly.types();
    let mut generic_stats = GenericStats::default();

    // Analyze generic types and methods
    for type_def in types.all_types().iter().take(100) {
        // Look for generic type indicators
        if type_def.name.contains('`') {
            generic_stats.generic_types += 1;

            // Extract generic parameter count
            if let Some(backtick_pos) = type_def.name.rfind('`') {
                if let Ok(param_count) = type_def.name[backtick_pos + 1..].parse::<u32>() {
                    generic_stats.total_type_parameters += param_count;
                    if param_count > generic_stats.max_type_parameters {
                        generic_stats.max_type_parameters = param_count;
                        generic_stats.most_generic_type = type_def.name.clone();
                    }
                }
            }

            if generic_stats.generic_types <= 5 {
                println!("    Generic type: {}.{}", type_def.namespace, type_def.name);
            }
        }
    }

    // Analyze methods for generic parameters
    let methods = assembly.methods();
    for entry in methods.iter().take(100) {
        let method = entry.value();

        // Simple heuristic: methods with generic naming patterns
        if method.name.contains('<') || method.name.contains("Generic") {
            generic_stats.potentially_generic_methods += 1;
        }
    }

    println!("  Generic type statistics:");
    println!("    Generic types found: {}", generic_stats.generic_types);
    println!(
        "    Total type parameters: {}",
        generic_stats.total_type_parameters
    );
    println!(
        "    Max type parameters: {} (in '{}')",
        generic_stats.max_type_parameters, generic_stats.most_generic_type
    );
    println!(
        "    Potentially generic methods: {}",
        generic_stats.potentially_generic_methods
    );

    if generic_stats.generic_types > 5 {
        println!("    ... (showing first 5 generic types)");
    }
}

fn print_inheritance_analysis(assembly: &CilObject) {
    println!("\n🌳 Inheritance Hierarchy Analysis:");

    let types = assembly.types();
    let mut inheritance_stats = InheritanceStats::default();
    let mut base_class_counts: HashMap<String, u32> = HashMap::new();

    for type_def in types.all_types().iter().take(50) {
        inheritance_stats.total_types += 1;

        // Check if type has a base class (extends something)
        if let Some(base_type) = type_def.base() {
            inheritance_stats.types_with_base_class += 1;

            let base_class_name = format!("{}:{}", base_type.namespace, base_type.name);
            *base_class_counts.entry(base_class_name).or_insert(0) += 1;
        } else {
            inheritance_stats.root_types += 1;
        }

        // Check for interface implementations
        // Note: This would require analyzing the InterfaceImpl table in a real implementation
        if type_def.flags.bits() & 0x20 == 0 {
            // Not an interface itself
            // This is a placeholder - real implementation would check InterfaceImpl table
            inheritance_stats.types_implementing_interfaces += 1;
        }
    }

    println!("  Inheritance statistics:");
    println!("    Types analyzed: {}", inheritance_stats.total_types);
    println!(
        "    Types with base classes: {}",
        inheritance_stats.types_with_base_class
    );
    println!(
        "    Root types (no base class): {}",
        inheritance_stats.root_types
    );
    println!(
        "    Types implementing interfaces: {}",
        inheritance_stats.types_implementing_interfaces
    );

    println!("  Common base classes:");
    let mut sorted_bases: Vec<_> = base_class_counts.iter().collect();
    sorted_bases.sort_by(|a, b| b.1.cmp(a.1));
    for (base_class, count) in sorted_bases.iter().take(5) {
        println!("    {base_class}: {count} derived types");
    }
}

fn print_interface_analysis(assembly: &CilObject) {
    println!("\n🔌 Interface Analysis:");

    let types = assembly.types();
    let mut interface_stats = InterfaceStats::default();
    let mut interface_names = Vec::new();

    for type_def in types.all_types().iter().take(100) {
        if type_def.flags.bits() & 0x20 != 0 {
            // Interface flag
            interface_stats.interface_count += 1;
            interface_names.push(format!("{}.{}", type_def.namespace, type_def.name));

            // Analyze interface naming patterns
            if type_def.name.starts_with('I')
                && type_def.name.len() > 1
                && type_def.name.chars().nth(1).unwrap().is_uppercase()
            {
                interface_stats.conventionally_named += 1;
            }

            if type_def.namespace.starts_with("System") {
                interface_stats.system_interfaces += 1;
            }
        }
    }

    println!("  Interface statistics:");
    println!("    Total interfaces: {}", interface_stats.interface_count);
    println!(
        "    Conventionally named (IXxx): {}",
        interface_stats.conventionally_named
    );
    println!(
        "    System namespace interfaces: {}",
        interface_stats.system_interfaces
    );

    if !interface_names.is_empty() {
        println!("  Sample interfaces:");
        for interface_name in interface_names.iter().take(8) {
            println!("    {interface_name}");
        }
        if interface_names.len() > 8 {
            println!("    ... (showing first 8 interfaces)");
        }
    }
}

fn print_signature_analysis(assembly: &CilObject) {
    println!("\n✍️  Signature Analysis:");

    let methods = assembly.methods();
    let mut signature_stats = SignatureStats::default();

    for entry in methods.iter().take(50) {
        let method = entry.value();
        signature_stats.methods_analyzed += 1;

        // Analyze method naming patterns for signature complexity
        let param_count =
            method.name.matches(',').count() + if method.name.contains('(') { 1 } else { 0 };

        if param_count > signature_stats.max_parameters {
            signature_stats.max_parameters = param_count;
            signature_stats.most_complex_method = method.name.clone();
        }

        signature_stats.total_parameters += param_count;

        // Check for special method types
        if method.name.starts_with("get_") || method.name.starts_with("set_") {
            signature_stats.property_accessors += 1;
        } else if method.name.starts_with("add_") || method.name.starts_with("remove_") {
            signature_stats.event_accessors += 1;
        } else if method.name.starts_with("op_") {
            signature_stats.operator_overloads += 1;
        } else if method.name == ".ctor" {
            signature_stats.constructors += 1;
        } else if method.name == ".cctor" {
            signature_stats.static_constructors += 1;
        }
    }

    println!("  Method signature statistics:");
    println!("    Methods analyzed: {}", signature_stats.methods_analyzed);
    println!(
        "    Property accessors: {}",
        signature_stats.property_accessors
    );
    println!("    Event accessors: {}", signature_stats.event_accessors);
    println!(
        "    Operator overloads: {}",
        signature_stats.operator_overloads
    );
    println!("    Constructors: {}", signature_stats.constructors);
    println!(
        "    Static constructors: {}",
        signature_stats.static_constructors
    );

    if signature_stats.methods_analyzed > 0 {
        println!(
            "    Average parameters per method: {:.1}",
            signature_stats.total_parameters as f64 / signature_stats.methods_analyzed as f64
        );
        println!(
            "    Most complex method: '{}' ({} parameters)",
            signature_stats.most_complex_method, signature_stats.max_parameters
        );
    }

    println!("  Signature parsing capabilities:");
    println!("    • Method signature decoding");
    println!("    • Field signature analysis");
    println!("    • Local variable signature parsing");
    println!("    • Generic constraint resolution");
    println!("    • Calling convention analysis");
}

#[derive(Default)]
struct TypeCategories {
    total_types: usize,
    public: usize,
    not_public: usize,
    nested_public: usize,
    nested_private: usize,
    nested_family: usize,
    nested_assembly: usize,
    nested_fam_and_assem: usize,
    nested_fam_or_assem: usize,
    auto_layout: usize,
    sequential_layout: usize,
    explicit_layout: usize,
    class_types: usize,
    interface_types: usize,
    abstract_types: usize,
    sealed_types: usize,
    serializable_types: usize,
    attribute_types: usize,
    exception_types: usize,
    event_arg_types: usize,
    interface_named_types: usize,
}

#[derive(Default)]
struct GenericStats {
    generic_types: usize,
    total_type_parameters: u32,
    max_type_parameters: u32,
    most_generic_type: String,
    potentially_generic_methods: usize,
}

#[derive(Default)]
struct InheritanceStats {
    total_types: usize,
    types_with_base_class: usize,
    root_types: usize,
    types_implementing_interfaces: usize,
}

#[derive(Default)]
struct InterfaceStats {
    interface_count: usize,
    conventionally_named: usize,
    system_interfaces: usize,
}

#[derive(Default)]
struct SignatureStats {
    methods_analyzed: usize,
    total_parameters: usize,
    max_parameters: usize,
    most_complex_method: String,
    property_accessors: usize,
    event_accessors: usize,
    operator_overloads: usize,
    constructors: usize,
    static_constructors: usize,
}