AddMethod() публичный Метод

public AddMethod ( MethodReference method ) : XNodeOut
method Mono.Cecil.MethodReference
Результат XNodeOut
Пример #1
0
        private void SaveCode(XNodeOut classNode, MethodDefinition method)
        {
            XNodeOut methodNode = classNode.AddMethod(method);

            if (Build.DecompileCSharp)
            {
                methodNode.CSharp = DecompileMethod(method);
            }

            if (method.Body == null)
            {
                return;
            }

            // record MSIL
            if (Build.SaveMsil)
            {
                methodNode.Msil = new List <XInstruction>();
                foreach (var inst in method.Body.Instructions)
                {
                    var xInst = new XInstruction();
                    xInst.Offset = inst.Offset;
                    xInst.OpCode = inst.OpCode.Name;
                    xInst.Line   = (inst.Operand != null) ? inst.Operand.ToString() : "";

                    if (inst.OpCode == OpCodes.Call ||
                        inst.OpCode == OpCodes.Calli ||
                        inst.OpCode == OpCodes.Callvirt ||
                        inst.OpCode == OpCodes.Newobj ||
                        inst.OpCode == OpCodes.Ldftn) // pushes a function pointer to the stack
                    {
                        var call = inst.Operand as MethodReference;
                        if (call != null)
                        {
                            var classRef  = GetClassRef(call.DeclaringType);
                            var methodRef = classRef.AddMethod(call);

                            xInst.RefId = methodRef.ID;
                        }
                        else
                        {
                            Debug.WriteLine("Unable to track: " + inst.Operand.ToString());
                        }
                    }
                    else if (inst.OpCode == OpCodes.Stfld ||
                             inst.OpCode == OpCodes.Stsfld ||
                             inst.OpCode == OpCodes.Ldfld ||
                             inst.OpCode == OpCodes.Ldflda ||
                             inst.OpCode == OpCodes.Ldsfld ||
                             inst.OpCode == OpCodes.Ldsflda)
                    {
                        var fieldDef = inst.Operand as FieldReference;
                        var classRef = GetClassRef(fieldDef.DeclaringType);
                        var fieldRef = classRef.AddField(fieldDef);
                        xInst.RefId = fieldRef.ID;
                    }
                    else if (inst.OpCode.FlowControl == FlowControl.Branch ||
                             inst.OpCode.FlowControl == FlowControl.Cond_Branch)
                    {
                        var op = inst.Operand as Instruction;
                        if (op != null)
                        {
                            int offset = op.Offset;
                            xInst.Line  = "goto " + offset.ToString("X");
                            xInst.RefId = offset;
                        }
                    }

                    methodNode.Msil.Add(xInst);
                }
            }
        }
