internal static RuntimeGuardToken Rewrite(AssemblyDefinition assembly, AssemblyGuardSettings settings = null)
        {
            var id = Guid.NewGuid();

            settings = settings ?? AssemblyGuardSettings.Default;
            var validator = new AssemblyValidator(settings, assembly, new StackSizeValidator(settings), new PointerOperationValidator(settings));

            foreach (var module in assembly.Modules)
            {
                var guardInstanceField = EmitGuardInstance(module, id);
                var guard = new RuntimeGuardReferences(guardInstanceField, module);

                validator.ValidateDefinition(module);
                foreach (var type in module.Types)
                {
                    ValidateAndRewriteType(type, guard, validator, settings);
                }
            }
            return(new RuntimeGuardToken(id));
        }
        private static void ValidateAndRewriteMethod(MethodDefinition method, RuntimeGuardReferences guard, AssemblyValidator validator, AssemblyGuardSettings settings)
        {
            if (method.DeclaringType == guard.InstanceField.DeclaringType)
            {
                return;
            }

            validator.ValidateDefinition(method);
            if (!method.HasBody)
            {
                return;
            }

            if (method.Body.Instructions.Count == 0)
            {
                return; // weird, but happens with 'extern'
            }
            var isStaticConstructor = method.Name == ".cctor" && method.IsStatic && method.IsRuntimeSpecialName;
            var il            = method.Body.GetILProcessor();
            var guardVariable = new VariableDefinition(guard.InstanceField.FieldType);

            il.Body.Variables.Add(guardVariable);

            var instructions = il.Body.Instructions;
            var start        = instructions[0];
            var skipFirst    = 4;

            il.InsertBefore(start, il.Create(OpCodes.Ldsfld, guard.InstanceField));
            il.InsertBefore(start, il.Create(OpCodes.Dup));
            il.InsertBefore(start, il.CreateStlocBest(guardVariable));
            il.InsertBefore(start, il.Create(OpCodes.Call, isStaticConstructor ? guard.GuardEnterStaticConstructorMethod : guard.GuardEnterMethod));

            for (var i = skipFirst; i < instructions.Count; i++)
            {
                var instruction = instructions[i];
                var memberRule  = validator.ValidateInstructionAndGetPolicy(instruction, method);
                var code        = instruction.OpCode.Code;
                if (code == Code.Newarr)
                {
                    il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable));
                    il.InsertBefore(instruction, il.CreateCall(guard.FlowThroughGuardCountIntPtrMethod));
                    i += 2;
                    continue;
                }

                if (memberRule != null && memberRule.Rewriters.Count > 0)
                {
                    var instructionCountBefore = instructions.Count;
                    var rewritten = false;
                    var context   = new MemberRewriterContext(il, guardVariable, guard);
                    foreach (var rewriter in memberRule.InternalRewriters)
                    {
                        rewritten = rewriter.Rewrite(instruction, context) || rewritten;
                    }
                    if (rewritten)
                    {
                        i += instructions.Count - instructionCountBefore;
                        continue;
                    }
                }

                if (isStaticConstructor && instruction.OpCode.Code == Code.Ret)
                {
                    il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable));
                    il.InsertBefore(instruction, il.CreateCall(guard.GuardExitStaticConstructorMethod));
                    i += 2;
                    continue;
                }

                if (!ShouldInsertJumpGuardBefore(instruction))
                {
                    continue;
                }

                il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable));
                il.InsertBefore(instruction, il.Create(OpCodes.Call, guard.GuardJumpMethod));
                i += 2;
            }

            il.CorrectAllAfterChanges();
        }
 private static void ValidateAndRewriteType(TypeDefinition type, RuntimeGuardReferences guard, AssemblyValidator validator, AssemblyGuardSettings settings)
 {
     validator.ValidateDefinition(type);
     foreach (var nested in type.NestedTypes)
     {
         ValidateAndRewriteType(nested, guard, validator, settings);
     }
     foreach (var method in type.Methods)
     {
         ValidateAndRewriteMethod(method, guard, validator, settings);
     }
 }
        public static RuntimeGuardToken Rewrite(Stream assemblySourceStream, Stream assemblyTargetStream, AssemblyGuardSettings settings = null)
        {
            Argument.NotNull(nameof(assemblySourceStream), assemblySourceStream);
            Argument.NotNull(nameof(assemblyTargetStream), assemblyTargetStream);
            if (assemblyTargetStream == assemblySourceStream) // Cecil limitation? Causes some weird issues.
            {
                throw new ArgumentException("Target stream must be different from source stream.", nameof(assemblyTargetStream));
            }

            var assembly = AssemblyDefinition.ReadAssembly(assemblySourceStream);
            var token    = Rewrite(assembly, settings);

            assembly.Write(assemblyTargetStream);
            //assembly.Write($@"d:\Temp\unbreakable\{Guid.NewGuid()}.dll");
            return(token);
        }
