Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support confusing dot Net standard libraries #553

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions Confuser.Core/ConfuserAssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,50 @@ public AssemblyDef Resolve(IAssembly assembly, ModuleDef sourceModule) {
if (assembly is AssemblyDef assemblyDef)
return assemblyDef;

var resolvedAssemblyDef = InternalExactResolver.Resolve(assembly, sourceModule);
return resolvedAssemblyDef ?? InternalFuzzyResolver.Resolve(assembly, sourceModule);
var resolvedAssemblyDef =
InternalExactResolver.Resolve(assembly, sourceModule) ??
InternalFuzzyResolver.Resolve(assembly, sourceModule);

// Remove AssemblyAttributes.PA_NoPlatform
if (null != resolvedAssemblyDef &&
(AssemblyAttributes.PA_Mask & resolvedAssemblyDef.Attributes) == AssemblyAttributes.PA_NoPlatform) {
resolvedAssemblyDef.Attributes =
resolvedAssemblyDef.Attributes & ~AssemblyAttributes.PA_FullMask;
}

if (resolvedAssemblyDef?.Name == "netstandard" && 0 < resolvedAssemblyDef.ManifestModule.ExportedTypes.Count) {
// Move types from AssemblyRef to here
var module = resolvedAssemblyDef.ManifestModule;
var newTypes = new List<TypeDef>();
var allAssemblyRefs = new List<AssemblyDef>();

module.ExportedTypes.Clear();

foreach (var assemblyRef in module.GetAssemblyRefs()) {
var subAss =
InternalExactResolver.Resolve(assemblyRef, module) ??
InternalFuzzyResolver.Resolve(assemblyRef, module);
allAssemblyRefs.Add(subAss);
foreach (var subModule in subAss?.Modules) {
foreach (var defType in subModule.Types) {
newTypes.Add(defType);
}
subModule.Types.Clear();
foreach (var defType in newTypes) {
module.Types.Add(defType);
}
newTypes.Clear();
}
}

// Remove them because their types has been removed.
foreach (var subAss in allAssemblyRefs) {
InternalExactResolver.Remove(subAss);
InternalFuzzyResolver.Remove(subAss);
}
}

return resolvedAssemblyDef;
}

