diff --git a/Confuser.Core/Annotations.cs b/Confuser.Core/Annotations.cs index 866c932..793aff0 100644 --- a/Confuser.Core/Annotations.cs +++ b/Confuser.Core/Annotations.cs @@ -107,7 +107,12 @@ namespace Confuser.Core return defValue; if (!objAnno.Contains(key)) return defValue; - return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue)); + + var valueType = typeof(TValue); + if (valueType.IsValueType) + return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue)); + else + return (TValue)objAnno[key]; } /// @@ -133,7 +138,12 @@ namespace Confuser.Core return defValueFactory(key); if (!objAnno.Contains(key)) return defValueFactory(key); - return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue)); + + var valueType = typeof(TValue); + if (valueType.IsValueType) + return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue)); + else + return (TValue)objAnno[key]; } /// @@ -159,7 +169,13 @@ namespace Confuser.Core objAnno = annotations[new WeakReferenceKey(obj)] = new ListDictionary(); TValue ret; if (objAnno.Contains(key)) - ret = (TValue)Convert.ChangeType(objAnno[key], typeof(TValue)); + { + var valueType = typeof(TValue); + if (valueType.IsValueType) + return (TValue)Convert.ChangeType(objAnno[key], typeof(TValue)); + else + return (TValue)objAnno[key]; + } else objAnno[key] = ret = factory(key); return ret; diff --git a/Confuser.Protections/AntiTamper/AntiTamperProtection.cs b/Confuser.Protections/AntiTamper/AntiTamperProtection.cs new file mode 100644 index 0000000..f0a4d39 --- /dev/null +++ b/Confuser.Protections/AntiTamper/AntiTamperProtection.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Confuser.Core; +using Confuser.Protections.AntiTamper; +using dnlib.DotNet; + +namespace Confuser.Protections +{ + [BeforeProtection("Ki.ControlFlow"), AfterProtection("Ki.Constants")] + class AntiTamperProtection : Protection + { + public const string _Id = "anti tamper"; + public const string _FullId = "Ki.AntiTamper"; + public const string _ServiceId = "Ki.AntiTamper"; + + protected override void Initialize(ConfuserContext context) + { + // + } + + protected override void PopulatePipeline(ProtectionPipeline pipeline) + { + pipeline.InsertPreStage(PipelineStage.OptimizeMethods, new InjectPhase(this)); + pipeline.InsertPreStage(PipelineStage.EndModule, new MDPhase(this)); + } + + public override string Name + { + get { return "Anti Tamper Protection"; } + } + + public override string Description + { + get { return "This protection ensures the integrity of application."; } + } + + public override string Id + { + get { return _Id; } + } + + public override string FullId + { + get { return _FullId; } + } + + public override ProtectionPreset Preset + { + get { return ProtectionPreset.Maximum; } + } + + public void ExcludeMethod(ConfuserContext context, MethodDef method) + { + ProtectionParameters.GetParameters(context, method).Remove(this); + } + + static readonly object HandlerKey = new object(); + + enum Mode + { + Normal, + JIT + } + + class InjectPhase : ProtectionPhase + { + public InjectPhase(AntiTamperProtection parent) + : base(parent) + { + } + + public override ProtectionTargets Targets + { + get { return ProtectionTargets.Modules; } + } + + protected override void Execute(ConfuserContext context, ProtectionParameters parameters) + { + Mode mode = parameters.GetParameter(context, context.CurrentModule, "mode", Mode.Normal); + IModeHandler modeHandler; + switch (mode) + { + case Mode.Normal: + modeHandler = new NormalMode(); + break; + default: + throw new UnreachableException(); + } + modeHandler.HandleInject((AntiTamperProtection)Parent, context, parameters); + context.Annotations.Set(context.CurrentModule, HandlerKey, modeHandler); + } + } + + class MDPhase : ProtectionPhase + { + public MDPhase(AntiTamperProtection parent) + : base(parent) + { + } + + public override ProtectionTargets Targets + { + get { return ProtectionTargets.Methods; } + } + + protected override void Execute(ConfuserContext context, ProtectionParameters parameters) + { + var modeHandler = context.Annotations.Get(context.CurrentModule, HandlerKey); + modeHandler.HandleMD((AntiTamperProtection)Parent, context, parameters); + } + } + } +} diff --git a/Confuser.Protections/AntiTamper/DynamicDeriver.cs b/Confuser.Protections/AntiTamper/DynamicDeriver.cs new file mode 100644 index 0000000..1e1fafa --- /dev/null +++ b/Confuser.Protections/AntiTamper/DynamicDeriver.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using dnlib.DotNet.Emit; +using dnlib.DotNet; +using Confuser.Core.Helpers; +using Confuser.Core; +using Confuser.DynCipher; +using System.Diagnostics; +using Confuser.DynCipher.AST; +using Confuser.DynCipher.Generation; +using Confuser.Core.Services; + +namespace Confuser.Protections.AntiTamper +{ + class DynamicDeriver : IKeyDeriver + { + class CodeGen : CILCodeGen + { + Local block; + Local key; + public CodeGen(Local block, Local key, MethodDef method, IList instrs) + : base(method, instrs) + { + this.block = block; + this.key = key; + } + protected override Local Var(Variable var) + { + if (var.Name == "{BUFFER}") + return block; + else if (var.Name == "{KEY}") + return key; + else + return base.Var(var); + } + } + + StatementBlock derivation; + Action encryptFunc; + + public void Init(ConfuserContext ctx, RandomGenerator random) + { + StatementBlock dummy; + ctx.Registry.GetService().GenerateCipherPair(random, out derivation, out dummy); + + var dmCodeGen = new DMCodeGen(typeof(void), new[] { + Tuple.Create("{BUFFER}", typeof(uint[])), + Tuple.Create("{KEY}", typeof(uint[])) + }); + dmCodeGen.GenerateCIL(derivation); + encryptFunc = dmCodeGen.Compile>(); + } + + public uint[] DeriveKey(uint[] a, uint[] b) + { + uint[] ret = new uint[0x10]; + Buffer.BlockCopy(a, 0, ret, 0, a.Length * sizeof(uint)); + encryptFunc(ret, b); + return ret; + } + + public IEnumerable EmitDerivation(MethodDef method, ConfuserContext ctx, Local dst, Local src) + { + var ret = new List(); + var codeGen = new CodeGen(dst, src, method, ret); + codeGen.GenerateCIL(derivation); + codeGen.Commit(method.Body); + return ret; + } + } +} diff --git a/Confuser.Protections/AntiTamper/IKeyDeriver.cs b/Confuser.Protections/AntiTamper/IKeyDeriver.cs new file mode 100644 index 0000000..f896004 --- /dev/null +++ b/Confuser.Protections/AntiTamper/IKeyDeriver.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using dnlib.DotNet; +using Confuser.Core; +using dnlib.DotNet.Emit; +using Confuser.Core.Services; + +namespace Confuser.Protections.AntiTamper +{ + enum Mode + { + Normal, + Dynamic + } + interface IKeyDeriver + { + void Init(ConfuserContext ctx, RandomGenerator random); + uint[] DeriveKey(uint[] a, uint[] b); + IEnumerable EmitDerivation(MethodDef method, ConfuserContext ctx, Local dst, Local src); + } +} diff --git a/Confuser.Protections/AntiTamper/IModeHandler.cs b/Confuser.Protections/AntiTamper/IModeHandler.cs new file mode 100644 index 0000000..ac9eaca --- /dev/null +++ b/Confuser.Protections/AntiTamper/IModeHandler.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Confuser.Core; + +namespace Confuser.Protections.AntiTamper +{ + interface IModeHandler + { + void HandleInject(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters); + void HandleMD(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters); + } +} diff --git a/Confuser.Protections/AntiTamper/NormalDeriver.cs b/Confuser.Protections/AntiTamper/NormalDeriver.cs new file mode 100644 index 0000000..2127120 --- /dev/null +++ b/Confuser.Protections/AntiTamper/NormalDeriver.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using dnlib.DotNet; +using Confuser.Core; +using dnlib.DotNet.Emit; +using Confuser.Core.Services; + +namespace Confuser.Protections.AntiTamper +{ + class NormalDeriver : IKeyDeriver + { + public void Init(ConfuserContext ctx, RandomGenerator random) + { + // + } + + public uint[] DeriveKey(uint[] a, uint[] b) + { + uint[] ret = new uint[0x10]; + for (int i = 0; i < 0x10; i++) + { + switch (i % 3) + { + case 0: + ret[i] = a[i] ^ b[i]; + break; + case 1: + ret[i] = a[i] * b[i]; + break; + case 2: + ret[i] = a[i] + b[i]; + break; + } + } + return ret; + } + + public IEnumerable EmitDerivation(MethodDef method, ConfuserContext ctx, Local dst, Local src) + { + for (int i = 0; i < 0x10; i++) + { + yield return Instruction.Create(OpCodes.Ldloc, dst); + yield return Instruction.Create(OpCodes.Ldc_I4, i); + yield return Instruction.Create(OpCodes.Ldloc, dst); + yield return Instruction.Create(OpCodes.Ldc_I4, i); + yield return Instruction.Create(OpCodes.Ldelem_U4); + yield return Instruction.Create(OpCodes.Ldloc, src); + yield return Instruction.Create(OpCodes.Ldc_I4, i); + yield return Instruction.Create(OpCodes.Ldelem_U4); + switch (i % 3) + { + case 0: + yield return Instruction.Create(OpCodes.Xor); + break; + case 1: + yield return Instruction.Create(OpCodes.Mul); + break; + case 2: + yield return Instruction.Create(OpCodes.Add); + break; + } + yield return Instruction.Create(OpCodes.Stelem_I4); + } + } + } +} diff --git a/Confuser.Protections/AntiTamper/NormalMode.cs b/Confuser.Protections/AntiTamper/NormalMode.cs new file mode 100644 index 0000000..80a7e45 --- /dev/null +++ b/Confuser.Protections/AntiTamper/NormalMode.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Confuser.Core; +using Confuser.Core.Services; +using Confuser.Core.Helpers; +using dnlib.DotNet.Emit; +using Confuser.Renamer; +using dnlib.DotNet; +using System.Diagnostics; +using dnlib.DotNet.Writer; +using System.IO; + +namespace Confuser.Protections.AntiTamper +{ + class NormalMode : IModeHandler + { + RandomGenerator random; + uint z, x, c, v; + uint name1, name2; + IKeyDeriver deriver; + + List methods; + public void HandleInject(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters) + { + random = context.Registry.GetService().GetRandomGenerator(parent.FullId); + z = random.NextUInt32(); + x = random.NextUInt32(); + c = random.NextUInt32(); + v = random.NextUInt32(); + name1 = random.NextUInt32() & 0x7f7f7f7f; + name2 = random.NextUInt32() & 0x7f7f7f7f; + + switch (parameters.GetParameter(context, context.CurrentModule, "key", Mode.Normal)) + { + case Mode.Normal: + deriver = new NormalDeriver(); + break; + case Mode.Dynamic: + deriver = new DynamicDeriver(); + break; + default: + throw new UnreachableException(); + } + deriver.Init(context, random); + + var rt = context.Registry.GetService(); + var initMethod = rt.GetRuntimeType("Confuser.Runtime.AntiTamperNormal").FindMethod("Initialize"); + initMethod = InjectHelper.Inject(initMethod, context.CurrentModule); + context.CurrentModule.GlobalType.Methods.Add(initMethod); + + var instrs = initMethod.Body.Instructions.ToList(); + for (int i = 0; i < instrs.Count; i++) + { + var instr = instrs[i]; + if (instr.OpCode == OpCodes.Ldtoken) + { + instr.Operand = context.CurrentModule.GlobalType; + } + else if (instr.OpCode == OpCodes.Call) + { + IMethod method = (IMethod)instr.Operand; + if (method.DeclaringType.Name == "Mutation" && + method.Name == "Crypt") + { + Instruction ldDst = instrs[i - 2]; + Instruction ldSrc = instrs[i - 1]; + Debug.Assert(ldDst.OpCode == OpCodes.Ldloc && ldSrc.OpCode == OpCodes.Ldloc); + instrs.RemoveAt(i); + instrs.RemoveAt(i - 1); + instrs.RemoveAt(i - 2); + instrs.InsertRange(i - 2, deriver.EmitDerivation(initMethod, context, (Local)ldDst.Operand, (Local)ldSrc.Operand)); + } + } + } + initMethod.Body.Instructions.Clear(); + foreach (var instr in instrs) + initMethod.Body.Instructions.Add(instr); + + MutationHelper.InjectKeys(initMethod, + new int[] { 0, 1, 2, 3, 4 }, + new int[] { (int)(name1 * name2), (int)z, (int)x, (int)c, (int)v }); + + var name = context.Registry.GetService(); + var marker = context.Registry.GetService(); + name.MarkHelper(initMethod, marker); + + var cctor = context.CurrentModule.GlobalType.FindStaticConstructor(); + cctor.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, initMethod)); + + parent.ExcludeMethod(context, cctor); + parent.ExcludeMethod(context, initMethod); + } + + public void HandleMD(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters) + { + methods = parameters.Targets.OfType().ToList(); + context.CurrentModuleWriterListener.OnWriterEvent += OnWriterEvent; + } + + void OnWriterEvent(object sender, ModuleWriterListenerEventArgs e) + { + var writer = (ModuleWriter)sender; + if (e.WriterEvent == ModuleWriterEvent.MDEndCreateTables) + { + CreateSections(writer); + } + else if (e.WriterEvent == ModuleWriterEvent.BeginStrongNameSign) + { + EncryptSection(writer); + } + } + + void CreateSections(ModuleWriter writer) + { + byte[] nameBuffer = new byte[8]; + nameBuffer[0] = (byte)(name1 >> 0); + nameBuffer[1] = (byte)(name1 >> 8); + nameBuffer[2] = (byte)(name1 >> 16); + nameBuffer[3] = (byte)(name1 >> 24); + nameBuffer[4] = (byte)(name2 >> 0); + nameBuffer[5] = (byte)(name2 >> 8); + nameBuffer[6] = (byte)(name2 >> 16); + nameBuffer[7] = (byte)(name2 >> 24); + var newSection = new PESection(Encoding.ASCII.GetString(nameBuffer), 0xE0000040); + writer.Sections.Insert(0, newSection); // insert first to ensure proper RVA + + uint alignment; + + alignment = writer.TextSection.Remove(writer.MetaData).Value; + writer.TextSection.Add(writer.MetaData, alignment); + + alignment = writer.TextSection.Remove(writer.NetResources).Value; + writer.TextSection.Add(writer.NetResources, alignment); + + alignment = writer.TextSection.Remove(writer.Constants).Value; + newSection.Add(writer.Constants, alignment); + + // move some PE parts to separate section to prevent it from being hashed + var peSection = new PESection("", 0x60000020); + bool moved = false; + if (writer.StrongNameSignature != null) + { + alignment = writer.TextSection.Remove(writer.StrongNameSignature).Value; + peSection.Add(writer.StrongNameSignature, alignment); + moved = true; + } + if (writer.ImportAddressTable != null) + { + alignment = writer.TextSection.Remove(writer.ImportAddressTable).Value; + peSection.Add(writer.ImportAddressTable, alignment); + moved = true; + } + if (writer.StartupStub != null) + { + alignment = writer.TextSection.Remove(writer.StartupStub).Value; + peSection.Add(writer.StartupStub, alignment); + moved = true; + } + if (moved) + writer.Sections.Add(peSection); + + // move encrypted methods + var encryptedChunk = new MethodBodyChunks(writer.TheOptions.ShareMethodBodies); + newSection.Add(encryptedChunk, 4); + foreach (var method in methods) + { + if (!method.HasBody) + continue; + var body = writer.MetaData.GetMethodBody(method); + var ok = writer.MethodBodies.Remove(body); + encryptedChunk.Add(body); + } + + // padding to prevent bad size due to shift division + newSection.Add(new ByteArrayChunk(new byte[4]), 4); + } + + void EncryptSection(ModuleWriter writer) + { + var stream = writer.DestinationStream; + var reader = new BinaryReader(writer.DestinationStream); + stream.Position = 0x3C; + stream.Position = reader.ReadUInt32(); + + stream.Position += 6; + ushort sections = reader.ReadUInt16(); + stream.Position += 0xc; + ushort optSize = reader.ReadUInt16(); + stream.Position += 2 + optSize; + + uint encLoc = 0, encSize = 0; + for (int i = 0; i < sections; i++) + { + uint nameHash = reader.ReadUInt32() * reader.ReadUInt32(); + stream.Position += 8; + if (nameHash == name1 * name2) + { + encSize = reader.ReadUInt32(); + encLoc = reader.ReadUInt32(); + } + else if (nameHash != 0) + { + uint sectSize = reader.ReadUInt32(); + uint sectLoc = reader.ReadUInt32(); + Hash(stream, reader, sectLoc, sectSize); + } + stream.Position += 16; + } + + uint[] key = DeriveKey(); + encSize >>= 2; + stream.Position = encLoc; + uint[] result = new uint[encSize]; + for (uint i = 0; i < encSize; i++) + { + uint data = reader.ReadUInt32(); + result[i] = data ^ key[i & 0xf]; + key[i & 0xf] = (key[i & 0xf] ^ data) + 0x3dbb2819; + } + byte[] byteResult = new byte[encSize << 2]; + Buffer.BlockCopy(result, 0, byteResult, 0, byteResult.Length); + stream.Position = encLoc; + stream.Write(byteResult, 0, byteResult.Length); + } + + void Hash(Stream stream, BinaryReader reader, uint offset, uint size) + { + long original = stream.Position; + stream.Position = offset; + size >>= 2; + for (uint i = 0; i < size; i++) + { + uint data = reader.ReadUInt32(); + uint tmp = (z ^ data) + x + c * v; + z = x; + x = c; + x = v; + v = tmp; + } + stream.Position = original; + } + + uint[] DeriveKey() + { + uint[] dst = new uint[0x10], src = new uint[0x10]; + for (int i = 0; i < 0x10; i++) + { + dst[i] = v; + src[i] = x; + z = (x >> 5) | (x << 27); + x = (c >> 3) | (c << 29); + c = (v >> 7) | (v << 25); + v = (z >> 11) | (z << 21); + } + return deriver.DeriveKey(dst, src); + } + } +} diff --git a/Confuser.Protections/Confuser.Protections.csproj b/Confuser.Protections/Confuser.Protections.csproj index 6625d41..0452a3f 100644 --- a/Confuser.Protections/Confuser.Protections.csproj +++ b/Confuser.Protections/Confuser.Protections.csproj @@ -43,6 +43,12 @@ + + + + + + Code diff --git a/Confuser.Protections/InvalidMetadataProtection.cs b/Confuser.Protections/InvalidMetadataProtection.cs index 522c148..165abde 100644 --- a/Confuser.Protections/InvalidMetadataProtection.cs +++ b/Confuser.Protections/InvalidMetadataProtection.cs @@ -165,30 +165,6 @@ namespace Confuser.Protections 0x7fff7fff, (uint)ManifestResourceAttributes.Private, 0x7fff7fff, 2)); */ } - else if (e.WriterEvent == ModuleWriterEvent.BeginCalculateRvasAndFileOffsets) - { - foreach (var section in writer.Sections) - section.Name = ""; - } - else if (e.WriterEvent == ModuleWriterEvent.ChunksAddedToSections) - { - var newSection = new PESection("", 0xE0000040); - writer.Sections.Insert(0, newSection); - - uint alignment; - - alignment = writer.TextSection.Remove(writer.MetaData).Value; - writer.TextSection.Add(writer.MetaData, alignment); - - alignment = writer.TextSection.Remove(writer.NetResources).Value; - writer.TextSection.Add(writer.NetResources, alignment); - - alignment = writer.TextSection.Remove(writer.MethodBodies).Value; - newSection.Add(writer.MethodBodies, alignment); - - alignment = writer.TextSection.Remove(writer.Constants).Value; - newSection.Add(writer.Constants, alignment); - } } } } diff --git a/Confuser.Runtime/AntiTamper.Normal.cs b/Confuser.Runtime/AntiTamper.Normal.cs new file mode 100644 index 0000000..660c268 --- /dev/null +++ b/Confuser.Runtime/AntiTamper.Normal.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +namespace Confuser.Runtime +{ + class AntiTamperNormal + { + unsafe static void Initialize() + { + var m = typeof(AntiTamperNormal).Module; + string n = m.FullyQualifiedName; + bool f = n.Length > 0 && n[0] == '<'; + byte* b = (byte*)Marshal.GetHINSTANCE(m); + byte* p = b + *(uint*)(b + 0x3c); + ushort s = *(ushort*)(p + 0x6); + ushort o = *(ushort*)(p + 0x14); + + uint* e = null; + uint l = 0; + uint* r = (uint*)(p + 0x18 + o); + uint z = (uint)Mutation.KeyI1, x = (uint)Mutation.KeyI2, c = (uint)Mutation.KeyI3, v = (uint)Mutation.KeyI4; + for (int i = 0; i < s; i++) + { + uint g = (*r++) * (*r++); + if (g == (uint)Mutation.KeyI0) + { + e = (uint*)(b + (f ? *(r + 3) : *(r + 1))); + l = (f ? *(r + 2) : *(r + 0)) >> 2; + } + else if (g != 0) + { + uint* q = (uint*)(b + (f ? *(r + 3) : *(r + 1))); + uint j = *(r + 2) >> 2; + for (uint k = 0; k < j; k++) + { + uint t = (z ^ (*q++)) + x + c * v; + z = x; + x = c; + x = v; + v = t; + } + } + r += 8; + } + + uint[] y = new uint[0x10], d = new uint[0x10]; + for (int i = 0; i < 0x10; i++) + { + y[i] = v; + d[i] = x; + z = (x >> 5) | (x << 27); + x = (c >> 3) | (c << 29); + c = (v >> 7) | (v << 25); + v = (z >> 11) | (z << 21); + } + Mutation.Crypt(y, d); + + uint h = 0; + for (uint i = 0; i < l; i++) + { + *e ^= y[h & 0xf]; + y[h & 0xf] = (y[h & 0xf] ^ (*e++)) + 0x3dbb2819; + h++; + } + } + } +} diff --git a/Confuser.Runtime/Confuser.Runtime.csproj b/Confuser.Runtime/Confuser.Runtime.csproj index 77f874f..2e50514 100644 --- a/Confuser.Runtime/Confuser.Runtime.csproj +++ b/Confuser.Runtime/Confuser.Runtime.csproj @@ -48,6 +48,7 @@ + diff --git a/dnlib b/dnlib index c0bdc93..9d2bb28 160000 --- a/dnlib +++ b/dnlib @@ -1 +1 @@ -Subproject commit c0bdc93a2763f2fbc3dc46a68c40ca1c09ab5e2d +Subproject commit 9d2bb28baffc35278696a9319cdf2ec0afc11111