在 .NET 中从混淆中排除构造函数

Exclude constructor from obfuscation in .NET

.NET 中有 ObfuscationAttibute。但是我不明白,如何从混淆中排除构造函数中的代码。

// Obfuscated class
class MyClass {
    [Obfuscation(Exclude = true)] // "Attribute 'Obfuscation' is not valid on this declaration type"
    public MyClass() {
        //some code, I need to exclude this code from obfuscation
    }
    // Obfuscated method
    public void Method1() {
        //some code
    |
    // Obfuscated method
    public void Method2() {
        //some code
    |
}

UPD:问题不在于重命名构造函数。它的名字显然变成了“.ctor”。我需要防止混淆代码本身。是的,一些混淆器不仅重命名符号,而且还更改代码。是的,我知道我不能用这个属性来做。编译器说的一样。我已经知道我不能做什么。我在问我能做什么,最好只使用标准的 .net 工具。

构造函数在内部始终重命名为 .ctor,您不能使用经过混淆的名称(但也不能使用原始名称)。反编译器将使用混淆的 class 名称命名构造函数。

我想你的意思是混淆函数内部的代码,而不是成员名称?据推测,支持代码重新排列而不仅仅是名称混淆的更高级的混淆器将具有自己的属性来控制...因为 System.Reflection.ObfuscationAttribute 不适合控制更强大的混淆技术。

特别是 ObfuscationAttribute class 上的 AttributeUsageAttribute 不包括 AttributeTargets.Constructor 作为允许的用法。

为什么构造函数中不允许使用 [ObfuscationAttribute]

您不能将 [Obfuscation(Exclude = true)] 放在构造函数上,因为混淆 重命名符号 ,而不是方法 内容 45=](通常 - 更高级的混淆器可以改变代码流、修改常量等,使逆向工程更难).

例如,考虑以下内容:

// obfuscated class
public class MyClass
{
    public MyClass()
    {
    }

    public void MyMethod()
    {
    }
}

// unobfuscated class
public class CallingClass
{
    public static void TestMyClass()
    {
        MyClass class = new MyClass();
        class.MyMethod();
    }
}

混淆器会将 MyClass 重命名为其他名称(例如 qfghjigffvvb),将 MyMethod() 重命名为其他名称(例如 ghjbvxdghh())并更改所有引用,以便代码仍然有效,即

public class qfghjigffvvb
{
    public qfghjigffvvb()
    {
    }

    public void ghjbvxdghh()
    {
    }
}

// unobfuscated class
public class CallingClass
{
    public static void TestMyClass()
    {
        qfghjigffvvb class = new qfghjigffvvb();
        class.ghjbvxdghh();
    }
}

如果将 [Obfuscation(Exclude = true)] 属性放到 MyClass 的构造函数中,那么 CallingClass.TestMyClass() 将如下所示:

public class CallingClass
{
    public static void TestMyClass()
    {
        qfghjigffvvb class = new MyClass(); // ?
        class.ghjbvxdghh();
    }
}

如果您需要 MyClass 构造函数的 contents 不被混淆,您需要将 [Obfuscation(Exclude = true)] 属性放在它所包含的所有内容上调用以便符号不会被重命名。


思想实验:排除构造函数的内容

假设您有一个 [ContentObfuscation] 属性,您可以使用它来阻止方法(或构造函数或 属性)的 内容 被混淆。您希望它在这里做什么?

public class MyClass
{
    [ContentObfuscation(Exclude = true)]
    public MyClass()
    {
        // SecurityCriticalClass is obfuscated
        var securityCriticalClass = new SecurityCriticalClass();
        securityCriticalClass.DoSomeTopSecretStuff();
    }
}

如果构造函数的 内容 没有被混淆,那么 SecurityCriticalClass 也必须没有被混淆,可能会产生安全问题。

您可以只使用 ObfuscationAttribute 做您想做的事,但这很乏味:将 [Obfuscation(ApplyToMembers=false)] 应用于 class,并将 [Obfuscation] 应用于每个成员 除了 构造函数。

或者,使用混淆器的配置将构造函数排除在考虑之外。由于 ObfuscationAttribute 仅提供非常有限的控制(基本上只是打开和关闭功能),大多数都具有单独的配置以进行细粒度控制。

最后,考虑让您的构造函数变得如此简单和无趣,以至于流程是否被混淆并不重要。无论如何,理想情况下应该是这样——如果您的构造函数只执行成员初始化,那么一开始就没有太多需要混淆的地方。您可以为更复杂的东西调用成员函数,并且您可以控制那些使用属性的混淆。

我非常同意评论指出混淆并不仅仅与重命名有关,而且构造函数未被视为 [Obfuscation] 的有效目标似乎确实是一种疏忽。我有 运行 处理死代码删除的问题,其中混淆器可以删除无法访问的代码(我正在简化)。覆盖此行为有时是必要的,例如 reflection/serialization 场景,并且可以像任何其他代码元素一样同样适用于构造函数。请注意,[AttributeUsage] 只是建议性的,由 C#(或 VB)编译器强制执行。它不是由 CLR 强制执行的。因此,如果您的混淆器被设计为在构造函数上寻找 [Obfuscation](这很可能是偶然的,因为某些混淆器逻辑可能会平等对待所有方法),并且您可以使用某种 post-compilation IL处理框架(例如 PostSharp),那么您可以将 [Obfuscation] 放到构造函数中。对于 PostSharp 用户,这是 ObfuscationAttribute 的多播版本,它将愉快地将 [Obfuscation] 应用于构造函数:

using System;
using PostSharp.Aspects;
using PostSharp.Extensibility;
using PostSharp.Reflection;
using System.Collections.Generic;
using System.Reflection;

/// <summary>
/// A multicast adapter for <see cref="ObfuscationAttribute"/>. Instructs obfuscation tools to take the specified actions for the target assembly, type, or member.
/// </summary>
[AttributeUsage( AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.Constructor, AllowMultiple = true, Inherited = false )]
[MulticastAttributeUsage( 
    MulticastTargets.Assembly | MulticastTargets.Class | MulticastTargets.Struct | MulticastTargets.Enum | MulticastTargets.Method | MulticastTargets.Property | MulticastTargets.Field | MulticastTargets.Event | MulticastTargets.Interface | MulticastTargets.Parameter | MulticastTargets.Delegate | MulticastTargets.InstanceConstructor | MulticastTargets.InstanceConstructor,
    AllowMultiple = true,
    PersistMetaData = false)]
public sealed class MulticastObfuscationAttribute : MulticastAttribute, IAspectProvider
{
    bool _stripAfterObfuscation = true;
    bool _exclude = true;
    bool _applyToMembers = true;
    string _feature = "all";

