예제 #1
0
        public static void MethodInstrument(string targetAssembly, string src, string dst)
        {
            var inputAssembly = targetAssembly;
            var path          = Path.GetDirectoryName(inputAssembly);

            var assemblyResolver = new DefaultAssemblyResolver();
            var assemblyLocation = Path.GetDirectoryName(inputAssembly);

            assemblyResolver.AddSearchDirectory(assemblyLocation);

            var readerParameters = new ReaderParameters {
                AssemblyResolver = assemblyResolver
            };
            var  writerParameters = new WriterParameters();
            var  origPdb          = Path.ChangeExtension(inputAssembly, "pdb");
            bool existpdb         = false;

            if (File.Exists(origPdb))
            {
                existpdb = true;

                var symbolReaderProvider = new PdbReaderProvider();
                readerParameters.SymbolReaderProvider = symbolReaderProvider;
                readerParameters.ReadSymbols          = true;

                //var symbolWriterProvider = new PdbWriterProvider();
                //writerParameters.SymbolWriterProvider = symbolWriterProvider;
                writerParameters.WriteSymbols = true;
            }
            else
            {
                Console.WriteLine(".pdb file is unavailable.");
            }

            var assemblyDefinition = AssemblyDefinition.ReadAssembly(inputAssembly, readerParameters);
            var module             = assemblyDefinition.MainModule;
            //Mono.Collections.Generic.Collection<ModuleDefinition> modules = assemblyDefinition.Modules;
            //var module2 = ModuleDefinition.ReadModule(inputAssembly, readerParameters);


            //----------------------

            bool isChanged = false;

            //----------------------------

            foreach (var typeDefinition in module.Types)
            {
                if (typeDefinition.IsInterface)
                {
                    continue;
                }

                foreach (var methodDefinition in typeDefinition.Methods)
                {
                    ILProcessor ilprocessor = methodDefinition.Body.GetILProcessor();

                    Mono.Collections.Generic.Collection <Mono.Cecil.Cil.Instruction> allinstructions = methodDefinition.Body.Instructions;
                    int instructioncnt = allinstructions.Count;
                    for (int i = 0; i < instructioncnt; i++)
                    {
                        Mono.Cecil.Cil.Instruction instruction = allinstructions[i];

                        string instructpresent = instruction.ToString();

                        OpCode code        = instruction.OpCode;
                        string codepresent = code.Name;

                        var    operand    = instruction.Operand;
                        string methodname = "";
                        if (operand != null)
                        {
                            methodname = operand.ToString(); //with parameter
                        }

                        if ((code == OpCodes.Callvirt || code == OpCodes.Call) && //codepresent.Contains("call")
                            (isSameMethod(methodname, src)))                   //or (operand as MethodReference == module.Import(src as MethodInfo))
                        {
                            Console.WriteLine(codepresent + " " + methodname); //debug

                            //---------------------- remove --------------------------//
                            /**** remove instruction in case of no replacing method is provided ****/
                            //TODO: 1. I note the stack depth after the call and start removing instructions, beginning with the Code.Call
                            //  and moving backward until I get to that stack depth again.
                            //  2. return value handling

                            ilprocessor.Remove(instruction);
                            instructioncnt--;
                            if (allinstructions[i].OpCode == OpCodes.Pop) //TODO: just for test
                            {
                                ilprocessor.Remove(allinstructions[i]);
                                instructioncnt--;
                            }
                            i--;

                            if (dst == "")
                            {
                                continue;
                            }

                            //---------------------- replace (remove + insert) ------------------------//

                            /**** create new MethodInfo ****/
                            string     m_type = getNamespace(dst);
                            string     m_name = getMethodName(dst);
                            string []  m_para = getParameters(dst);
                            MethodInfo writeLineMethod;

                            //e.g. MethodInfo writeLineMethod = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
                            if (m_para != null)
                            {
                                int    paraCnt     = m_para.Length;
                                Type[] m_para_type = new Type[paraCnt];
                                for (int k = 0; k < paraCnt; k++)
                                {
                                    m_para_type[k] = Type.GetType(m_para[k]);
                                }
                                writeLineMethod = Type.GetType(m_type).GetMethod(m_name, m_para_type);
                            }
                            else //the method does not have input parameters
                            {
                                writeLineMethod = Type.GetType(m_type).GetMethod(m_name, new Type[] { });
                            }

                            if (writeLineMethod == null)
                            {
                                throw new InvalidFileFormatException("no MethodInfo is created -- possibly a wrong method delaration.");
                            }

                            /*** Import the new (e.g. Console.WriteLine() method) ***/
                            MethodReference writeLine;
                            writeLine = module.Import(writeLineMethod); //convert "MethodInfo/MethodDefinition" to "MethodReference"

                            /*** Creates the CIL instruction for calling the new method (e.g. Console.WriteLine(string value) method) ***/
                            Mono.Cecil.Cil.Instruction callWriteLine;
                            callWriteLine = ilprocessor.Create(OpCodes.Call, writeLine);

                            /*** replace old instruction ***/
                            //TODO: 1. for simple-type parameters, create one instance and push to stack;
                            //  otherwise just a null object?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                            //  2. return value handling

                            ilprocessor.InsertAfter(allinstructions[i], callWriteLine);

                            //----------------------------------------------//

                            isChanged = true;
                        }
                    } //foreach instruction of a method
                }     //foreach method of a type/class
            }         //foreach type/class of loaded assembly

            if (isChanged)
            {
                var temporaryFileName = Path.Combine(path, string.Format("{0}_temp{1}", Path.GetFileNameWithoutExtension(inputAssembly),
                                                                         Path.GetExtension(inputAssembly)));
                var backupFileName = Path.Combine(path, string.Format("{0}_orig{1}", Path.GetFileNameWithoutExtension(inputAssembly),
                                                                      Path.GetExtension(inputAssembly)));

                var tmpPdb    = Path.Combine(path, string.Format("{0}_temp{1}", Path.GetFileNameWithoutExtension(inputAssembly), ".pdb"));
                var backupPdb = Path.Combine(path, string.Format("{0}_orig{1}", Path.GetFileNameWithoutExtension(inputAssembly), ".pdb"));

                //write an assembly with symbol file being rewritten
                //module.Write(temporaryFileName, writerParameters);
                assemblyDefinition.Write(temporaryFileName, writerParameters);

                File.Replace(temporaryFileName, inputAssembly, backupFileName);

                if (existpdb)
                {
                    File.Replace(tmpPdb, origPdb, backupPdb);
                }

                //TODO: add snkfile in configuration file and parse ([email protected])
                // If you have access to the key pair, you can ask Cecil to sign it directly.
                //if (snkFile != "")
                //{
                //sn -R inputAssembly snkFile
                //}
            }
        }