Пример #2
0
        private void RecompileMethod(XNodeOut classNode, MethodDefinition method)
        {
            XNodeOut methodNode = classNode.AddMethod(method);

            // return
            if (method.ReturnType != null)
            {
                var returnNode = SetClassDependency(classNode, method.ReturnType);
                if (returnNode != null)
                {
                    methodNode.ReturnID = returnNode.ID;
                }
            }

            // params
            for (int i = 0; i < method.Parameters.Count; i++)
            {
                var p = method.Parameters[i];

                if (methodNode.ParamIDs == null)
                {
                    methodNode.ParamIDs   = new int[method.Parameters.Count];
                    methodNode.ParamNames = new string[method.Parameters.Count];
                }

                var paramNode = SetClassDependency(classNode, p.ParameterType);
                methodNode.ParamIDs[i]   = paramNode.ID;
                methodNode.ParamNames[i] = p.Name;
            }

            if (method.Body == null)
            {
                return;
            }

            // local vars
            foreach (var local in method.Body.Variables)
            {
                SetClassDependency(classNode, local.VariableType);
            }

            // expands branches/jumps to support adddresses > 255
            // possibly needed if injecting code into large functions
            // OptimizeMacros at end of the function re-optimizes
            method.Body.SimplifyMacros();

            methodNode.Lines = method.Body.Instructions.Count;

            var processor = method.Body.GetILProcessor();

            for (int i = 0; i < method.Body.Instructions.Count; i++)
            {
                var instruction = method.Body.Instructions[i];

                // record method exited
                if (Build.TrackFlow && instruction.OpCode == OpCodes.Ret)
                {
                    instruction.OpCode = OpCodes.Nop; // any 'goto return' will go to here so method exit gets logged first
                    AddInstruction(method, i + 1, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                    AddInstruction(method, i + 2, processor.Create(OpCodes.Call, ExitMethodRef));
                    AddInstruction(method, i + 3, processor.Create(OpCodes.Ret));

                    i += 3;
                }

                // if we're tracking calls to non-xrayed assemblies
                else if (instruction.OpCode == OpCodes.Call ||
                         instruction.OpCode == OpCodes.Calli ||
                         instruction.OpCode == OpCodes.Callvirt)
                {
                    var call = instruction.Operand as MethodReference;

                    if (call == null)
                    {
                        Debug.WriteLine("Unable to track not xrayed: " + instruction.Operand.ToString());
                        continue;
                    }

                    SetClassDependency(classNode, call.ReturnType);
                    SetClassDependency(classNode, call.DeclaringType);
                    foreach (var p in call.Parameters)
                    {
                        SetClassDependency(classNode, p.ParameterType);
                    }

                    var calledRef = GetClassRef(call.DeclaringType);

                    var calledNode = calledRef.AddMethod(call);


                    /*if( TrackExternal &&
                     *  !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System") &&
                     *  (instruction.Operand as MethodReference).DeclaringType.Namespace != EnterMethodRef.DeclaringType.Namespace )*/
                    if (Build.TrackExternal &&
                        calledNode.External &&
                        calledRef.Name != "XRay")
                    // !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System"))
                    {
                        if (method.Name == ".cctor" && call.Name == "GetTypeFromHandle")
                        {
                            continue; // call added to cctor by xray
                        }
                        calledNode.Lines = 1;

                        // in function is prefixed by .constrained, wrap enter/exit around those 2 lines
                        int offset = 0;
                        if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Constrained)
                        {
                            i     -= 1;
                            offset = 1;
                        }

                        var oldPos = method.Body.Instructions[i];

                        // wrap the call with enter and exit, because enter to an external method may cause
                        // an xrayed method to be called, we want to track the flow of that process
                        int pos = i;
                        AddInstruction(method, pos++, processor.Create(OpCodes.Ldc_I4, calledNode.ID));
                        AddInstruction(method, pos++, processor.Create(OpCodes.Call, EnterMethodRef));

                        // method
                        pos += 1 + offset;

                        AddInstruction(method, pos++, processor.Create(OpCodes.Ldc_I4, calledNode.ID));
                        AddInstruction(method, pos, processor.Create(OpCodes.Call, ExitMethodRef));

                        var newPos = method.Body.Instructions[i];
                        UpdateExceptionHandlerPositions(method, oldPos, newPos);

                        i = pos; // loop end will add 1 putting us right after last added function
                    }
                }

                else if (Build.TrackFields &&
                         (instruction.OpCode == OpCodes.Stfld ||
                          instruction.OpCode == OpCodes.Stsfld ||
                          instruction.OpCode == OpCodes.Ldfld ||
                          instruction.OpCode == OpCodes.Ldflda ||
                          instruction.OpCode == OpCodes.Ldsfld ||
                          instruction.OpCode == OpCodes.Ldsflda))
                {
                    var fieldDef = instruction.Operand as FieldReference;

                    var classRef = GetClassRef(fieldDef.DeclaringType);
                    var fieldRef = classRef.AddField(fieldDef);

                    // some times volitile prefixes set/get field
                    int offset = 0;
                    if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Volatile)
                    {
                        i--;
                        offset = 1;
                    }

                    AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, fieldRef.ID));

                    if (instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld)
                    {
                        AddInstruction(method, i + 1, processor.Create(OpCodes.Call, SetFieldRef));
                    }
                    else
                    {
                        AddInstruction(method, i + 1, processor.Create(OpCodes.Call, LoadFieldRef));
                    }

                    i = i + 2 + offset; // skip instuction added, and field itself, end of loop will iterate this again

                    // Debug.WriteLine("{0} in Module: {1}, Namespace: {2}, Class: {3}, Name: {4}, Type: {5}", instruction.OpCode, fieldDef.DeclaringType.Scope.Name, namespaces, className, fieldName, fieldType);
                }

                else if (instruction.OpCode == OpCodes.Newobj)
                {
                    var newObj = instruction.Operand as MethodReference;
                    SetClassDependency(classNode, newObj.DeclaringType);
                }

                /* Still not really working - goal - to get side by side wpf apps to work
                 * else if (instruction.OpCode == OpCodes.Ldstr && !Build.ReplaceOriginal)
                 * {
                 *  // rename Pack URIs in WPF so resources can be found with an XRay.. namespace
                 *  // ex:  "/MyApp;component/views/aboutview.xaml" ->  "/XRay.MyApp;component/views/aboutview.xaml"
                 *  var packUri = instruction.Operand as String;
                 *
                 *  foreach (var file in XRayedFiles)
                 *      packUri = packUri.Replace("/" + file.AssemblyName + ";", "/XRay." + file.AssemblyName + ";");
                 *      //packUri = packUri.Replace(file.AssemblyName, "XRay." + file.AssemblyName);
                 *
                 *  instruction.Operand = packUri;
                 * }*/
            } // end iterating through instructions


            // record function was entered
            if (Build.TrackFunctions)
            {
                AddInstruction(method, 0, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                AddInstruction(method, 1, processor.Create(OpCodes.Call, EnterMethodRef));
            }

            // record catches
            if (Build.TrackFlow)
            {
                foreach (var handler in method.Body.ExceptionHandlers)
                {
                    if (handler.HandlerType != ExceptionHandlerType.Catch)
                    {
                        continue;
                    }

                    int i = method.Body.Instructions.IndexOf(handler.HandlerStart);

                    AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                    AddInstruction(method, i + 1, processor.Create(OpCodes.Call, CatchMethodRef));

                    var oldPos = handler.HandlerStart;
                    var newPos = method.Body.Instructions[i];

                    UpdateExceptionHandlerPositions(method, oldPos, newPos);
                }
            }

            method.Body.OptimizeMacros();
        }
