这个问题末尾的代码在 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() 不会引发异常。
重命名混淆
这些方法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;
}