From 7a330a5c2a1aa9116728005bef2cb805950bef09 Mon Sep 17 00:00:00 2001 From: Harry Stoltz Date: Fri, 7 Apr 2023 21:15:25 +0100 Subject: [PATCH] Fix minor issues with AntiTamper and Constants. Start work on dynamic/x86 Constants --- .../AntiTamper/AntiTamperRemover.cs | 29 +++-- .../Protections/Constants/ConstantsRemover.cs | 9 +- .../Protections/Constants/IResolver.cs | 112 +++++++++++++++++- .../Protections/Constants/NormalResolver.cs | 98 +-------------- .../Protections/Constants/X86Resolver.cs | 68 +++++++---- 5 files changed, 183 insertions(+), 133 deletions(-) diff --git a/UnConfuserEx/Protections/AntiTamper/AntiTamperRemover.cs b/UnConfuserEx/Protections/AntiTamper/AntiTamperRemover.cs index 725291e..b9ed3e3 100644 --- a/UnConfuserEx/Protections/AntiTamper/AntiTamperRemover.cs +++ b/UnConfuserEx/Protections/AntiTamper/AntiTamperRemover.cs @@ -12,6 +12,7 @@ using UnConfuserEx.Protections; using System.IO; using UnConfuserEx.Protections.AntiTamper; using log4net; +using de4dot.blocks; namespace UnConfuserEx.Protections { @@ -198,17 +199,15 @@ namespace UnConfuserEx.Protections return (null, null); } - int derivationLength = (lastInstr - firstInstr); - List derivation = new(); - for (int i = 0; i <= derivationLength; i++) + for (int i = 0; i <= (lastInstr - firstInstr); i++) { derivation.Add(instrs[firstInstr + i]); } // Normal deriver is 16 iterations of 10 instructions const int normalDeriationLength = 16 * 10; - DeriverType type = (derivationLength == normalDeriationLength) ? DeriverType.Normal : DeriverType.Dynamic; + DeriverType type = (derivation.Count == normalDeriationLength) ? DeriverType.Normal : DeriverType.Dynamic; return (type, derivation); } @@ -216,18 +215,30 @@ namespace UnConfuserEx.Protections { var cctor = module.GlobalType.FindStaticConstructor(); - if (cctor == null || !(cctor.HasBody) || cctor.Body.Instructions[0].OpCode != OpCodes.Call) + if (cctor == null || !(cctor.HasBody)) return null; - var method = cctor.Body.Instructions[0].Operand as MethodDef; + IList instrs; - foreach (var instr in method!.Body.Instructions) + // Check the first call in the cctor first + if (cctor.Body.Instructions[0].OpCode == OpCodes.Call) { - if (instr.Operand != null && instr.Operand.ToString()!.Contains("Marshal::GetHINSTANCE")) + var method = cctor.Body.Instructions[0].Operand as MethodDef; + + instrs = method!.Body.Instructions; + for (int i = 0; i < instrs.Count - 2; i++) { - return method; + if (instrs[i].OpCode == OpCodes.Ldtoken && + instrs[i].Operand == module.GlobalType && + instrs[i + 1].OpCode == OpCodes.Call && + instrs[i + 1].Operand is MemberRef m && + m.Name == "GetTypeFromHandle") + { + return method; + } } } + return null; } diff --git a/UnConfuserEx/Protections/Constants/ConstantsRemover.cs b/UnConfuserEx/Protections/Constants/ConstantsRemover.cs index d3050d2..2ac09e0 100644 --- a/UnConfuserEx/Protections/Constants/ConstantsRemover.cs +++ b/UnConfuserEx/Protections/Constants/ConstantsRemover.cs @@ -143,13 +143,14 @@ namespace UnConfuserEx.Protections resolver.Resolve(getter, instances.ToList()); Logger.Debug($"Removed all instances of getter ${getter.FullName}"); - //module.GlobalType.Remove(getter); } module.GlobalType.Fields.Remove(dataField); + var cctor = module.GlobalType.FindStaticConstructor(); + // Delete the encrypted data and decryption instructions - if (initializeMethod == module.GlobalType.FindStaticConstructor()) + if (initializeMethod == cctor) { for (int i = 0; i < initializeMethod.Body.Instructions.Count; i++) { @@ -168,7 +169,9 @@ namespace UnConfuserEx.Protections } else { - throw new NotImplementedException("Constants decryption not done in the static constructor!"); + cctor.Body.Instructions.RemoveAt(0); + initializeMethod.Body.Instructions.Clear(); + initializeMethod.Body.Instructions.Add(new Instruction(OpCodes.Ret)); } return true; } diff --git a/UnConfuserEx/Protections/Constants/IResolver.cs b/UnConfuserEx/Protections/Constants/IResolver.cs index aabc037..1e7cab4 100644 --- a/UnConfuserEx/Protections/Constants/IResolver.cs +++ b/UnConfuserEx/Protections/Constants/IResolver.cs @@ -1,12 +1,120 @@ using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System; using System.Collections.Generic; +using System.Linq; +using static System.Runtime.InteropServices.JavaScript.JSType; +using System.Runtime.InteropServices; +using System.Text; namespace UnConfuserEx.Protections.Constants { - internal interface IResolver + internal abstract class IResolver { + protected byte[]? data; - public void Resolve(MethodDef method, IList instances); + public abstract void Resolve(MethodDef method, IList instances); + protected (int stringId, int numId, int objectId) GetIdsFromGetter(MethodDef getter) + { + var locals = getter.Body.Variables; + var keyLocal = getter.Body.Variables.Locals[0]; + + var indices = getter.Body.Instructions + .Where(i => i.IsLdloc() && i.GetLocal(locals) == keyLocal) + .Select(i => getter.Body.Instructions.IndexOf(i) + 1).ToList(); + + var stringId = getter.Body.Instructions[indices[0]].GetLdcI4Value(); + var numId = getter.Body.Instructions[indices[1]].GetLdcI4Value(); + var objectId = getter.Body.Instructions[indices[2]].GetLdcI4Value(); + + return (stringId, numId, objectId); + } + + protected int GetNextInstanceInMethod(MethodDef getter, MethodDef method, out TypeSig? genericType) + { + var instrs = method.Body.Instructions; + + for (int i = 0; i < instrs.Count; i++) + { + if (instrs[i].OpCode == OpCodes.Call && + instrs[i].Operand is MethodSpec ms && + ms.Method.ResolveMethodDef() is MethodDef md && + md.Equals(getter)) + { + genericType = ms.GenericInstMethodSig.GenericArguments[0]; + + if (instrs[i - 1].IsBr() && + instrs[i - 1].Operand is Instruction target && + target == instrs[i]) + { + method.Body.Instructions.RemoveAt(i - 1); + i--; + } + + return i - 1; + } + } + genericType = null; + return -1; + } + + protected void FixStringConstant(MethodDef method, int instrOffset, int id) + { + uint count = (uint)(data![id] | (data[id + 1] << 8) | (data[id + 2] << 16) | (data[id + 3] << 24)); + //count = (count << 4) | (count >> 0x1C); + string result = string.Intern(Encoding.UTF8.GetString(data, id + 4, (int)count)); + + method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldstr; + method.Body.Instructions[instrOffset].Operand = result; + method.Body.Instructions.RemoveAt(instrOffset + 1); + } + + protected void FixNumberConstant(MethodDef method, int instrOffset, int id, TypeSig type) + { + switch (type.ElementType) + { + case ElementType.I4: + FixNumberConstant(method, instrOffset, id); + method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldc_I4; + break; + case ElementType.R8: + FixNumberConstant(method, instrOffset, id); + method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldc_R8; + break; + case ElementType.R4: + FixNumberConstant(method, instrOffset, id); + method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldc_R4; + break; + + default: + throw new NotImplementedException($"Can't fix number constant. Type is {type.TypeName}"); + } + } + + protected void FixNumberConstant(MethodDef method, int instrOffset, int id) + { + T[] array = new T[1]; + Buffer.BlockCopy(data!, id, array, 0, Marshal.SizeOf(default(T))); + + method.Body.Instructions[instrOffset].Operand = array[0]; + method.Body.Instructions.RemoveAt(instrOffset + 1); + } + + protected void FixObjectConstant(MethodDef method, int instrOffset, int id, TypeSig type) + { + int num0 = data![id] | (data[id + 1] << 8) | (data[id + 2] << 16) | (data[id + 3] << 24); + uint num1 = (uint)(data![id + 4] | (data[id + 5] << 8) | (data[id + 6] << 16) | (data[id + 7] << 24)); + num1 = (num1 << 4) | (num1 >> 0x1C); + + throw new NotImplementedException("Object constant not handled"); + } + + protected void FixDefaultConstant(MethodDef method, int instrOffset, TypeSig type) + { + method.Body.Instructions[instrOffset].OpCode = OpCodes.Initobj; + method.Body.Instructions[instrOffset].Operand = type.ToTypeDefOrRef(); + method.Body.Instructions.RemoveAt(instrOffset + 1); + } } } diff --git a/UnConfuserEx/Protections/Constants/NormalResolver.cs b/UnConfuserEx/Protections/Constants/NormalResolver.cs index cc2a322..1aa79e7 100644 --- a/UnConfuserEx/Protections/Constants/NormalResolver.cs +++ b/UnConfuserEx/Protections/Constants/NormalResolver.cs @@ -13,14 +13,12 @@ namespace UnConfuserEx.Protections.Constants { private static ILog Logger = LogManager.GetLogger("Constants"); - private byte[] data; - public NormalResolver(byte[] data) { this.data = data; } - public void Resolve(MethodDef getter, IList instances) + public override void Resolve(MethodDef getter, IList instances) { var key1 = (int)getter.Body.Instructions[5].Operand; var key2 = (int)getter.Body.Instructions[7].Operand; @@ -78,99 +76,5 @@ namespace UnConfuserEx.Protections.Constants method.Body.UpdateInstructionOffsets(); } } - - private int GetNextInstanceInMethod(MethodDef getter, MethodDef method, out TypeSig? genericType) - { - var instrs = method.Body.Instructions; - - for (int i = 0; i < instrs.Count; i++) - { - if (instrs[i].OpCode == OpCodes.Call && - instrs[i].Operand is MethodSpec ms && - ms.Method.ResolveMethodDef() is MethodDef md && - md.Equals(getter) && - instrs[i - 1].IsLdcI4()) - { - genericType = ms.GenericInstMethodSig.GenericArguments[0]; - return i - 1; - } - } - genericType = null; - return -1; - } - - private void FixStringConstant(MethodDef method, int instrOffset, int id) - { - uint count = (uint)(data![id] | (data[id + 1] << 8) | (data[id + 2] << 16) | (data[id + 3] << 24)); - count = (count << 4) | (count >> 0x1C); - string result = string.Intern(Encoding.UTF8.GetString(data, id + 4, (int)count)); - - method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldstr; - method.Body.Instructions[instrOffset].Operand = result; - method.Body.Instructions.RemoveAt(instrOffset + 1); - } - - private void FixNumberConstant(MethodDef method, int instrOffset, int id, TypeSig type) - { - switch (type.ElementType) - { - case ElementType.I4: - FixNumberConstant(method, instrOffset, id); - method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldc_I4; - break; - case ElementType.R8: - FixNumberConstant(method, instrOffset, id); - method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldc_R8; - break; - case ElementType.R4: - FixNumberConstant(method, instrOffset, id); - method.Body.Instructions[instrOffset].OpCode = OpCodes.Ldc_R4; - break; - - default: - throw new NotImplementedException($"Can't fix number constant. Type is {type.TypeName}"); - } - } - - private void FixNumberConstant(MethodDef method, int instrOffset, int id) - { - T[] array = new T[1]; - Buffer.BlockCopy(data!, id, array, 0, Marshal.SizeOf(default(T))); - - method.Body.Instructions[instrOffset].Operand = array[0]; - method.Body.Instructions.RemoveAt(instrOffset + 1); - } - - private void FixObjectConstant(MethodDef method, int instrOffset, int id, TypeSig type) - { - int num0 = data![id] | (data[id + 1] << 8) | (data[id + 2] << 16) | (data[id + 3] << 24); - uint num1 = (uint)(data![id + 4] | (data[id + 5] << 8) | (data[id + 6] << 16) | (data[id + 7] << 24)); - num1 = (num1 << 4) | (num1 >> 0x1C); - - throw new NotImplementedException("Object constant not handled"); - } - - private void FixDefaultConstant(MethodDef method, int instrOffset, TypeSig type) - { - method.Body.Instructions[instrOffset].OpCode = OpCodes.Initobj; - method.Body.Instructions[instrOffset].Operand = type.ToTypeDefOrRef(); - method.Body.Instructions.RemoveAt(instrOffset + 1); - } - - private (int stringId, int numId, int objectId) GetIdsFromGetter(MethodDef getter) - { - var locals = getter.Body.Variables; - var keyLocal = getter.Body.Variables.Locals[0]; - - var indices = getter.Body.Instructions - .Where(i => i.IsLdloc() && i.GetLocal(locals) == keyLocal) - .Select(i => getter.Body.Instructions.IndexOf(i) + 1).ToList(); - - var stringId = getter.Body.Instructions[indices[0]].GetLdcI4Value(); - var numId = getter.Body.Instructions[indices[1]].GetLdcI4Value(); - var objectId = getter.Body.Instructions[indices[2]].GetLdcI4Value(); - - return (stringId, numId, objectId); - } } } diff --git a/UnConfuserEx/Protections/Constants/X86Resolver.cs b/UnConfuserEx/Protections/Constants/X86Resolver.cs index 56408dc..1318032 100644 --- a/UnConfuserEx/Protections/Constants/X86Resolver.cs +++ b/UnConfuserEx/Protections/Constants/X86Resolver.cs @@ -3,6 +3,7 @@ using dnlib.DotNet.Emit; using log4net; using System; using System.Collections.Generic; +using System.Windows.Forms; using X86Emulator; namespace UnConfuserEx.Protections.Constants @@ -12,46 +13,69 @@ namespace UnConfuserEx.Protections.Constants private static ILog Logger = LogManager.GetLogger("Constants"); private ModuleDefMD Module; - private byte[] Data; public X86Resolver(ModuleDefMD module, byte[] data) { Module = module; - Data = data; + this.data = data; } - public void Resolve(MethodDef getter, IList instances) + public override void Resolve(MethodDef getter, IList instances) { var x86MethodDef = (MethodDef)getter.Body.Instructions[5].Operand; - var x86Method = new X86Method(Module, x86MethodDef); + var (stringId, numId, objectId) = GetIdsFromGetter(getter); + + foreach (var method in instances) { - int id = -1; - TypeSig? genericType = null; - var instrs = method.Body.Instructions; - for (int i = 0; i < instrs.Count; i++) + if (ConstantsCFG.IsCFGPresent(method)) { - if (instrs[i].OpCode == OpCodes.Call - && instrs[i].Operand is MethodSpec m - && m.Method.ResolveMethodDef().Equals(getter)) - { - id = (int)instrs[i - 1].Operand; - genericType = m.GenericInstMethodSig.GenericArguments[0]; - break; - } + new ConstantsCFG(method).RemoveFromMethod(); } - if (id == -1) - throw new Exception("Failed to get ID for constant decryption"); + TypeSig? genericType; + int instanceOffset = GetNextInstanceInMethod(getter, method, out genericType); + + while (instanceOffset != -1) + { + var instrs = method.Body.Instructions; + + var id = instrs[instanceOffset].GetLdcI4Value(); + id = x86Method.Emulate(new int[] { id }); + int type = (int)((uint)id >> 0x1e); + id = (id & 0x3fffffff) << 2; + + try + { + if (type == stringId) + { + FixStringConstant(method, instanceOffset, id); + } + else if (type == numId) + { + FixNumberConstant(method, instanceOffset, id, genericType!); + } + else if (type == objectId) + { + FixObjectConstant(method, instanceOffset, id, genericType!); + } + else + { + FixDefaultConstant(method, instanceOffset, genericType!); + } + } + catch (Exception ex) + { + Logger.Error($"Failed to remove constants obfuscation from method ${method.FullName} ({ex.Message})"); + return; + } - id = x86Method.Emulate(new int[] { id }); - var type = (int)((uint)id >> 0x1E); - id = (id & 0x3FFFFFFF) << 2; + instanceOffset = GetNextInstanceInMethod(getter, method, out genericType); + } - Logger.Debug($"X86 Constant getter:\n\tid: {id}\n\ttype: {type}\n\tgenericType: {genericType}"); } }