Пример #3
0
        private void RecompileMethod(XNodeOut classNode, TypeDefinition classDef, MethodDefinition method)
        {
            XNodeOut methodNode = classNode.AddMethod(method);

            // return
            if (method.ReturnType != null)
            {
                var returnNode = SetClassDependency(classNode, method.ReturnType);
                if (returnNode != null)
                    methodNode.ReturnID = returnNode.ID;
            }

            // params
            for (int i = 0; i < method.Parameters.Count; i++)
            {
                var p = method.Parameters[i];

                if (methodNode.ParamIDs == null)
                {
                    methodNode.ParamIDs = new int[method.Parameters.Count];
                    methodNode.ParamNames = new string[method.Parameters.Count];
                }

                var paramNode = SetClassDependency(classNode, p.ParameterType);
                methodNode.ParamIDs[i] = paramNode.ID;
                methodNode.ParamNames[i] = p.Name;
            }

            if (method.Body == null)
                return;

            // local vars
            foreach (var local in method.Body.Variables)
                SetClassDependency(classNode, local.VariableType);

            // expands branches/jumps to support adddresses > 255
            // possibly needed if injecting code into large functions
            // OptimizeMacros at end of the function re-optimizes
            method.Body.SimplifyMacros();

            methodNode.Lines = method.Body.Instructions.Count;

            var processor = method.Body.GetILProcessor();

            for (int i = 0; i < method.Body.Instructions.Count; i++)
            {
                var instruction = method.Body.Instructions[i];

                // record method exited
                if (Build.TrackFlow && instruction.OpCode == OpCodes.Ret)
                {
                    instruction.OpCode = OpCodes.Nop; // any 'goto return' will go to here so method exit gets logged first

                    i = TrackMethodExit(method, method, methodNode, processor, i);

                    AddInstruction(method, ++i, processor.Create(OpCodes.Ret));
                }

                // if we're tracking calls to non-xrayed assemblies
                else if (instruction.OpCode == OpCodes.Call ||
                          instruction.OpCode == OpCodes.Calli ||
                          instruction.OpCode == OpCodes.Callvirt)
                {
                    var call = instruction.Operand as MethodReference;

                    if (call == null)
                    {
                        Debug.WriteLine("Unable to track not xrayed: " + instruction.Operand.ToString());
                        continue;
                    }

                    SetClassDependency(classNode, call.ReturnType);
                    SetClassDependency(classNode, call.DeclaringType);
                    foreach (var p in call.Parameters)
                        SetClassDependency(classNode, p.ParameterType);

                    var calledRef = GetClassRef(call.DeclaringType);

                    var calledNode = calledRef.AddMethod(call);

                    /*if( TrackExternal &&
                        !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System") &&
                        (instruction.Operand as MethodReference).DeclaringType.Namespace != EnterMethodRef.DeclaringType.Namespace )*/
                    if (Build.TrackExternal &&
                        calledNode.External &&
                        calledRef.Name != "XRay")
                    // !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System"))
                    {
                        if (method.Name == ".cctor" && call.Name == "GetTypeFromHandle")
                            continue; // call added to cctor by xray

                        calledNode.Lines = 1;
                        bool isConstrained = false;

                        // in function is prefixed by .constrained, wrap enter/exit around those 2 lines
                        int offset = 0;
                        if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Constrained)
                        {
                            i -= 1;
                            offset = 1;
                            isConstrained = true;
                        }

                        // put stuff below in here so instructions dont get messed up
                        var oldPos = method.Body.Instructions[i];
                        int pos = i;

                        // if this is an external call and we want to track the parameters, then we have to wrap the function
                        if (Build.TrackParameters && call.HasParameters)
                        {
                            // wrap call in a new function with the same parameters and return type as the original call, we do this
                            // because it's maybe impossible to build the object[] of parameters from the current stack because it's unknown
                            // in a wrapped function we can access the arguments easily to build the object[] and pass to method enter
                            var wrappedName = string.Format("{0}_{1}_XRay{2}", call.DeclaringType.Name, call.Name, UniqueEnterSig++);

                            var resolvedCall = ResolveGenericMethod(call);

                            var wrapFunc = new MethodDefinition(wrappedName, new Mono.Cecil.MethodAttributes(), resolvedCall.ReturnType);

                            // the wrapper can be static because we pass the called function's declaring type into the wrapper
                            wrapFunc.IsPrivate = true;
                            wrapFunc.IsStatic = true;
                            wrapFunc.HasThis = false;
                            wrapFunc.IsHideBySig = true;

                            if (call.HasThis)
                            {
                                // calling functions against a value type is done by calling against its address eg DateTime.AddMinutes()
                                if (call.DeclaringType.IsValueType)
                                    wrapFunc.Parameters.Add(new ParameterDefinition(new ByReferenceType(call.DeclaringType)));
                                else
                                    wrapFunc.Parameters.Add(new ParameterDefinition(call.DeclaringType));
                            }

                            foreach (var p in resolvedCall.Parameters)
                                wrapFunc.Parameters.Add(p);

                            // write body of method
                            var wrapProcessor = wrapFunc.Body.GetILProcessor();

                            TrackMethodEnterParams(wrapFunc, calledNode.ID, wrapProcessor, call.HasThis);

                            // load 'this' and arguemnts
                            for(int x = 0; x < wrapFunc.Parameters.Count; x++)
                                 wrapFunc.Body.Instructions.Add(wrapProcessor.Create(OpCodes.Ldarg, x));

                            if (isConstrained)
                            {
                                // test with oldfashionedfun with 'no parameter' filter above turned off
                                var inst = method.Body.Instructions[pos];
                                wrapFunc.Body.Instructions.Add(wrapProcessor.Create(inst.OpCode, inst.Operand as TypeReference));
                            }

                            // call original function
                            wrapFunc.Body.Instructions.Add(wrapProcessor.Create(instruction.OpCode, call));

                            // return
                            wrapFunc.Body.Instructions.Add(wrapProcessor.Create(OpCodes.Ret));

                            classDef.Methods.Add(wrapFunc);

                            // replace current call instruction with call to copy method
                            var wrapRef = new MethodReference(wrapFunc.Name, wrapFunc.ReturnType);
                            foreach (var parameter in wrapFunc.Parameters)
                                wrapRef.Parameters.Add(parameter);

                            if (classDef.HasGenericParameters)
                            {
                                // have to add arguments to declaring type manually for some reason
                                var genericClassDef = new GenericInstanceType(classDef);
                                foreach (var parameter in classDef.GenericParameters)
                                    genericClassDef.GenericArguments.Add(parameter);
                                wrapRef.DeclaringType = genericClassDef;
                            }
                            else
                                wrapRef.DeclaringType = classDef;

                            method.Body.Instructions[pos++].OpCode = OpCodes.Nop;

                            if(isConstrained)
                                method.Body.Instructions[pos++].OpCode = OpCodes.Nop; // sets the actual call to nop

                            AddInstruction(method, pos, processor.Create(OpCodes.Call, wrapRef));
                            // not incrementing pos because enter function takes un-inc'd pos as a param
                            // really need to go back through and standardize pos setting
                        }
                        else
                        {
                            // wrap the call with enter and exit, because enter to an external method may cause
                            // an xrayed method to be called, we want to track the flow of that process
                            AddInstruction(method, pos++, processor.Create(OpCodes.Ldc_I4, calledNode.ID));
                            AddInstruction(method, pos++, processor.Create(OpCodes.Call, MethodEnterRef));

                            // method
                            pos += offset;
                        }

                        pos = TrackMethodExit(method, call, calledNode, processor, pos);

                        var newPos = method.Body.Instructions[i]; // get new instruction at original position, inserting stuff changed it

                        UpdateExceptionHandlerPositions(method, oldPos, newPos);

                        i = pos; // loop end will add 1 putting us right after last added function
                    }
                }

                else if (Build.TrackFields &&
                         (instruction.OpCode == OpCodes.Stfld ||
                          instruction.OpCode == OpCodes.Stsfld ||
                          instruction.OpCode == OpCodes.Ldfld ||
                          instruction.OpCode == OpCodes.Ldflda ||
                          instruction.OpCode == OpCodes.Ldsfld ||
                          instruction.OpCode == OpCodes.Ldsflda))
                {
                    var fieldDef = instruction.Operand as FieldReference;

                    var classRef = GetClassRef(fieldDef.DeclaringType);
                    var fieldRef = classRef.AddField(fieldDef);

                    // some times volitile prefixes set/get field
                    int offset = 0;
                    if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Volatile)
                    {
                        i--;
                        offset = 1;
                    }

                    AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, fieldRef.ID));

                    if (instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld)
                        AddInstruction(method, i + 1, processor.Create(OpCodes.Call, SetFieldRef));
                    else
                        AddInstruction(method, i + 1, processor.Create(OpCodes.Call, LoadFieldRef));

                    i = i + 2 + offset; // skip instuction added, and field itself, end of loop will iterate this again

                    // Debug.WriteLine("{0} in Module: {1}, Namespace: {2}, Class: {3}, Name: {4}, Type: {5}", instruction.OpCode, fieldDef.DeclaringType.Scope.Name, namespaces, className, fieldName, fieldType);
                }

                else if (instruction.OpCode == OpCodes.Newobj)
                {
                    var newObj = instruction.Operand as MethodReference;
                    SetClassDependency(classNode, newObj.DeclaringType);
                }

                /* Still not really working - goal - to get side by side wpf apps to work
                 * else if (instruction.OpCode == OpCodes.Ldstr && !Build.ReplaceOriginal)
                {
                    // rename Pack URIs in WPF so resources can be found with an XRay.. namespace
                    // ex:  "/MyApp;component/views/aboutview.xaml" ->  "/XRay.MyApp;component/views/aboutview.xaml"
                    var packUri = instruction.Operand as String;

                    foreach (var file in XRayedFiles)
                        packUri = packUri.Replace("/" + file.AssemblyName + ";", "/XRay." + file.AssemblyName + ";");
                        //packUri = packUri.Replace(file.AssemblyName, "XRay." + file.AssemblyName);

                    instruction.Operand = packUri;
                }*/

            } // end iterating through instructions

            // record function was entered
            if (Build.TrackFunctions)
            {
                if (Build.TrackParameters && method.HasParameters)
                    TrackMethodEnterParams(method, methodNode.ID, processor, false);

                else
                {
                    AddInstruction(method, 0, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                    AddInstruction(method, 1, processor.Create(OpCodes.Call, MethodEnterRef));
                }
            }

            // record catches
            if (Build.TrackFlow)
                foreach (var handler in method.Body.ExceptionHandlers)
                {
                    if (handler.HandlerType != ExceptionHandlerType.Catch)
                        continue;

                    int i = method.Body.Instructions.IndexOf(handler.HandlerStart);

                    AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                    AddInstruction(method, i + 1, processor.Create(OpCodes.Call, MethodCatchRef));

                    var oldPos = handler.HandlerStart;
                    var newPos = method.Body.Instructions[i];

                    UpdateExceptionHandlerPositions(method, oldPos, newPos);
                }

            method.Body.OptimizeMacros();
        }