public void Clear() {
Expand Down
11 changes: 10 additions & 1 deletion Confuser.Core/Helpers/InjectHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,16 @@ public override ITypeDefOrRef Map(ITypeDefOrRef source) {
// check if the assembly reference needs to be fixed.
if (source is TypeRef sourceRef) {
var targetAssemblyRef = TargetModule.GetAssemblyRef(sourceRef.DefinitionAssembly.Name);
if (!(targetAssemblyRef is null) && !string.Equals(targetAssemblyRef.FullName, source.DefinitionAssembly.FullName, StringComparison.Ordinal)) {
if (targetAssemblyRef is null) {
// Handle assemblies referenced by netstandard
var corLibAssemblyRef = TargetModule.CorLibTypes.AssemblyRef;
var corLibAssembly = TargetModule.Context.AssemblyResolver.Resolve(corLibAssemblyRef, TargetModule);
if (null != corLibAssembly?.ManifestModule.GetAssemblyRef(sourceRef.DefinitionAssembly.Name)) {
var fixedTypeRef = new TypeRefUser(sourceRef.Module, sourceRef.Namespace, sourceRef.Name, corLibAssemblyRef);
return Importer.Import(fixedTypeRef);
}
}
else if (!string.Equals(targetAssemblyRef.FullName, source.DefinitionAssembly.FullName, StringComparison.Ordinal)) {
// We got a matching assembly by the simple name, but not by the full name.
// This means the injected code uses a different assembly version than the target assembly.
// We'll fix the assembly reference, to avoid breaking anything.
Expand Down
6 changes: 3 additions & 3 deletions Confuser.Protections/Compress/Compressor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@

uint state = 0x6fff61;
foreach (byte chr in name)
state = state * 0x5e3f1f + chr;

Check notice on line 164 in Confuser.Protections/Compress/Compressor.cs

View check run for this annotation

codefactor.io / CodeFactor

Confuser.Protections/Compress/Compressor.cs#L164

Arithmetic expressions should declare precedence. (SA1407)
byte[] encrypted = compCtx.Encrypt(comp, entry.Value, state, progress => {
progress = (progress + moduleIndex) / modules.Count;
context.Logger.Progress((int)(progress * 10000), 10000);
Expand All @@ -175,7 +175,7 @@
context.Logger.EndProgress();
}

void InjectData(ModuleDef stubModule, MethodDef method, byte[] data) {
void InjectData(ConfuserContext context, ModuleDef stubModule, MethodDef method, byte[] data) {
var dataType = new TypeDefUser("", "DataType", stubModule.CorLibTypes.GetTypeRef("System", "ValueType"));
dataType.Layout = TypeAttributes.ExplicitLayout;
dataType.Visibility = TypeAttributes.NestedPrivate;
Expand All @@ -197,7 +197,7 @@
repl.Add(Instruction.Create(OpCodes.Dup));
repl.Add(Instruction.Create(OpCodes.Ldtoken, dataField));
repl.Add(Instruction.Create(OpCodes.Call, stubModule.Import(
typeof(RuntimeHelpers).GetMethod("InitializeArray"))));
context, typeof(RuntimeHelpers), "InitializeArray")));
return repl.ToArray();
});
}
Expand Down Expand Up @@ -254,7 +254,7 @@
MutationHelper.InjectKeys(entryPoint,
new[] { 0, 1 },
new[] { encryptedModule.Length >> 2, (int)seed });
InjectData(stubModule, entryPoint, encryptedModule);
InjectData(context, stubModule, entryPoint, encryptedModule);

// Decrypt
MethodDef decrypter = defs.OfType<MethodDef>().Single(method => method.Name == "Decrypt");
Expand Down
2 changes: 1 addition & 1 deletion Confuser.Protections/Constants/EncodePhase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
repl.Add(Instruction.Create(OpCodes.Dup));
repl.Add(Instruction.Create(OpCodes.Ldtoken, moduleCtx.DataField));
repl.Add(Instruction.Create(OpCodes.Call, moduleCtx.Module.Import(
typeof(RuntimeHelpers).GetMethod("InitializeArray"))));
context, typeof(RuntimeHelpers), "InitializeArray")));
return repl.ToArray();
});
}
Expand Down Expand Up @@ -191,7 +191,7 @@
// byte[] -> uint[]
int integral = buff.Length / 4, remainder = buff.Length % 4;
for (int i = 0; i < integral; i++) {
var data = (uint)(buff[i * 4] | (buff[i * 4 + 1] << 8) | (buff[i * 4 + 2] << 16) | (buff[i * 4 + 3] << 24));

Check notice on line 194 in Confuser.Protections/Constants/EncodePhase.cs

View check run for this annotation

codefactor.io / CodeFactor

Confuser.Protections/Constants/EncodePhase.cs#L194

Arithmetic expressions should declare precedence. (SA1407)

Check notice on line 194 in Confuser.Protections/Constants/EncodePhase.cs

View check run for this annotation

codefactor.io / CodeFactor

Confuser.Protections/Constants/EncodePhase.cs#L194

Arithmetic expressions should declare precedence. (SA1407)

Check notice on line 194 in Confuser.Protections/Constants/EncodePhase.cs

View check run for this annotation

codefactor.io / CodeFactor

Confuser.Protections/Constants/EncodePhase.cs#L194

Arithmetic expressions should declare precedence. (SA1407)
moduleCtx.EncodedBuffer.Add(data);
}
if (remainder > 0) {
Expand Down Expand Up @@ -228,128 +228,128 @@
}
}

