use crate::{
metadata::marshalling::{parse_marshalling_descriptor, NativeType},
prelude::{
ArgumentValue, AssemblyRaw, AssemblyRefRaw, CilFlavor, ClassLayoutRaw, CodedIndex,
CodedIndexType, ConstantRaw, CustomAttributeRaw, DeclSecurityRaw, EventMapRaw, EventRaw,
FieldLayoutRaw, FieldMarshalRaw, FieldRaw, FieldRvaRaw, GenericParamConstraintRaw,
GenericParamRaw, ImplMapRaw, ImportType, InterfaceImplRaw, MemberRefRaw, MethodDefRaw,
MethodImplRaw, MethodSemanticsRaw, ModuleRaw, ModuleRefRaw, NestedClassRaw, ParamRaw,
PermissionSet, PermissionSetFormat, PropertyMapRaw, PropertyRaw, StandAloneSigRaw, TableId,
TypeDefRaw, TypeRefRaw, TypeSpecRaw, CIL_HEADER_MAGIC,
},
CilObject,
};
pub fn verify_crafted_2(asm: &CilObject) {
verify_cor20(asm);
verify_root(asm);
verify_tableheader(asm);
verify_custom_attributes(asm);
test_complex_generic_type(asm);
test_generic_struct_type(asm);
test_generic_delegate_type(asm);
test_generic_method_specs(asm);
test_extension_method_generic(asm);
test_inheritance_relationships(asm);
test_interface_implementations(asm);
test_type_flavor_classification(asm);
test_method_associations(asm);
test_event_and_property_semantics(asm);
test_nested_type_relationships(asm);
test_enum_and_constant_validation(asm);
test_generic_constraint_validation(asm);
test_pinvoke_and_security_validation(asm);
test_method_signature_validation(asm);
test_field_validation(asm);
test_assembly_metadata_validation(asm);
test_table_count_validation(asm);
test_custom_attribute_validation(asm);
test_xml_permission_set_parsing(asm);
}
fn verify_cor20(asm: &CilObject) {
let cor20 = asm.cor20header();
assert_eq!(cor20.cb, 0x48);
assert_eq!(cor20.major_runtime_version, 2);
assert_eq!(cor20.minor_runtime_version, 5);
assert_eq!(cor20.meta_data_rva, 0x26DC);
assert_eq!(cor20.meta_data_size, 0x2BA4);
assert_eq!(cor20.flags, 0x1);
assert_eq!(cor20.entry_point_token, 0x06000039);
assert_eq!(cor20.resource_rva, 0);
assert_eq!(cor20.resource_size, 0);
assert_eq!(cor20.strong_name_signature_rva, 0);
assert_eq!(cor20.strong_name_signature_size, 0);
assert_eq!(cor20.code_manager_table_rva, 0);
assert_eq!(cor20.code_manager_table_size, 0);
assert_eq!(cor20.vtable_fixups_rva, 0);
assert_eq!(cor20.vtable_fixups_size, 0);
assert_eq!(cor20.export_address_table_jmp_rva, 0);
assert_eq!(cor20.export_address_table_jmp_size, 0);
assert_eq!(cor20.managed_native_header_rva, 0);
assert_eq!(cor20.managed_native_header_size, 0);
}
fn verify_root(asm: &CilObject) {
let root = asm.metadata_root();
assert_eq!(root.signature, CIL_HEADER_MAGIC);
assert_eq!(root.major_version, 1);
assert_eq!(root.minor_version, 1);
assert_eq!(root.version, "v4.0.30319\0\0");
assert_eq!(root.flags, 0);
assert_eq!(root.stream_number, 5);
{
let stream = &root.stream_headers[0];
assert_eq!(stream.name, "#~");
assert_eq!(stream.offset, 0x6C);
assert_eq!(stream.size, 0x135C);
}
{
let stream = &root.stream_headers[1];
assert_eq!(stream.name, "#Strings");
assert_eq!(stream.offset, 0x13C8);
assert_eq!(stream.size, 0xEC4);
}
{
let stream = &root.stream_headers[2];
assert_eq!(stream.name, "#US");
assert_eq!(stream.offset, 0x228C);
assert_eq!(stream.size, 0xD4);
}
{
let stream = &root.stream_headers[3];
assert_eq!(stream.name, "#GUID");
assert_eq!(stream.offset, 0x2360);
assert_eq!(stream.size, 0x10);
}
{
let stream = &root.stream_headers[4];
assert_eq!(stream.name, "#Blob");
assert_eq!(stream.offset, 0x2370);
assert_eq!(stream.size, 0x834);
}
}
fn verify_tableheader(asm: &CilObject) {
let tables_header = asm.tables().unwrap();
assert_eq!(tables_header.major_version, 2);
assert_eq!(tables_header.minor_version, 0);
assert_eq!(tables_header.valid, 0x1E093FB7FF57);
assert_eq!(tables_header.sorted, 0x16003301FA00);
assert_eq!(tables_header.table_count(), 31);
match tables_header.table::<ModuleRaw>() {
Some(table) => {
assert_eq!(table.row_count, 1);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.generation, 0);
assert_eq!(row.name, 0x9CF);
assert_eq!(row.mvid, 1);
assert_eq!(row.encid, 0);
assert_eq!(row.encbaseid, 0);
let guids = asm.guids().unwrap();
let guid = guids.get(row.mvid as usize).unwrap();
assert_eq!(guid, uguid::guid!("85b7e7e7-adf5-40ee-b525-c916a61712f0"));
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<TypeRefRaw>() {
Some(table) => {
assert_eq!(table.row_count, 65);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.token.value(), 0x01000001);
assert_eq!(
row.resolution_scope,
CodedIndex::new(TableId::AssemblyRef, 1, CodedIndexType::ResolutionScope)
);
assert_eq!(row.type_name, 0x80D);
assert_eq!(row.type_namespace, 0xBE8);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<TypeDefRaw>() {
Some(table) => {
assert_eq!(table.row_count, 36);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.flags, 0);
assert_eq!(row.type_name, 0x1FD);
assert_eq!(row.type_namespace, 0);
assert_eq!(
row.extends,
CodedIndex::new(TableId::TypeDef, 0, CodedIndexType::TypeDefOrRef)
);
assert_eq!(row.field_list, 1);
assert_eq!(row.method_list, 1);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<FieldRaw>() {
Some(table) => {
assert_eq!(table.row_count, 48);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.flags, 0x26);
assert_eq!(row.name, 0xABB);
assert_eq!(row.signature, 0x505);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<MethodDefRaw>() {
Some(table) => {
assert_eq!(table.row_count, 97);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.rva, 0x2050);
assert_eq!(row.impl_flags, 0);
assert_eq!(row.flags, 0x1886);
assert_eq!(row.name, 0xBA5);
assert_eq!(row.signature, 1);
assert_eq!(row.param_list, 1);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<ParamRaw>() {
Some(table) => {
assert_eq!(table.row_count, 72);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.flags, 0);
assert_eq!(row.sequence, 1);
assert_eq!(row.name, 0x995);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<InterfaceImplRaw>() {
Some(table) => {
assert_eq!(table.row_count, 5);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.class, 7);
assert_eq!(
row.interface,
CodedIndex::new(TableId::TypeDef, 6, CodedIndexType::TypeDefOrRef)
);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<MemberRefRaw>() {
Some(table) => {
assert_eq!(table.row_count, 67);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(
row.class,
CodedIndex::new(TableId::TypeRef, 1, CodedIndexType::MemberRefParent)
);
assert_eq!(row.name, 0xBA5);
assert_eq!(row.signature, 1);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<ConstantRaw>() {
Some(table) => {
assert_eq!(table.row_count, 7);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.base, 0xA);
assert_eq!(
row.parent,
CodedIndex::new(TableId::Field, 7, CodedIndexType::HasConstant)
);
assert_eq!(row.value, 0x265);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<CustomAttributeRaw>() {
Some(table) => {
assert_eq!(table.row_count, 88);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(
row.parent,
CodedIndex::new(TableId::Module, 1, CodedIndexType::HasCustomAttribute)
);
assert_eq!(
row.constructor,
CodedIndex::new(TableId::MemberRef, 10, CodedIndexType::CustomAttributeType)
);
assert_eq!(row.value, 0x297);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<FieldMarshalRaw>() {
Some(table) => {
assert_eq!(table.row_count, 1);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(
row.parent,
CodedIndex::new(TableId::Field, 18, CodedIndexType::HasFieldMarshal)
);
assert_eq!(row.native_type, 0x503);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<DeclSecurityRaw>() {
Some(table) => {
assert_eq!(table.row_count, 2);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.action, 8);
assert_eq!(
row.parent,
CodedIndex::new(TableId::Assembly, 1, CodedIndexType::HasDeclSecurity)
);
assert_eq!(row.permission_set, 0x29C);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<ClassLayoutRaw>() {
Some(table) => {
assert_eq!(table.row_count, 3);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.packing_size, 0);
assert_eq!(row.class_size, 0x10);
assert_eq!(row.parent, 0xB);
}
None => {
panic!("This tables should be there");
}
}
match tables_header.table::<FieldLayoutRaw>() {
Some(table) => {
assert_eq!(table.row_count, 3);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.field_offset, 0);
assert_eq!(row.field, 0xC);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<StandAloneSigRaw>() {
Some(table) => {
assert_eq!(table.row_count, 11);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.signature, 0x53);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<EventMapRaw>() {
Some(module) => {
assert_eq!(module.row_count, 2);
let row = module.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.parent, 0x7);
assert_eq!(row.event_list, 0x1);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<EventRaw>() {
Some(table) => {
assert_eq!(table.row_count, 3);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.flags, 0);
assert_eq!(row.name, 0x102);
assert_eq!(
row.event_type,
CodedIndex::new(TableId::TypeRef, 23, CodedIndexType::TypeDefOrRef)
);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<PropertyMapRaw>() {
Some(table) => {
assert_eq!(table.row_count, 8);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.parent, 0x4);
assert_eq!(row.property_list, 1);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<PropertyRaw>() {
Some(table) => {
assert_eq!(table.row_count, 13);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.flags, 0);
assert_eq!(row.name, 0x9B4);
assert_eq!(row.signature, 0x66E);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<MethodSemanticsRaw>() {
Some(table) => {
assert_eq!(table.row_count, 31);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.semantics, 8);
assert_eq!(row.method, 0xE);
assert_eq!(
row.association,
CodedIndex::new(TableId::Event, 1, CodedIndexType::HasSemantics)
);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<MethodImplRaw>() {
Some(table) => {
assert_eq!(table.row_count, 4);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.class, 0xE);
assert_eq!(
row.method_body,
CodedIndex::new(TableId::MethodDef, 39, CodedIndexType::MethodDefOrRef)
);
assert_eq!(
row.method_declaration,
CodedIndex::new(TableId::MethodDef, 13, CodedIndexType::MethodDefOrRef)
);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<ModuleRefRaw>() {
Some(table) => {
assert_eq!(table.row_count, 2);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.name, 0xA33);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<TypeSpecRaw>() {
Some(module) => {
assert_eq!(module.row_count, 16);
let row = module.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.signature, 0x40);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<ImplMapRaw>() {
Some(table) => {
assert_eq!(table.row_count, 2);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.mapping_flags, 0x104);
assert_eq!(
row.member_forwarded,
CodedIndex::new(TableId::MethodDef, 8, CodedIndexType::MemberForwarded)
);
assert_eq!(row.import_name, 0xE86);
assert_eq!(row.import_scope, 0x1);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<FieldRvaRaw>() {
Some(module) => {
assert_eq!(module.row_count, 1);
let row = module.get(1).unwrap();
assert_eq!(row.rva, 0x5410);
assert_eq!(row.field, 0x1E);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<AssemblyRaw>() {
Some(table) => {
assert_eq!(table.row_count, 1);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.hash_alg_id, 0x8004);
assert_eq!(row.major_version, 1);
assert_eq!(row.minor_version, 2);
assert_eq!(row.build_number, 3);
assert_eq!(row.revision_number, 4);
assert_eq!(row.flags, 0);
assert_eq!(row.public_key, 0);
assert_eq!(row.name, 0x14E);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<AssemblyRefRaw>() {
Some(table) => {
assert_eq!(table.row_count, 2);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.major_version, 4);
assert_eq!(row.minor_version, 0);
assert_eq!(row.build_number, 0);
assert_eq!(row.revision_number, 0);
assert_eq!(row.flags, 0);
assert_eq!(row.public_key_or_token, 0x25C);
assert_eq!(row.name, 0x24B);
assert_eq!(row.hash_value, 0);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<NestedClassRaw>() {
Some(table) => {
assert_eq!(table.row_count, 10);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.nested_class, 0x1B);
assert_eq!(row.enclosing_class, 0xE);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<GenericParamRaw>() {
Some(table) => {
assert_eq!(table.row_count, 19);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.number, 0);
assert_eq!(row.flags, 4);
assert_eq!(
row.owner,
CodedIndex::new(TableId::TypeDef, 9, CodedIndexType::TypeOrMethodDef)
);
assert_eq!(row.name, 0x22F);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<MethodImplRaw>() {
Some(table) => {
assert_eq!(table.row_count, 4);
let row = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(
row.method_body,
CodedIndex::new(TableId::MethodDef, 39, CodedIndexType::MethodDefOrRef)
);
assert_eq!(
row.method_declaration,
CodedIndex::new(TableId::MethodDef, 13, CodedIndexType::MethodDefOrRef)
);
}
None => {
panic!("This table should be there");
}
}
match tables_header.table::<GenericParamConstraintRaw>() {
Some(table) => {
assert_eq!(table.row_count, 16);
let row: GenericParamConstraintRaw = table.get(1).unwrap();
assert_eq!(row.rid, 1);
assert_eq!(row.owner, 0x3);
assert_eq!(
row.constraint,
CodedIndex::new(TableId::TypeRef, 24, CodedIndexType::TypeDefOrRef)
);
}
None => {
panic!("This table should be there");
}
}
}
fn verify_custom_attributes(asm: &CilObject) {
let custom_attr_table = asm.tables().unwrap().table::<CustomAttributeRaw>().unwrap();
assert_eq!(
custom_attr_table.row_count, 88,
"Expected 88 custom attributes total"
);
verify_assembly_custom_attributes(asm);
verify_module_custom_attributes(asm);
verify_type_custom_attributes(asm);
verify_method_custom_attributes(asm);
verify_specialized_attribute_tables(asm);
}
fn verify_assembly_custom_attributes(asm: &CilObject) {
let custom_attr_table = asm.tables().unwrap().table::<CustomAttributeRaw>().unwrap();
let mut assembly_attr_count = 0;
for attr_row in custom_attr_table.iter() {
if attr_row.parent.token.value() == 0x20000001 {
assembly_attr_count += 1;
}
}
assert!(
assembly_attr_count >= 8,
"Expected at least 8 assembly-level custom attributes, found {assembly_attr_count}"
);
}
fn verify_module_custom_attributes(asm: &CilObject) {
let custom_attr_table = asm.tables().unwrap().table::<CustomAttributeRaw>().unwrap();
let mut module_attr_count = 0;
for attr_row in custom_attr_table.iter() {
if attr_row.parent.token.value() == 0x00000001 {
module_attr_count += 1;
}
}
assert!(
module_attr_count >= 1,
"Expected at least 1 module-level custom attribute, found {module_attr_count}"
);
}
fn verify_type_custom_attributes(asm: &CilObject) {
let types = asm.types();
let mut found_attributes = 0;
let mut specific_types_found = 0;
for entry in types.iter() {
let type_def = entry.value();
let custom_attrs = &type_def.custom_attributes;
let attr_count = custom_attrs.iter().count();
if attr_count > 0 {
found_attributes += attr_count;
match type_def.name.as_str() {
"MetadataTestAttribute" => {
assert!(
attr_count >= 1,
"MetadataTestAttribute should have AttributeUsage attribute"
);
specific_types_found += 1;
}
"TestEnum" => {
assert!(attr_count >= 1, "TestEnum should have Flags attribute");
specific_types_found += 1;
}
"StructWithExplicitLayout" => {
assert!(
attr_count >= 1,
"StructWithExplicitLayout should have StructLayout attribute"
);
specific_types_found += 1;
}
"BaseClass" => {
assert!(
attr_count >= 1,
"BaseClass should have Serializable attribute"
);
specific_types_found += 1;
}
"DerivedClass" => {
assert!(
attr_count >= 1,
"DerivedClass should have MetadataTest attribute"
);
specific_types_found += 1;
}
_ => {}
}
}
}
assert!(
found_attributes > 0,
"Expected to find some type-level custom attributes"
);
assert!(
specific_types_found >= 2,
"Expected to find at least 2 specific types with attributes, found {specific_types_found}"
);
}
fn verify_method_custom_attributes(asm: &CilObject) {
let methods = asm.methods();
let mut found_method_attributes = 0;
let mut specific_methods_found = 0;
for entry in methods.iter() {
let method = entry.value();
let custom_attrs = &method.custom_attributes;
let attr_count = custom_attrs.iter().count();
if attr_count > 0 {
found_method_attributes += attr_count;
match method.name.as_str() {
"ComplexMethod" => {
assert!(
attr_count >= 1,
"ComplexMethod should have Obsolete attribute"
);
specific_methods_found += 1;
}
"SecureMethod" => {
assert!(
attr_count >= 1,
"SecureMethod should have at least 1 custom attribute"
);
specific_methods_found += 1;
}
"AsyncMethod" => {
assert!(
attr_count >= 1,
"AsyncMethod should have compiler-generated attributes"
);
specific_methods_found += 1;
}
"ToCustomString" => {
assert!(
attr_count >= 1,
"ToCustomString should have extension method attribute"
);
specific_methods_found += 1;
}
_ => {}
}
}
}
assert!(
found_method_attributes > 0,
"Expected to find some method-level custom attributes"
);
assert!(
specific_methods_found >= 4,
"Expected to find at least 4 specific methods with attributes, found {specific_methods_found}"
);
}
fn verify_specialized_attribute_tables(asm: &CilObject) {
let tables = asm.tables().unwrap();
if let Some(field_layout_table) = tables.table::<FieldLayoutRaw>() {
let layout_count = field_layout_table.row_count;
assert!(
layout_count > 0,
"Expected FieldLayout entries for explicit layout fields"
);
assert_eq!(
layout_count, 3,
"Expected 3 FieldLayout entries for StructWithExplicitLayout fields"
);
}
if let Some(field_marshal_table) = tables.table::<FieldMarshalRaw>() {
let marshal_count = field_marshal_table.row_count;
assert!(
marshal_count > 0,
"Expected FieldMarshal entries for marshaled fields"
);
assert_eq!(
marshal_count, 1,
"Expected 1 FieldMarshal entry for _marshaledField"
);
let row = field_marshal_table.get(1).unwrap();
let blob_heap = asm.blob().expect("Expected blob heap to be present");
let descriptor_blob = blob_heap.get(row.native_type as usize).unwrap();
let marshalling_info = parse_marshalling_descriptor(descriptor_blob).unwrap();
match &marshalling_info.primary_type {
NativeType::LPWStr { size_param_index } => {
assert_eq!(
size_param_index, &None,
"Expected no size parameter for simple LPWStr"
);
println!("✓ Marshalling descriptor parsed successfully: {marshalling_info:?}");
}
_ => panic!(
"Expected LPWStr marshalling for _marshaledField, got {:?}",
marshalling_info.primary_type
),
}
}
if let Some(decl_security_table) = tables.table::<DeclSecurityRaw>() {
let security_count = decl_security_table.row_count;
assert!(
security_count > 0,
"Expected DeclSecurity entries for security attributes"
);
assert_eq!(
security_count, 2,
"Expected 2 DeclSecurity entries for assembly and method security attributes"
);
}
}
fn _verify_imports(asm: &CilObject) {
let imports = asm.imports();
let set_state_machine_class = imports.cil().by_name("SetStateMachine").unwrap();
assert_eq!(set_state_machine_class.token.value(), 0x0A000018);
assert_eq!(set_state_machine_class.name, "SetStateMachine");
assert_eq!(
set_state_machine_class.namespace,
"System.Runtime.CompilerServices"
);
match &set_state_machine_class.import {
ImportType::Method(ref_cell) => {
assert!(
ref_cell.rva.is_none(),
"The imported method should have no RVA"
);
}
_ => panic!("The import should be a method"),
}
}
fn test_complex_generic_type(asm: &CilObject) {
let types = asm.types();
let all_types = types.all_types();
let complex_generic = all_types
.iter()
.find(|t| t.name == "ComplexGeneric`3")
.expect("Should find ComplexGeneric`3 type");
assert_eq!(complex_generic.name, "ComplexGeneric`3");
assert_eq!(complex_generic.namespace, "");
assert_eq!(complex_generic.generic_params.count(), 3);
let params: Vec<_> = complex_generic.generic_params.iter().collect();
println!("ComplexGeneric`3 generic parameters:");
for (i, (_, param)) in params.iter().enumerate() {
println!(" [{}]: {} (number: {})", i, param.name, param.number);
}
assert_eq!(params.len(), 3, "Should have exactly 3 generic parameters");
let param_names: Vec<_> = params.iter().map(|(_, p)| p.name.as_str()).collect();
assert!(param_names.contains(&"TKey"), "Should have TKey parameter");
assert!(
param_names.contains(&"TValue"),
"Should have TValue parameter"
);
assert!(
param_names.contains(&"TOutput"),
"Should have TOutput parameter"
);
let mut complex_methods = Vec::new();
for (_, method_ref) in complex_generic.methods.iter() {
complex_methods.push(method_ref);
}
println!("ComplexGeneric has {} methods:", complex_methods.len());
for (i, method_ref) in complex_methods.iter().enumerate() {
if let Some(method) = method_ref.upgrade() {
println!(" Method {}: {}", i, method.name);
}
}
let methods = asm.methods();
println!("All methods related to ComplexGeneric:");
for entry in methods.iter() {
let method = entry.value();
if method.name.contains("ConstrainedMethod")
|| method.name.contains("ProcessValues")
|| method.name.contains("ComplexGeneric")
{
println!(
" Found method: {} (Token: 0x{:08X})",
method.name,
method.token.value()
);
}
}
println!(
"ComplexGeneric type found with {} generic parameters",
complex_generic.generic_params.count()
);
let methods = asm.methods();
let constrained_method = methods
.iter()
.find(|entry| entry.value().name == "ConstrainedMethod")
.expect("Should find ConstrainedMethod");
assert!(
constrained_method.value().generic_params.count() >= 2,
"ConstrainedMethod should have at least 2 generic parameters"
);
}
fn test_generic_struct_type(asm: &CilObject) {
let types = asm.types();
let all_types = types.all_types();
let generic_struct = all_types
.iter()
.find(|t| t.name == "GenericStruct`2")
.expect("Should find GenericStruct`2 type");
assert_eq!(generic_struct.name, "GenericStruct`2");
let actual_flavor = generic_struct.flavor();
println!("GenericStruct`2 flavor: {actual_flavor:?}");
assert!(matches!(*generic_struct.flavor(), CilFlavor::ValueType));
assert_eq!(generic_struct.generic_params.count(), 2);
let params: Vec<_> = generic_struct.generic_params.iter().collect();
let param_names: Vec<&str> = params
.iter()
.map(|(_, param)| param.name.as_str())
.collect();
assert!(
param_names.contains(&"T"),
"Should have generic parameter T"
);
assert!(
param_names.contains(&"U"),
"Should have generic parameter U"
);
println!("GenericStruct`2 generic parameters: {param_names:?}");
}
fn test_generic_delegate_type(asm: &CilObject) {
let types = asm.types();
let all_types = types.all_types();
let generic_delegate = all_types
.iter()
.find(|t| t.name == "GenericDelegate`2")
.expect("Should find GenericDelegate`2 type");
assert_eq!(generic_delegate.name, "GenericDelegate`2");
let actual_delegate_flavor = generic_delegate.flavor();
println!("GenericDelegate`2 flavor: {actual_delegate_flavor:?}");
assert!(matches!(*generic_delegate.flavor(), CilFlavor::Class));
assert_eq!(generic_delegate.generic_params.count(), 2);
let params: Vec<_> = generic_delegate.generic_params.iter().collect();
assert_eq!(params[0].1.name, "T");
assert_eq!(params[1].1.name, "TResult");
}
fn test_generic_method_specs(asm: &CilObject) {
let method_specs = asm.method_specs();
assert!(
method_specs.iter().count() > 0,
"Should have generic method instantiations"
);
for entry in method_specs.iter().take(5) {
let (token, method_spec) = (entry.key(), entry.value());
assert!(
token.value() >= 0x2B000000 && token.value() < 0x2C000000,
"MethodSpec token should be in 0x2B range"
);
if method_spec.generic_args.count() > 0 {
println!(
"MethodSpec 0x{:08X} has {} resolved type arguments",
token.value(),
method_spec.generic_args.count()
);
for (i, resolved_type) in method_spec.generic_args.iter().enumerate() {
if let Some(type_name) = resolved_type.1.name() {
println!(" Arg[{i}]: {type_name}");
assert!(
!type_name.is_empty(),
"Resolved type should have a non-empty name"
);
} else {
println!(" Arg[{i}]: <Unknown type>");
}
}
}
if !method_spec.instantiation.generic_args.is_empty() {
println!(
"MethodSpec 0x{:08X} has {} signature arguments",
token.value(),
method_spec.instantiation.generic_args.len()
);
}
}
}
fn test_extension_method_generic(asm: &CilObject) {
let methods = asm.methods();
let to_custom_string = methods
.iter()
.find(|entry| entry.value().name == "ToCustomString")
.expect("Should find ToCustomString extension method");
let method = to_custom_string.value();
assert!(
method.generic_params.count() >= 1,
"ToCustomString should have at least 1 generic parameter"
);
assert!(method.is_static(), "Extension method should be static");
if method.generic_args.count() > 0 {
println!(
"ToCustomString has {} generic instantiations",
method.generic_args.count()
);
for (i, method_spec) in method.generic_args.iter().enumerate() {
println!(
" Instantiation[{}]: Token 0x{:08X}",
i,
method_spec.1.token.value()
);
for (j, resolved_type) in method_spec.1.generic_args.iter().enumerate() {
if let Some(type_name) = resolved_type.1.name() {
println!(" Type[{j}]: {type_name}");
}
}
}
}
}
fn test_inheritance_relationships(asm: &CilObject) {
println!("Testing inheritance relationships...");
let types = asm.types();
let all_types = types.all_types();
let base_class = all_types
.iter()
.find(|t| t.name == "BaseClass")
.expect("Should find BaseClass");
let derived_class = all_types
.iter()
.find(|t| t.name == "DerivedClass")
.expect("Should find DerivedClass");
let base_type = derived_class
.base()
.expect("DerivedClass should have a base class");
println!(
"DerivedClass base type: {}:{}",
base_type.namespace, base_type.name
);
assert_eq!(
base_type.name, "BaseClass",
"DerivedClass should inherit from BaseClass"
);
println!("✓ DerivedClass correctly inherits from BaseClass");
let _person_class = all_types
.iter()
.find(|t| t.name == "Person")
.expect("Should find Person class");
let employee_class = all_types
.iter()
.find(|t| t.name == "Employee")
.expect("Should find Employee class");
let employee_base_type = employee_class
.base()
.expect("Employee should have a base class");
println!(
"Employee base type: {}:{}",
employee_base_type.namespace, employee_base_type.name
);
assert_eq!(
employee_base_type.name, "Person",
"Employee should inherit from Person"
);
println!("✓ Employee correctly inherits from Person");
let virtual_method = base_class.methods.iter().find(|(_, method_ref)| {
if let Some(method) = method_ref.upgrade() {
method.name == "VirtualMethod"
} else {
false
}
});
assert!(
virtual_method.is_some(),
"BaseClass should have VirtualMethod"
);
println!("✓ BaseClass has VirtualMethod");
let abstract_method = base_class.methods.iter().find(|(_, method_ref)| {
if let Some(method) = method_ref.upgrade() {
method.name == "AbstractMethod"
} else {
false
}
});
assert!(
abstract_method.is_some(),
"BaseClass should have AbstractMethod"
);
println!("✓ BaseClass has AbstractMethod");
println!("✓ Inheritance relationships validated");
}
fn test_interface_implementations(asm: &CilObject) {
println!("Testing interface implementations...");
let types = asm.types();
let all_types = types.all_types();
let base_interface = all_types
.iter()
.find(|t| t.name == "IBaseInterface")
.expect("Should find IBaseInterface");
let derived_interface = all_types
.iter()
.find(|t| t.name == "IDerivedInterface")
.expect("Should find IDerivedInterface");
let base_interface_flavor = base_interface.flavor();
let derived_interface_flavor = derived_interface.flavor();
println!("IBaseInterface flavor: {base_interface_flavor:?}");
println!("IDerivedInterface flavor: {derived_interface_flavor:?}");
assert!(
!derived_interface.interfaces.is_empty(),
"IDerivedInterface should implement/inherit from at least one interface"
);
let base_interface_ref = derived_interface
.interfaces
.iter()
.find(|(_, iface)| {
if let Some(iface_type) = iface.upgrade() {
iface_type.name == "IBaseInterface"
} else {
false
}
})
.expect("IDerivedInterface should inherit from IBaseInterface");
let base_type = base_interface_ref
.1
.upgrade()
.expect("IBaseInterface reference should be valid");
println!(
"IDerivedInterface inherits from: {}:{}",
base_type.namespace, base_type.name
);
assert_eq!(
base_type.name, "IBaseInterface",
"IDerivedInterface should inherit from IBaseInterface"
);
let derived_class = all_types
.iter()
.find(|t| t.name == "DerivedClass")
.expect("Should find DerivedClass");
let method1_impl = derived_class.methods.iter().find(|(_, method_ref)| {
if let Some(method) = method_ref.upgrade() {
method.name == "Method1"
} else {
false
}
});
assert!(
method1_impl.is_some(),
"DerivedClass should implement Method1 from IBaseInterface"
);
println!("✓ DerivedClass implements Method1 from IBaseInterface");
println!("✓ Interface implementations validated");
}
fn test_type_flavor_classification(asm: &CilObject) {
println!("Testing type flavor classification...");
let types = asm.types();
let all_types = types.all_types();
let mut classification_results = Vec::new();
for type_def in all_types.iter() {
let flavor = type_def.flavor();
classification_results.push((type_def.name.clone(), format!("{flavor:?}")));
match type_def.name.as_str() {
"GenericStruct`2" => {
println!("GenericStruct`2 flavor: {flavor:?}");
assert!(
matches!(flavor, CilFlavor::ValueType),
"GenericStruct should be ValueType"
);
}
"GenericDelegate`2" => {
println!("GenericDelegate`2 flavor: {flavor:?}");
assert!(
matches!(flavor, CilFlavor::Class),
"GenericDelegate should be Class"
);
}
"IBaseInterface" | "IDerivedInterface" => {
println!("{} flavor: {:?}", type_def.name, flavor);
assert!(
matches!(flavor, CilFlavor::Interface),
"Interfaces should be Interface flavor"
);
}
"TestEnum" => {
println!("TestEnum flavor: {flavor:?}");
assert!(
matches!(flavor, CilFlavor::ValueType),
"Enums should be ValueType"
);
}
"StructWithExplicitLayout" => {
println!("StructWithExplicitLayout flavor: {flavor:?}");
assert!(
matches!(flavor, CilFlavor::ValueType),
"Structs should be ValueType"
);
}
"BaseClass" | "DerivedClass" => {
println!("{} flavor: {:?}", type_def.name, flavor);
assert!(
matches!(flavor, CilFlavor::Class),
"Classes should be Class flavor"
);
}
"ComplexGeneric`3" => {
println!("{} flavor: {:?}", type_def.name, flavor);
assert!(
matches!(flavor, CilFlavor::Class | CilFlavor::GenericInstance),
"Generic classes should be Class or GenericInstance flavor"
);
}
_ => {}
}
}
println!("Type flavor classification summary:");
for (name, flavor) in &classification_results {
if !name.starts_with('<') && !name.is_empty() {
println!(" {name}: {flavor}");
}
}
println!("✓ Type flavor classification validated");
}
fn test_method_associations(asm: &CilObject) {
println!("Testing method associations...");
let types = asm.types();
let all_types = types.all_types();
let complex_generic = all_types
.iter()
.find(|t| t.name == "ComplexGeneric`3")
.expect("Should find ComplexGeneric`3");
let method_count = complex_generic.methods.iter().count();
println!("ComplexGeneric`3 has {method_count} associated methods");
for (i, (_, method_ref)) in complex_generic.methods.iter().enumerate() {
if let Some(method) = method_ref.upgrade() {
println!(
" Method {}: {} (Token: 0x{:08X})",
i,
method.name,
method.token.value()
);
}
}
assert!(method_count >= 3, "ComplexGeneric`3 should have at least 3 methods (constructor, ConstrainedMethod, ProcessValues)");
println!("✓ ComplexGeneric`3 method association is working");
let method_names: Vec<String> = complex_generic
.methods
.iter()
.filter_map(|(_, method_ref)| method_ref.upgrade().map(|m| m.name.clone()))
.collect();
assert!(
method_names.iter().any(|name| name == "ConstrainedMethod"),
"Should find ConstrainedMethod"
);
assert!(
method_names.iter().any(|name| name == "ProcessValues"),
"Should find ProcessValues"
);
assert!(
method_names.iter().any(|name| name == ".ctor"),
"Should find constructor"
);
for type_def in all_types.iter().take(10) {
let method_count = type_def.methods.iter().count();
if method_count > 0 {
println!("Type '{}' has {} methods", type_def.name, method_count);
}
}
println!("✓ Method associations tested");
}
fn test_event_and_property_semantics(asm: &CilObject) {
println!("Testing event and property semantics...");
let types = asm.types();
let all_types = types.all_types();
let derived_class = all_types
.iter()
.find(|t| t.name == "DerivedClass")
.expect("Should find DerivedClass");
let events_count = derived_class.events.iter().count();
println!("DerivedClass has {events_count} events");
assert_eq!(
events_count, 2,
"DerivedClass should have exactly 2 events (Event1 and CustomEvent)"
);
let mut expected_events = std::collections::HashSet::from(["Event1", "CustomEvent"]);
for (_, event) in derived_class.events.iter() {
println!(" Event: {}", event.name);
assert!(
expected_events.remove(event.name.as_str()),
"Found unexpected event: {}",
event.name
);
let add_method_name = format!("add_{}", event.name);
let remove_method_name = format!("remove_{}", event.name);
let has_add_method = derived_class.methods.iter().any(|(_, method_ref)| {
method_ref
.upgrade()
.map(|m| m.name == add_method_name)
.unwrap_or(false)
});
let has_remove_method = derived_class.methods.iter().any(|(_, method_ref)| {
method_ref
.upgrade()
.map(|m| m.name == remove_method_name)
.unwrap_or(false)
});
assert!(
has_add_method,
"Event {} should have add method {}",
event.name, add_method_name
);
assert!(
has_remove_method,
"Event {} should have remove method {}",
event.name, remove_method_name
);
println!(" Has add method ({add_method_name}): {has_add_method}");
println!(" Has remove method ({remove_method_name}): {has_remove_method}");
}
assert!(
expected_events.is_empty(),
"Missing expected events: {expected_events:?}"
);
let properties_count = derived_class.properties.iter().count();
println!("DerivedClass has {properties_count} properties");
assert_eq!(
properties_count, 1,
"DerivedClass should have exactly 1 property (Property1)"
);
for (_, property) in derived_class.properties.iter() {
println!(" Property: {}", property.name);
assert_eq!(
property.name, "Property1",
"Expected property should be Property1"
);
let get_method_name = format!("get_{}", property.name);
let set_method_name = format!("set_{}", property.name);
let has_get_method = derived_class.methods.iter().any(|(_, method_ref)| {
method_ref
.upgrade()
.map(|m| m.name == get_method_name)
.unwrap_or(false)
});
let has_set_method = derived_class.methods.iter().any(|(_, method_ref)| {
method_ref
.upgrade()
.map(|m| m.name == set_method_name)
.unwrap_or(false)
});
assert!(
has_get_method,
"Property {} should have get method {}",
property.name, get_method_name
);
assert!(
has_set_method,
"Property {} should have set method {}",
property.name, set_method_name
);
println!(" Has get method ({get_method_name}): {has_get_method}");
println!(" Has set method ({set_method_name}): {has_set_method}");
}
println!("✓ Event and property semantics tested");
}
fn test_nested_type_relationships(asm: &CilObject) {
println!("Testing nested type relationships...");
let types = asm.types();
let all_types = types.all_types();
let mut nested_types_found = 0;
let mut enclosing_types = std::collections::HashMap::new();
for type_def in all_types.iter() {
let nested_count = type_def.nested_types.iter().count();
if nested_count > 0 {
println!(
"Type '{}' has {} nested types:",
type_def.name, nested_count
);
for (_, nested_ref) in type_def.nested_types.iter() {
if let Some(nested_type) = nested_ref.upgrade() {
nested_types_found += 1;
println!(" Nested: {}", nested_type.name);
enclosing_types.insert(nested_type.name.clone(), type_def.name.clone());
}
}
}
if type_def.name.contains("Nested") || type_def.name.contains("+") {
if !enclosing_types.contains_key(&type_def.name) {
nested_types_found += 1;
}
println!(
"Found nested type: {} (Namespace: '{}')",
type_def.name, type_def.namespace
);
if let Some(base_type) = type_def.base() {
println!(" Base type: {}", base_type.name);
} else {
println!(" No base type found");
}
}
}
println!("Found {nested_types_found} nested types total");
let expected_nested = vec![
"NestedClass",
"NestedEnum",
"NestedGeneric`1",
"NestedStruct",
];
for nested_name in expected_nested {
let found_nested = all_types.iter().find(|t| t.name == nested_name);
assert!(
found_nested.is_some(),
"Expected nested type not found: {nested_name}"
);
println!("✓ Found expected nested type: {nested_name}");
if let Some(enclosing_name) = enclosing_types.get(nested_name) {
println!(" ✓ Correctly enclosed by: {enclosing_name}");
match nested_name {
"NestedClass" | "NestedEnum" | "NestedGeneric`1" => {
assert_eq!(
enclosing_name, "DerivedClass",
"{nested_name} should be enclosed by DerivedClass"
);
}
"NestedStruct" => {
assert_eq!(
enclosing_name, "ComplexGeneric`3",
"NestedStruct should be enclosed by ComplexGeneric`3"
);
}
_ => {}
}
} else {
println!(" ? Enclosing relationship not found in nested_types collections");
println!(" (This might indicate the NestedClass table parsing needs verification)");
}
}
let tables = asm.tables().unwrap();
if let Some(nested_table) = tables.table::<NestedClassRaw>() {
println!("NestedClass table has {} entries:", nested_table.row_count);
assert_eq!(
nested_table.row_count, 10,
"Expected exactly 10 nested class entries"
);
for nested_row in nested_table.iter().take(5) {
println!(
" NestedClass: nested=0x{:X}, enclosing=0x{:X}",
nested_row.nested_class, nested_row.enclosing_class
);
}
}
println!("✓ Nested type relationships tested");
}
fn test_enum_and_constant_validation(asm: &CilObject) {
println!("Testing enum and constant validation...");
let types = asm.types();
let all_types = types.all_types();
let test_enum = all_types
.iter()
.find(|t| t.name == "TestEnum")
.expect("Should find TestEnum");
println!("TestEnum type found");
println!(" Flavor: {:?}", *test_enum.flavor());
let fields_count = test_enum.fields.iter().count();
println!(" Has {fields_count} fields");
assert_eq!(
fields_count, 6,
"TestEnum should have 6 fields (value__ + 5 enum values)"
);
for (_, field) in test_enum.fields.iter() {
println!(" Field: {} (Flags: 0x{:X})", field.name, field.flags);
}
let expected_enum_fields = vec!["value__", "None", "Value1", "Value2", "Value3", "All"];
for expected_field in expected_enum_fields {
let found_field = test_enum
.fields
.iter()
.find(|(_, field)| field.name == expected_field);
assert!(
found_field.is_some(),
"Expected enum field not found: {expected_field}"
);
println!(" ✓ Found expected enum field: {expected_field}");
}
let tables = asm.tables().unwrap();
if let Some(constant_table) = tables.table::<ConstantRaw>() {
println!("Constant table has {} entries", constant_table.row_count);
assert_eq!(
constant_table.row_count, 7,
"Expected exactly 7 constant entries"
);
let constant_rows: Vec<_> = constant_table.iter().take(5).collect();
for constant_row in constant_rows {
println!(
" Constant: type=0x{:X}, parent=0x{:08X}, value=0x{:X}",
constant_row.base,
constant_row.parent.token.value(),
constant_row.value
);
}
}
println!("✓ Enum and constant validation tested");
}
fn test_generic_constraint_validation(asm: &CilObject) {
println!("Testing generic constraint validation...");
let types = asm.types();
let all_types = types.all_types();
let complex_generic = all_types
.iter()
.find(|t| t.name == "ComplexGeneric`3")
.expect("Should find ComplexGeneric`3");
println!("ComplexGeneric`3 generic parameters and constraints:");
assert_eq!(
complex_generic.generic_params.count(),
3,
"ComplexGeneric`3 should have exactly 3 generic parameters"
);
let mut found_params = std::collections::HashMap::new();
for (_, param) in complex_generic.generic_params.iter() {
println!(" Parameter: {} (Number: {})", param.name, param.number);
println!(" Flags: 0x{:X}", param.flags);
let constraints_count = param.constraints.iter().count();
println!(" Has {constraints_count} constraints");
let constraint_names: Vec<String> = param
.constraints
.iter()
.filter_map(|(_, constraint)| constraint.name())
.collect();
for constraint_name in &constraint_names {
println!(" Constraint: {constraint_name}");
}
match param.name.as_str() {
"TKey" => {
println!(" Expected: struct + IEquatable<TKey>");
assert!(
constraints_count >= 1,
"TKey should have at least 1 constraint"
);
}
"TValue" => {
println!(" Expected: class + IDisposable + new()");
assert!(
constraints_count >= 1,
"TValue should have at least 1 constraint (IDisposable)"
);
assert!(
constraint_names
.iter()
.any(|name| name.contains("IDisposable")),
"TValue should have IDisposable constraint"
);
}
"TOutput" => {
println!(" Expected: IBaseInterface");
assert!(
constraints_count >= 1,
"TOutput should have at least 1 constraint"
);
assert!(
constraint_names
.iter()
.any(|name| name.contains("IBaseInterface")),
"TOutput should have IBaseInterface constraint"
);
}
_ => {}
}
found_params.insert(param.name.clone(), constraint_names);
}
assert!(
found_params.contains_key("TKey"),
"Should find TKey parameter"
);
assert!(
found_params.contains_key("TValue"),
"Should find TValue parameter"
);
assert!(
found_params.contains_key("TOutput"),
"Should find TOutput parameter"
);
let methods = asm.methods();
if let Some(constrained_method) = methods
.iter()
.find(|entry| entry.value().name == "ConstrainedMethod")
{
let method = constrained_method.value();
println!("ConstrainedMethod<T, U> generic parameters:");
assert_eq!(
method.generic_params.count(),
2,
"ConstrainedMethod should have exactly 2 generic parameters"
);
let mut method_params = std::collections::HashMap::new();
for (_, param) in method.generic_params.iter() {
println!(
" Method parameter: {} (Number: {})",
param.name, param.number
);
let constraints_count = param.constraints.iter().count();
println!(" Has {constraints_count} constraints");
let constraint_names: Vec<String> = param
.constraints
.iter()
.filter_map(|(_, constraint)| constraint.name())
.collect();
for constraint_name in &constraint_names {
println!(" Constraint: {constraint_name}");
}
method_params.insert(param.name.clone(), constraint_names);
}
assert!(
method_params.contains_key("T"),
"Should find T method parameter"
);
assert!(
method_params.contains_key("U"),
"Should find U method parameter"
);
if let Some(u_constraints) = method_params.get("U") {
assert!(
u_constraints
.iter()
.any(|name| name.contains("IConvertible")),
"Method parameter U should have IConvertible constraint"
);
}
}
println!("✓ Generic constraint validation tested");
}
fn test_pinvoke_and_security_validation(asm: &CilObject) {
println!("Testing P/Invoke and security validation...");
let methods = asm.methods();
let expected_pinvoke = vec!["LoadLibrary", "MessageBox"];
let mut found_pinvoke = std::collections::HashSet::new();
let mut pinvoke_methods = Vec::new();
for entry in methods.iter() {
let method = entry.value();
if expected_pinvoke.contains(&method.name.as_str()) {
pinvoke_methods.push(method.clone());
found_pinvoke.insert(method.name.clone());
println!("Found P/Invoke method: {}", method.name);
println!(
" Flags: 0x{:X}",
method
.flags_pinvoke
.load(std::sync::atomic::Ordering::Relaxed)
);
println!(" Impl management: (details not printable)");
}
}
for expected in &expected_pinvoke {
assert!(
found_pinvoke.contains(*expected),
"Expected P/Invoke method not found: {expected}"
);
}
assert_eq!(
found_pinvoke.len(),
2,
"Should find exactly 2 P/Invoke methods"
);
let tables = asm.tables().unwrap();
if let Some(implmap_table) = tables.table::<ImplMapRaw>() {
println!("ImplMap table has {} entries", implmap_table.row_count);
assert_eq!(
implmap_table.row_count, 2,
"Expected exactly 2 ImplMap entries"
);
for implmap_row in implmap_table.iter() {
println!(
" ImplMap: flags=0x{:X}, member=0x{:08X}, name=0x{:X}, scope=0x{:X}",
implmap_row.mapping_flags,
implmap_row.member_forwarded.token.value(),
implmap_row.import_name,
implmap_row.import_scope
);
}
}
if let Some(declsecurity_table) = tables.table::<DeclSecurityRaw>() {
println!(
"DeclSecurity table has {} entries",
declsecurity_table.row_count
);
assert_eq!(
declsecurity_table.row_count, 2,
"Expected exactly 2 DeclSecurity entries"
);
for security_row in declsecurity_table.iter() {
println!(
" Security: action={}, parent=0x{:08X}, permission_set=0x{:X}",
security_row.action,
security_row.parent.token.value(),
security_row.permission_set
);
}
}
let secure_method = methods
.iter()
.find(|entry| entry.value().name == "SecureMethod");
assert!(
secure_method.is_some(),
"Should find SecureMethod with security attributes"
);
if let Some(secure_method_entry) = secure_method {
let method = secure_method_entry.value();
println!("Found security method: {}", method.name);
let attr_count = method.custom_attributes.iter().count();
println!(" Has {attr_count} custom attributes");
assert!(
attr_count >= 1,
"SecureMethod should have at least 1 custom attribute (SecurityCritical)"
);
for (i, _attr) in method.custom_attributes.iter().take(3) {
println!(" Attribute {}: (details would need parsing)", i + 1);
}
}
println!("✓ P/Invoke and security validation tested");
}
fn test_method_signature_validation(asm: &CilObject) {
println!("Testing method signature validation...");
let methods = asm.methods();
let complex_method = methods
.iter()
.find(|entry| entry.value().name == "ComplexMethod");
if let Some(complex_method_entry) = complex_method {
let method = complex_method_entry.value();
println!("ComplexMethod signature validation:");
let param_count = method.params.iter().count();
println!(" Parameter count: {param_count}");
assert_eq!(
param_count, 5,
"ComplexMethod should have exactly 5 input parameters"
);
let param_names: Vec<String> = method
.params
.iter()
.filter_map(|(_, param)| param.name.clone())
.collect();
println!(" Parameter names: {param_names:?}");
let expected_params = vec![
"normalParam",
"refParam",
"outParam",
"optionalParam",
"paramsArray",
];
assert!(
param_names.len() >= 4,
"Should have at least 4 named parameters"
);
for expected_param in &expected_params {
if param_names.iter().any(|name| name == expected_param) {
println!(" ✓ Found expected parameter: {expected_param}");
}
}
assert!(
param_names.iter().any(|name| name == "normalParam"),
"Should find normalParam"
);
println!(" ✓ ComplexMethod parameter validation completed");
}
let constrained_method = methods
.iter()
.find(|entry| entry.value().name == "ConstrainedMethod");
if let Some(constrained_method_entry) = constrained_method {
let method = constrained_method_entry.value();
println!("ConstrainedMethod signature validation:");
let param_count = method.params.iter().count();
println!(" Parameter count: {param_count}");
assert!(
param_count >= 2,
"ConstrainedMethod should have at least 2 parameters (excluding return)"
);
let param_names: Vec<String> = method
.params
.iter()
.filter(|(_, param)| param.sequence > 0) .filter_map(|(_, param)| param.name.clone())
.collect();
println!(" ✓ Generic method parameters validated: {param_names:?}");
}
let load_library = methods
.iter()
.find(|entry| entry.value().name == "LoadLibrary");
if let Some(load_library_entry) = load_library {
let method = load_library_entry.value();
println!("LoadLibrary P/Invoke signature validation:");
let param_count = method.params.iter().count();
println!(" Parameter count: {param_count}");
assert!(
param_count >= 1,
"LoadLibrary should have at least 1 parameter"
);
println!(" ✓ P/Invoke method validated");
}
println!("✓ Method signature validation completed");
}
fn test_field_validation(asm: &CilObject) {
println!("Testing field validation...");
let types = asm.types();
let all_types = types.all_types();
let explicit_struct = all_types
.iter()
.find(|t| t.name == "StructWithExplicitLayout");
if let Some(struct_type) = explicit_struct {
println!("StructWithExplicitLayout field validation:");
let field_count = struct_type.fields.iter().count();
println!(" Field count: {field_count}");
assert_eq!(
field_count, 3,
"StructWithExplicitLayout should have exactly 3 fields"
);
let expected_fields = vec!["Field1", "Field2", "Overlay"];
let field_names: Vec<String> = struct_type
.fields
.iter()
.map(|(_, field)| field.name.clone())
.collect();
for expected_field in expected_fields {
assert!(
field_names.iter().any(|name| name == expected_field),
"Should find field: {expected_field}"
);
}
println!(" ✓ All expected fields found: {field_names:?}");
}
let generic_struct = all_types.iter().find(|t| t.name == "GenericStruct`2");
if let Some(struct_type) = generic_struct {
println!("GenericStruct`2 field validation:");
let field_count = struct_type.fields.iter().count();
println!(" Field count: {field_count}");
assert_eq!(
field_count, 2,
"GenericStruct`2 should have exactly 2 fields"
);
let expected_fields = vec!["Field1", "Field2"];
let field_names: Vec<String> = struct_type
.fields
.iter()
.map(|(_, field)| field.name.clone())
.collect();
for expected_field in expected_fields {
assert!(
field_names.iter().any(|name| name == expected_field),
"Should find field: {expected_field}"
);
}
println!(" ✓ Generic struct fields validated: {field_names:?}");
}
let base_class = all_types.iter().find(|t| t.name == "BaseClass");
if let Some(class_type) = base_class {
println!("BaseClass field validation:");
let field_count = class_type.fields.iter().count();
println!(" Field count: {field_count}");
let field_names: Vec<String> = class_type
.fields
.iter()
.map(|(_, field)| field.name.clone())
.collect();
assert!(
field_names.iter().any(|name| name == "StaticData"),
"BaseClass should have StaticData field"
);
println!(" ✓ BaseClass fields include: {field_names:?}");
}
let derived_class = all_types.iter().find(|t| t.name == "DerivedClass");
if let Some(class_type) = derived_class {
println!("DerivedClass field validation:");
let field_count = class_type.fields.iter().count();
println!(" Field count: {field_count}");
let field_names: Vec<String> = class_type
.fields
.iter()
.map(|(_, field)| field.name.clone())
.collect();
println!(" DerivedClass fields: {field_names:?}");
assert!(field_count > 0, "DerivedClass should have fields");
println!(" ✓ DerivedClass field count validated");
}
println!("✓ Field validation completed");
}
fn test_table_count_validation(asm: &CilObject) {
println!("Testing table count validation...");
let tables = asm.tables().unwrap();
if let Some(typedef_table) = tables.table::<TypeDefRaw>() {
let typedef_count = typedef_table.row_count;
println!("TypeDef table has {typedef_count} entries");
assert!(
typedef_count >= 10,
"Should have at least 10 type definitions"
);
}
if let Some(methoddef_table) = tables.table::<MethodDefRaw>() {
let methoddef_count = methoddef_table.row_count;
println!("MethodDef table has {methoddef_count} entries");
assert!(
methoddef_count >= 20,
"Should have at least 20 method definitions"
);
}
if let Some(field_table) = tables.table::<FieldRaw>() {
let field_count = field_table.row_count;
println!("Field table has {field_count} entries");
assert!(
field_count >= 10,
"Should have at least 10 field definitions"
);
}
if let Some(param_table) = tables.table::<ParamRaw>() {
let param_count = param_table.row_count;
println!("Param table has {param_count} entries");
assert!(
param_count >= 15,
"Should have at least 15 parameter definitions"
);
}
if let Some(generic_param_table) = tables.table::<GenericParamRaw>() {
let generic_param_count = generic_param_table.row_count;
println!("GenericParam table has {generic_param_count} entries");
assert!(
generic_param_count >= 5,
"Should have at least 5 generic parameters"
);
}
if let Some(memberref_table) = tables.table::<MemberRefRaw>() {
let memberref_count = memberref_table.row_count;
println!("MemberRef table has {memberref_count} entries");
assert!(
memberref_count >= 20,
"Should have at least 20 member references"
);
}
if let Some(typeref_table) = tables.table::<TypeRefRaw>() {
let typeref_count = typeref_table.row_count;
println!("TypeRef table has {typeref_count} entries");
assert!(
typeref_count >= 30,
"Should have at least 30 type references"
);
}
println!("✓ Table count validation completed");
}
fn test_custom_attribute_validation(asm: &CilObject) {
println!("Testing custom attribute validation...");
let methods = asm.methods();
let secure_method = methods
.iter()
.find(|entry| entry.value().name == "SecureMethod");
if let Some(secure_method_entry) = secure_method {
let method = secure_method_entry.value();
println!("SecureMethod custom attribute validation:");
let attr_count = method.custom_attributes.iter().count();
println!(" Custom attribute count: {attr_count}");
assert!(
attr_count >= 1,
"SecureMethod should have at least 1 custom attribute"
);
for (i, _attr) in method.custom_attributes.iter().take(3) {
println!(" Attribute {}: (present)", i + 1);
}
println!(" ✓ SecureMethod has expected custom attributes");
}
let complex_method = methods
.iter()
.find(|entry| entry.value().name == "ComplexMethod");
if let Some(complex_method_entry) = complex_method {
let method = complex_method_entry.value();
println!("ComplexMethod custom attribute validation:");
let attr_count = method.custom_attributes.iter().count();
println!(" Custom attribute count: {attr_count}");
assert!(
attr_count >= 1,
"ComplexMethod should have at least 1 custom attribute (Obsolete)"
);
println!(" ✓ ComplexMethod has expected custom attributes");
}
let types = asm.types();
let all_types = types.all_types();
let derived_class = all_types.iter().find(|t| t.name == "DerivedClass");
if let Some(class_type) = derived_class {
println!("DerivedClass custom attribute validation:");
let attr_count = class_type.custom_attributes.iter().count();
println!(" Custom attribute count: {attr_count}");
assert!(
attr_count >= 1,
"DerivedClass should have at least 1 custom attribute"
);
println!(" ✓ DerivedClass has expected custom attributes");
}
println!("✓ Custom attribute validation completed");
}
fn test_assembly_metadata_validation(asm: &CilObject) {
println!("Testing assembly metadata validation...");
let tables = asm.tables().unwrap();
if let Some(assembly_table) = tables.table::<AssemblyRaw>() {
let assembly_count = assembly_table.row_count;
println!("Assembly table has {assembly_count} entries");
assert_eq!(assembly_count, 1, "Should have exactly 1 assembly entry");
if let Some(assembly_row) = assembly_table.get(1) {
println!("Assembly metadata:");
println!(" Major version: {}", assembly_row.major_version);
println!(" Minor version: {}", assembly_row.minor_version);
println!(" Build number: {}", assembly_row.build_number);
println!(" Revision number: {}", assembly_row.revision_number);
println!(" Flags: 0x{:X}", assembly_row.flags);
println!(" Hash algorithm ID: 0x{:X}", assembly_row.hash_alg_id);
println!(
" Assembly version: {}.{}.{}.{}",
assembly_row.major_version,
assembly_row.minor_version,
assembly_row.build_number,
assembly_row.revision_number
);
println!(" ✓ Assembly metadata validated");
}
}
if let Some(module_table) = tables.table::<ModuleRaw>() {
let module_count = module_table.row_count;
println!("Module table has {module_count} entries");
assert!(module_count >= 1, "Should have at least 1 module");
if let Some(module_row) = module_table.get(1) {
println!(" Module generation: {}", module_row.generation);
println!(" ✓ Module metadata validated");
}
}
let strings = asm.strings();
if let Some(string_heap) = strings {
let mut found_strings = 0;
for i in 1..10 {
if string_heap.get(i).is_ok() {
found_strings += 1;
}
}
println!("String heap validation: {found_strings} test accesses successful");
assert!(found_strings > 0, "Should be able to access string heap");
println!(" ✓ String heap accessible");
}
let blob = asm.blob();
if let Some(blob_heap) = blob {
if blob_heap.get(1).is_ok() {
println!(" ✓ Blob heap accessible");
}
}
let us = asm.userstrings();
if let Some(us_heap) = us {
println!("UserStrings heap accessible");
let mut found_entries = 0;
for (_offset, _string) in us_heap.iter().take(5) {
found_entries += 1;
}
println!("UserStrings heap validation: {found_entries} test accesses successful");
println!(" ✓ UserStrings heap accessible");
}
let metadata_rva = asm.cor20header().meta_data_rva;
let metadata_size = asm.cor20header().meta_data_size;
println!("Metadata directory RVA: 0x{metadata_rva:X}");
println!("Metadata directory size: {metadata_size} bytes");
assert!(metadata_rva > 0, "Metadata directory should have valid RVA");
assert!(
metadata_size > 0,
"Metadata directory should have valid size"
);
println!(" ✓ Metadata directory accessible");
println!("✓ Assembly metadata validation completed");
}
fn test_xml_permission_set_parsing(asm: &CilObject) {
println!("Testing XML permission set parsing...");
let tables = asm.tables().unwrap();
if let Some(decl_security_table) = tables.table::<DeclSecurityRaw>() {
let mut found_xml_permission_set = false;
for security_row in decl_security_table.iter() {
if let Some(blob_heap) = asm.blob() {
if let Ok(blob_data) = blob_heap.get(security_row.permission_set as usize) {
if !blob_data.is_empty() && blob_data[0] == b'<' {
found_xml_permission_set = true;
println!("Found XML permission set data: {} bytes", blob_data.len());
let permission_set = PermissionSet::new(blob_data).unwrap();
match permission_set.format() {
PermissionSetFormat::Xml => {
println!(
"Successfully parsed XML permission set with {} permissions",
permission_set.permissions().len()
);
for permission in permission_set.permissions() {
println!("Permission: {}", permission.class_name);
if permission.class_name.contains("SecurityPermission") {
println!(
"Found SecurityPermission with {} arguments",
permission.named_arguments.len()
);
for arg in &permission.named_arguments {
if arg.name == "Assertion" {
match &arg.value {
ArgumentValue::Boolean(b) => {
assert!(*b);
}
_ => panic!(
"Expected boolean value for Assertion"
),
}
println!("Verified Assertion=true");
}
}
}
if permission.class_name.contains("FileIOPermission") {
println!(
"Found FileIOPermission with {} arguments",
permission.named_arguments.len()
);
for arg in &permission.named_arguments {
if arg.name == "Read" {
match &arg.value {
ArgumentValue::String(s) => {
assert!(s.contains("TestData"));
println!("Verified Read path contains TestData: {s}");
}
_ => panic!("Expected string value for Read"),
}
}
}
}
}
}
other_format => {
println!("Permission set format detected as: {other_format:?}");
assert!(
!permission_set.permissions().is_empty(),
"Permission set should contain permissions"
);
}
}
break;
}
} else {
println!(
"Could not read blob data at index {}",
security_row.permission_set
);
}
}
}
assert!(
decl_security_table.row_count > 0,
"Should have DeclSecurity entries from crafted_2.exe"
);
if !found_xml_permission_set {
println!("Note: No XML permission sets found in crafted_2.exe");
println!(
"This is normal - modern compilers typically use binary format instead of XML"
);
for security_row in decl_security_table.iter() {
if let Some(blob_heap) = asm.blob() {
if let Ok(blob_data) = blob_heap.get(security_row.permission_set as usize) {
let permission_set = PermissionSet::new(blob_data).unwrap();
println!(
"Parsed {:?} permission set with {} permissions",
permission_set.format(),
permission_set.permissions().len()
);
for permission in permission_set.permissions() {
println!(" - {}", permission.class_name);
}
break;
}
}
}
}
} else {
panic!("No DeclSecurity table found in crafted_2.exe");
}
println!("✓ XML permission set parsing tested");
}