dotscope 0.6.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
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
//! Raw signature validation for .NET assembly signature blob integrity and format compliance.
//!
//! This validator ensures the structural integrity of signature blobs in metadata tables,
//! validating proper ECMA-335 binary format, calling convention compliance, and blob bounds
//! checking before signature parsing occurs. It operates on raw blob heap data to validate
//! the foundational requirements before higher-level signature validation can proceed.
//! This validator runs with priority 175 in the raw validation stage.
//!
//! # Architecture
//!
//! The signature validation system implements comprehensive blob format validation:
//! 1. **Method Signature Validation** - Validates method signature blobs in MethodDef table
//! 2. **Field Signature Validation** - Validates field type signatures in Field table
//! 3. **Property Signature Validation** - Validates property signatures in Property table
//! 4. **LocalVar Signature Validation** - Validates local variable signatures in StandAloneSig table
//! 5. **TypeSpec Signature Validation** - Validates type specification signatures in TypeSpec table
//! 6. **MemberRef Signature Validation** - Validates member reference signatures in MemberRef table
//!
//! The implementation validates signature blob format according to ECMA-335 specifications,
//! ensuring proper calling convention encoding, compressed integer format, and blob bounds
//! checking without performing full signature parsing.
//!
//! # Key Components
//!
//! - [`RawSignatureValidator`] - Main validator implementation providing comprehensive signature blob validation
//! - [`RawSignatureValidator::validate_signature_blob_integrity`] - Core blob format validation with calling convention checking
//! - [`RawSignatureValidator::validate_calling_convention`] - Calling convention byte validation
//! - [`RawSignatureValidator::validate_compressed_integer`] - Compressed integer format validation
//! - [`RawSignatureValidator::validate_blob_bounds`] - Blob boundary and size validation
//!
//! # Usage Examples
//!
//! ```rust,no_run
//! use dotscope::metadata::validation::{RawSignatureValidator, RawValidator, RawValidationContext};
//!
//! # fn get_context() -> RawValidationContext<'static> { unimplemented!() }
//! let context = get_context();
//! let validator = RawSignatureValidator::new();
//!
//! // Check if validation should run based on configuration
//! if validator.should_run(&context) {
//!     validator.validate_raw(&context)?;
//! }
//! # Ok::<(), dotscope::Error>(())
//! ```
//!
//! # Error Handling
//!
//! This validator returns [`crate::Error::ValidationRawFailed`] for:
//! - Invalid calling convention bytes in signature blobs
//! - Malformed compressed integer encoding in signatures
//! - Signature blobs extending beyond blob heap boundaries
//! - Invalid signature blob size encoding
//! - Signature blobs with insufficient data for declared size
//! - Recursive type definitions exceeding maximum nesting depth
//!
//! # Thread Safety
//!
//! All validation operations are read-only and thread-safe. The validator implements [`Send`] + [`Sync`]
//! and can be used concurrently across multiple threads without synchronization as it operates on
//! immutable signature blob structures.
//!
//! # Integration
//!
//! This validator integrates with:
//! - raw structure validators - Part of the foundational structural validation stage
//! - [`crate::metadata::validation::engine::ValidationEngine`] - Orchestrates validator execution with fail-fast behavior
//! - [`crate::metadata::validation::traits::RawValidator`] - Implements the raw validation interface
//! - [`crate::metadata::cilassemblyview::CilAssemblyView`] - Source of metadata tables and blob heap
//! - [`crate::metadata::validation::context::RawValidationContext`] - Provides validation execution context
//! - [`crate::metadata::validation::config::ValidationConfig`] - Controls validation execution via enable_token_validation flag
//! - owned metadata signature validator - Complemented by semantic signature validation
//!
//! # References
//!
//! - [ECMA-335 II.23.2](https://ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf) - Blobs and signatures
//! - [ECMA-335 II.23.2.1](https://ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf) - Method signatures
//! - [ECMA-335 II.23.2.4](https://ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf) - Field signatures
//! - [ECMA-335 II.23.2.5](https://ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf) - Property signatures

use crate::{
    metadata::{
        cilassemblyview::CilAssemblyView,
        tables::{
            FieldRaw, MemberRefRaw, MethodDefRaw, PropertyRaw, StandAloneSigRaw, TypeSpecRaw,
        },
        validation::{
            context::{RawValidationContext, ValidationContext},
            traits::RawValidator,
        },
    },
    Error, Result,
};