void ExtractConstants(
ConfuserContext context, ProtectionParameters parameters, CEContext moduleCtx,
Dictionary<object, List<Tuple<MethodDef, Instruction>>> ldc,
Dictionary<byte[], List<Tuple<MethodDef, Instruction>>> ldInit) {
var dataFields = new HashSet<FieldDef>();
var fieldRefs = new HashSet<Instruction>();
foreach (MethodDef method in parameters.Targets.OfType<MethodDef>().WithProgress(context.Logger)) {
if (!method.HasBody)
continue;

moduleCtx.Elements = 0;
string elements = parameters.GetParameter(context, method, "elements", "SI");
foreach (char elem in elements)
switch (elem) {
case 'S':
case 's':
moduleCtx.Elements |= EncodeElements.Strings;
break;
case 'N':
case 'n':
moduleCtx.Elements |= EncodeElements.Numbers;
break;
case 'P':
case 'p':
moduleCtx.Elements |= EncodeElements.Primitive;
break;
case 'I':
case 'i':
moduleCtx.Elements |= EncodeElements.Initializers;
break;
}

if (moduleCtx.Elements == 0)
continue;

foreach (Instruction instr in method.Body.Instructions) {
bool eligible = false;
if (instr.OpCode == OpCodes.Ldstr && (moduleCtx.Elements & EncodeElements.Strings) != 0) {
var operand = (string)instr.Operand;
if (string.IsNullOrEmpty(operand) && (moduleCtx.Elements & EncodeElements.Primitive) == 0)
continue;
eligible = true;
}
else if (instr.OpCode == OpCodes.Call && (moduleCtx.Elements & EncodeElements.Initializers) != 0) {
var operand = (IMethod)instr.Operand;
if (operand.DeclaringType.DefinitionAssembly.IsCorLib() &&
operand.DeclaringType.Namespace == "System.Runtime.CompilerServices" &&
operand.DeclaringType.Name == "RuntimeHelpers" &&
operand.Name == "InitializeArray") {
IList<Instruction> instrs = method.Body.Instructions;
int i = instrs.IndexOf(instr);
if (instrs[i - 1].OpCode != OpCodes.Ldtoken) continue;
if (instrs[i - 2].OpCode != OpCodes.Dup) continue;
if (instrs[i - 3].OpCode != OpCodes.Newarr) continue;
if (instrs[i - 4].OpCode != OpCodes.Ldc_I4) continue;
if (!(instrs[i - 3].Operand is ITypeDefOrRef arrayType)) continue;
if (!arrayType.IsPrimitive) continue;

var dataField = instrs[i - 1].Operand as FieldDef;
if (dataField == null)
continue;
if (!dataField.HasFieldRVA || dataField.InitialValue == null)
continue;

// 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]);
if (list.Count == 0)
ldc.Remove(arrLen);
}

dataFields.Add(dataField);
fieldRefs.Add(instrs[i - 1]);

var value = new byte[dataField.InitialValue.Length + 4];
value[0] = (byte)(arrLen >> 0);
value[1] = (byte)(arrLen >> 8);
value[2] = (byte)(arrLen >> 16);
value[3] = (byte)(arrLen >> 24);
Buffer.BlockCopy(dataField.InitialValue, 0, value, 4, dataField.InitialValue.Length);
ldInit.AddListEntry(value, Tuple.Create(method, instr));
}
}
else if ((moduleCtx.Elements & EncodeElements.Numbers) != 0) {
if (instr.OpCode == OpCodes.Ldc_I4) {
var operand = (int)instr.Operand;
if ((operand >= -1 && operand <= 8) && (moduleCtx.Elements & EncodeElements.Primitive) == 0)
continue;
eligible = true;
}
else if (instr.OpCode == OpCodes.Ldc_I8) {
var operand = (long)instr.Operand;
if ((operand >= -1 && operand <= 1) && (moduleCtx.Elements & EncodeElements.Primitive) == 0)
continue;
eligible = true;
}
else if (instr.OpCode == OpCodes.Ldc_R4) {
var operand = (float)instr.Operand;
if ((operand == -1 || operand == 0 || operand == 1) && (moduleCtx.Elements & EncodeElements.Primitive) == 0)
continue;
eligible = true;
}
else if (instr.OpCode == OpCodes.Ldc_R8) {
var operand = (double)instr.Operand;
if ((operand == -1 || operand == 0 || operand == 1) && (moduleCtx.Elements & EncodeElements.Primitive) == 0)
continue;
eligible = true;
}
}

if (eligible)
ldc.AddListEntry(instr.Operand, Tuple.Create(method, instr));
}

context.CheckCancellation();
}
RemoveDataFieldRefs(context, dataFields, fieldRefs);
}

class ByteArrayComparer : IEqualityComparer<byte[]> {

Check notice on line 352 in Confuser.Protections/Constants/EncodePhase.cs

View check run for this annotation

codefactor.io / CodeFactor

Confuser.Protections/Constants/EncodePhase.cs#L231-L352

Complex Method
public bool Equals(byte[] x, byte[] y) {
return x.SequenceEqual(y);
}
Expand All @@ -357,7 +357,7 @@
public int GetHashCode(byte[] obj) {
int ret = 31;
foreach (byte v in obj)
ret = ret * 17 + v;

Check notice on line 360 in Confuser.Protections/Constants/EncodePhase.cs

View check run for this annotation

codefactor.io / CodeFactor

Confuser.Protections/Constants/EncodePhase.cs#L360

Arithmetic expressions should declare precedence. (SA1407)
return ret;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Confuser.Protections/Resources/InjectPhase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ void MutateInitializer(REContext moduleCtx, MethodDef decomp) {
repl.Add(Instruction.Create(OpCodes.Dup));
repl.Add(Instruction.Create(OpCodes.Ldtoken, moduleCtx.DataField));
repl.Add(Instruction.Create(OpCodes.Call, moduleCtx.Module.Import(
typeof(RuntimeHelpers).GetMethod("InitializeArray"))));
moduleCtx.Context, typeof(RuntimeHelpers), "InitializeArray")));
return repl.ToArray();
});
moduleCtx.Context.Registry.GetService<IConstantService>().ExcludeMethod(moduleCtx.Context, moduleCtx.InitMethod);
}
}
}
}
18 changes: 18 additions & 0 deletions Confuser.Protections/Utils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Confuser.Core;
using dnlib.DotNet;

namespace Confuser.Protections {
internal static class Utils {
public static IMethod Import(this ModuleDef module, ConfuserContext context, Type classType, string method) {
var corLib = context.Resolver.Resolve(context.CurrentModule?.CorLibTypes.AssemblyRef, context.CurrentModule);
var typeInfo = corLib?.ManifestModule.Find(classType.FullName, true);
return (typeInfo == null) ? module.Import(classType.GetMethod(method)) : module.Import(typeInfo.FindMethod(method));
}
}
}