Пример #4
0
        private void SaveCode(XNodeOut classNode, MethodDefinition method)
        {
            XNodeOut methodNode = classNode.AddMethod(method);

            if (Build.DecompileCSharp)
                methodNode.CSharp = DecompileMethod(method);

            if (method.Body == null)
                return;

            // record MSIL
            if (Build.SaveMsil)
            {
                methodNode.Msil = new List<XInstruction>();
                foreach (var inst in method.Body.Instructions)
                {
                    var xInst = new XInstruction();
                    xInst.Offset = inst.Offset;
                    xInst.OpCode = inst.OpCode.Name;
                    xInst.Line = (inst.Operand != null) ? inst.Operand.ToString() : "";

                    if (inst.OpCode == OpCodes.Call ||
                        inst.OpCode == OpCodes.Calli ||
                        inst.OpCode == OpCodes.Callvirt ||
                        inst.OpCode == OpCodes.Newobj ||
                        inst.OpCode == OpCodes.Ldftn) // pushes a function pointer to the stack
                    {
                        var call = inst.Operand as MethodReference;
                        if (call != null)
                        {
                            var classRef = GetClassRef(call.DeclaringType);
                            var methodRef = classRef.AddMethod(call);

                            xInst.RefId = methodRef.ID;
                        }
                        else
                            Debug.WriteLine("Unable to track: " + inst.Operand.ToString());
                    }
                    else if (inst.OpCode == OpCodes.Stfld ||
                              inst.OpCode == OpCodes.Stsfld ||
                              inst.OpCode == OpCodes.Ldfld ||
                              inst.OpCode == OpCodes.Ldflda ||
                              inst.OpCode == OpCodes.Ldsfld ||
                              inst.OpCode == OpCodes.Ldsflda)
                    {
                        var fieldDef = inst.Operand as FieldReference;
                        var classRef = GetClassRef(fieldDef.DeclaringType);
                        var fieldRef = classRef.AddField(fieldDef);
                        xInst.RefId = fieldRef.ID;
                    }
                    else if (inst.OpCode.FlowControl == FlowControl.Branch ||
                             inst.OpCode.FlowControl == FlowControl.Cond_Branch)
                    {
                        var op = inst.Operand as Instruction;
                        if (op != null)
                        {
                            int offset = op.Offset;
                            xInst.Line = "goto " + offset.ToString("X");
                            xInst.RefId = offset;
                        }
                    }

                    methodNode.Msil.Add(xInst);
                }
            }
        }
