1

这个问题末尾的代码在 Source 上有一个 GetHashCode()。匿名类型也是如此:编译器生成类似的代码。

使用 Crypto Obfuscator,GetHashCode 公共方法会在您指定重命名方法时被重命名。结果是 GroupBy 返回了错误的元素:应该相等的元素被认为是不同的,因为它们引用了不同的对象并且使用了默认的 GetHashCode 实现。

是否有一种明智的方法可以在 GetHashCode() 等基本方法上禁用 Crypto Obfuscator 的重命名,尤其是当它们由编译器自动生成时?

还是我们必须完全停止使用匿名类型,用显式类型替换它们,并确保所有 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() 不会引发异常。

重命名混淆

这些方法WZ不应该被介绍,因为它们被使用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;
}
4

0 回答 0