Rummage attributes

Most of the time, the exact name of a type or a member does not matter because compiled binaries refer to types and members via numeric identifiers. Similarly, the absence of any such references usually indicates that a member is completely unused.

Whenever reflection is used, the above stops being true. Rummage uses a number of algorithms to figure out which classes and members must be kept unchanged to support reflection-based access. Unfortunately, it isn't always possible to do this with 100% accuracy.

This doesn't mean that Rummage and reflection do not mix. You can use reflection reliably whenever all of it occurs within the binary being obfuscated. Rummage is also aware of many .NET APIs which perform reflection-like accesses under the hood, such as serialization or installer services, to name a few.

To accommodate the occasional problem caused by patterns of which Rummage is not yet aware, several attributes may be used to mark reflection-sensitive code. Additionally, if you are using a standard .NET API or a very popular third-party component, please do contact us so that we can improve Rummage's automatic inference.

Declaring the attributes

You do not need to reference any additional DLLs to use Rummage attributes. You may simply copy & paste the relevant attributes into your own code. Rummage only looks for attributes with the specific names.

Download attribute definitions for C# or for VB.NET. You don't have to copy & paste attributes you don't use.

Using the attributes

Once you have found the class or member which needs to be excluded from obfuscation, simply tag it with one of the Rummage attributes:

[RummageKeepReflectionSafe]
class MyClass
{
    ...
}

You can verify that the attribute had the required effect by using the Obfuscation Results window displayed after obfuscation.

Some of the attributes can be used to mark multiple types in one go. For example, it is possible to mark a generic type parameter with an attribute which will tell Rummage to avoid obfuscating every type passed via this parameter.

Recommended for reflection-sensitive types

  • RummageKeepReflectionSafe: keep the specified type, member or parameter safe for all types of reflection. The subject of this attribute will not be renamed, removed, reordered or otherwise modified in reflection-unsafe ways. Methods marked with this attribute will still have their IL code obfuscated.
  • RummageKeepArgumentsReflectionSafe: keep all the types reflection-safe which are passed in for the given generic parameter. Recommended whenever a generic method or class is known to perform reflection on whichever type is passed as a generic type argument. For example:

    class ReflectionHelpers
    {
        bool IsNestedType<[RummageKeepArgumentsReflectionSafe] T>();
    }
    

Other attributes for reflection-sensitive types

  • RummageNoRemove: keep a specific type, method, constructor or field, even if Rummage determines that it is unused.
  • RummageNoRename: keep the original name of a specific element.
  • RummageNoRenameAnything: keep the original name of a specific type, all of its members, and all the members in all of its nested types.
  • RummageNoUnnest: avoid un-nesting the specified type.
  • RummageNoMarkPublic: keep the original access modifier of a specific element.

Attributes for inlining control

Note that Rummage inlining is orthogonal to other obfuscations. Rummage may choose to inline one method into another without deleting the inlined method because, for example, it is invoked directly via reflection.

  • RummageNoInline: instructs Rummage not to inline a specific method or property that would otherwise be automatically inlined. Takes precedence over RummageInline if both are specified.
  • RummageInline: instructs Rummage to inline a specific method or property that would otherwise not be automatically inlined, for example because the method is large and called from multiple locations.

Attributes for overriding Rummage analysis

Rummage uses a number of algorithms to decide whether reflection is performed on a type in a way that requires it to remain unchanged. It is not always possible to decide whether this is necessary. In those cases Rummage tends to err on the side of caution, preventing obfuscation in order to ensure correct operation.

If you find that Rummage is being too cautious, it is possible to override this decision using the following attributes. Note that using these attributes incorrectly may result in program breakage.

  • RummageAssumeTypeSafe: use on a method parameter of type Type to indicate that the method does not perform unsafe reflection on the type passed in. For example:

    void UseType([RummageAssumeTypeSafe] Type myType)
    {
        someCollection.Add(myType); // would normally be considered unsafe
    }
    

This attribute may also be specified on a generic parameter:

void UseType<[RummageAssumeTypeSafe] T>()
{
    someCollection.Add(typeof(T)); // would normally be considered unsafe
}
  • Rummage.Safe: this is a helper function which uses RummageAssumeTypeSafe to mark a specific use of a type safe:

    someCollection.Add(Rummage.Safe(typeof(MyClass)));