Skip to content

Commit

Permalink
Merge pull request mkaring#384 from PositiveTechnologies/ReversibleRe…
Browse files Browse the repository at this point in the history
…namingImprovements

Renaming improvements
  • Loading branch information
mkaring authored Jul 28, 2021
2 parents b8f7e0d + c8efe56 commit 7241bb2
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 117 deletions.
5 changes: 3 additions & 2 deletions Confuser.Renamer/AnalyzePhase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa
foreach (var res in module.Resources)
service.AddReservedIdentifier(res.Name);
}
else
service.SetOriginalName(def);
else {
service.StoreNames(def);
}

if (def is TypeDef typeDef) {
service.GetVTables().GetVTable(typeDef);
Expand Down
6 changes: 3 additions & 3 deletions Confuser.Renamer/Analyzers/VTableAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ public static void Analyze(INameService service, ICollection<ModuleDefMD> module
var typeDef = type.BaseType?.ResolveTypeDef();
var baseMethod = typeDef?.FindMethod(methodDef.Name, methodDef.Signature as MethodSig);
if (baseMethod != null) {
string unifiedName = service.GetOriginalFullName(slot.Overrides.MethodDef);
service.SetOriginalName(slot.MethodDef, unifiedName);
service.SetOriginalName(baseMethod, unifiedName);
string unifiedName = service.GetNormalizedName(slot.Overrides.MethodDef);
service.SetNormalizedName(slot.MethodDef, unifiedName);
service.SetNormalizedName(baseMethod, unifiedName);
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions Confuser.Renamer/DisplayNormalizedName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Confuser.Renamer {
public struct DisplayNormalizedName {
public string DisplayName { get; }

public string NormalizedName { get; }

public DisplayNormalizedName(string displayName, string normalizedName) {
DisplayName = displayName;
NormalizedName = normalizedName;
}
}
}
38 changes: 20 additions & 18 deletions Confuser.Renamer/MessageDeobfuscator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

namespace Confuser.Renamer {
public class MessageDeobfuscator {
static readonly Regex MapSymbolMatcher = new Regex("_[a-zA-Z0-9]+");
static readonly Regex PasswordSymbolMatcher = new Regex("[a-zA-Z0-9_$]{23,}");
static readonly Regex MapSymbolRegex = new Regex("_[a-zA-Z0-9]+");
static readonly Regex PasswordSymbolRegex = new Regex("[a-zA-Z0-9_$]{23,}");

readonly Dictionary<string, string> _symbolMap;
readonly ReversibleRenamer _renamer;
Expand Down Expand Up @@ -34,32 +34,34 @@ public static MessageDeobfuscator Load(string symbolMapFileName) {

public MessageDeobfuscator(string password) => _renamer = new ReversibleRenamer(password);

public string Deobfuscate(string obfuscatedMessage) {
public string DeobfuscateMessage(string message) {
if (_symbolMap != null) {
return MapSymbolMatcher.Replace(obfuscatedMessage, DecodeSymbolMap);
return MapSymbolRegex.Replace(message, m => DeobfuscateSymbol(m.Value, true));
}

return PasswordSymbolMatcher.Replace(obfuscatedMessage, DecodeSymbolPassword);
return PasswordSymbolRegex.Replace(message, m => DeobfuscateSymbol(m.Value, true));
}

string DecodeSymbolMap(Match match) {
var symbol = match.Value;
if (_symbolMap.TryGetValue(symbol, out string result))
return ExtractShortName(result);
return ExtractShortName(symbol);
}
public string DeobfuscateSymbol(string obfuscatedIdentifier, bool shortName) {
string fullName;

string DecodeSymbolPassword(Match match) {
var sym = match.Value;
try {
return ExtractShortName(_renamer.Decrypt(sym));
if (_symbolMap != null) {
if (!_symbolMap.TryGetValue(obfuscatedIdentifier, out fullName))
fullName = obfuscatedIdentifier;
}
catch {
return sym;
else {
try {
fullName = _renamer.Decrypt(obfuscatedIdentifier);
}
catch {
fullName = obfuscatedIdentifier;
}
}

return shortName ? ExtractShortName(fullName) : fullName;
}

static string ExtractShortName(string fullName) {
public static string ExtractShortName(string fullName) {
const string doubleParen = "::";
int doubleParenIndex = fullName.IndexOf(doubleParen, StringComparison.Ordinal);
if (doubleParenIndex != -1) {
Expand Down
146 changes: 97 additions & 49 deletions Confuser.Renamer/NameService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ public interface INameService {
void AddReference<T>(T obj, INameReference<T> reference);
IList<INameReference> GetReferences(object obj);

void SetOriginalName(IDnlibDef obj, string fullName = null);
string GetOriginalFullName(IDnlibDef obj);
void StoreNames(IDnlibDef obj);
void SetNormalizedName(IDnlibDef obj, string name);
string GetDisplayName(IDnlibDef obj);
string GetNormalizedName(IDnlibDef obj);

bool IsRenamed(IDnlibDef def);
void SetIsRenamed(IDnlibDef def);
Expand All @@ -48,7 +50,8 @@ internal class NameService : INameService {
static readonly object CanRenameKey = new object();
static readonly object RenameModeKey = new object();
static readonly object ReferencesKey = new object();
static readonly object OriginalFullNameKey = new object();
static readonly object DisplayNameKey = new object();
static readonly object NormalizedNameKey = new object();
static readonly object IsRenamedKey = new object();

readonly ConfuserContext context;
Expand All @@ -62,6 +65,7 @@ internal class NameService : INameService {
readonly byte[] nameId = new byte[8];
readonly Dictionary<string, string> _originalToObfuscatedNameMap = new Dictionary<string, string>();
readonly Dictionary<string, string> _obfuscatedToOriginalNameMap = new Dictionary<string, string>();
readonly Dictionary<string, string> _prefixesMap = new Dictionary<string, string>();
internal ReversibleRenamer reversibleRenamer;

public NameService(ConfuserContext context) {
Expand Down Expand Up @@ -99,6 +103,7 @@ public bool CanRename(object obj) {
return false;
return context.Annotations.Get(obj, CanRenameKey, true);
}

return false;
}

Expand Down Expand Up @@ -140,8 +145,8 @@ public void ReduceRenameMode(object obj, RenameMode val) {
if (original < val)
context.Annotations.Set(obj, RenameModeKey, val);
if (val <= RenameMode.Reflection && obj is IDnlibDef dnlibDef) {
string nameWithoutParams = GetSimplifiedFullName(dnlibDef, true);
SetOriginalName(dnlibDef, nameWithoutParams);
var name = ExtractDisplayNormalizedName(dnlibDef, true);
SetNormalizedName(dnlibDef, name.NormalizedName);
}
}

Expand All @@ -153,10 +158,11 @@ public void Analyze(IDnlibDef def) {
if (analyze == null)
analyze = context.Pipeline.FindPhase<AnalyzePhase>();

SetOriginalName(def);
StoreNames(def);
if (def is TypeDef typeDef) {
GetVTables().GetVTable(typeDef);
}

analyze.Analyze(this, context, ProtectionParameters.Empty, def, true);
}

Expand Down Expand Up @@ -199,6 +205,7 @@ string ParseGenericName(string name, out int count) {
return name.Substring(0, graveIndex);
}
}

count = 0;
return name;
}
Expand All @@ -208,10 +215,10 @@ string ParseGenericName(string name, out int count) {
public string ObfuscateName(string name, RenameMode mode) => ObfuscateName(null, name, mode, false);

public string ObfuscateName(IDnlibDef dnlibDef, RenameMode mode) {
var originalFullName = GetOriginalFullName(dnlibDef);
var normalizedName = GetNormalizedName(dnlibDef);
bool preserveGenericParams = GetParam(dnlibDef, "preserveGenericParams")
?.Equals("true", StringComparison.OrdinalIgnoreCase) == true;
return ObfuscateName(null, originalFullName, mode, preserveGenericParams);
return ObfuscateName(null, normalizedName, mode, preserveGenericParams);
}

public string ObfuscateName(string format, string name, RenameMode mode, bool preserveGenericParams = false) {
Expand All @@ -225,20 +232,17 @@ public string ObfuscateName(string format, string name, RenameMode mode, bool pr
if (string.IsNullOrEmpty(name) || mode == RenameMode.Empty)
return string.Empty;

if (mode == RenameMode.Debug || mode == RenameMode.Retain)
{
if (mode == RenameMode.Debug || mode == RenameMode.Retain) {
// When flattening there are issues, in case there is a . in the name of the assembly.
newName = name.Replace('.', '_');
newName = mode == RenameMode.Debug ? "_" + newName : newName;
}
else if (mode == RenameMode.Reversible)
{
else if (mode == RenameMode.Reversible) {
if (reversibleRenamer == null)
throw new ArgumentException("Password not provided for reversible renaming.");
newName = reversibleRenamer.Encrypt(name);
}
else if (!_originalToObfuscatedNameMap.TryGetValue(name, out newName))
{
else if (!_originalToObfuscatedNameMap.TryGetValue(name, out newName)) {
byte[] hash = Utils.Xor(Utils.SHA1(Encoding.UTF8.GetBytes(name)), nameSeed);
while (true) {
newName = ObfuscateNameInternal(hash, mode);
Expand Down Expand Up @@ -277,13 +281,19 @@ public string RandomName(RenameMode mode) {
return ObfuscateName(Utils.ToHexString(random.NextBytes(16)), mode);
}

public void SetOriginalName(IDnlibDef dnlibDef, string newFullName = null) {
public void StoreNames(IDnlibDef dnlibDef) {
AddReservedIdentifier(dnlibDef.Name);
if (dnlibDef is TypeDef typeDef) {
AddReservedIdentifier(typeDef.Namespace);
}
string fullName = newFullName ?? GetSimplifiedFullName(dnlibDef);
context.Annotations.Set(dnlibDef, OriginalFullNameKey, fullName);

var name = ExtractDisplayNormalizedName(dnlibDef);
context.Annotations.Set(dnlibDef, DisplayNameKey, name.DisplayName);
context.Annotations.Set(dnlibDef, NormalizedNameKey, name.NormalizedName);
}

public void SetNormalizedName(IDnlibDef dnlibDef, string name) {
context.Annotations.Set(dnlibDef, NormalizedNameKey, name);
}

public void AddReservedIdentifier(string id) => identifiers.Add(id);
Expand Down Expand Up @@ -318,6 +328,7 @@ public void MarkHelper(IDnlibDef def, IMarkerService marker, ConfuserComponent p
if (!type.IsSpecialName && !type.IsRuntimeSpecialName)
type.Name = RandomName();
}

SetCanRename(def, false);
Analyze(def);
marker.Mark(def, parentComp);
Expand All @@ -326,28 +337,28 @@ public void MarkHelper(IDnlibDef def, IMarkerService marker, ConfuserComponent p
#region Charsets

static readonly char[] asciiCharset = Enumerable.Range(32, 95)
.Select(ord => (char)ord)
.Except(new[] { '.' })
.ToArray();
.Select(ord => (char)ord)
.Except(new[] {'.'})
.ToArray();

static readonly char[] reflectionCharset = asciiCharset.Except(new[] { ' ', '[', ']' }).ToArray();
static readonly char[] reflectionCharset = asciiCharset.Except(new[] {' ', '[', ']'}).ToArray();

static readonly char[] letterCharset = Enumerable.Range(0, 26)
.SelectMany(ord => new[] { (char)('a' + ord), (char)('A' + ord) })
.ToArray();
.SelectMany(ord => new[] {(char)('a' + ord), (char)('A' + ord)})
.ToArray();

static readonly char[] alphaNumCharset = Enumerable.Range(0, 26)
.SelectMany(ord => new[] { (char)('a' + ord), (char)('A' + ord) })
.Concat(Enumerable.Range(0, 10).Select(ord => (char)('0' + ord)))
.ToArray();
.SelectMany(ord => new[] {(char)('a' + ord), (char)('A' + ord)})
.Concat(Enumerable.Range(0, 10).Select(ord => (char)('0' + ord)))
.ToArray();

// Especially chosen, just to mess with people.
// Inspired by: http://xkcd.com/1137/ :D
static readonly char[] unicodeCharset = new char[] { }
.Concat(Enumerable.Range(0x200b, 5).Select(ord => (char)ord))
.Concat(Enumerable.Range(0x2029, 6).Select(ord => (char)ord))
.Concat(Enumerable.Range(0x206a, 6).Select(ord => (char)ord))
.Except(new[] { '\u2029' })
.Except(new[] {'\u2029'})
.ToArray();

#endregion
Expand All @@ -360,51 +371,88 @@ public IList<INameReference> GetReferences(object obj) {
return context.Annotations.GetLazy(obj, ReferencesKey, key => new List<INameReference>());
}

public string GetOriginalFullName(IDnlibDef obj) =>
context.Annotations.Get(obj, OriginalFullNameKey, (string)null) ?? GetSimplifiedFullName(obj);
public string GetDisplayName(IDnlibDef obj) =>
context.Annotations.Get(obj, DisplayNameKey, (string)null);

public string GetNormalizedName(IDnlibDef obj) =>
context.Annotations.Get(obj, NormalizedNameKey, (string)null);

public IReadOnlyDictionary<string, string> GetNameMap() => _obfuscatedToOriginalNameMap;

public bool IsRenamed(IDnlibDef def) => context.Annotations.Get(def, IsRenamedKey, !CanRename(def));

public void SetIsRenamed(IDnlibDef def) => context.Annotations.Set(def, IsRenamedKey, true);

string GetSimplifiedFullName(IDnlibDef dnlibDef, bool forceShortNames = false) {
string result;

DisplayNormalizedName ExtractDisplayNormalizedName(IDnlibDef dnlibDef, bool forceShortNames = false) {
var shortNames = forceShortNames ||
GetParam(dnlibDef, "shortNames")?.Equals("true", StringComparison.OrdinalIgnoreCase) ==
true;
if (shortNames) {
result = dnlibDef is MethodDef ? (string)dnlibDef.Name : dnlibDef.FullName;
var renameMode = GetRenameMode(dnlibDef);

if (dnlibDef is TypeDef typeDef) {
if (typeDef.DeclaringType != null) {
var outerClassName = CompressTypeName(typeDef.DeclaringType.FullName, renameMode);
return
new DisplayNormalizedName(dnlibDef.FullName, $"{outerClassName.NormalizedName}/{dnlibDef.Name}");
}

return new DisplayNormalizedName(dnlibDef.FullName, dnlibDef.FullName);
}
else {
if (dnlibDef is MethodDef methodDef) {
var resultBuilder = new StringBuilder();
resultBuilder.Append(methodDef.DeclaringType2?.FullName);
resultBuilder.Append("::");
resultBuilder.Append(dnlibDef.Name);

resultBuilder.Append('(');

var displayNameBuilder = new StringBuilder();
var normalizedNameBuilder = new StringBuilder();
if (dnlibDef is IMemberDef memberDef) {
var declaringTypeName = CompressTypeName(memberDef.DeclaringType?.FullName ?? "", renameMode);

displayNameBuilder.Append(declaringTypeName.DisplayName);
displayNameBuilder.Append("::");
displayNameBuilder.Append(dnlibDef.Name);

normalizedNameBuilder.Append(declaringTypeName.NormalizedName);
normalizedNameBuilder.Append("::");
normalizedNameBuilder.Append(dnlibDef.Name);

if (memberDef is MethodDef methodDef) {
displayNameBuilder.Append('(');
normalizedNameBuilder.Append('(');
if (methodDef.Signature is MethodSig methodSig) {
var methodParams = methodSig.Params;
for (var index = 0; index < methodParams.Count; index++) {
resultBuilder.Append(methodParams[index]);
var parameterName = CompressTypeName(methodParams[index].ToString(), renameMode);
displayNameBuilder.Append(parameterName.DisplayName);
normalizedNameBuilder.Append(parameterName.NormalizedName);

if (index < methodParams.Count - 1) {
resultBuilder.Append(',');
displayNameBuilder.Append(',');
normalizedNameBuilder.Append(',');
}
}
}
resultBuilder.Append(')');

result = resultBuilder.ToString();
displayNameBuilder.Append(')');
normalizedNameBuilder.Append(')');
}
else {
result = dnlibDef.FullName;
}

return new DisplayNormalizedName(displayNameBuilder.ToString(),
shortNames ? dnlibDef.Name.ToString() : normalizedNameBuilder.ToString());
}

DisplayNormalizedName CompressTypeName(string typeName, RenameMode renameMode)
{
if (renameMode == RenameMode.Reversible)
{
if (!_prefixesMap.TryGetValue(typeName, out string prefix))
{
IncrementNameId();
prefix = Utils.EncodeString(nameId, alphaNumCharset);
_prefixesMap.Add(typeName, prefix);
}

return new DisplayNormalizedName(typeName, prefix);
}

return result;
return new DisplayNormalizedName(typeName, typeName);
}
}
}
2 changes: 1 addition & 1 deletion Confuser.Renamer/ReferenceUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal static StringBuilder AppendDescription(this StringBuilder builder, IDnl
return builder.AppendHashedIdentifier("Name", def.FullName);

builder.Append("Original Name").Append(": ");
builder.Append(nameService.GetOriginalFullName(def));
builder.Append(nameService.GetDisplayName(def));
builder.Append("; ");
return builder.AppendHashedIdentifier("Name", def.FullName);
}
Expand Down
Loading

0 comments on commit 7241bb2

Please sign in to comment.