Пример #5
0
        private void RecompileMethods(TypeDefinition classDef, XNodeOut classNode)
        {
            ILProcessor processor = null;

            // add fields
            if (TrackFields && classDef.HasFields)
                foreach (var fieldDef in classDef.Fields)
                {
                    var fieldNode = classNode.AddField(fieldDef);

                    SetClassDependency(classNode, fieldDef.DeclaringType);

                    if (fieldDef.FieldType.IsGenericParameter)
                        Debug.WriteLine("Generic parameter ignored - " + fieldDef.FieldType.ToString());
                    else
                        fieldNode.ReturnID = GetClassRef(fieldDef.FieldType).ID;
                }

            if(TrackCode)
                foreach (var method in classDef.Methods)
                {
                    XNodeOut methodNode = classNode.AddMethod(method);

                    if(DecompileCSharp)
                        methodNode.CSharp = DecompileMethod(method);

                    if (method.Body == null)
                        continue;

                    // record MSIL
                    if (SaveMsil)
                    {
                        methodNode.Msil = new List<XInstruction>();
                        foreach (var inst in method.Body.Instructions)
                        {
                            var xInst = new XInstruction();
                            xInst.Offset = inst.Offset;
                            xInst.OpCode = inst.OpCode.Name;
                            xInst.Line = (inst.Operand != null) ? inst.Operand.ToString() : "";

                            if (inst.OpCode == OpCodes.Call ||
                                inst.OpCode == OpCodes.Calli ||
                                inst.OpCode == OpCodes.Callvirt ||
                                inst.OpCode == OpCodes.Newobj ||
                                inst.OpCode == OpCodes.Ldftn) // pushes a function pointer to the stack
                            {
                                var call = inst.Operand as MethodReference;
                                if (call != null)
                                {
                                    var classRef = GetClassRef(call.DeclaringType);
                                    var methodRef = classRef.AddMethod(call);

                                    xInst.RefId = methodRef.ID;
                                }
                                else
                                    Debug.WriteLine("Unable to track: " + inst.Operand.ToString());
                            }
                            else if (inst.OpCode == OpCodes.Stfld ||
                                      inst.OpCode == OpCodes.Stsfld ||
                                      inst.OpCode == OpCodes.Ldfld ||
                                      inst.OpCode == OpCodes.Ldflda ||
                                      inst.OpCode == OpCodes.Ldsfld ||
                                      inst.OpCode == OpCodes.Ldsflda)
                            {
                                var fieldDef = inst.Operand as FieldReference;
                                var classRef = GetClassRef(fieldDef.DeclaringType);
                                var fieldRef = classRef.AddField(fieldDef);
                                xInst.RefId = fieldRef.ID;
                            }
                            else if (inst.OpCode.FlowControl == FlowControl.Branch ||
                                     inst.OpCode.FlowControl == FlowControl.Cond_Branch)
                            {
                                var op = inst.Operand as Instruction;
                                if (op != null)
                                {
                                    int offset = op.Offset;
                                    xInst.Line = "goto " + offset.ToString("X");
                                    xInst.RefId = offset;
                                }
                            }

                            methodNode.Msil.Add(xInst);
                        }
                    }
                }

            if (TrackInstances && !classDef.IsValueType)
            {
                bool hasCtor = false;

                // add tracking to constructor
                foreach(var ctorMethod in classDef.Methods.Where(m =>  (m.Name == ".ctor" || m.Name == ".cctor") && m.Body != null))
                {
                    ctorMethod.Body.SimplifyMacros();

                    processor = ctorMethod.Body.GetILProcessor();

                    // to prevent warnings in verify, our code should be put after the base constructor call

                    AddInstruction(ctorMethod, 0, processor.Create(OpCodes.Ldc_I4, classNode.ID));

                    if (ctorMethod.Name == ".ctor")
                    {
                        hasCtor = true;
                        AddInstruction(ctorMethod, 1, processor.Create(OpCodes.Ldarg, 0));
                        AddInstruction(ctorMethod, 2, processor.Create(OpCodes.Call, ClassConstructedRef));
                    }
                    // else static constructor
                    else
                    {
                        // ldtoken    XTestLib.SmallStatic
                        // ldtoken    XTestLib.StaticTemplateClass`1<!T> (for generic static classes)
                        // call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)

                        if (classDef.HasGenericParameters)
                        {
                            // for some reason the GenericInstanceType does not carry over the parameters
                            var genericDef = new GenericInstanceType(classDef);
                            foreach (var p in classDef.GenericParameters)
                                genericDef.GenericArguments.Add(p);

                            AddInstruction(ctorMethod, 1, processor.Create(OpCodes.Ldtoken, genericDef));
                        }
                        else
                            AddInstruction(ctorMethod, 1, processor.Create(OpCodes.Ldtoken, classDef));

                        AddInstruction(ctorMethod, 2, processor.Create(OpCodes.Call, GetTypeFromHandleRef));
                        AddInstruction(ctorMethod, 3, processor.Create(OpCodes.Call, ClassConstructedRef));
                    }
                    ctorMethod.Body.OptimizeMacros();
                }

                // add tracking to desconstructor (only add if ctor tracking added)
                if (hasCtor)
                {
                     var finMethod = classDef.Methods.FirstOrDefault(m => m.Name == "Finalize");
                     bool callObjectFinalize = false;

                     if (finMethod == null)
                     {
                         finMethod = new MethodDefinition("Finalize", Mono.Cecil.MethodAttributes.Family | Mono.Cecil.MethodAttributes.HideBySig | Mono.Cecil.MethodAttributes.Virtual, VoidRef);
                         callObjectFinalize = true;
                         classDef.Methods.Add(finMethod);
                     }

                     finMethod.Body.SimplifyMacros();

                     processor = finMethod.Body.GetILProcessor();

                     AddInstruction(finMethod, 0, processor.Create(OpCodes.Ldc_I4, classNode.ID));
                     AddInstruction(finMethod, 1, processor.Create(OpCodes.Ldarg, 0));
                     AddInstruction(finMethod, 2, processor.Create(OpCodes.Call, ClassDeconstructedRef));

                     if (callObjectFinalize)
                     {
                         AddInstruction(finMethod, 3, processor.Create(OpCodes.Ldarg, 0));
                         AddInstruction(finMethod, 4, processor.Create(OpCodes.Call, ObjectFinalizeRef));

                         AddInstruction(finMethod, 5, processor.Create(OpCodes.Ret));
                     }

                     finMethod.Body.OptimizeMacros();
                }
            }

            // iterate method nodes
            foreach (var method in classDef.Methods)
            {
                XNodeOut methodNode = classNode.AddMethod(method);

                // return
                if(method.ReturnType != null)
                {
                    var returnNode = SetClassDependency(classNode, method.ReturnType);
                    if(returnNode != null)
                        methodNode.ReturnID = returnNode.ID;
                }
                // params
                for(int i = 0; i < method.Parameters.Count; i++)
                {
                    var p = method.Parameters[i];

                    if (methodNode.ParamIDs == null)
                    {
                        methodNode.ParamIDs = new int[method.Parameters.Count];
                        methodNode.ParamNames = new string[method.Parameters.Count];
                    }

                    var paramNode = SetClassDependency(classNode, p.ParameterType);
                    methodNode.ParamIDs[i] = paramNode.ID;
                    methodNode.ParamNames[i] = p.Name;
                }

                if (method.Body == null)
                    continue;

                // local vars
                foreach (var local in method.Body.Variables)
                    SetClassDependency(classNode, local.VariableType);

                // expands branches/jumps to support adddresses > 255
                // possibly needed if injecting code into large functions
                // OptimizeMacros at end of the function re-optimizes
                method.Body.SimplifyMacros();

                methodNode.Lines = method.Body.Instructions.Count;

                processor = method.Body.GetILProcessor();

                for (int i = 0; i < method.Body.Instructions.Count; i++)
                {
                    var instruction = method.Body.Instructions[i];

                    // record method exited
                    if (TrackFlow && instruction.OpCode == OpCodes.Ret)
                    {
                        instruction.OpCode = OpCodes.Nop; // any 'goto return' will go to here so method exit gets logged first
                        AddInstruction(method, i + 1, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                        AddInstruction(method, i + 2, processor.Create(OpCodes.Call, ExitMethodRef));
                        AddInstruction(method, i + 3, processor.Create(OpCodes.Ret));

                        i += 3;
                    }

                    // if we're tracking calls to non-xrayed assemblies
                    else if ( instruction.OpCode == OpCodes.Call ||
                              instruction.OpCode == OpCodes.Calli ||
                              instruction.OpCode == OpCodes.Callvirt)
                    {
                        var call = instruction.Operand as MethodReference;

                        if (call == null)
                        {
                            Debug.WriteLine("Unable to track not xrayed: " + instruction.Operand.ToString());
                            continue;
                        }

                        SetClassDependency(classNode, call.ReturnType);
                        SetClassDependency(classNode, call.DeclaringType);
                        foreach(var p in call.Parameters)
                            SetClassDependency(classNode, p.ParameterType);

                        var calledRef = GetClassRef(call.DeclaringType);

                        var calledNode = calledRef.AddMethod(call);

                        /*if( TrackExternal &&
                            !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System") &&
                            (instruction.Operand as MethodReference).DeclaringType.Namespace != EnterMethodRef.DeclaringType.Namespace )*/
                        if (TrackExternal &&
                            calledNode.External &&
                            calledRef.Name != "XRay")
                           // !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System"))
                        {
                            if (method.Name == ".cctor" && call.Name == "GetTypeFromHandle")
                                continue; // call added to cctor by xray

                            calledNode.Lines = 1;

                            // in function is prefixed by .constrained, wrap enter/exit around those 2 lines
                            int offset = 0;
                            if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Constrained)
                            {
                                i -= 1;
                                offset = 1;
                            }

                            var oldPos = method.Body.Instructions[i];

                            // wrap the call with enter and exit, because enter to an external method may cause
                            // an xrayed method to be called, we want to track the flow of that process
                            int pos = i;
                            AddInstruction(method, pos++, processor.Create(OpCodes.Ldc_I4, calledNode.ID));
                            AddInstruction(method, pos++, processor.Create(OpCodes.Call, EnterMethodRef));

                            // method
                            pos += 1 + offset;

                            AddInstruction(method, pos++, processor.Create(OpCodes.Ldc_I4, calledNode.ID));
                            AddInstruction(method, pos, processor.Create(OpCodes.Call, ExitMethodRef));

                            var newPos = method.Body.Instructions[i];
                            UpdateExceptionHandlerPositions(method, oldPos, newPos);

                            i = pos; // loop end will add 1 putting us right after last added function
                        }
                    }

                    else if (TrackFields &&
                             (instruction.OpCode == OpCodes.Stfld ||
                              instruction.OpCode == OpCodes.Stsfld ||
                              instruction.OpCode == OpCodes.Ldfld ||
                              instruction.OpCode == OpCodes.Ldflda ||
                              instruction.OpCode == OpCodes.Ldsfld ||
                              instruction.OpCode == OpCodes.Ldsflda))
                    {
                        var fieldDef = instruction.Operand as FieldReference;

                        var classRef = GetClassRef(fieldDef.DeclaringType);
                        var fieldRef = classRef.AddField(fieldDef);

                        // some times volitile prefixes set/get field
                        int offset = 0;
                        if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Volatile)
                        {
                            i--;
                            offset = 1;
                        }

                        AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, fieldRef.ID));

                        if (instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld)
                            AddInstruction(method, i + 1, processor.Create(OpCodes.Call, SetFieldRef));
                        else
                            AddInstruction(method, i + 1, processor.Create(OpCodes.Call, LoadFieldRef));

                        i = i + 2 + offset; // skip instuction added, and field itself, end of loop will iterate this again

                        // Debug.WriteLine("{0} in Module: {1}, Namespace: {2}, Class: {3}, Name: {4}, Type: {5}", instruction.OpCode, fieldDef.DeclaringType.Scope.Name, namespaces, className, fieldName, fieldType);
                    }

                    else if (instruction.OpCode == OpCodes.Newobj)
                    {
                        var newObj = instruction.Operand as MethodReference;
                        SetClassDependency(classNode, newObj.DeclaringType);
                    }

                    /* Still not really working - goal - to get side by side wpf apps to work
                     * else if (instruction.OpCode == OpCodes.Ldstr && SideBySide)
                    {
                        // rename Pack URIs in WPF so resources can be found with an XRay.. namespace
                        // ex:  "/MyApp;component/views/aboutview.xaml" ->  "/XRay.MyApp;component/views/aboutview.xaml"
                        var packUri = instruction.Operand as String;

                        foreach (var file in XRayedFiles)
                            packUri = packUri.Replace("/" + file.AssemblyName + ";", "/XRay." + file.AssemblyName + ";");
                            //packUri = packUri.Replace(file.AssemblyName, "XRay." + file.AssemblyName);

                        instruction.Operand = packUri;
                    }*/

                } // end iterating through instructions

                // record function was entered
                AddInstruction(method, 0, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                AddInstruction(method, 1, processor.Create(OpCodes.Call, EnterMethodRef));

                // record catches
                if (TrackFlow)
                    foreach (var handler in method.Body.ExceptionHandlers)
                    {
                        if (handler.HandlerType != ExceptionHandlerType.Catch)
                            continue;

                        int i = method.Body.Instructions.IndexOf(handler.HandlerStart);

                        AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                        AddInstruction(method, i + 1, processor.Create(OpCodes.Call, CatchMethodRef));

                        var oldPos = handler.HandlerStart;
                        var newPos = method.Body.Instructions[i];

                        UpdateExceptionHandlerPositions(method, oldPos, newPos);
                    }

                method.Body.OptimizeMacros();
            }
        }