/// Foundation validator for signature blob structure and ECMA-335 format compliance.
///
/// Ensures the structural integrity and format compliance of all signature blobs
/// in a .NET assembly, validating proper calling convention encoding, compressed
/// integer format, and blob bounds checking. This validator operates at the binary
/// format level before signature parsing, providing essential guarantees for safe
/// signature processing.
///
/// The validator implements comprehensive coverage of all signature types according to
/// ECMA-335 specifications, ensuring proper binary format compliance, calling convention
/// validity, and structural integrity across all signature blob formats.
///
/// # Thread Safety
///
/// This validator is [`Send`] and [`Sync`] as all validation operations are read-only
/// and operate on immutable signature blob structures.
pub struct RawSignatureValidator;

/// Signature kind enumeration for blob validation context.
///
/// Defines the expected signature type for blob validation to ensure proper
/// calling convention and format validation according to ECMA-335 specifications.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SignatureKind {
    /// Method signature (ECMA-335 II.23.2.1)
    Method,
    /// Field signature (ECMA-335 II.23.2.4)
    Field,
    /// Property signature (ECMA-335 II.23.2.5)
    Property,
    /// Local variable signature (ECMA-335 II.23.2.6)
    LocalVar,
    /// Type specification signature (ECMA-335 II.23.2.14)
    TypeSpec,
    /// Member reference signature (method or field)
    MemberRef,
}

impl RawSignatureValidator {
    /// Creates a new signature blob validator.
    ///
    /// Initializes a validator instance that can be used to validate signature
    /// blob structures across multiple assemblies. The validator is stateless
    /// and can be reused safely across multiple validation operations.
    ///
    /// # Returns
    ///
    /// A new [`RawSignatureValidator`] instance ready for validation operations.
    ///
    /// # Thread Safety
    ///
    /// The returned validator is thread-safe and can be used concurrently.
    #[must_use]
    pub fn new() -> Self {
        Self
    }

    /// Validates the integrity and format compliance of a signature blob.
    ///
    /// Performs comprehensive validation of signature blob format including:
    /// 1. Blob existence and minimum size validation
    /// 2. Calling convention byte validation for signature kind
    /// 3. Compressed integer encoding validation
    /// 4. Blob boundary checking to prevent buffer overruns
    /// 5. Basic ECMA-335 format compliance verification
    ///
    /// This method provides foundational guarantees about signature blob integrity
    /// that signature parsers can rely upon during content parsing.
    ///
    /// # Arguments
    ///
    /// * `assembly_view` - Assembly metadata view containing blob heap data
    /// * `blob_index` - Index into the blob heap for the signature
    /// * `expected_kind` - Expected signature type for validation context
    ///
    /// # Returns
    ///
    /// * `Ok(())` - Signature blob is valid and properly formatted
    /// * `Err(`[`crate::Error::ValidationRawFailed`]`)` - Signature blob violations found
    ///
    /// # Errors
    ///
    /// Returns validation errors for:
    /// - Blob index pointing beyond heap boundaries
    /// - Invalid calling convention for signature kind
    /// - Malformed compressed integer encoding
    /// - Insufficient blob data for declared signature size
    fn validate_signature_blob_integrity(
        assembly_view: &CilAssemblyView,
        blob_index: u32,
        expected_kind: SignatureKind,
    ) -> Result<()> {
        if blob_index == 0 {
            return Ok(());
        }

        let Some(blob_heap) = assembly_view.blobs() else {
            return Err(Error::ValidationRawFailed {
                validator: "RawSignatureValidator".to_string(),
                message: "Signature validation requires blob heap access".to_string(),
            });
        };

        let blob_data =
            blob_heap
                .get(blob_index as usize)
                .map_err(|_| Error::ValidationRawFailed {
                    validator: "RawSignatureValidator".to_string(),
                    message: format!("Signature blob index {blob_index} exceeds blob heap bounds"),
                })?;

        if blob_data.is_empty() {
            return Err(Error::ValidationRawFailed {
                validator: "RawSignatureValidator".to_string(),
                message: format!("Signature blob at index {blob_index} is empty"),
            });
        }

        let calling_convention = blob_data[0];
        Self::validate_calling_convention(calling_convention, expected_kind, blob_index)?;

        if matches!(
            expected_kind,
            SignatureKind::Method | SignatureKind::LocalVar | SignatureKind::Property
        ) {
            if blob_data.len() < 2 {
                return Err(Error::ValidationRawFailed {
                    validator: "RawSignatureValidator".to_string(),
                    message: format!(
                        "Signature blob at index {blob_index} too short for parameter count"
                    ),
                });
            }

            Self::validate_compressed_integer(&blob_data[1..], blob_index)?;
        }

        Self::validate_blob_bounds(blob_data, blob_index)?;

        Ok(())
    }

