Исключить конструктор из обфускации в .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.


person Alex Butenko    schedule 26.02.2015    source источник
comment
ObfuscationAttribute не включает AttributeTargets.Constructor в качестве допустимого использования атрибута... так что это невозможно.   -  person Simon Whitehead    schedule 26.02.2015
comment
@SimonWhitehead Да, и вопрос в том, как обойти это, желательно, используя встроенные инструменты .net.   -  person Alex Butenko    schedule 26.02.2015
comment
Имя конструктора не может выбрать ни вы, ни обфускатор. В C# имя конструктора совпадает с именем содержащего его типа. В IL имя конструктора всегда .ctor (нестатический) или .cctor (статический конструктор, он же инициализатор типа). Чего вы пытаетесь достичь? Вы хотите, чтобы конструктор выглядел как обычный метод?   -  person Jeppe Stig Nielsen    schedule 26.02.2015


Ответы (4)


Вы можете делать все, что хотите, используя только ObfuscationAttribute, но это утомительно: применяйте [Obfuscation(ApplyToMembers=false)] к классу и [Obfuscation] к каждому отдельному члену, кроме конструктора.

В качестве альтернативы используйте конфигурацию вашего обфускатора, чтобы исключить конструктор из рассмотрения. Поскольку ObfuscationAttribute предлагает лишь очень ограниченный контроль (в основном просто включение и выключение функций), большинство из них имеют отдельную конфигурацию для мелкозернистого управления.

Наконец, рассмотрите возможность сделать свой конструктор настолько простым и неинтересным, чтобы не имело значения, запутан поток или нет. В идеале это должно быть в любом случае - если ваш конструктор выполняет только инициализацию члена, в первую очередь нечего запутывать. Вы можете вызывать функции-члены для более сложных вещей, и вы можете контролировать запутывание тех, кто использует атрибут.

person Jeroen Mostert    schedule 26.02.2015

Конструкторы всегда переименовываются в .ctor внутренне, вы не можете использовать запутанное имя (но вы также не можете использовать исходное имя). И декомпиляторы назовут конструктор запутанным именем класса.

Я полагаю, вы имеете в виду запутывание кода внутри функции, а не имени члена? Предположительно, более продвинутый обфускатор, который поддерживает перестановку кода, а не только обфускацию имен, будет иметь свой собственный атрибут для управления этим... потому что System.Reflection.ObfuscationAttribute не подходит для управления более мощными методами обфускации.

В частности, AttributeUsageAttribute в классе ObfuscationAttribute не включает AttributeTargets.Constructor в качестве разрешенного использования.

person Ben Voigt    schedule 26.02.2015
comment
Отсутствие применимости к конструкторам кажется упущением, основанным на предположении, что это указывает только на переименование, даже несмотря на то, что ObfuscationAttribute имеет свойство Feature для более тонкого контроля над запутыванием. Это используется (например) Dotfuscator, чтобы указать, что не переименовывать это (но делать обфускацию потока), что, безусловно, имеет смысл для конструктора. - person Jeroen Mostert; 26.02.2015
comment
@JeroenMostert: Учитывая ориентированный на переименование характер атрибута, кажется, что Dotfuscator лучше обслуживать, предоставив собственный настраиваемый атрибут для управления запутыванием потока. - person Ben Voigt; 26.02.2015
comment
Ничего не стоит, что в в документации слово rename нигде не используется. Обфускацию следует описывать с точки зрения функций. Если бы они пошли на это, они могли бы также признать, что есть функции запутывания, которые применяются к конструкторам... но, конечно, поскольку переименование является наиболее заметной функцией запутывания, это понятное упущение . - person Jeroen Mostert; 26.02.2015

Я очень согласен с комментариями, отмечая, что запутывание — это не только переименование, и кажется упущением, что конструкторы не считались допустимой целью для [Obfuscation]. Я столкнулся с проблемой удаления мертвого кода, когда обфускатор может удалить недостижимый код (я упрощаю). Переопределение этого поведения иногда необходимо, например, для сценариев отражения/сериализации, и может в равной степени применяться как к конструкторам, так и к любому другому элементу кода. Обратите внимание, что [AttributeUsage] носит только рекомендательный характер и применяется компилятором C# (или VB). Это не применяется CLR. Таким образом, если ваш обфускатор предназначен для поиска [Obfuscation] в конструкторах (что вполне может быть случайным случаем, учитывая, что некоторая логика обфускатора может обрабатывать все методы одинаково), и вы можете использовать какую-то среду обработки 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;
        }
    }
}
person tg73    schedule 12.06.2019

Почему [ObfuscationAttribute] не разрешен для конструкторов

Вы не можете поместить [Obfuscation(Exclude = true)] в конструктор, потому что обфускация переименовывает символы, а не содержимое методов (обычно более продвинутые обфускаторы могут изменять поток кода, модифицировать константы и т. д., чтобы усложнить обратный инжиниринг).

Например, рассмотрим следующее:

// 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 не было запутанным, вам нужно поместить атрибут [Obfuscation(Exclude = true)] во все, что он вызывает, чтобы символы не переименовывались.


Мысленный эксперимент: исключение содержимого конструктора

Допустим, у вас есть атрибут [ContentObfuscation], который вы можете использовать, чтобы предотвратить запутывание содержимого метода (или конструктора, или свойства). Что бы вы хотели, чтобы он здесь делал?

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

Если содержимое конструктора не обфусцировано, то SecurityCriticalClass также не должно быть обфусцировано, что может создать проблему безопасности.

person Wai Ha Lee    schedule 26.02.2015