Сгенерированный компилятором класс для анонимных типов неправильно запутан - PullRequest
0 голосов
/ 25 октября 2018

Код в конце этого вопроса имеет GetHashCode () на Source.То же самое относится и к анонимным типам: компилятор генерирует похожий код.

Используя Crypto Obfuscator, публичный метод GetHashCode переименовывается при указании метода переименования.В результате GroupBy возвращает неправильные элементы: элементы, которые должны быть равными, считаются разными, поскольку они ссылаются на разные объекты и используется реализация GetHashCode по умолчанию.

Есть ли разумный способ отключить переименование Crypto Obfuscator внеобходимые методы, такие как GetHashCode (), особенно когда они автоматически генерируются компилятором?

Или мы должны полностью прекратить использование анонимных типов, заменить их явными типами и убедиться, что все методы GetHashCode и Equals являютсяправильно оформлен с помощью атрибута «Обфускация»?

Необфусцированный код

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Invantive.Basics
{
    /// <summary>
    /// Check obfuscation not too aggressive.
    /// </summary>
    [Obfuscation(ApplyToMembers=false, Exclude = true)]
    public static class ObfuscationAsserter
    {
        private class Source
        {
            public string S1;
            public string S2;

            public Source(string s1, string s2)
            {
                this.S1 = s1;
                this.S2 = s2;
            }

            public override int GetHashCode()
            {
                return 23 * this.S1.GetHashCode() + this.S2.GetHashCode();
            }

            public override bool Equals(object o)
            {
                if (o is Source s)
                {
                    return s.S1 == this.S1 && s.S2 == this.S2;
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Check anonymous types etc. are correctly obfuscated.
        /// </summary>
        public static void Check()
        {
            List<Source> strings = new List<Source>();
            strings.Add(new Source("a", "a"));
            strings.Add(new Source("a", "b"));
            strings.Add(new Source("a", "b"));
            strings.Add(new Source("c", "c"));

            int expected = 3;
            int t1cnt = strings.GroupBy(x => new { x.S1, x.S2 }).ToList().Count();
            int t2cnt = strings.GroupBy(x => new { x.S1, x.S2 }.GetHashCode()).ToList().Count();
            int t3cnt = strings.GroupBy(x => x.S1 + x.S2).ToList().Count();
            int t4cnt = strings.GroupBy(x => (x.S1 + x.S2).GetHashCode()).ToList().Count();
            int t5cnt = strings.GroupBy(x => x).ToList().Count();
            int t6cnt = strings.GroupBy(x => x.GetHashCode()).ToList().Count();
            int t7cnt = strings.GroupBy(x => (x.S1, x.S2)).ToList().Count();
            int t8cnt = strings.GroupBy(x => (x.S1, x.S2).GetHashCode()).ToList().Count();

            InvantiveTrace.WriteLine($"t1: {t1cnt}, t2: {t2cnt}, t3: {t3cnt}, t4: {t4cnt}, t5: {t5cnt}, t6: {t6cnt}, t7: {t7cnt}, t8: {t8cnt}.");

            if (t1cnt != expected)
            {
                throw new Exception($"t1 is {t1cnt:N0} versus expected {expected:N0}.");
            }

            if (t2cnt != expected)
            {
                throw new Exception($"t2 is {t2cnt:N0} versus expected {expected:N0}.");
            }

            if (t3cnt != expected)
            {
                throw new Exception($"t3 is {t3cnt:N0} versus expected {expected:N0}.");
            }

            if (t4cnt != expected)
            {
                throw new Exception($"t4 is {t4cnt:N0} versus expected {expected:N0}.");
            }

            if (t5cnt != expected)
            {
                throw new Exception($"t5 is {t5cnt:N0} versus expected {expected:N0}.");
            }

            if (t6cnt != expected)
            {
                throw new Exception($"t6 is {t6cnt:N0} versus expected {expected:N0}.");
            }

            if (t7cnt != expected)
            {
                throw new Exception($"t7 is {t7cnt:N0} versus expected {expected:N0}.");
            }

            if (t8cnt != expected)
            {
                throw new Exception($"t8 is {t8cnt:N0} versus expected {expected:N0}.");
            }
        }
    }
}

Работает нормально: Check () не вызывает исключений.

Обфусцирован переименованием

методы W и Z не должны были быть введены, так как они используются GroupBy:

private class D
{
    public string J;
    public string V;

    public D(string text1, string text2)
    {
        this.J = text1;
        this.V = text2;
    }

    public override int W() => 
        ((0x17 * this.J.GetHashCode()) + this.V.GetHashCode());

    public override bool Z(object obj1)
    {
        ObfuscationAsserter.D d = obj1 as ObfuscationAsserter.D;
        if (d == null)
        {
            return false;
        }
        return ((d.J == this.J) && (d.V == this.V));
    }
}

Запутано без переименования

Этот по-прежнему работает нормально:

private class Source
{
    public string S1;
    public string S2;

    public Source(string s1, string s2)
    {
        this.S1 = s1;
        this.S2 = s2;
    }

    public override bool Equals(object o)
    {
        ObfuscationAsserter.Source source = o as ObfuscationAsserter.Source;
        if (source == null)
        {
            return false;
        }
        return ((source.S1 == this.S1) && (source.S2 == this.S2));
    }

    public override int GetHashCode() => 
        ((0x17 * this.S1.GetHashCode()) + this.S2.GetHashCode());
}

Пример класса анонимного типа

Класс, сгенерированный компилятором, имеет тот жепроблема, поскольку GetHashCode () больше не существует:

[Serializable, CompilerGenerated]
private sealed class <>c
{
    public static Func<ObfuscationAsserter.D, int> E;
    public static Func<ObfuscationAsserter.D, int> F;
    public static Func<ObfuscationAsserter.D, ObfuscationAsserter.D> I;
    public static readonly ObfuscationAsserter.<>c J = new ObfuscationAsserter.<>c();
    public static Func<ObfuscationAsserter.D, J<string, string>> V;
    public static Func<ObfuscationAsserter.D, int> W;
    [TupleElementNames(new string[] { "S1", "S2" })]
    public static Func<ObfuscationAsserter.D, (string S1, string S2)> X;
    public static Func<ObfuscationAsserter.D, int> Y;
    public static Func<ObfuscationAsserter.D, string> Z;

    internal int B(ObfuscationAsserter.D d1)
    {
        (string, string) tuple = (d1.J, d1.V);
        return tuple.GetHashCode();
    }

    [return: TupleElementNames(new string[] { "S1", "S2" })]
    internal (string S1, string S2) L(ObfuscationAsserter.D d1) => 
        (d1.J, d1.V);

    internal int N(ObfuscationAsserter.D d1) => 
        d1.GetHashCode();

    internal string P(ObfuscationAsserter.D d1) => 
        (d1.J + d1.V);

    internal J<string, string> R(ObfuscationAsserter.D d1) => 
        new J<string, string>(d1.J, d1.V);

    internal int S(ObfuscationAsserter.D d1) => 
        (d1.J + d1.V).GetHashCode();

    internal int T(ObfuscationAsserter.D d1) => 
        new J<string, string>(d1.J, d1.V).GetHashCode();

    internal ObfuscationAsserter.D U(ObfuscationAsserter.D d1) => 
        d1;
}
...