Skip to content

Commit 0c35448

Browse files
authored
Merge 398eb97 into cfccc32
2 parents cfccc32 + 398eb97 commit 0c35448

File tree

24 files changed

+549
-308
lines changed

24 files changed

+549
-308
lines changed

Confuser.Core/Services/CompressionService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public MethodDef GetRuntimeDecompressor(ModuleDef module, Action<IDnlibDef> init
5959
}
6060
}
6161
}
62-
members.RemoveWhere(def => def is FieldDef && ((FieldDef)def).IsLiteral);
62+
members.RemoveAll(def => def is FieldDef fieldDef && fieldDef.IsLiteral);
6363

6464
Debug.Assert(decomp != null);
6565
return Tuple.Create(decomp, members);
@@ -156,4 +156,4 @@ public interface ICompressionService {
156156
/// <returns>The compressed data.</returns>
157157
byte[] Compress(byte[] data, Action<double> progressFunc = null);
158158
}
159-
}
159+
}

Confuser.Core/Utils.cs

Lines changed: 27 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ namespace Confuser.Core {
1212
public static class Utils {
1313
static readonly char[] hexCharset = "0123456789abcdef".ToCharArray();
1414

15+
// Use not thread safe buffers since app is not multithreaded
16+
static readonly StringBuilder Buffer = new StringBuilder();
17+
static readonly SHA1Managed Sha1Managed = new SHA1Managed();
18+
static readonly SHA256Managed Sha256Managed = new SHA256Managed();
19+
1520
/// <summary>
1621
/// Gets the value associated with the specified key, or default value if the key does not exists.
1722
/// </summary>
@@ -24,9 +29,8 @@ public static class Utils {
2429
public static TValue GetValueOrDefault<TKey, TValue>(
2530
this Dictionary<TKey, TValue> dictionary,
2631
TKey key,
27-
TValue defValue = default(TValue)) {
28-
TValue ret;
29-
if (dictionary.TryGetValue(key, out ret))
32+
TValue defValue = default) {
33+
if (dictionary.TryGetValue(key, out var ret))
3034
return ret;
3135
return defValue;
3236
}
@@ -44,8 +48,7 @@ public static TValue GetValueOrDefaultLazy<TKey, TValue>(
4448
this Dictionary<TKey, TValue> dictionary,
4549
TKey key,
4650
Func<TKey, TValue> defValueFactory) {
47-
TValue ret;
48-
if (dictionary.TryGetValue(key, out ret))
51+
if (dictionary.TryGetValue(key, out var ret))
4952
return ret;
5053
return defValueFactory(key);
5154
}
@@ -62,8 +65,7 @@ public static TValue GetValueOrDefaultLazy<TKey, TValue>(
6265
public static void AddListEntry<TKey, TValue>(this IDictionary<TKey, List<TValue>> self, TKey key, TValue value) {
6366
if (key == null)
6467
throw new ArgumentNullException("key");
65-
List<TValue> list;
66-
if (!self.TryGetValue(key, out list))
68+
if (!self.TryGetValue(key, out var list))
6769
list = self[key] = new List<TValue>();
6870
list.Add(value);
6971
}
@@ -102,36 +104,14 @@ public static string NullIfEmpty(this string val) {
102104
/// </summary>
103105
/// <param name="buffer">The input buffer.</param>
104106
/// <returns>The SHA1 hash of the input buffer.</returns>
105-
public static byte[] SHA1(byte[] buffer) {
106-
var sha = new SHA1Managed();
107-
return sha.ComputeHash(buffer);
108-
}
109-
110-
/// <summary>
111-
/// Xor the values in the two buffer together.
112-
/// </summary>
113-
/// <param name="buffer1">The input buffer 1.</param>
114-
/// <param name="buffer2">The input buffer 2.</param>
115-
/// <returns>The result buffer.</returns>
116-
/// <exception cref="System.ArgumentException">Length of the two buffers are not equal.</exception>
117-
public static byte[] Xor(byte[] buffer1, byte[] buffer2) {
118-
if (buffer1.Length != buffer2.Length)
119-
throw new ArgumentException("Length mismatched.");
120-
var ret = new byte[buffer1.Length];
121-
for (int i = 0; i < ret.Length; i++)
122-
ret[i] = (byte)(buffer1[i] ^ buffer2[i]);
123-
return ret;
124-
}
107+
public static byte[] SHA1(byte[] buffer) => Sha1Managed.ComputeHash(buffer);
125108

126109
/// <summary>
127110
/// Compute the SHA256 hash of the input buffer.
128111
/// </summary>
129112
/// <param name="buffer">The input buffer.</param>
130113
/// <returns>The SHA256 hash of the input buffer.</returns>
131-
public static byte[] SHA256(byte[] buffer) {
132-
var sha = new SHA256Managed();
133-
return sha.ComputeHash(buffer);
134-
}
114+
public static byte[] SHA256(byte[] buffer) => Sha256Managed.ComputeHash(buffer);
135115

136116
/// <summary>
137117
/// Encoding the buffer to a string using specified charset.
@@ -140,65 +120,32 @@ public static byte[] SHA256(byte[] buffer) {
140120
/// <param name="charset">The charset.</param>
141121
/// <returns>The encoded string.</returns>
142122
public static string EncodeString(byte[] buff, char[] charset) {
123+
Buffer.Clear();
143124
int current = buff[0];
144-
var ret = new StringBuilder();
145125
for (int i = 1; i < buff.Length; i++) {
146126
current = (current << 8) + buff[i];
147127
while (current >= charset.Length) {
148-
ret.Append(charset[current % charset.Length]);
149-
current /= charset.Length;
128+
current = Math.DivRem(current, charset.Length, out int remainder);
129+
Buffer.Append(charset[remainder]);
150130
}
151131
}
152132
if (current != 0)
153-
ret.Append(charset[current % charset.Length]);
154-
return ret.ToString();
155-
}
156-
157-
/// <summary>
158-
/// Returns a new string in which all occurrences of a specified string in
159-
/// <paramref name="str" /> are replaced with another specified string.
160-
/// </summary>
161-
/// <returns>
162-
/// A <see cref="string" /> equivalent to <paramref name="str" /> but with all instances of
163-
/// <paramref name="oldValue" />
164-
/// replaced with <paramref name="newValue" />.
165-
/// </returns>
166-
/// <param name="str">A string to do the replace in. </param>
167-
/// <param name="oldValue">A string to be replaced. </param>
168-
/// <param name="newValue">A string to replace all occurrences of <paramref name="oldValue" />. </param>
169-
/// <param name="comparison">One of the <see cref="StringComparison" /> values. </param>
170-
/// <remarks>Adopted from http://stackoverflow.com/a/244933 </remarks>
171-
public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) {
172-
StringBuilder sb = new StringBuilder();
173-
174-
int previousIndex = 0;
175-
int index = str.IndexOf(oldValue, comparison);
176-
while (index != -1) {
177-
sb.Append(str.Substring(previousIndex, index - previousIndex));
178-
sb.Append(newValue);
179-
index += oldValue.Length;
180-
previousIndex = index;
181-
index = str.IndexOf(oldValue, index, comparison);
182-
}
183-
sb.Append(str.Substring(previousIndex));
184-
185-
return sb.ToString();
133+
Buffer.Append(charset[current % charset.Length]);
134+
return Buffer.ToString();
186135
}
187136

188-
189137
/// <summary>
190138
/// Encode the buffer to a hexadecimal string.
191139
/// </summary>
192140
/// <param name="buff">The input buffer.</param>
193141
/// <returns>A hexadecimal representation of input buffer.</returns>
194142
public static string ToHexString(byte[] buff) {
195-
var ret = new char[buff.Length * 2];
196-
int i = 0;
143+
Buffer.Clear();
197144
foreach (byte val in buff) {
198-
ret[i++] = hexCharset[val >> 4];
199-
ret[i++] = hexCharset[val & 0xf];
145+
Buffer.Append(hexCharset[val >> 4]);
146+
Buffer.Append(hexCharset[val & 0xf]);
200147
}
201-
return new string(ret);
148+
return Buffer.ToString();
202149
}
203150

204151
/// <summary>
@@ -208,12 +155,17 @@ public static string ToHexString(byte[] buff) {
208155
/// <param name="self">The list to remove from.</param>
209156
/// <param name="match">The predicate that defines the conditions of the elements to remove.</param>
210157
/// <returns><paramref name="self" /> for method chaining.</returns>
211-
public static IList<T> RemoveWhere<T>(this IList<T> self, Predicate<T> match) {
158+
public static void RemoveWhere<T>(this IList<T> self, Predicate<T> match) {
159+
if (self is List<T> list) {
160+
list.RemoveAll(match);
161+
return;
162+
}
163+
164+
// Switch to slow algorithm
212165
for (int i = self.Count - 1; i >= 0; i--) {
213166
if (match(self[i]))
214167
self.RemoveAt(i);
215168
}
216-
return self;
217169
}
218170

219171
/// <summary>

Confuser.Protections/AntiDebugProtection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa
121121
}
122122
}
123123
if (ren) {
124-
member.Name = name.ObfuscateName(member.Name, RenameMode.Unicode);
124+
member.Name = name.ObfuscateName(member, RenameMode.Unicode);
125125
name.SetCanRename(member, false);
126126
}
127127
}

Confuser.Protections/Constants/EncodePhase.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ void EncodeConstant64(CEContext moduleCtx, uint hi, uint lo, TypeSig valueType,
150150
if (buffIndex + 1 < moduleCtx.EncodedBuffer.Count && moduleCtx.EncodedBuffer[buffIndex + 1] == hi)
151151
break;
152152
} while (buffIndex >= 0);
153-
153+
154154
if (buffIndex == -1) {
155155
buffIndex = moduleCtx.EncodedBuffer.Count;
156156
moduleCtx.EncodedBuffer.Add(lo);
@@ -295,8 +295,8 @@ void ExtractConstants(
295295
// Prevent array length from being encoded
296296
var arrLen = (int)instrs[i - 4].Operand;
297297
if (ldc.ContainsKey(arrLen)) {
298-
List<Tuple<MethodDef, Instruction>> list = ldc[arrLen];
299-
list.RemoveWhere(entry => entry.Item2 == instrs[i - 4]);
298+
var list = ldc[arrLen];
299+
list.RemoveAll(entry => entry.Item2 == instrs[i - 4]);
300300
if (list.Count == 0)
301301
ldc.Remove(arrLen);
302302
}

Confuser.Renamer/AnalyzePhase.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,15 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa
3434
foreach (IDnlibDef def in parameters.Targets.WithProgress(context.Logger)) {
3535
ParseParameters(def, context, service, parameters);
3636

37-
if (def is ModuleDef) {
38-
var module = (ModuleDef)def;
39-
foreach (Resource res in module.Resources)
40-
service.SetOriginalName(res, res.Name);
37+
if (def is ModuleDef module) {
38+
foreach (var res in module.Resources)
39+
service.AddReservedIdentifier(res.Name);
4140
}
4241
else
43-
service.SetOriginalName(def, def.Name);
42+
service.SetOriginalName(def);
4443

45-
if (def is TypeDef) {
46-
service.GetVTables().GetVTable((TypeDef)def);
47-
service.SetOriginalNamespace(def, ((TypeDef)def).Namespace);
44+
if (def is TypeDef typeDef) {
45+
service.GetVTables().GetVTable(typeDef);
4846
}
4947
context.CheckCancellation();
5048
}

Confuser.Renamer/Analyzers/VTableAnalyzer.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,24 @@ public static void Analyze(INameService service, ICollection<ModuleDefMD> module
3333
// derived type. If the base type/interface is not in our control, we should
3434
// not rename the methods.
3535
bool baseUnderCtrl = modules.Contains(slot.MethodDef.DeclaringType.Module as ModuleDefMD);
36-
bool ifaceUnderCtrl = modules.Contains(slot.Overrides.MethodDef.DeclaringType.Module as ModuleDefMD);
37-
if ((!baseUnderCtrl && ifaceUnderCtrl) || !service.CanRename(slot.MethodDef)) {
36+
bool interfaceUnderCtrl = modules.Contains(slot.Overrides.MethodDef.DeclaringType.Module as ModuleDefMD);
37+
if (!baseUnderCtrl && interfaceUnderCtrl || !service.CanRename(slot.MethodDef)) {
3838
service.SetCanRename(slot.Overrides.MethodDef, false);
3939
}
40-
else if (baseUnderCtrl && !ifaceUnderCtrl || !service.CanRename(slot.Overrides.MethodDef)) {
40+
else if (baseUnderCtrl && !interfaceUnderCtrl || !service.CanRename(slot.Overrides.MethodDef)) {
4141
service.SetCanRename(slot.MethodDef, false);
4242
}
43+
44+
// For the case when method in base type implements an interface method for a derived type
45+
// do not consider method parameters to make method name the same in base type, derived type and interface
46+
var methodDef = slot.MethodDef;
47+
var typeDef = type.BaseType?.ResolveTypeDef();
48+
var baseMethod = typeDef?.FindMethod(methodDef.Name, methodDef.Signature as MethodSig);
49+
if (baseMethod != null) {
50+
string unifiedName = service.GetOriginalFullName(slot.Overrides.MethodDef);
51+
service.SetOriginalName(slot.MethodDef, unifiedName);
52+
service.SetOriginalName(baseMethod, unifiedName);
53+
}
4354
}
4455
}
4556
}
@@ -207,7 +218,7 @@ private static IEnumerable<MethodDef> FindBaseDeclarations(INameService service,
207218
unprocessed.Enqueue(slot.Overrides.MethodDef);
208219
slotsExists = true;
209220
}
210-
221+
211222
if (!slotsExists && method != currentMethod)
212223
yield return currentMethod;
213224
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text.RegularExpressions;
5+
6+
namespace Confuser.Renamer {
7+
public class MessageDeobfuscator {
8+
static readonly Regex MapSymbolMatcher = new Regex("_[a-zA-Z0-9]+", RegexOptions.Compiled);
9+
static readonly Regex PasswordSymbolMatcher = new Regex("[a-zA-Z0-9_$]{23,}", RegexOptions.Compiled);
10+
11+
readonly Dictionary<string, string> _symbolMap;
12+
readonly ReversibleRenamer _renamer;
13+
14+
public static MessageDeobfuscator Load(string symbolMapFileName) {
15+
var symbolMap = new Dictionary<string, string>();
16+
using (var reader = new StreamReader(File.OpenRead(symbolMapFileName))) {
17+
var line = reader.ReadLine();
18+
while (line != null) {
19+
int tabIndex = line.IndexOf('\t');
20+
if (tabIndex == -1)
21+
throw new FileFormatException();
22+
symbolMap.Add(line.Substring(0, tabIndex), line.Substring(tabIndex + 1));
23+
line = reader.ReadLine();
24+
}
25+
}
26+
27+
return new MessageDeobfuscator(symbolMap);
28+
}
29+
30+
public MessageDeobfuscator(Dictionary<string, string> map) => _symbolMap = map ?? throw new ArgumentNullException(nameof(map));
31+
32+
public MessageDeobfuscator(string password) => _renamer = new ReversibleRenamer(password);
33+
34+
public string Deobfuscate(string obfuscatedMessage) {
35+
if (_symbolMap != null) {
36+
return MapSymbolMatcher.Replace(obfuscatedMessage, DecodeSymbolMap);
37+
}
38+
39+
return PasswordSymbolMatcher.Replace(obfuscatedMessage, DecodeSymbolPassword);
40+
}
41+
42+
string DecodeSymbolMap(Match match) {
43+
var symbol = match.Value;
44+
if (_symbolMap.TryGetValue(symbol, out string result))
45+
return ExtractShortName(result);
46+
return ExtractShortName(symbol);
47+
}
48+
49+
string DecodeSymbolPassword(Match match) {
50+
var sym = match.Value;
51+
try {
52+
return ExtractShortName(_renamer.Decrypt(sym));
53+
}
54+
catch {
55+
return sym;
56+
}
57+
}
58+
59+
string ExtractShortName(string fullName) {
60+
const string doubleParen = "::";
61+
int doubleParenIndex = fullName.IndexOf(doubleParen);
62+
if (doubleParenIndex != -1) {
63+
int resultStringStartIndex = doubleParenIndex + doubleParen.Length;
64+
int parenIndex = fullName.IndexOf('(', doubleParenIndex);
65+
return fullName.Substring(resultStringStartIndex,
66+
(parenIndex == -1 ? fullName.Length : parenIndex) - resultStringStartIndex);
67+
}
68+
69+
int slashIndex = fullName.IndexOf('/');
70+
if (slashIndex != -1) {
71+
return fullName.Substring(slashIndex + 1);
72+
}
73+
74+
return fullName;
75+
}
76+
}
77+
}

Confuser.Renamer/NameProtection.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa
6666
if (!Directory.Exists(dir))
6767
Directory.CreateDirectory(dir);
6868

69-
using (var writer = new StreamWriter(File.OpenWrite(path))) {
69+
using (var writer = new StreamWriter(File.Create(path))) {
7070
foreach (var entry in map)
7171
writer.WriteLine("{0}\t{1}", entry.Key, entry.Value);
7272
}
7373
}
7474
}
7575
}
76-
}
76+
}

0 commit comments

Comments
 (0)