Exemple #5
0
        private static void ValidateAndRewriteMethod(MethodDefinition method, RuntimeGuardReferences guard, AssemblyValidator validator, AssemblyGuardSettings settings)
        {
            if (method.DeclaringType == guard.InstanceField.DeclaringType)
            {
                return;
            }

            validator.ValidateDefinition(method);
            if (!method.HasBody)
            {
                return;
            }

            if (method.Body.Instructions.Count == 0)
            {
                return; // weird, but happens with 'extern'
            }
            var isStaticConstructor = method.Name == ".cctor" && method.IsStatic && method.IsRuntimeSpecialName;
            var il = method.Body.GetILProcessor();
            // IAsyncStateMachine.MoveNext has special rules, but to be safe, only apply them when its type is compiler-generated
            // which is checked by verifying that it has unspeakable name
            var isStateMachineMoveNext =
                method.Name == "MoveNext" &&
                method.Overrides.Count == 1 &&
                method.Overrides[0].FullName == "System.Void System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext()" &&
                method.DeclaringType.Name.Contains("<") &&
                il.Body.ExceptionHandlers.Count >= 1;
            var guardVariable = new VariableDefinition(guard.InstanceField.FieldType);

            il.Body.Variables.Add(guardVariable);

            var instructions = il.Body.Instructions;
            var start        = instructions[0];
            var skipStart    = 0;
            var skipCount    = 4;
            ExceptionHandler?stateMachineExceptionHandler = null;

            if (isStateMachineMoveNext)
            {
                // in IAsyncStateMachine.MoveNext, the GuardEnter call has to be inside the try block, so that the exception is propagated to the Task
                stateMachineExceptionHandler = il.Body.ExceptionHandlers.OrderBy(eh => eh.TryStart.Offset).First();
                start     = stateMachineExceptionHandler.TryStart;
                skipStart = instructions.IndexOf(start);
            }

            il.InsertBeforeAndRetargetJumps(start, il.Create(OpCodes.Ldsfld, guard.InstanceField));
            il.InsertBefore(start, il.Create(OpCodes.Dup));
            il.InsertBefore(start, il.CreateStlocBest(guardVariable));
            il.InsertBefore(start, il.Create(OpCodes.Call, isStaticConstructor ? guard.GuardEnterStaticConstructorMethod : guard.GuardEnterMethod));

            for (var i = 0; i < instructions.Count; i++)
            {
                if (i >= skipStart && i < skipStart + skipCount)
                {
                    continue;
                }

                // in IAsyncStateMachine.MoveNext, guard calls are only allowed inside the try block
                if (stateMachineExceptionHandler != null &&
                    (i < instructions.IndexOf(stateMachineExceptionHandler.TryStart) || i > instructions.IndexOf(stateMachineExceptionHandler.TryEnd)))
                {
                    continue;
                }

                var instruction = instructions[i];
                var memberRule  = validator.ValidateInstructionAndGetPolicy(instruction, method);
                var code        = instruction.OpCode.Code;
                if (code == Code.Newarr)
                {
                    il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable));
                    il.InsertBefore(instruction, il.CreateCall(guard.FlowThroughGuardCountIntPtrMethod));
                    i += 2;
                    continue;
                }

                if (memberRule != null && memberRule.Rewriters.Count > 0)
                {
                    var instructionCountBefore = instructions.Count;
                    var rewritten = false;
                    var context   = new MemberRewriterContext(il, guardVariable, guard);
                    foreach (var rewriter in memberRule.InternalRewriters)
                    {
                        rewritten = rewriter.Rewrite(instruction, context) || rewritten;
                    }
                    if (rewritten)
                    {
                        i += instructions.Count - instructionCountBefore;
                        continue;
                    }
                }

                if (isStaticConstructor && instruction.OpCode.Code == Code.Ret)
                {
                    il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable));
                    il.InsertBefore(instruction, il.CreateCall(guard.GuardExitStaticConstructorMethod));
                    i += 2;
                    continue;
                }

                if (!ShouldInsertJumpGuardBefore(instruction))
                {
                    continue;
                }

                il.InsertBeforeAndRetargetJumps(instruction, il.CreateLdlocBest(guardVariable));
                il.InsertBefore(instruction, il.Create(OpCodes.Call, guard.GuardJumpMethod));
                i += 2;
            }

            il.CorrectAllAfterChanges();
        }