    /// Validates calling convention byte for the expected signature kind.
    ///
    /// Ensures the calling convention byte is valid for the signature type
    /// according to ECMA-335 calling convention specifications.
    ///
    /// # Arguments
    ///
    /// * `calling_convention` - The calling convention byte from signature
    /// * `expected_kind` - Expected signature type
    /// * `blob_index` - Blob index for error reporting
    ///
    /// # Returns
    ///
    /// Returns validation error if calling convention is invalid for signature kind.
    fn validate_calling_convention(
        calling_convention: u8,
        expected_kind: SignatureKind,
        blob_index: u32,
    ) -> Result<()> {
        match expected_kind {
            SignatureKind::Method | SignatureKind::MemberRef => {
                // Method calling conventions (ECMA-335 II.23.2.1)
                // 0x00 = DEFAULT, 0x01 = C, 0x02 = STDCALL, 0x03 = THISCALL, 0x04 = FASTCALL, 0x05 = VARARG
                // Can also have HASTHIS (0x20) and EXPLICIT_THIS (0x40) flags
                let base_convention = calling_convention & 0x0F;
                if base_convention > 0x05 {
                    return Err(Error::ValidationRawFailed {
                        validator: "RawSignatureValidator".to_string(),
                        message: format!("Invalid method calling convention 0x{calling_convention:02X} in signature blob {blob_index}"),

                    });
                }
            }
            SignatureKind::Field => {
                // Field signature (ECMA-335 II.23.2.4) - should be 0x06
                if calling_convention != 0x06 {
                    return Err(Error::ValidationRawFailed {
                        validator: "RawSignatureValidator".to_string(),
                        message: format!("Invalid field signature marker 0x{calling_convention:02X} in blob {blob_index}, expected 0x06"),

                    });
                }
            }
            SignatureKind::Property => {
                // Property signature (ECMA-335 II.23.2.5) - should be 0x08 (PROPERTY)
                // Can also have HASTHIS (0x20) flag
                let base_convention = calling_convention & 0x0F;
                if base_convention != 0x08 {
                    return Err(Error::ValidationRawFailed {
                        validator: "RawSignatureValidator".to_string(),
                        message: format!("Invalid property signature marker 0x{calling_convention:02X} in blob {blob_index}, expected 0x08"),

                    });
                }
            }
            SignatureKind::LocalVar => {
                // Local variable signature (ECMA-335 II.23.2.6) - should be 0x07
                if calling_convention != 0x07 {
                    return Err(Error::ValidationRawFailed {
                        validator: "RawSignatureValidator".to_string(),
                        message: format!("Invalid local variable signature marker 0x{calling_convention:02X} in blob {blob_index}, expected 0x07"),

                    });
                }
            }
            SignatureKind::TypeSpec => {
                // TypeSpec signature has various type encodings, basic validation for known ranges
                // Valid element types are in ranges 0x01-0x16, 0x1B-0x20, etc.
                if calling_convention == 0x00 {
                    return Err(Error::ValidationRawFailed {
                        validator: "RawSignatureValidator".to_string(),
                        message: format!("Invalid type specification signature marker 0x{calling_convention:02X} in blob {blob_index}"),

                    });
                }
            }
        }
        Ok(())
    }