Пример #6
0
        private void RecompileMethod(XNodeOut classNode, MethodDefinition method)
        {
            XNodeOut methodNode = classNode.AddMethod(method);

            // return
            if (method.ReturnType != null)
            {
                var returnNode = SetClassDependency(classNode, method.ReturnType);
                if (returnNode != null)
                    methodNode.ReturnID = returnNode.ID;
            }

            // params
            for (int i = 0; i < method.Parameters.Count; i++)
            {
                var p = method.Parameters[i];

                if (methodNode.ParamIDs == null)
                {
                    methodNode.ParamIDs = new int[method.Parameters.Count];
                    methodNode.ParamNames = new string[method.Parameters.Count];
                }

                var paramNode = SetClassDependency(classNode, p.ParameterType);
                methodNode.ParamIDs[i] = paramNode.ID;
                methodNode.ParamNames[i] = p.Name;
            }

            if (method.Body == null)
                return;

            // local vars
            foreach (var local in method.Body.Variables)
                SetClassDependency(classNode, local.VariableType);

            // expands branches/jumps to support adddresses > 255
            // possibly needed if injecting code into large functions
            // OptimizeMacros at end of the function re-optimizes
            method.Body.SimplifyMacros();

            methodNode.Lines = method.Body.Instructions.Count;

            var processor = method.Body.GetILProcessor();

            for (int i = 0; i < method.Body.Instructions.Count; i++)
            {
                var instruction = method.Body.Instructions[i];

                // record method exited
                if (Build.TrackFlow && instruction.OpCode == OpCodes.Ret)
                {
                    instruction.OpCode = OpCodes.Nop; // any 'goto return' will go to here so method exit gets logged first
                    AddInstruction(method, i + 1, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                    AddInstruction(method, i + 2, processor.Create(OpCodes.Call, ExitMethodRef));
                    AddInstruction(method, i + 3, processor.Create(OpCodes.Ret));

                    i += 3;
                }

                // if we're tracking calls to non-xrayed assemblies
                else if (instruction.OpCode == OpCodes.Call ||
                          instruction.OpCode == OpCodes.Calli ||
                          instruction.OpCode == OpCodes.Callvirt)
                {
                    var call = instruction.Operand as MethodReference;

                    if (call == null)
                    {
                        Debug.WriteLine("Unable to track not xrayed: " + instruction.Operand.ToString());
                        continue;
                    }

                    SetClassDependency(classNode, call.ReturnType);
                    SetClassDependency(classNode, call.DeclaringType);
                    foreach (var p in call.Parameters)
                        SetClassDependency(classNode, p.ParameterType);

                    var calledRef = GetClassRef(call.DeclaringType);

                    var calledNode = calledRef.AddMethod(call);

                    /*if( TrackExternal &&
                        !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System") &&
                        (instruction.Operand as MethodReference).DeclaringType.Namespace != EnterMethodRef.DeclaringType.Namespace )*/
                    if (Build.TrackExternal &&
                        calledNode.External &&
                        calledRef.Name != "XRay")
                    // !(method.Name == "Finalize" && method.DeclaringType.Namespace == "System"))
                    {
                        if (method.Name == ".cctor" && call.Name == "GetTypeFromHandle")
                            continue; // call added to cctor by xray

                        calledNode.Lines = 1;

                        // in function is prefixed by .constrained, wrap enter/exit around those 2 lines
                        int offset = 0;
                        if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Constrained)
                        {
                            i -= 1;
                            offset = 1;
                        }

                        var oldPos = method.Body.Instructions[i];

                        // wrap the call with enter and exit, because enter to an external method may cause
                        // an xrayed method to be called, we want to track the flow of that process
                        int pos = i;
                        AddInstruction(method, pos++, processor.Create(OpCodes.Ldc_I4, calledNode.ID));
                        AddInstruction(method, pos++, processor.Create(OpCodes.Call, EnterMethodRef));

                        // method
                        pos += 1 + offset;

                        AddInstruction(method, pos++, processor.Create(OpCodes.Ldc_I4, calledNode.ID));
                        AddInstruction(method, pos, processor.Create(OpCodes.Call, ExitMethodRef));

                        var newPos = method.Body.Instructions[i];
                        UpdateExceptionHandlerPositions(method, oldPos, newPos);

                        i = pos; // loop end will add 1 putting us right after last added function
                    }
                }

                else if (Build.TrackFields &&
                         (instruction.OpCode == OpCodes.Stfld ||
                          instruction.OpCode == OpCodes.Stsfld ||
                          instruction.OpCode == OpCodes.Ldfld ||
                          instruction.OpCode == OpCodes.Ldflda ||
                          instruction.OpCode == OpCodes.Ldsfld ||
                          instruction.OpCode == OpCodes.Ldsflda))
                {
                    var fieldDef = instruction.Operand as FieldReference;

                    var classRef = GetClassRef(fieldDef.DeclaringType);
                    var fieldRef = classRef.AddField(fieldDef);

                    // some times volitile prefixes set/get field
                    int offset = 0;
                    if (i > 0 && method.Body.Instructions[i - 1].OpCode == OpCodes.Volatile)
                    {
                        i--;
                        offset = 1;
                    }

                    AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, fieldRef.ID));

                    if (instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld)
                        AddInstruction(method, i + 1, processor.Create(OpCodes.Call, SetFieldRef));
                    else
                        AddInstruction(method, i + 1, processor.Create(OpCodes.Call, LoadFieldRef));

                    i = i + 2 + offset; // skip instuction added, and field itself, end of loop will iterate this again

                    // Debug.WriteLine("{0} in Module: {1}, Namespace: {2}, Class: {3}, Name: {4}, Type: {5}", instruction.OpCode, fieldDef.DeclaringType.Scope.Name, namespaces, className, fieldName, fieldType);
                }

                else if (instruction.OpCode == OpCodes.Newobj)
                {
                    var newObj = instruction.Operand as MethodReference;
                    SetClassDependency(classNode, newObj.DeclaringType);
                }

                /* Still not really working - goal - to get side by side wpf apps to work
                 * else if (instruction.OpCode == OpCodes.Ldstr && !Build.ReplaceOriginal)
                {
                    // rename Pack URIs in WPF so resources can be found with an XRay.. namespace
                    // ex:  "/MyApp;component/views/aboutview.xaml" ->  "/XRay.MyApp;component/views/aboutview.xaml"
                    var packUri = instruction.Operand as String;

                    foreach (var file in XRayedFiles)
                        packUri = packUri.Replace("/" + file.AssemblyName + ";", "/XRay." + file.AssemblyName + ";");
                        //packUri = packUri.Replace(file.AssemblyName, "XRay." + file.AssemblyName);

                    instruction.Operand = packUri;
                }*/

            } // end iterating through instructions

            // record function was entered
            if (Build.TrackFunctions)
            {
                AddInstruction(method, 0, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                AddInstruction(method, 1, processor.Create(OpCodes.Call, EnterMethodRef));
            }

            // record catches
            if (Build.TrackFlow)
                foreach (var handler in method.Body.ExceptionHandlers)
                {
                    if (handler.HandlerType != ExceptionHandlerType.Catch)
                        continue;

                    int i = method.Body.Instructions.IndexOf(handler.HandlerStart);

                    AddInstruction(method, i, processor.Create(OpCodes.Ldc_I4, methodNode.ID));
                    AddInstruction(method, i + 1, processor.Create(OpCodes.Call, CatchMethodRef));

                    var oldPos = handler.HandlerStart;
                    var newPos = method.Body.Instructions[i];

                    UpdateExceptionHandlerPositions(method, oldPos, newPos);
                }

            method.Body.OptimizeMacros();
        }