Skip to content

Commit

Permalink
Merge 22d25eb into cfccc32
Browse files Browse the repository at this point in the history
  • Loading branch information
KvanTTT authored Jan 17, 2021
2 parents cfccc32 + 22d25eb commit 17712c5
Show file tree
Hide file tree
Showing 23 changed files with 547 additions and 304 deletions.
4 changes: 2 additions & 2 deletions Confuser.Core/Services/CompressionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public MethodDef GetRuntimeDecompressor(ModuleDef module, Action<IDnlibDef> init
}
}
}
members.RemoveWhere(def => def is FieldDef && ((FieldDef)def).IsLiteral);
members.RemoveAll(def => def is FieldDef fieldDef && fieldDef.IsLiteral);
Debug.Assert(decomp != null);
return Tuple.Create(decomp, members);
Expand Down Expand Up @@ -156,4 +156,4 @@ public interface ICompressionService {
/// <returns>The compressed data.</returns>
byte[] Compress(byte[] data, Action<double> progressFunc = null);
}
}
}
102 changes: 27 additions & 75 deletions Confuser.Core/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ namespace Confuser.Core {
public static class Utils {
static readonly char[] hexCharset = "0123456789abcdef".ToCharArray();

// Use not thread safe buffers since app is not multithreaded
static readonly StringBuilder Buffer = new StringBuilder();
static readonly SHA1Managed Sha1Managed = new SHA1Managed();
static readonly SHA256Managed Sha256Managed = new SHA256Managed();

/// <summary>
/// Gets the value associated with the specified key, or default value if the key does not exists.
/// </summary>
Expand All @@ -24,9 +29,8 @@ public static class Utils {
public static TValue GetValueOrDefault<TKey, TValue>(
this Dictionary<TKey, TValue> dictionary,
TKey key,
TValue defValue = default(TValue)) {
TValue ret;
if (dictionary.TryGetValue(key, out ret))
TValue defValue = default) {
if (dictionary.TryGetValue(key, out var ret))
return ret;
return defValue;
}
Expand All @@ -44,8 +48,7 @@ public static TValue GetValueOrDefaultLazy<TKey, TValue>(
this Dictionary<TKey, TValue> dictionary,
TKey key,
Func<TKey, TValue> defValueFactory) {
TValue ret;
if (dictionary.TryGetValue(key, out ret))
if (dictionary.TryGetValue(key, out var ret))
return ret;
return defValueFactory(key);
}
Expand All @@ -62,8 +65,7 @@ public static TValue GetValueOrDefaultLazy<TKey, TValue>(
public static void AddListEntry<TKey, TValue>(this IDictionary<TKey, List<TValue>> self, TKey key, TValue value) {
if (key == null)
throw new ArgumentNullException("key");
List<TValue> list;
if (!self.TryGetValue(key, out list))
if (!self.TryGetValue(key, out var list))
list = self[key] = new List<TValue>();
list.Add(value);
}
Expand Down Expand Up @@ -102,36 +104,14 @@ public static string NullIfEmpty(this string val) {
/// </summary>
/// <param name="buffer">The input buffer.</param>
/// <returns>The SHA1 hash of the input buffer.</returns>
public static byte[] SHA1(byte[] buffer) {
var sha = new SHA1Managed();
return sha.ComputeHash(buffer);
}

/// <summary>
/// Xor the values in the two buffer together.
/// </summary>
/// <param name="buffer1">The input buffer 1.</param>
/// <param name="buffer2">The input buffer 2.</param>
/// <returns>The result buffer.</returns>
/// <exception cref="System.ArgumentException">Length of the two buffers are not equal.</exception>
public static byte[] Xor(byte[] buffer1, byte[] buffer2) {
if (buffer1.Length != buffer2.Length)
throw new ArgumentException("Length mismatched.");
var ret = new byte[buffer1.Length];
for (int i = 0; i < ret.Length; i++)
ret[i] = (byte)(buffer1[i] ^ buffer2[i]);
return ret;
}
public static byte[] SHA1(byte[] buffer) => Sha1Managed.ComputeHash(buffer);

/// <summary>
/// Compute the SHA256 hash of the input buffer.
/// </summary>
/// <param name="buffer">The input buffer.</param>
/// <returns>The SHA256 hash of the input buffer.</returns>
public static byte[] SHA256(byte[] buffer) {
var sha = new SHA256Managed();
return sha.ComputeHash(buffer);
}
public static byte[] SHA256(byte[] buffer) => Sha256Managed.ComputeHash(buffer);

/// <summary>
/// Encoding the buffer to a string using specified charset.
Expand All @@ -140,65 +120,32 @@ public static byte[] SHA256(byte[] buffer) {
/// <param name="charset">The charset.</param>
/// <returns>The encoded string.</returns>
public static string EncodeString(byte[] buff, char[] charset) {
Buffer.Clear();
int current = buff[0];
var ret = new StringBuilder();
for (int i = 1; i < buff.Length; i++) {
current = (current << 8) + buff[i];
while (current >= charset.Length) {
ret.Append(charset[current % charset.Length]);
current /= charset.Length;
current = Math.DivRem(current, charset.Length, out int remainder);
Buffer.Append(charset[remainder]);
}
}
if (current != 0)
ret.Append(charset[current % charset.Length]);
return ret.ToString();
}

/// <summary>
/// Returns a new string in which all occurrences of a specified string in
/// <paramref name="str" /> are replaced with another specified string.
/// </summary>
/// <returns>
/// A <see cref="string" /> equivalent to <paramref name="str" /> but with all instances of
/// <paramref name="oldValue" />
/// replaced with <paramref name="newValue" />.
/// </returns>
/// <param name="str">A string to do the replace in. </param>
/// <param name="oldValue">A string to be replaced. </param>
/// <param name="newValue">A string to replace all occurrences of <paramref name="oldValue" />. </param>
/// <param name="comparison">One of the <see cref="StringComparison" /> values. </param>
/// <remarks>Adopted from http://stackoverflow.com/a/244933 </remarks>
public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) {
StringBuilder sb = new StringBuilder();

int previousIndex = 0;
int index = str.IndexOf(oldValue, comparison);
while (index != -1) {
sb.Append(str.Substring(previousIndex, index - previousIndex));
sb.Append(newValue);
index += oldValue.Length;
previousIndex = index;
index = str.IndexOf(oldValue, index, comparison);
}
sb.Append(str.Substring(previousIndex));

return sb.ToString();
Buffer.Append(charset[current % charset.Length]);
return Buffer.ToString();
}


/// <summary>
/// Encode the buffer to a hexadecimal string.
/// </summary>
/// <param name="buff">The input buffer.</param>
/// <returns>A hexadecimal representation of input buffer.</returns>
public static string ToHexString(byte[] buff) {
var ret = new char[buff.Length * 2];
int i = 0;
Buffer.Clear();
foreach (byte val in buff) {
ret[i++] = hexCharset[val >> 4];
ret[i++] = hexCharset[val & 0xf];
Buffer.Append(hexCharset[val >> 4]);
Buffer.Append(hexCharset[val & 0xf]);
}
return new string(ret);
return Buffer.ToString();
}

/// <summary>
Expand All @@ -208,12 +155,17 @@ public static string ToHexString(byte[] buff) {
/// <param name="self">The list to remove from.</param>
/// <param name="match">The predicate that defines the conditions of the elements to remove.</param>
/// <returns><paramref name="self" /> for method chaining.</returns>
public static IList<T> RemoveWhere<T>(this IList<T> self, Predicate<T> match) {
public static void RemoveWhere<T>(this IList<T> self, Predicate<T> match) {
if (self is List<T> list) {
list.RemoveAll(match);
return;
}

// Switch to slow algorithm
for (int i = self.Count - 1; i >= 0; i--) {
if (match(self[i]))
self.RemoveAt(i);
}
return self;
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Confuser.Protections/AntiDebugProtection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa
}
}
if (ren) {
member.Name = name.ObfuscateName(member.Name, RenameMode.Unicode);
member.Name = name.ObfuscateName(member, RenameMode.Unicode);
name.SetCanRename(member, false);
}
}
Expand Down
6 changes: 3 additions & 3 deletions Confuser.Protections/Constants/EncodePhase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ void EncodeConstant64(CEContext moduleCtx, uint hi, uint lo, TypeSig valueType,
if (buffIndex + 1 < moduleCtx.EncodedBuffer.Count && moduleCtx.EncodedBuffer[buffIndex + 1] == hi)
break;
} while (buffIndex >= 0);

if (buffIndex == -1) {
buffIndex = moduleCtx.EncodedBuffer.Count;
moduleCtx.EncodedBuffer.Add(lo);
Expand Down Expand Up @@ -295,8 +295,8 @@ void ExtractConstants(
// Prevent array length from being encoded
var arrLen = (int)instrs[i - 4].Operand;
if (ldc.ContainsKey(arrLen)) {
List<Tuple<MethodDef, Instruction>> list = ldc[arrLen];
list.RemoveWhere(entry => entry.Item2 == instrs[i - 4]);
var list = ldc[arrLen];
list.RemoveAll(entry => entry.Item2 == instrs[i - 4]);
if (list.Count == 0)
ldc.Remove(arrLen);
}
Expand Down
14 changes: 6 additions & 8 deletions Confuser.Renamer/AnalyzePhase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,15 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa
foreach (IDnlibDef def in parameters.Targets.WithProgress(context.Logger)) {
ParseParameters(def, context, service, parameters);

if (def is ModuleDef) {
var module = (ModuleDef)def;
foreach (Resource res in module.Resources)
service.SetOriginalName(res, res.Name);
if (def is ModuleDef module) {
foreach (var res in module.Resources)
service.AddReservedIdentifier(res.Name);
}
else
service.SetOriginalName(def, def.Name);
service.SetOriginalName(def);

if (def is TypeDef) {
service.GetVTables().GetVTable((TypeDef)def);
service.SetOriginalNamespace(def, ((TypeDef)def).Namespace);
if (def is TypeDef typeDef) {
service.GetVTables().GetVTable(typeDef);
}
context.CheckCancellation();
}
Expand Down
19 changes: 15 additions & 4 deletions Confuser.Renamer/Analyzers/VTableAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,24 @@ public static void Analyze(INameService service, ICollection<ModuleDefMD> module
// derived type. If the base type/interface is not in our control, we should
// not rename the methods.
bool baseUnderCtrl = modules.Contains(slot.MethodDef.DeclaringType.Module as ModuleDefMD);
bool ifaceUnderCtrl = modules.Contains(slot.Overrides.MethodDef.DeclaringType.Module as ModuleDefMD);
if ((!baseUnderCtrl && ifaceUnderCtrl) || !service.CanRename(slot.MethodDef)) {
bool interfaceUnderCtrl = modules.Contains(slot.Overrides.MethodDef.DeclaringType.Module as ModuleDefMD);
if (!baseUnderCtrl && interfaceUnderCtrl || !service.CanRename(slot.MethodDef)) {
service.SetCanRename(slot.Overrides.MethodDef, false);
}
else if (baseUnderCtrl && !ifaceUnderCtrl || !service.CanRename(slot.Overrides.MethodDef)) {
else if (baseUnderCtrl && !interfaceUnderCtrl || !service.CanRename(slot.Overrides.MethodDef)) {
service.SetCanRename(slot.MethodDef, false);
}

// For the case when method in base type implements an interface method for a derived type
// do not consider method parameters to make method name the same in base type, derived type and interface
var methodDef = slot.MethodDef;
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);
}
}
}
}
Expand Down Expand Up @@ -207,7 +218,7 @@ private static IEnumerable<MethodDef> FindBaseDeclarations(INameService service,
unprocessed.Enqueue(slot.Overrides.MethodDef);
slotsExists = true;
}

if (!slotsExists && method != currentMethod)
yield return currentMethod;
}
Expand Down
77 changes: 77 additions & 0 deletions Confuser.Renamer/MessageDeobfuscator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

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

readonly Dictionary<string, string> _symbolMap;
readonly ReversibleRenamer _renamer;

public static MessageDeobfuscator Load(string symbolMapFileName) {
var symbolMap = new Dictionary<string, string>();
using (var reader = new StreamReader(File.OpenRead(symbolMapFileName))) {
var line = reader.ReadLine();
while (line != null) {
int tabIndex = line.IndexOf('\t');
if (tabIndex == -1)
throw new FileFormatException();
symbolMap.Add(line.Substring(0, tabIndex), line.Substring(tabIndex + 1));
line = reader.ReadLine();
}
}

return new MessageDeobfuscator(symbolMap);
}

public MessageDeobfuscator(Dictionary<string, string> map) => _symbolMap = map ?? throw new ArgumentNullException(nameof(map));

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

public string Deobfuscate(string obfuscatedMessage) {
if (_symbolMap != null) {
return MapSymbolMatcher.Replace(obfuscatedMessage, DecodeSymbolMap);
}

return PasswordSymbolMatcher.Replace(obfuscatedMessage, DecodeSymbolPassword);
}

string DecodeSymbolMap(Match match) {
var symbol = match.Value;
if (_symbolMap.TryGetValue(symbol, out string result))
return ExtractShortName(result);
return ExtractShortName(symbol);
}

string DecodeSymbolPassword(Match match) {
var sym = match.Value;
try {
return ExtractShortName(_renamer.Decrypt(sym));
}
catch {
return sym;
}
}

string ExtractShortName(string fullName) {
const string doubleParen = "::";
int doubleParenIndex = fullName.IndexOf(doubleParen);
if (doubleParenIndex != -1) {
int resultStringStartIndex = doubleParenIndex + doubleParen.Length;
int parenIndex = fullName.IndexOf('(', doubleParenIndex);
return fullName.Substring(resultStringStartIndex,
(parenIndex == -1 ? fullName.Length : parenIndex) - resultStringStartIndex);
}

int slashIndex = fullName.IndexOf('/');
if (slashIndex != -1) {
return fullName.Substring(slashIndex + 1);
}

return fullName;
}
}
}
Loading

0 comments on commit 17712c5

Please sign in to comment.