    /// Validates compressed integer encoding format.
    ///
    /// Ensures compressed integers follow ECMA-335 encoding rules:
    /// - 1-byte: 0bbbbbbb (0-127)
    /// - 2-byte: 10bbbbbb xxxxxxxx (128-16383)
    /// - 4-byte: 110bbbbb xxxxxxxx yyyyyyyy zzzzzzzz (16384+)
    ///
    /// # Arguments
    ///
    /// * `data` - Blob data starting at compressed integer
    /// * `blob_index` - Blob index for error reporting
    ///
    /// # Returns
    ///
    /// Returns validation error if compressed integer encoding is malformed.
    fn validate_compressed_integer(data: &[u8], blob_index: u32) -> Result<()> {
        if data.is_empty() {
            return Err(Error::ValidationRawFailed {
                validator: "RawSignatureValidator".to_string(),
                message: format!("Insufficient data for compressed integer in blob {blob_index}"),
            });
        }

        let first_byte = data[0];

        if (first_byte & 0x80) == 0 {
            // 1-byte encoding: 0bbbbbbb
            // Valid as-is
            Ok(())
        } else if (first_byte & 0xC0) == 0x80 {
            // 2-byte encoding: 10bbbbbb xxxxxxxx
            if data.len() < 2 {
                return Err(Error::ValidationRawFailed {
                    validator: "RawSignatureValidator".to_string(),
                    message: format!(
                        "Insufficient data for 2-byte compressed integer in blob {blob_index}"
                    ),
                });
            }
            Ok(())
        } else if (first_byte & 0xE0) == 0xC0 {
            // 4-byte encoding: 110bbbbb xxxxxxxx yyyyyyyy zzzzzzzz
            if data.len() < 4 {
                return Err(Error::ValidationRawFailed {
                    validator: "RawSignatureValidator".to_string(),
                    message: format!(
                        "Insufficient data for 4-byte compressed integer in blob {blob_index}"
                    ),
                });
            }
            Ok(())
        } else {
            // Invalid encoding pattern
            Err(Error::ValidationRawFailed {
                validator: "RawSignatureValidator".to_string(),
                message: format!(
                    "Invalid compressed integer encoding 0x{first_byte:02X} in blob {blob_index}"
                ),
            })
        }
    }

    /// Validates blob boundary constraints and structural integrity.
    ///
    /// Performs basic structural validation to ensure the blob data is
    /// consistent and does not contain obvious corruption indicators.
    ///
    /// # Arguments
    ///
    /// * `blob_data` - The blob data to validate
    /// * `blob_index` - Blob index for error reporting
    ///
    /// # Returns
    ///
    /// Returns validation error for structural inconsistencies.
    fn validate_blob_bounds(blob_data: &[u8], blob_index: u32) -> Result<()> {
        if blob_data.len() > 65536 {
            return Err(Error::ValidationRawFailed {
                validator: "RawSignatureValidator".to_string(),
                message: format!(
                    "Signature blob {} exceeds maximum reasonable size ({})",
                    blob_index,
                    blob_data.len()
                ),
            });
        }

        // ToDo: Additional bounds checking can be added here for specific signature format constraints
        Ok(())
    }
}

