diff --git a/Confuser.Protections/AntiTamper/AntiTamperProtection.cs b/Confuser.Protections/AntiTamper/AntiTamperProtection.cs index a528f845c..2f316cb39 100644 --- a/Confuser.Protections/AntiTamper/AntiTamperProtection.cs +++ b/Confuser.Protections/AntiTamper/AntiTamperProtection.cs @@ -95,9 +95,6 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa case Mode.Anti: modeHandler = new AntiMode(); break; - case Mode.JIT: - modeHandler = new JITMode(); - break; default: throw new UnreachableException(); } @@ -129,8 +126,7 @@ protected override void Execute(ConfuserContext context, ProtectionParameters pa enum Mode { Normal, - Anti, - JIT + Anti } } } diff --git a/Confuser.Protections/AntiTamper/JITBody.cs b/Confuser.Protections/AntiTamper/JITBody.cs deleted file mode 100644 index 7149899f8..000000000 --- a/Confuser.Protections/AntiTamper/JITBody.cs +++ /dev/null @@ -1,267 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using dnlib.DotNet; -using dnlib.DotNet.Emit; -using dnlib.DotNet.Writer; -using dnlib.IO; -using dnlib.PE; - -namespace Confuser.Protections.AntiTamper { - internal struct JITEHClause { - public uint ClassTokenOrFilterOffset; - public uint Flags; - public uint HandlerLength; - public uint HandlerOffset; - public uint TryLength; - public uint TryOffset; - } - - internal class JITMethodBody : IChunk { - public byte[] Body; - public JITEHClause[] EHs; - public byte[] ILCode; - public byte[] LocalVars; - public uint MaxStack; - public uint MulSeed; - - public uint Offset; - public uint Options; - - public FileOffset FileOffset { get; set; } - - public RVA RVA { get; set; } - - public void SetOffset(FileOffset offset, RVA rva) { - this.FileOffset = offset; - this.RVA = rva; - } - - public uint GetFileLength() { - return (uint)Body.Length + 4; - } - - public uint GetVirtualSize() { - return GetFileLength(); - } - - // dnlib 4.x added IChunk.CalculateAlignment. Return 0 for default/no alignment, - // matching the implicit behaviour before the method existed in 3.x. - public uint CalculateAlignment() { - return 0; - } - - public void WriteTo(DataWriter writer) { - writer.WriteUInt32((uint)(Body.Length >> 2)); - writer.WriteBytes(Body); - } - - public void Serialize(uint token, uint key, byte[] fieldLayout) { - using (var ms = new MemoryStream()) { - var writer = new DataWriter(ms); - foreach (byte i in fieldLayout) - switch (i) { - case 0: - writer.WriteUInt32((uint)ILCode.Length); - break; - case 1: - writer.WriteUInt32(MaxStack); - break; - case 2: - writer.WriteUInt32((uint)EHs.Length); - break; - case 3: - writer.WriteUInt32((uint)LocalVars.Length); - break; - case 4: - writer.WriteUInt32(Options); - break; - case 5: - writer.WriteUInt32(MulSeed); - break; - } - - writer.WriteBytes(ILCode); - writer.WriteBytes(LocalVars); - foreach (JITEHClause clause in EHs) { - writer.WriteUInt32(clause.Flags); - writer.WriteUInt32(clause.TryOffset); - writer.WriteUInt32(clause.TryLength); - writer.WriteUInt32(clause.HandlerOffset); - writer.WriteUInt32(clause.HandlerLength); - writer.WriteUInt32(clause.ClassTokenOrFilterOffset); - } - writer.WriteZeroes(4 - ((int)ms.Length & 3)); // pad to 4 bytes - Body = ms.ToArray(); - } - Debug.Assert(Body.Length % 4 == 0); - // encrypt body - uint state = token * key; - uint counter = state; - for (uint i = 0; i < Body.Length; i += 4) { - uint data = Body[i] | (uint)(Body[i + 1] << 8) | (uint)(Body[i + 2] << 16) | (uint)(Body[i + 3] << 24); - Body[i + 0] ^= (byte)(state >> 0); - Body[i + 1] ^= (byte)(state >> 8); - Body[i + 2] ^= (byte)(state >> 16); - Body[i + 3] ^= (byte)(state >> 24); - state += data ^ counter; - counter ^= (state >> 5) | (state << 27); - } - } - } - - internal class JITMethodBodyWriter : MethodBodyWriterBase { - readonly CilBody body; - readonly JITMethodBody jitBody; - readonly bool keepMaxStack; - readonly Metadata metadata; - - public JITMethodBodyWriter(Metadata md, CilBody body, JITMethodBody jitBody, uint mulSeed, bool keepMaxStack) : - base(body.Instructions, body.ExceptionHandlers) { - metadata = md; - this.body = body; - this.jitBody = jitBody; - this.keepMaxStack = keepMaxStack; - this.jitBody.MulSeed = mulSeed; - } - - public void Write() { - uint codeSize = InitializeInstructionOffsets(); - jitBody.MaxStack = keepMaxStack ? body.MaxStack : GetMaxStack(); - - jitBody.Options = 0; - if (body.InitLocals) - jitBody.Options |= 0x10; - - if (body.Variables.Count > 0) { - var local = new LocalSig(body.Variables.Select(var => var.Type).ToList()); - jitBody.LocalVars = SignatureWriter.Write(metadata, local); - } - else - jitBody.LocalVars = Array.Empty(); - - { - var newCode = new byte[codeSize]; - var writer = new ArrayWriter(newCode); - uint _codeSize = WriteInstructions(ref writer); - Debug.Assert(codeSize == _codeSize); - jitBody.ILCode = newCode; - } - - jitBody.EHs = new JITEHClause[exceptionHandlers.Count]; - if (exceptionHandlers.Count > 0) { - jitBody.Options |= 8; - for (int i = 0; i < exceptionHandlers.Count; i++) { - ExceptionHandler eh = exceptionHandlers[i]; - jitBody.EHs[i].Flags = (uint)eh.HandlerType; - - uint tryStart = GetOffset(eh.TryStart); - uint tryEnd = GetOffset(eh.TryEnd); - jitBody.EHs[i].TryOffset = tryStart; - jitBody.EHs[i].TryLength = tryEnd - tryStart; - - uint handlerStart = GetOffset(eh.HandlerStart); - uint handlerEnd = GetOffset(eh.HandlerEnd); - jitBody.EHs[i].HandlerOffset = handlerStart; - jitBody.EHs[i].HandlerLength = handlerEnd - handlerStart; - - if (eh.HandlerType == ExceptionHandlerType.Catch) { - uint token = metadata.GetToken(eh.CatchType).Raw; - if ((token & 0xff000000) == 0x1b000000) - jitBody.Options |= 0x80; - - jitBody.EHs[i].ClassTokenOrFilterOffset = token; - } - else if (eh.HandlerType == ExceptionHandlerType.Filter) { - jitBody.EHs[i].ClassTokenOrFilterOffset = GetOffset(eh.FilterStart); - } - } - } - } - - protected override void WriteInlineField(ref ArrayWriter writer, Instruction instr) { - writer.WriteUInt32(metadata.GetToken(instr.Operand).Raw); - } - - protected override void WriteInlineMethod(ref ArrayWriter writer, Instruction instr) { - writer.WriteUInt32(metadata.GetToken(instr.Operand).Raw); - } - - protected override void WriteInlineSig(ref ArrayWriter writer, Instruction instr) { - writer.WriteUInt32(metadata.GetToken(instr.Operand).Raw); - } - - protected override void WriteInlineString(ref ArrayWriter writer, Instruction instr) { - writer.WriteUInt32(metadata.GetToken(instr.Operand).Raw); - } - - protected override void WriteInlineTok(ref ArrayWriter writer, Instruction instr) { - writer.WriteUInt32(metadata.GetToken(instr.Operand).Raw); - } - - protected override void WriteInlineType(ref ArrayWriter writer, Instruction instr) { - writer.WriteUInt32(metadata.GetToken(instr.Operand).Raw); - } - } - - internal class JITBodyIndex : IChunk { - readonly Dictionary bodies; - - public JITBodyIndex(IEnumerable tokens) { - bodies = tokens.ToDictionary(token => token, token => (JITMethodBody)null); - } - - public FileOffset FileOffset { get; set; } - - public RVA RVA { get; set; } - - public void SetOffset(FileOffset offset, RVA rva) { - this.FileOffset = offset; - this.RVA = rva; - } - - public uint GetFileLength() { - return (uint)bodies.Count * 8 + 4; - } - - public uint GetVirtualSize() { - return GetFileLength(); - } - - // dnlib 4.x added IChunk.CalculateAlignment. Return 0 for default/no alignment, - // matching the implicit behaviour before the method existed in 3.x. - public uint CalculateAlignment() { - return 0; - } - - public void WriteTo(DataWriter writer) { - uint length = GetFileLength() - 4; // minus length field - writer.WriteUInt32((uint)bodies.Count); - foreach (var entry in bodies.OrderBy(entry => entry.Key)) { - writer.WriteUInt32(entry.Key); - Debug.Assert(entry.Value != null); - Debug.Assert((length + entry.Value.Offset) % 4 == 0); - writer.WriteUInt32((length + entry.Value.Offset) >> 2); - } - } - - public void Add(uint token, JITMethodBody body) { - Debug.Assert(bodies.ContainsKey(token)); - bodies[token] = body; - } - - public void PopulateSection(PESection section) { - uint offset = 0; - foreach (var entry in bodies.OrderBy(entry => entry.Key)) { - Debug.Assert(entry.Value != null); - section.Add(entry.Value, 4); - entry.Value.Offset = offset; - - Debug.Assert(entry.Value.GetFileLength() % 4 == 0); - offset += entry.Value.GetFileLength(); - } - } - } -} diff --git a/Confuser.Protections/AntiTamper/JITMode.cs b/Confuser.Protections/AntiTamper/JITMode.cs deleted file mode 100644 index 4ace926fa..000000000 --- a/Confuser.Protections/AntiTamper/JITMode.cs +++ /dev/null @@ -1,334 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using Confuser.Core; -using Confuser.Core.Helpers; -using Confuser.Core.Services; -using Confuser.Renamer; -using dnlib.DotNet; -using dnlib.DotNet.Emit; -using dnlib.DotNet.MD; -using dnlib.DotNet.Writer; -using Microsoft.Extensions.Logging; - -namespace Confuser.Protections.AntiTamper { - internal class JITMode : IModeHandler { - static readonly CilBody NopBody = new CilBody { - Instructions = { - Instruction.Create(OpCodes.Ldnull), - Instruction.Create(OpCodes.Throw) - } - }; - - uint c; - MethodDef cctor; - MethodDef cctorRepl; - ConfuserContext context; - IKeyDeriver deriver; - byte[] fieldLayout; - - MethodDef initMethod; - uint key; - List methods; - uint name1, name2; - RandomGenerator random; - int[] rotShifts; - uint v; - uint x; - uint z; - - public void HandleInject(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters) { - this.context = context; - 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; - key = random.NextUInt32(); - // Randomize the key-derivation rotation amounts (were the fixed 5/3/7/11); the same - // amounts are injected into the runtime (Mutation.KeyI6..KeyI9) below. - rotShifts = RotationKey.PickShifts(random); - - fieldLayout = new byte[6]; - for (int i = 0; i < 6; i++) { - int index = random.NextInt32(0, 6); - while (fieldLayout[index] != 0) - index = random.NextInt32(0, 6); - fieldLayout[index] = (byte)i; - } - - 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(); - TypeDef initType = rt.GetRuntimeType("Confuser.Runtime.AntiTamperJIT"); - IEnumerable defs = InjectHelper.Inject(initType, context.CurrentModule.GlobalType, context.CurrentModule); - initMethod = defs.OfType().Single(method => method.Name == "Initialize"); - - initMethod.Body.SimplifyMacros(initMethod.Parameters); - List instrs = initMethod.Body.Instructions.ToList(); - for (int i = 0; i < instrs.Count; i++) { - Instruction instr = instrs[i]; - if (instr.OpCode == OpCodes.Ldtoken) { - instr.Operand = context.CurrentModule.GlobalType; - } - else if (instr.OpCode == OpCodes.Call) { - var 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 (Instruction instr in instrs) - initMethod.Body.Instructions.Add(instr); - - MutationHelper.InjectKeys(initMethod, - new[] { 0, 1, 2, 3, 4, 6, 7, 8, 9 }, - new[] { - (int)(name1 * name2), (int)z, (int)x, (int)c, (int)v, - rotShifts[0], rotShifts[1], rotShifts[2], rotShifts[3] - }); - - var name = context.Registry.GetService(); - var marker = context.Registry.GetService(); - - cctor = context.CurrentModule.GlobalType.FindStaticConstructor(); - - cctorRepl = new MethodDefUser(name.RandomName(), MethodSig.CreateStatic(context.CurrentModule.CorLibTypes.Void)); - cctorRepl.IsStatic = true; - cctorRepl.Access = MethodAttributes.CompilerControlled; - cctorRepl.Body = new CilBody(); - cctorRepl.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); - context.CurrentModule.GlobalType.Methods.Add(cctorRepl); - name.MarkHelper(cctorRepl, marker, parent); - - MutationHelper.InjectKeys(defs.OfType().Single(method => method.Name == "HookHandler"), - new[] { 0 }, new[] { (int)key }); - foreach (IDnlibDef def in defs) { - if (def.Name == "MethodData") { - var dataType = (TypeDef)def; - FieldDef[] fields = dataType.Fields.ToArray(); - var layout = fieldLayout.Clone() as byte[]; - Array.Sort(layout, fields); - for (byte j = 0; j < 6; j++) - layout[j] = j; - Array.Sort(fieldLayout, layout); - fieldLayout = layout; - dataType.Fields.Clear(); - foreach (FieldDef f in fields) - dataType.Fields.Add(f); - } - name.MarkHelper(def, marker, parent); - if (def is MethodDef) - parent.ExcludeMethod(context, (MethodDef)def); - } - parent.ExcludeMethod(context, cctor); - } - - public void HandleMD(AntiTamperProtection parent, ConfuserContext context, ProtectionParameters parameters) { - // move initialization away from module initializer - cctorRepl.Body = cctor.Body; - cctor.Body = new CilBody(); - cctor.Body.Instructions.Add(Instruction.Create(OpCodes.Call, initMethod)); - cctor.Body.Instructions.Add(Instruction.Create(OpCodes.Call, cctorRepl)); - cctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); - - methods = parameters.Targets.OfType().Where(method => method.HasBody).ToList(); - context.CurrentModuleWriterOptions.WriterEvent += OnWriterEvent; - } - - void OnWriterEvent(object sender, ModuleWriterEventArgs e) { - var writer = (ModuleWriterBase)sender; - if (e.Event == ModuleWriterEvent.MDBeginWriteMethodBodies) { - context.Logger.LogDebug("Extracting method bodies..."); - CreateSection(writer); - } - else if (e.Event == ModuleWriterEvent.BeginStrongNameSign) { - context.Logger.LogDebug("Encrypting method section..."); - EncryptSection(writer); - } - } - - void CreateSection(ModuleWriterBase writer) { - // move some PE parts to separate section to prevent it from being hashed - var peSection = new PESection("", 0x60000020); - bool moved = false; - uint alignment; - if (writer.StrongNameSignature != null) { - alignment = writer.TextSection.Remove(writer.StrongNameSignature).Value; - peSection.Add(writer.StrongNameSignature, alignment); - moved = true; - } - var managedWriter = writer as ModuleWriter; - if (managedWriter != null) { - if (managedWriter.ImportAddressTable != null) { - alignment = writer.TextSection.Remove(managedWriter.ImportAddressTable).Value; - peSection.Add(managedWriter.ImportAddressTable, alignment); - moved = true; - } - if (managedWriter.StartupStub != null) { - alignment = writer.TextSection.Remove(managedWriter.StartupStub).Value; - peSection.Add(managedWriter.StartupStub, alignment); - moved = true; - } - } - if (moved) - writer.Sections.AddBeforeReloc(peSection); - - // create section - var 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.InsertBeforeReloc(random.NextInt32(writer.Sections.Count), newSection); - - // random padding at beginning to prevent revealing hash key - newSection.Add(new ByteArrayChunk(random.NextBytes(0x10)), 0x10); - - // create index - var bodyIndex = new JITBodyIndex(methods.Select(method => writer.Metadata.GetToken(method).Raw)); - newSection.Add(bodyIndex, 0x10); - - // save methods - foreach (MethodDef method in methods.WithProgress(context.ProgressReporter)) { - if (!method.HasBody) - continue; - - MDToken token = writer.Metadata.GetToken(method); - - var jitBody = new JITMethodBody(); - var bodyWriter = new JITMethodBodyWriter(writer.Metadata, method.Body, jitBody, random.NextUInt32(), writer.Metadata.KeepOldMaxStack || method.Body.KeepOldMaxStack); - bodyWriter.Write(); - jitBody.Serialize(token.Raw, key, fieldLayout); - bodyIndex.Add(token.Raw, jitBody); - - method.Body = NopBody; - RawMethodRow methodRow = writer.Metadata.TablesHeap.MethodTable[token.Rid]; - writer.Metadata.TablesHeap.MethodTable[token.Rid] = new RawMethodRow( - methodRow.RVA, - (ushort)(methodRow.ImplFlags | (ushort)MethodImplAttributes.NoInlining), - methodRow.Flags, - methodRow.Name, - methodRow.Signature, - methodRow.ParamList); - - context.CheckCancellation(); - } - bodyIndex.PopulateSection(newSection); - - // padding to prevent bad size due to shift division - newSection.Add(new ByteArrayChunk(new byte[4]), 4); - } - - void EncryptSection(ModuleWriterBase writer) { - Stream 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; - int origSects = -1; - if (writer is NativeModuleWriter && writer.Module is ModuleDefMD) - origSects = ((ModuleDefMD)writer.Module).Metadata.PEImage.ImageSectionHeaders.Count; - for (int i = 0; i < sections; i++) { - uint nameHash; - if (origSects > 0) { - origSects--; - stream.Write(new byte[8], 0, 8); - nameHash = 0; - } - else - 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); - } - else - stream.Position += 8; - stream.Position += 16; - } - - uint[] key = DeriveKey(); - encSize >>= 2; - stream.Position = encLoc; - var 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; - } - var 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 >> rotShifts[0]) | (x << (32 - rotShifts[0])); - x = (c >> rotShifts[1]) | (c << (32 - rotShifts[1])); - c = (v >> rotShifts[2]) | (v << (32 - rotShifts[2])); - v = (z >> rotShifts[3]) | (z << (32 - rotShifts[3])); - } - return deriver.DeriveKey(dst, src); - } - } -} diff --git a/Confuser.Runtime/AntiTamper.JIT.cs b/Confuser.Runtime/AntiTamper.JIT.cs deleted file mode 100644 index 01f066dd6..000000000 --- a/Confuser.Runtime/AntiTamper.JIT.cs +++ /dev/null @@ -1,541 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Confuser.Runtime { - internal static unsafe class AntiTamperJIT { - static uint* ptr; - static uint len; - static IntPtr moduleHnd; - static compileMethod originalDelegate; - - static bool ver4; - static bool ver5; - - static compileMethod handler; - - public static void Initialize() { - Module m = typeof(AntiTamperNormal).Module; - string n = m.FullyQualifiedName; - bool f = n.Length > 0 && n[0] == '<'; - var 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; - var 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) { - var 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 >> Mutation.KeyI6) | (x << (32 - Mutation.KeyI6)); - x = (c >> Mutation.KeyI7) | (c << (32 - Mutation.KeyI7)); - c = (v >> Mutation.KeyI8) | (v << (32 - Mutation.KeyI8)); - v = (z >> Mutation.KeyI9) | (z << (32 - Mutation.KeyI9)); - } - Mutation.Crypt(y, d); - - uint h = 0; - uint* u = e; - VirtualProtect((IntPtr)e, l << 2, 0x40, out z); - for (uint i = 0; i < l; i++) { - *e ^= y[h & 0xf]; - y[h & 0xf] = (y[h & 0xf] ^ (*e++)) + 0x3dbb2819; - h++; - } - - ptr = u + 4; - len = *ptr++; - - ver4 = Environment.Version.Major == 4; - ver5 = ver4 && Environment.Version.Revision > 17020; - ModuleHandle hnd = m.ModuleHandle; - var obj = GetFieldValue(hnd, "m_ptr"); - if (obj is IntPtr) { - moduleHnd = (IntPtr)obj; - } - else if (obj.GetType().ToString() == "System.Reflection.RuntimeModule") { - moduleHnd = (IntPtr)GetFieldValue(obj, "m_pData"); - } - else { - throw new ApplicationException($"Failed to get pointer for module handle: {hnd.ToString()}"); - } - - Hook(); - } - - static object GetFieldValue(object obj, string fieldName) { - var field = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - if (field == null) - throw new ApplicationException($"Could not get field {obj.GetType()}::{fieldName}"); - return field.GetValue(obj); - } - - [DllImport("kernel32.dll")] - static extern IntPtr LoadLibrary(string lib); - - [DllImport("kernel32.dll")] - static extern IntPtr GetProcAddress(IntPtr lib, string proc); - - [DllImport("kernel32.dll")] - static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); - - static void Hook() { - ulong* ptr = stackalloc ulong[2]; - if (ver4) { - ptr[0] = 0x642e74696a726c63; //clrjit.d - ptr[1] = 0x0000000000006c6c; //ll...... - } - else { - ptr[0] = 0x74696a726f63736d; //mscorjit - ptr[1] = 0x000000006c6c642e; //.dll.... - } - IntPtr jit = LoadLibrary(new string((sbyte*)ptr)); - ptr[0] = 0x000074694a746567; //getJit - var get = (getJit)Marshal.GetDelegateForFunctionPointer(GetProcAddress(jit, new string((sbyte*)ptr)), typeof(getJit)); - IntPtr hookPosition = *get(); - IntPtr original = *(IntPtr*)hookPosition; - - IntPtr trampoline; - uint oldPl; - if (IntPtr.Size == 8) { - trampoline = Marshal.AllocHGlobal(16); - var tptr = (ulong*)trampoline; - tptr[0] = 0xffffffffffffb848; - tptr[1] = 0x90909090e0ffffff; - - VirtualProtect(trampoline, 12, 0x40, out oldPl); - Marshal.WriteIntPtr(trampoline, 2, original); - } - else { - trampoline = Marshal.AllocHGlobal(8); - var tptr = (ulong*)trampoline; - tptr[0] = 0x90e0ffffffffffb8; - - VirtualProtect(trampoline, 7, 0x40, out oldPl); - Marshal.WriteIntPtr(trampoline, 1, original); - } - - originalDelegate = (compileMethod)Marshal.GetDelegateForFunctionPointer(trampoline, typeof(compileMethod)); - handler = HookHandler; - - RuntimeHelpers.PrepareDelegate(originalDelegate); - RuntimeHelpers.PrepareDelegate(handler); - - VirtualProtect(hookPosition, (uint)IntPtr.Size, 0x40, out oldPl); - Marshal.WriteIntPtr(hookPosition, Marshal.GetFunctionPointerForDelegate(handler)); - VirtualProtect(hookPosition, (uint)IntPtr.Size, oldPl, out oldPl); - } - - static void ExtractLocalVars(CORINFO_METHOD_INFO* info, uint len, byte* localVar) { - void* sigInfo; - if (ver4) { - if (IntPtr.Size == 8) - sigInfo = (CORINFO_SIG_INFO_x64*)((uint*)(info + 1) + (ver5 ? 7 : 5)) + 1; - else - sigInfo = (CORINFO_SIG_INFO_x86*)((uint*)(info + 1) + (ver5 ? 5 : 4)) + 1; - } - else { - if (IntPtr.Size == 8) - sigInfo = (CORINFO_SIG_INFO_x64*)((uint*)(info + 1) + 3) + 1; - else - sigInfo = (CORINFO_SIG_INFO_x86*)((uint*)(info + 1) + 3) + 1; - } - - if (IntPtr.Size == 8) - ((CORINFO_SIG_INFO_x64*)sigInfo)->sig = (IntPtr)localVar; - else - ((CORINFO_SIG_INFO_x86*)sigInfo)->sig = (IntPtr)localVar; - localVar++; - byte b = *localVar; - ushort numArgs; - IntPtr args; - if ((b & 0x80) == 0) { - numArgs = b; - args = (IntPtr)(localVar + 1); - } - else { - numArgs = (ushort)(((uint)(b & ~0x80) << 8) | *(localVar + 1)); - args = (IntPtr)(localVar + 2); - } - - if (IntPtr.Size == 8) { - var sigInfox64 = (CORINFO_SIG_INFO_x64*)sigInfo; - sigInfox64->callConv = 0; - sigInfox64->retType = 1; - sigInfox64->flags = 1; - sigInfox64->numArgs = numArgs; - sigInfox64->args = args; - } - else { - var sigInfox86 = (CORINFO_SIG_INFO_x86*)sigInfo; - sigInfox86->callConv = 0; - sigInfox86->retType = 1; - sigInfox86->flags = 1; - sigInfox86->numArgs = numArgs; - sigInfox86->args = args; - } - } - - static uint HookHandler(IntPtr self, ICorJitInfo* comp, CORINFO_METHOD_INFO* info, uint flags, byte** nativeEntry, uint* nativeSizeOfCode) { - if (info != null && info->scope == moduleHnd && info->ILCode[0] == 0x14) { - uint token; - if (ver5) { - var getMethodDef = (getMethodDefFromMethod)Marshal.GetDelegateForFunctionPointer(comp->vfptr[0x64], typeof(getMethodDefFromMethod)); - token = getMethodDef((IntPtr)comp, info->ftn); - } - else { - ICorClassInfo* clsInfo = ICorStaticInfo.ICorClassInfo(ICorDynamicInfo.ICorStaticInfo(ICorJitInfo.ICorDynamicInfo(comp))); - int gmdSlot = 12 + (ver4 ? 2 : 1); - var getMethodDef = (getMethodDefFromMethod)Marshal.GetDelegateForFunctionPointer(clsInfo->vfptr[gmdSlot], typeof(getMethodDefFromMethod)); - token = getMethodDef((IntPtr)clsInfo, info->ftn); - } - - uint lo = 0, hi = len; - uint? offset = null; - while (hi >= lo) { - uint mid = lo + ((hi - lo) >> 1); - uint midTok = *(ptr + (mid << 1)); - if (midTok == token) { - offset = *(ptr + (mid << 1) + 1); - break; - } - if (midTok < token) - lo = mid + 1; - else - hi = mid - 1; - } - if (offset == null) - return originalDelegate(self, comp, info, flags, nativeEntry, nativeSizeOfCode); - - uint* dataPtr = ptr + (uint)offset; - uint dataLen = *dataPtr++; - var newPtr = (uint*)Marshal.AllocHGlobal((int)dataLen << 2); - try { - var data = (MethodData*)newPtr; - uint* copyData = newPtr; - - uint state = token * (uint)Mutation.KeyI0; - uint counter = state; - for (uint i = 0; i < dataLen; i++) { - *copyData = *dataPtr++ ^ state; - state += (*copyData++) ^ counter; - counter ^= (state >> 5) | (state << 27); - } - - info->ILCodeSize = data->ILCodeSize; - if (ver4) { - *((uint*)(info + 1) + 0) = data->MaxStack; - *((uint*)(info + 1) + 1) = data->EHCount; - *((uint*)(info + 1) + 2) = data->Options; - } - else { - *((ushort*)(info + 1) + 0) = (ushort)data->MaxStack; - *((ushort*)(info + 1) + 1) = (ushort)data->EHCount; - *((uint*)(info + 1) + 1) = data->Options; - } - - var body = (byte*)(data + 1); - - info->ILCode = body; - body += info->ILCodeSize; - - if (data->LocalVars != 0) { - ExtractLocalVars(info, data->LocalVars, body); - body += data->LocalVars; - } - - var ehPtr = (CORINFO_EH_CLAUSE*)body; - - uint ret; - if (ver5) { - CorJitInfoHook hook = CorJitInfoHook.Hook(comp, info->ftn, ehPtr); - ret = originalDelegate(self, comp, info, flags, nativeEntry, nativeSizeOfCode); - hook.Dispose(); - } - else { - CorMethodInfoHook hook = CorMethodInfoHook.Hook(comp, info->ftn, ehPtr); - ret = originalDelegate(self, comp, info, flags, nativeEntry, nativeSizeOfCode); - hook.Dispose(); - } - - return ret; - } - finally { - Marshal.FreeHGlobal((IntPtr)newPtr); - } - } - return originalDelegate(self, comp, info, flags, nativeEntry, nativeSizeOfCode); - } - - #region JIT internal - - static bool hasLinkInfo; - - [StructLayout(LayoutKind.Sequential, Size = 0x18)] - struct CORINFO_EH_CLAUSE { } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct CORINFO_METHOD_INFO { - public IntPtr ftn; - public IntPtr scope; - public byte* ILCode; - public uint ILCodeSize; - } - - [StructLayout(LayoutKind.Sequential)] - struct CORINFO_SIG_INFO_x64 { - public uint callConv; - uint pad1; - public IntPtr retTypeClass; - public IntPtr retTypeSigClass; - public byte retType; - public byte flags; - public ushort numArgs; - uint pad2; - public CORINFO_SIG_INST_x64 sigInst; - public IntPtr args; - public IntPtr sig; - public IntPtr scope; - public uint token; - uint pad3; - } - - [StructLayout(LayoutKind.Sequential)] - struct CORINFO_SIG_INFO_x86 { - public uint callConv; - public IntPtr retTypeClass; - public IntPtr retTypeSigClass; - public byte retType; - public byte flags; - public ushort numArgs; - public CORINFO_SIG_INST_x86 sigInst; - public IntPtr args; - public IntPtr sig; - public IntPtr scope; - public uint token; - } - - [StructLayout(LayoutKind.Sequential, Size = 32)] - struct CORINFO_SIG_INST_x64 { } - - [StructLayout(LayoutKind.Sequential, Size = 16)] - struct CORINFO_SIG_INST_x86 { } - - [StructLayout(LayoutKind.Sequential)] - struct ICorClassInfo { - public readonly IntPtr* vfptr; - } - - [StructLayout(LayoutKind.Sequential)] - struct ICorDynamicInfo { - public IntPtr* vfptr; - public int* vbptr; - - public static ICorStaticInfo* ICorStaticInfo(ICorDynamicInfo* ptr) { - return (ICorStaticInfo*)((byte*)&ptr->vbptr + ptr->vbptr[hasLinkInfo ? 9 : 8]); - } - } - - [StructLayout(LayoutKind.Sequential)] - struct ICorJitInfo { - public IntPtr* vfptr; - public int* vbptr; - - public static ICorDynamicInfo* ICorDynamicInfo(ICorJitInfo* ptr) { - hasLinkInfo = ptr->vbptr[10] > 0 && ptr->vbptr[10] >> 16 == 0; // != 0 and hiword byte == 0 - return (ICorDynamicInfo*)((byte*)&ptr->vbptr + ptr->vbptr[hasLinkInfo ? 10 : 9]); - } - } - - [StructLayout(LayoutKind.Sequential)] - struct ICorMethodInfo { - public IntPtr* vfptr; - } - - [StructLayout(LayoutKind.Sequential)] - struct ICorModuleInfo { - public IntPtr* vfptr; - } - - [StructLayout(LayoutKind.Sequential)] - struct ICorStaticInfo { - public IntPtr* vfptr; - public int* vbptr; - - public static ICorMethodInfo* ICorMethodInfo(ICorStaticInfo* ptr) { - return (ICorMethodInfo*)((byte*)&ptr->vbptr + ptr->vbptr[1]); - } - - public static ICorModuleInfo* ICorModuleInfo(ICorStaticInfo* ptr) { - return (ICorModuleInfo*)((byte*)&ptr->vbptr + ptr->vbptr[2]); - } - - public static ICorClassInfo* ICorClassInfo(ICorStaticInfo* ptr) { - return (ICorClassInfo*)((byte*)&ptr->vbptr + ptr->vbptr[3]); - } - } - - #endregion - - class CorMethodInfoHook { - static int ehNum = -1; - public CORINFO_EH_CLAUSE* clauses; - public IntPtr ftn; - public ICorMethodInfo* info; - public getEHinfo n_getEHinfo; - public IntPtr* newVfTbl; - - public getEHinfo o_getEHinfo; - public IntPtr* oldVfTbl; - - void hookEHInfo(IntPtr self, IntPtr ftn, uint EHnumber, CORINFO_EH_CLAUSE* clause) { - if (ftn == this.ftn) { - *clause = clauses[EHnumber]; - } - else { - o_getEHinfo(self, ftn, EHnumber, clause); - } - } - - public void Dispose() { - Marshal.FreeHGlobal((IntPtr)newVfTbl); - info->vfptr = oldVfTbl; - } - - public static CorMethodInfoHook Hook(ICorJitInfo* comp, IntPtr ftn, CORINFO_EH_CLAUSE* clauses) { - ICorMethodInfo* mtdInfo = ICorStaticInfo.ICorMethodInfo(ICorDynamicInfo.ICorStaticInfo(ICorJitInfo.ICorDynamicInfo(comp))); - IntPtr* vfTbl = mtdInfo->vfptr; - const int SLOT_NUM = 0x1B; - var newVfTbl = (IntPtr*)Marshal.AllocHGlobal(SLOT_NUM * IntPtr.Size); - for (int i = 0; i < SLOT_NUM; i++) - newVfTbl[i] = vfTbl[i]; - if (ehNum == -1) - for (int i = 0; i < SLOT_NUM; i++) { - bool isEh = true; - for (var func = (byte*)vfTbl[i]; *func != 0xe9; func++) - if (IntPtr.Size == 8 ? - (*func == 0x48 && *(func + 1) == 0x81 && *(func + 2) == 0xe9) : - (*func == 0x83 && *(func + 1) == 0xe9)) { - isEh = false; - break; - } - if (isEh) { - ehNum = i; - break; - } - } - - var ret = new CorMethodInfoHook { - ftn = ftn, - info = mtdInfo, - clauses = clauses, - newVfTbl = newVfTbl, - oldVfTbl = vfTbl - }; - - ret.n_getEHinfo = ret.hookEHInfo; - ret.o_getEHinfo = (getEHinfo)Marshal.GetDelegateForFunctionPointer(vfTbl[ehNum], typeof(getEHinfo)); - newVfTbl[ehNum] = Marshal.GetFunctionPointerForDelegate(ret.n_getEHinfo); - - mtdInfo->vfptr = newVfTbl; - return ret; - } - } - - class CorJitInfoHook { - public CORINFO_EH_CLAUSE* clauses; - public IntPtr ftn; - public ICorJitInfo* info; - public getEHinfo n_getEHinfo; - public IntPtr* newVfTbl; - - public getEHinfo o_getEHinfo; - public IntPtr* oldVfTbl; - - void hookEHInfo(IntPtr self, IntPtr ftn, uint EHnumber, CORINFO_EH_CLAUSE* clause) { - if (ftn == this.ftn) { - *clause = clauses[EHnumber]; - } - else { - o_getEHinfo(self, ftn, EHnumber, clause); - } - } - - public void Dispose() { - Marshal.FreeHGlobal((IntPtr)newVfTbl); - info->vfptr = oldVfTbl; - } - - public static CorJitInfoHook Hook(ICorJitInfo* comp, IntPtr ftn, CORINFO_EH_CLAUSE* clauses) { - const int slotNum = 8; - - IntPtr* vfTbl = comp->vfptr; - const int SLOT_NUM = 0x9E; - var newVfTbl = (IntPtr*)Marshal.AllocHGlobal(SLOT_NUM * IntPtr.Size); - for (int i = 0; i < SLOT_NUM; i++) - newVfTbl[i] = vfTbl[i]; - - var ret = new CorJitInfoHook { - ftn = ftn, - info = comp, - clauses = clauses, - newVfTbl = newVfTbl, - oldVfTbl = vfTbl - }; - - ret.n_getEHinfo = ret.hookEHInfo; - ret.o_getEHinfo = (getEHinfo)Marshal.GetDelegateForFunctionPointer(vfTbl[slotNum], typeof(getEHinfo)); - newVfTbl[slotNum] = Marshal.GetFunctionPointerForDelegate(ret.n_getEHinfo); - - comp->vfptr = newVfTbl; - return ret; - } - } - - [StructLayout(LayoutKind.Sequential)] - struct MethodData { - public readonly uint ILCodeSize; - public readonly uint MaxStack; - public readonly uint EHCount; - public readonly uint LocalVars; - public readonly uint Options; - public readonly uint MulSeed; - } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - delegate uint compileMethod(IntPtr self, ICorJitInfo* comp, CORINFO_METHOD_INFO* info, uint flags, byte** nativeEntry, uint* nativeSizeOfCode); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - delegate void getEHinfo(IntPtr self, IntPtr ftn, uint EHnumber, CORINFO_EH_CLAUSE* clause); - - delegate IntPtr* getJit(); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - delegate uint getMethodDefFromMethod(IntPtr self, IntPtr ftn); - } -} diff --git a/Tests/AntiTamper.Test/AntiTamperTest.cs b/Tests/AntiTamper.Test/AntiTamperTest.cs index bc6a9d818..4cd538684 100644 --- a/Tests/AntiTamper.Test/AntiTamperTest.cs +++ b/Tests/AntiTamper.Test/AntiTamperTest.cs @@ -12,12 +12,9 @@ public AntiTamperTest(ITestOutputHelper outputHelper) : base(outputHelper) { } [Theory] [InlineData("normal")] [InlineData("anti")] - [InlineData("jit", Skip = "Runtime Component of the JIT AntiTamper protection is broken.")] [Trait("Category", "Protection")] [Trait("Protection", "anti tamper")] public Task ProtectAntiTamperAndExecute(string antiTamperMode) { - if (antiTamperMode == "jit") return Task.CompletedTask; - return Run("AntiTamper.exe", new[] { "This is a test." }, new SettingItem("anti tamper") { { "mode", antiTamperMode } }, diff --git a/docs/protections.md b/docs/protections.md index 48665fd9d..213ec8bcc 100644 --- a/docs/protections.md +++ b/docs/protections.md @@ -84,12 +84,12 @@ Encrypts method bodies at build time and decrypts them at runtime via a JIT hook | Name | Values | Default | Description | |------|--------|---------|-------------| -| `mode` | `jit`, `native` | `jit` | `jit` hooks the JIT compiler to decrypt methods on demand. `native` pre-compiles methods to native code. | +| `mode` | `normal`, `anti` | `normal` | Tamper-detection method. `normal` decrypts the protected section at load. `anti` additionally runs anti-debugger checks. | | `key` | `normal`, `dynamic` | `normal` | Key derivation mode. `dynamic` derives the key from the assembly contents for stronger tamper detection. | ```xml - + ``` @@ -309,13 +309,13 @@ Protections stack. You can start from a preset and add/remove individual protect ```xml - + ``` -This applies the `normal` preset, adds anti-tamper with JIT mode, and removes renaming. +This applies the `normal` preset, adds anti-tamper in `anti` mode, and removes renaming. ## Pattern Expressions