Is PEVerify warning about duplicate methods wrong here?

237 views Asked by At

I'm working on obfuscating an assembly and after obfuscation PEVerify issues the following error:

[MD]: Error: Method has a duplicate, token=0x060035d8. [token:0x060035D5]
[MD]: Error: Method has a duplicate, token=0x060035d5. [token:0x060035D8]

Here is the first method declaration with header:

// Token: 0x060035D5 RID: 13781 RVA: 0x000D7828 File Offset: 0x000D5A28
.method private final hidebysig newslot virtual 
    instance void b () cil managed
{
    .override method instance void [mscorlib]System.IDisposable::Dispose()
    // Header Size: 12 bytes
    // Code Size: 52 (0x34) bytes
    // LocalVarSig Token: 0x11000050 RID: 80
    .maxstack 2
    .locals init (
        [0] int32
    )

And here is the second one:

// Token: 0x060035D8 RID: 13784 RVA: 0x000248BC File Offset: 0x00022ABC
.method private hidebysig 
    instance void b () cil managed
{
    // Header Size: 1 byte
    // Code Size: 31 (0x1F) bytes
    .maxstack 8

Seems like an explicit IDisposable interface implementation to me. Both methods are called as well, so it is not that all calls to one were replaced with calls to another. They just share the same name

If similar code had been written in C# - compiler would emit methods System.IDisposable.Dispose() and Dispose() thus eliminating same names and making PEVerify silent.

To ensure that same names make valid IL when one of them is explicit override of an interface method and other is not I've written such sample app:

namespace ClassLibrary1 {
    public interface IX { void M(); }
    public class Class1 : IX {
        void IX.M() { Console.WriteLine("IX.M()"); }
        public void M() { Console.WriteLine("M()"); }
    }
    public class Class2 {
        public static void Main(string[] args) {
            var x = new Class1();
            x.M();
            ((IX)x).M();
        }
    }
}

IL looks like this:

// Token: 0x06000002 RID: 2 RVA: 0x00002050 File Offset: 0x00000250
.method private final hidebysig newslot virtual 
    instance void ClassLibrary1.IX.M () cil managed 
{
    .override method instance void ClassLibrary1.IX::M()
    // Header Size: 1 byte
    // Code Size: 13 (0xD) bytes
    .maxstack 8

    /* 0x00000251 00           */ IL_0000: nop
    /* 0x00000252 7201000070   */ IL_0001: ldstr     "IX.M()"
    /* 0x00000257 280F00000A   */ IL_0006: call      void [mscorlib]System.Console::WriteLine(string)
    /* 0x0000025C 00           */ IL_000B: nop
    /* 0x0000025D 2A           */ IL_000C: ret
} // end of method Class1::ClassLibrary1.IX.M

// Token: 0x06000003 RID: 3 RVA: 0x0000205E File Offset: 0x0000025E
.method public hidebysig 
    instance void M () cil managed 
{
    // Header Size: 1 byte
    // Code Size: 13 (0xD) bytes
    .maxstack 8

    /* 0x0000025F 00           */ IL_0000: nop
    /* 0x00000260 720F000070   */ IL_0001: ldstr     "M()"
    /* 0x00000265 280F00000A   */ IL_0006: call      void [mscorlib]System.Console::WriteLine(string)
    /* 0x0000026A 00           */ IL_000B: nop
    /* 0x0000026B 2A           */ IL_000C: ret
} // end of method Class1::M

Note different method names.

Then I took resulting exe and edited ClassLibrary1.IX.M to be just M in Class1 (I have used dnSpy to do that). PEVerify indeed started issuing the same issue about duplicate methods, but exe is still working just fine printing M() IX.M() as expected.

The question is if PEVerify being overcautious here or there's really an issue with coinciding names I don't see?

1

There are 1 answers

1
thehennyy On BEST ANSWER

Both methods share the same method signature and that is simply not allowed.

For the CLR, a method signature consists of the method name, generic arity, formal parameter arity, formal parameter types and kinds, and return type.

Definition of a method signature

Serge Lidin states in .Net IL Assembler, chapter 10 under Method Table Validity Rules:

No duplicate records—attributed to the same TypeDef and having the same name and signature—should exist unless the accessibility flag is privatescope.

of course you can find this rule in the ECMA-335 specifications, in partition II.22.1:

Unique Rows: No table shall contain duplicate rows, where “duplicate” is defined in terms of its key column, or combination of columns.