Exemplo n.º 1
0
        internal void ScanLines(bool test)
        {
            XIL.Length  = 0;
            CurrentNode = XFile.FileNode;

            if (!test)
            {
                InjectLibrary("XLibrary", "1:0:0:0");
            }

            bool stripSig = false;

            using (StreamReader reader = new StreamReader(ILPathOriginal))
            {
                while (!reader.EndOfStream)
                {
                    string[] line = reader.SplitNextLine(XIL);

                    if (test || line.Length == 0)
                    {
                        continue;
                    }

                    else if (line[0] == ".assembly")
                    {
                        // get last element, if in assemblies, replace with xray version
                        stripSig = false;
                        string assembly = line.Last();


                        // assemblies are referenced externally by xray. prefix, internally namespace names are the same
                        if (Build.Files.Any(f => f.AssemblyName == assembly))
                        {
                            if (!Build.ReplaceOriginal)
                            {
                                line[line.Length - 1] = "XRay." + line.Last();
                                XIL.RemoveLine();
                                XIL.AppendLine(String.Join(" ", line));
                            }

                            stripSig = true;
                        }
                    }

                    // the result dll is changed so a strong sig links need to be removed
                    else if (line[0] == ".publickeytoken")
                    {
                        if (stripSig)
                        {
                            XIL.RemoveLine();
                        }
                    }

                    else if (line[0] == ".file")
                    {
                        // embedded files have a .hash that we won't be messing with, they require a hash
                        stripSig = false;
                    }

                    else if (line[0] == ".hash" && stripSig)
                    {
                        XIL.RemoveLine();

                        if (line[1] != "algorithm")
                        {
                            string nextLine = string.Join(" ", line).FilterComment();

                            while (!nextLine.Contains(")"))
                            {
                                nextLine = reader.ReadLine().FilterComment();
                            }
                        }
                    }

                    // remove assembly's public key
                    else if (line[0] == ".publickey")
                    {
                        XIL.RemoveLine();

                        string nextLine = string.Join(" ", line).FilterComment();;

                        while (!nextLine.Contains(")"))
                        {
                            nextLine = reader.ReadLine().FilterComment();
                        }
                    }

                    else if (line[0] == ".class")
                    {
                        // read the whole class before the {
                        while (!line.Contains("{"))
                        {
                            line = line.Concat(reader.SplitNextLine(XIL)).ToArray();
                        }

                        // right before the class is extended that is the7 name
                        string name = line.TakeWhile(s => s != "extends" && s != "implements" && s != "{").LastOrDefault();

                        // add namespaces, and class, set current app obj to
                        string[] namespaces = name.Split('.');

                        if (!line.Contains("nested"))
                        {
                            CurrentNode = XFile.FileNode;

                            for (int i = 0; i < namespaces.Length - 1; i++)
                            {
                                CurrentNode = CurrentNode.AddNode(namespaces[i], XObjType.Namespace);
                            }
                        }

                        string className = namespaces.Last();
                        int    pos       = className.LastIndexOf('`');
                        if (pos != -1)
                        {
                            className = className.Substring(0, pos);
                        }

                        CurrentNode = CurrentNode.AddNode(className, XObjType.Class);

                        // exclude if we dont track anon classes
                        if (!Build.TrackAnon && className.StartsWith("'"))
                        {
                            CurrentNode.Exclude = true;
                        }
                    }

                    else if (line[0] == ".method")
                    {
                        // read the whole method before the {
                        while (!line.Contains("{"))
                        {
                            line = line.Concat(reader.SplitNextLine(XIL)).ToArray();
                        }

                        // method is the name with the ( in it
                        string name = line.Where(s => s.Contains('(')).LastOrDefault(); //pinvokes can have afdaf( before method name

                        name = name.Substring(0, name.IndexOf('('));

                        CurrentNode = CurrentNode.AddNode(name, XObjType.Method);

                        // dont inject tracking code under these conditions
                        if (line.Contains("abstract") ||
                            line.Where(s => s.StartsWith("pinvokeimpl")).FirstOrDefault() != null ||
                            (line.Contains("runtime") && line.Contains("managed")) || // 'runtime managed' / 'managed internalcall' at end of function indicates the body should be empty
                            (line.Contains("managed") && line.Contains("internalcall")))
                        {
                            CurrentNode.Exclude = true;
                            continue;
                        }

                        // exclude if we dont track anony methods, but dont continue cause entry point could still be inside
                        if (!Build.TrackAnon && name.StartsWith("'"))
                        {
                            CurrentNode.Exclude = true;
                        }

                        // scan for entry, break on .maxstack or }
                        // read the whole method before the {
                        // order method, entry, custom, maxstack, IL_
                        bool entry = false;

                        while (!line.Contains(".maxstack"))
                        {
                            line = reader.SplitNextLine(XIL);
                            Debug.Assert(!line.Contains("}"));

                            // inject gui after .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
                            // if not then thread state will not be set for app's gui
                            if (line.Contains(".entrypoint"))
                            {
                                entry = true;
                            }
                        }

                        if (line.Length > 1 && line[0] == ".maxstack")
                        {
                            XIL.RemoveLine();

                            int maxstack = int.Parse(line[1]);
                            if (maxstack == 0)
                            {
                                maxstack = 1; // for method enter function
                            }
                            // if we need to increase stack further for other added functions,
                            // this is how to do it, checksIndexOfAny  above, and check above this
                            if (entry && maxstack < 2)
                            {
                                maxstack = 2;
                            }

                            // needs to push 1 more item on stack above whats needed for return
                            // elements so that MethodExit can run
                            if (Build.TrackFlow)
                            {
                                maxstack++;
                            }

                            XIL.AppendLine(".maxstack " + maxstack); // increase stack enough for hit function - need room for thread, hit, constructor

                            if (entry)
                            {
                                InjectGui();
                                entry = false;
                            }
                        }

                        if (!CurrentNode.Exclude)
                        {
                            InjectMethodEnter(CurrentNode);
                        }
                    }

                    else if (line[0] == ".property") // ignore for now
                    {
                        while (!line.Contains("}"))
                        {
                            line = reader.SplitNextLine(XIL);
                        }
                    }

                    else if (line[0] == ".field")
                    {
                        //todo - add option to track fields
                        //string name = line.LastOrDefault();

                        //XNodeOut fieldNode = CurrentNode.AddNode(name, XObjType.Field);
                        //fieldNode.Lines = 1;
                    }

                    else if (CurrentNode.ObjType == XObjType.Method)
                    {
                        bool inCatch = false;

                        if (line[0] == "catch")
                        {
                            line    = reader.SplitNextLine(XIL);
                            inCatch = true; // inject after indent set
                        }

                        else if (Build.TrackFlow && !CurrentNode.Exclude && line.Length > 1 && line[1] == "ret")
                        {
                            Debug.Assert(line.Length == 2);

                            XIL.RemoveLine(); // remove ret call

                            XIL.AppendLine();
                            AddLine(line[0] + " nop // XRay - Redirected return address"); // anything jumping to return, will jump here instead and allow us to log exit

                            InjectMethodExit(CurrentNode);

                            AddLine("ret"); // put ret call back in
                        }

                        else if (line.Length > 1 && (Build.TrackFlow || Build.TrackExternal) && line[1].EndsWith(".s"))
                        {
                            // when we insert code we add more instructions, br is the branch instruction
                            // and br.s is the short version allowing a max jump of 255 places which may
                            // not be valid after our injection.  Strip the .s, and run ilasm with /optimize
                            // to add them back in

                            Debug.Assert(line.Length == 3);
                            XIL.RemoveLine();
                            line[1] = line[1].Replace(".s", "");
                            AddLine(string.Join(" ", line) + " // XRay - removed .s");
                        }

                        // external method call tracking
                        if (Build.TrackExternal && Build.TrackFlow && !CurrentNode.Exclude && line.Length > 1 &&
                            (line[1].StartsWith("constrained.") || line[1].StartsWith("call") ||
                             line[1].StartsWith("callvirt") || line[1].StartsWith("calli")))
                        {
                            Debug.Assert(!line[1].StartsWith("calli")); // whats the format of calli?

                            // any line starting with a constrained prefix is immediately followed by a call virt
                            string[] constrainedLine = null;
                            if (line[1].StartsWith("constrained."))
                            {
                                XIL.RemoveLine();                            // save constrained line a inject after external method tracking
                                constrainedLine = line;
                                line            = reader.SplitNextLine(XIL); // read in callvirt
                            }

                            string parse = string.Join(" ", line);

                            // get function name
                            if (!parse.Contains("::"))
                            {
                                continue; // if no :: then caller is accessing a global internal function that is already tracked
                            }
                            int pos = parse.LastIndexOf('(');
                            Debug.Assert(pos != -1);
                            int pos2 = parse.LastIndexOf("::") + 2;
                            Debug.Assert(pos2 != 1 && pos2 < pos);
                            string functionName = parse.Substring(pos2, pos - pos2);

                            parse = parse.Substring(0, pos2 - 2); // cut out what we just parsed

                            // strip template info, read forward mark if in template
                            string withoutTemplate = "";
                            int    inTemplate      = 0;
                            for (int i = 0; i < parse.Length; i++)
                            {
                                if (parse[i] == '<')
                                {
                                    inTemplate++;
                                }
                                else if (parse[i] == '>')
                                {
                                    inTemplate--;
                                }
                                else if (inTemplate == 0)
                                {
                                    withoutTemplate += parse[i];
                                }
                            }

                            parse = withoutTemplate;

                            // now should just be namespace and extern dec to space
                            pos   = parse.LastIndexOf(' ');
                            parse = parse.Substring(pos + 1);

                            // we only care if this function is external
                            if (!parse.StartsWith("["))
                            {
                                continue;
                            }

                            pos = parse.IndexOf("]");
                            string external   = parse.Substring(1, pos - 1);
                            string namespaces = parse.Substring(pos + 1);

                            pos = namespaces.LastIndexOf('`');
                            if (pos != -1)
                            {
                                namespaces = namespaces.Substring(0, pos);
                            }

                            // if already tracked, skip
                            if (Build.Files.Any(f => f.AssemblyName == external))
                            {
                                continue;
                            }

                            // add external file to root
                            XNodeOut node = ExtRoot.Nodes.FirstOrDefault(n => n.Name == external) as XNodeOut;

                            if (node == null)
                            {
                                node = ExtRoot.AddNode(external, XObjType.File);
                            }

                            // traverse or add namespace to root
                            string[] names = namespaces.Split('.');

                            for (int i = 0; i < names.Length; i++)
                            {
                                string name = names[i];

                                XNodeOut next = node.Nodes.FirstOrDefault(n => n.Name == name) as XNodeOut;

                                XObjType objType = (i == names.Length - 1) ? XObjType.Class : XObjType.Namespace;
                                node = next ?? node.AddNode(name, objType);
                            }

                            node       = node.AddNode(functionName, XObjType.Method);
                            node.Lines = 1;


                            // add wrapping for external tracking

                            // remove function
                            XIL.RemoveLine();

                            // inject method enter - re-route IL address to function to ensure it is logged
                            XIL.AppendLine();

                            string address = (constrainedLine != null) ? constrainedLine[0] : line[0];
                            AddLine(address + " nop // XRay - Redirect external method begin");
                            XIL.AppendLine();
                            InjectMethodEnter(node);

                            // add function line - strip IL address
                            if (constrainedLine != null)
                            {
                                XIL.AppendLine(string.Join(" ", constrainedLine.Skip(1).ToArray()));
                            }

                            string nextLine = string.Join(" ", line.Skip(1).ToArray());
                            XIL.AppendLine(nextLine);

                            // read over rest of function until ')'
                            while (!nextLine.Contains(")"))
                            {
                                nextLine = reader.ReadLine();
                                XIL.AppendLine(nextLine);
                            }

                            // inject exit
                            InjectMethodExit(node);
                        }


                        if (line[0] == "{") // try, catch, finallys, inside one another
                        {
                            CurrentNode.Indent++;
                            CurrentNode.IndentString += "  ";

                            if (inCatch && Build.TrackFlow && !CurrentNode.Exclude)
                            {
                                InjectMethodCatch(CurrentNode);
                            }
                        }

                        else if (line[0] == "}") // try, catch, finallys, inside one another
                        {
                            if (CurrentNode.Indent == 0)
                            {
                                CurrentNode = CurrentNode.Parent as XNodeOut;
                            }

                            else
                            {
                                CurrentNode.Indent--;
                                CurrentNode.IndentString = CurrentNode.IndentString.Substring(2);
                            }
                        }

                        CurrentNode.Lines++;
                    }

                    else if (CurrentNode.ObjType == XObjType.Class)
                    {
                        if (line[0] == "}") // try, catch, finallys, inside one another
                        {
                            CurrentNode = CurrentNode.Parent as XNodeOut;
                        }
                    }
                }
            }

            // change method call refs from old assembly to new
            if (!Build.ReplaceOriginal)
            {
                foreach (string assembly in Build.Files.Select(f => f.AssemblyName))
                {
                    XIL.Replace("[" + assembly + "]", "[XRay." + assembly + "]");
                }
            }
        }