    bool _stripAfterObfuscationIsSpecified;
    bool _excludeIsSpecified;
    bool _applyToMembersIsSpecified;
    bool _featureIsSpecified;

    static readonly ConstructorInfo ObfuscationAttributeCtor = typeof( ObfuscationAttribute ).GetConstructor( Type.EmptyTypes );

    IEnumerable<AspectInstance> IAspectProvider.ProvideAspects( object targetElement )
    {
        var oc = new ObjectConstruction( ObfuscationAttributeCtor );

        if ( _applyToMembersIsSpecified )
            oc.NamedArguments[ nameof( ObfuscationAttribute.ApplyToMembers ) ] = _applyToMembers;

        if ( _excludeIsSpecified )
            oc.NamedArguments[ nameof( ObfuscationAttribute.Exclude ) ] = _exclude;

        if ( _featureIsSpecified )
            oc.NamedArguments[ nameof( ObfuscationAttribute.Feature ) ] = _feature;

        if ( _stripAfterObfuscationIsSpecified )
            oc.NamedArguments[ nameof( ObfuscationAttribute.StripAfterObfuscation ) ] = _stripAfterObfuscation;

        return new[] { new AspectInstance( targetElement, new CustomAttributeIntroductionAspect( oc ) ) };
    }

    /// <summary>
    /// Gets or sets a <see cref="T:System.Boolean" /> value indicating whether the obfuscation tool should remove this attribute after processing.
    /// </summary>
    /// <returns>
    /// <see langword="true" /> if an obfuscation tool should remove the attribute after processing; otherwise, <see langword="false" />. The default is <see langword="true" />.
    /// </returns>
    public bool StripAfterObfuscation
    {
        get => _stripAfterObfuscation;
        set
        {
            _stripAfterObfuscationIsSpecified = true;
            _stripAfterObfuscation = value;
        }
    }

    /// <summary>
    /// Gets or sets a <see cref="T:System.Boolean" /> value indicating whether the obfuscation tool should exclude the type or member from obfuscation.
    /// </summary>
    /// <returns>
    /// <see langword="true" /> if the type or member to which this attribute is applied should be excluded from obfuscation; otherwise, <see langword="false" />. The default is <see langword="true" />.
    /// </returns>
    public bool Exclude
    {
        get => _exclude;
        set
        {
            _excludeIsSpecified = true;
            _exclude = value;
        }
    }

    /// <summary>
    /// Gets or sets a <see cref="T:System.Boolean" /> value indicating whether the attribute of a type is to apply to the members of the type.
    /// </summary>
    /// <returns>
    /// <see langword="true" /> if the attribute is to apply to the members of the type; otherwise, <see langword="false" />. The default is <see langword="true" />.
    /// </returns>
    public bool ApplyToMembers
    {
        get => _applyToMembers;
        set
        {
            _applyToMembersIsSpecified = true;
            _applyToMembers = value;
        }
    }

    /// <summary>
    /// Gets or sets a string value that is recognized by the obfuscation tool, and which specifies processing options.
    /// </summary>
    /// <returns>
    /// A string value that is recognized by the obfuscation tool, and which specifies processing options. The default is "all".
    /// </returns>
    public string Feature
    {
        get => _feature;
        set
        {
            _featureIsSpecified = true;
            _feature = value;
        }
    }
}