impl RawValidator for RawSignatureValidator {
    /// Validates the structural integrity and format compliance of all signature blobs.
    ///
    /// Performs comprehensive validation of signature blob structures, including:
    /// 1. Method signature validation in MethodDef table
    /// 2. Field signature validation in Field table
    /// 3. Property signature validation in Property table
    /// 4. Local variable signature validation in StandAloneSig table
    /// 5. Type specification signature validation in TypeSpec table
    /// 6. Member reference signature validation in MemberRef table
    ///
    /// This method provides foundational guarantees about signature blob integrity
    /// that higher-level signature validators and parsers can rely upon during content validation.
    ///
    /// # Arguments
    ///
    /// * `context` - Raw validation context containing assembly view and configuration
    ///
    /// # Returns
    ///
    /// * `Ok(())` - All signature blobs are valid and properly formatted
    /// * `Err(`[`crate::Error::ValidationRawFailed`]`)` - Signature blob violations found
    ///
    /// # Configuration
    ///
    /// Controlled by `enable_token_validation` flag in validation configuration.
    fn validate_raw(&self, context: &RawValidationContext) -> Result<()> {
        if !self.should_run(context) {
            return Ok(());
        }

        let assembly_view = context.assembly_view();

        let Some(tables) = assembly_view.tables() else {
            return Ok(());
        };

        if let Some(table) = tables.table::<MethodDefRaw>() {
            for method in table {
                if let Some(blob_heap) = assembly_view.blobs() {
                    if let Ok(blob_data) = blob_heap.get(method.signature as usize) {
                        if !blob_data.is_empty() {
                            let calling_convention = blob_data[0];
                            let signature_kind = match calling_convention {
                                0x06 => SignatureKind::Field,
                                _ => SignatureKind::Method,
                            };

                            Self::validate_signature_blob_integrity(
                                assembly_view,
                                method.signature,
                                signature_kind,
                            )?;
                        }
                    }
                } else {
                    Self::validate_signature_blob_integrity(
                        assembly_view,
                        method.signature,
                        SignatureKind::Method,
                    )?;
                }
            }
        }

        if let Some(table) = tables.table::<FieldRaw>() {
            for field in table {
                Self::validate_signature_blob_integrity(
                    assembly_view,
                    field.signature,
                    SignatureKind::Field,
                )?;
            }
        }

        if let Some(table) = tables.table::<PropertyRaw>() {
            for property in table {
                Self::validate_signature_blob_integrity(
                    assembly_view,
                    property.signature,
                    SignatureKind::Property,
                )?;
            }
        }

        if let Some(table) = tables.table::<StandAloneSigRaw>() {
            for standalone_sig in table {
                if let Some(blob_heap) = assembly_view.blobs() {
                    if let Ok(blob_data) = blob_heap.get(standalone_sig.signature as usize) {
                        if !blob_data.is_empty() {
                            let calling_convention = blob_data[0];
                            let signature_kind = match calling_convention {
                                0x07 => SignatureKind::LocalVar,
                                0x06 => SignatureKind::Field,
                                _ => SignatureKind::Method,
                            };

                            Self::validate_signature_blob_integrity(
                                assembly_view,
                                standalone_sig.signature,
                                signature_kind,
                            )?;
                        }
                    }
                }
            }
        }

        if let Some(table) = tables.table::<TypeSpecRaw>() {
            for type_spec in table {
                Self::validate_signature_blob_integrity(
                    assembly_view,
                    type_spec.signature,
                    SignatureKind::TypeSpec,
                )?;
            }
        }

        if let Some(table) = tables.table::<MemberRefRaw>() {
            for member_ref in table {
                if let Some(blob_heap) = assembly_view.blobs() {
                    if let Ok(blob_data) = blob_heap.get(member_ref.signature as usize) {
                        if !blob_data.is_empty() {
                            let calling_convention = blob_data[0];
                            let signature_kind = match calling_convention {
                                0x06 => SignatureKind::Field,
                                _ => SignatureKind::Method,
                            };

                            Self::validate_signature_blob_integrity(
                                assembly_view,
                                member_ref.signature,
                                signature_kind,
                            )?;
                        }
                    }
                } else {
                    Self::validate_signature_blob_integrity(
                        assembly_view,
                        member_ref.signature,
                        SignatureKind::MemberRef,
                    )?;
                }
            }
        }

        Ok(())
    }

    /// Returns the validation priority for signature blob validation.
    ///
    /// Signature validation runs with priority 175, after heap validation (180)
    /// but before other structural validators, ensuring blob integrity before
    /// signature parsing occurs.
    fn priority(&self) -> u32 {
        175
    }

    /// Returns the validator name for identification and logging.
    fn name(&self) -> &'static str {
        "RawSignatureValidator"
    }

    /// Determines if signature validation should run based on validation configuration.
    ///
    /// Signature validation is controlled by the `enable_token_validation` flag
    /// since signature blobs are part of the token validation infrastructure.
    fn should_run(&self, context: &RawValidationContext) -> bool {
        context.config().enable_token_validation
    }
}

impl Default for RawSignatureValidator {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        metadata::validation::ValidationConfig,
        test::{factories::validation::raw_structure_signature::*, validator_test},
        Result,
    };

    #[test]
    fn test_raw_signature_validator() -> Result<()> {
        let validator = RawSignatureValidator::new();
        let config = ValidationConfig {
            enable_token_validation: true,
            ..Default::default()
        };

        validator_test(
            raw_signature_validator_file_factory,
            "RawSignatureValidator",
            "ValidationRawFailed",
            config,
            |context| validator.validate_raw(context),
        )
    }
}