Example #1
0
        /// <summary>
        /// Replace relative offset in the disassembled instruction with the true target RVA.
        /// </summary>
        /// <param name="instruction">Disassembled instruction to modify</param>
        /// <param name="target">Target string to replace offset with</param>
        /// <param name="rtf">Runtime function being disassembled</param>
        private void ReplaceRelativeOffset(ref string instruction, int target, RuntimeFunction rtf)
        {
            int outputOffset = target;

            if (_options.Naked)
            {
                outputOffset -= rtf.StartAddress;
            }
            ReplaceRelativeOffset(ref instruction, string.Format("0x{0:X4}", outputOffset), rtf);
        }
Example #2
0
        /// <summary>
        /// Dumps disassembly and register liveness
        /// </summary>
        internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset)
        {
            int    indent       = (_options.Naked ? _options.HideOffsets ? 4 : 11 : 32);
            string indentString = new string(' ', indent);
            int    rtfOffset    = 0;
            int    codeOffset   = rtf.CodeOffset;

            while (rtfOffset < rtf.Size)
            {
                string instr;
                int    instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr);

                if (_r2r.Machine == Machine.Amd64 && ((ILCompiler.Reflection.ReadyToRun.Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes.ContainsKey(codeOffset))
                {
                    List <ILCompiler.Reflection.ReadyToRun.Amd64.UnwindCode> codes = ((ILCompiler.Reflection.ReadyToRun.Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes[codeOffset];
                    foreach (ILCompiler.Reflection.ReadyToRun.Amd64.UnwindCode code in codes)
                    {
                        _writer.Write($"{indentString}{code.UnwindOp} {code.OpInfoStr}");
                        if (code.NextFrameOffset != -1)
                        {
                            _writer.WriteLine($"{indentString}{code.NextFrameOffset}");
                        }
                        _writer.WriteLine();
                    }
                }

                if (!_options.HideTransitions && rtf.Method.GcInfo?.Transitions != null && rtf.Method.GcInfo.Transitions.TryGetValue(codeOffset, out List <BaseGcTransition> transitionsForOffset))
                {
                    string[] formattedTransitions = new string[transitionsForOffset.Count];
                    for (int transitionIndex = 0; transitionIndex < formattedTransitions.Length; transitionIndex++)
                    {
                        formattedTransitions[transitionIndex] = transitionsForOffset[transitionIndex].ToString();
                    }
                    if (_options.Normalize)
                    {
                        Array.Sort(formattedTransitions);
                    }
                    foreach (string transition in formattedTransitions)
                    {
                        _writer.WriteLine($"{indentString}{transition}");
                    }
                }

                /* According to https://msdn.microsoft.com/en-us/library/ck9asaa9.aspx and src/vm/gcinfodecoder.cpp
                 * UnwindCode and GcTransition CodeOffsets are encoded with a -1 adjustment (that is, it's the offset of the start of the next instruction)
                 */
                _writer.Write(instr);

                rtfOffset  += instrSize;
                codeOffset += instrSize;
            }
        }
Example #3
0
        /// <summary>
        /// Get the RVAs of the runtime functions for each method
        /// </summary>
        private void ParseRuntimeFunctions(bool[] isEntryPoint, int runtimeFunctionOffset, int runtimeFunctionSize)
        {
            int curOffset = 0;

            foreach (R2RMethod method in R2RMethods)
            {
                int runtimeFunctionId = method.EntryPointRuntimeFunctionId;
                if (runtimeFunctionId == -1)
                {
                    continue;
                }
                curOffset = runtimeFunctionOffset + runtimeFunctionId * runtimeFunctionSize;
                GcInfo gcInfo     = null;
                int    codeOffset = 0;
                do
                {
                    int startRva = NativeReader.ReadInt32(Image, ref curOffset);
                    int endRva   = -1;
                    if (Machine == Machine.Amd64)
                    {
                        endRva = NativeReader.ReadInt32(Image, ref curOffset);
                    }
                    int unwindRva    = NativeReader.ReadInt32(Image, ref curOffset);
                    int unwindOffset = GetOffset(unwindRva);

                    BaseUnwindInfo unwindInfo = null;
                    if (Machine == Machine.Amd64)
                    {
                        unwindInfo = new Amd64.UnwindInfo(Image, unwindOffset);
                        if (isEntryPoint[runtimeFunctionId])
                        {
                            gcInfo = new GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
                        }
                    }
                    else if (Machine == Machine.I386)
                    {
                        unwindInfo = new x86.UnwindInfo(Image, unwindOffset);
                        if (isEntryPoint[runtimeFunctionId])
                        {
                            //gcInfo = new GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
                        }
                    }

                    RuntimeFunction rtf = new RuntimeFunction(runtimeFunctionId, startRva, endRva, unwindRva, codeOffset, method, unwindInfo, gcInfo);
                    method.RuntimeFunctions.Add(rtf);
                    runtimeFunctionId++;
                    codeOffset += rtf.Size;
                }while (runtimeFunctionId < isEntryPoint.Length && !isEntryPoint[runtimeFunctionId]);
            }
        }
Example #4
0
        /// <summary>
        /// Check whether a given target RVA corresponds to another runtime function within the same method.
        /// </summary>
        /// <param name="rva">Target RVA to analyze</param>
        /// <param name="rtf">Runtime function being disassembled</param>
        /// <param name="runtimeFunctionIndex">Output runtime function index if found, -1 otherwise</param>
        /// <returns>true if target runtime function has been found, false otherwise</returns>
        private bool IsAnotherRuntimeFunctionWithinMethod(int rva, RuntimeFunction rtf, out int runtimeFunctionIndex)
        {
            for (int rtfIndex = 0; rtfIndex < rtf.Method.RuntimeFunctions.Count; rtfIndex++)
            {
                if (rva == rtf.Method.RuntimeFunctions[rtfIndex].StartAddress)
                {
                    runtimeFunctionIndex = rtfIndex;
                    return(true);
                }
            }

            runtimeFunctionIndex = -1;
            return(false);
        }
Example #5
0
        public unsafe static int GetInstruction(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, int rtfOffset, byte[] image, out string instr)
        {
            int instrSize = 1;

            fixed(byte *p = image)
            {
                IntPtr ptr = (IntPtr)(p + imageOffset + rtfOffset);

                instrSize = DumpInstruction(Disasm, (ulong)(rtf.StartAddress + rtfOffset), ptr, rtf.Size);
            }

            IntPtr pBuffer = GetOutputBuffer();

            instr = Marshal.PtrToStringAnsi(pBuffer);
            return(instrSize);
        }
Example #6
0
 /// <summary>
 /// Dumps one runtime function.
 /// </summary>
 private void DumpRuntimeFunction(R2RReader r2r, RuntimeFunction rtf)
 {
     if (_disasm)
     {
         _writer.WriteLine($"Id: {rtf.Id}");
         CoreDisTools.DumpCodeBlock(_disassembler, rtf.StartAddress, r2r.GetOffset(rtf.StartAddress), r2r.Image, rtf.Size);
     }
     else
     {
         _writer.Write($"{rtf}");
     }
     if (_raw)
     {
         DumpBytes(r2r, rtf.StartAddress, (uint)rtf.Size);
     }
     _writer.WriteLine();
 }
Example #7
0
        /// <summary>
        /// X86 disassembler has a bug in decoding absolute indirections, mistaking them for RIP-relative indirections
        /// </summary>
        /// <param name="rtf">Runtime function</param>
        /// <param name="imageOffset">Offset within the image byte array</param>
        /// <param name="rtfOffset">Offset within the runtime function</param>
        /// <param name="instrSize">Instruction size</param>
        /// <param name="instruction">Textual representation of the instruction</param>
        private void ProbeX86Quirks(RuntimeFunction rtf, int imageOffset, int rtfOffset, int instrSize, ref string instruction)
        {
            int leftBracket;
            int rightBracketPlusOne;
            int absoluteAddress;

            if (TryParseRipRelative(instruction, out leftBracket, out rightBracketPlusOne, out absoluteAddress))
            {
                int target = absoluteAddress - (int)_reader.ImageBase;

                StringBuilder translated = new StringBuilder();
                translated.Append(instruction, 0, leftBracket);

                _reader.ImportCellNames.TryGetValue(target, out string targetName);

                if (_options.Naked)
                {
                    if (targetName != null)
                    {
                        translated.AppendFormat("[{0}]", targetName);
                    }
                    else
                    {
                        translated.AppendFormat("[0x{0:x4}]", target);
                    }
                    translated.Append(instruction, rightBracketPlusOne, instruction.Length - rightBracketPlusOne);
                }
                else
                {
                    translated.AppendFormat("[0x{0:x4}]", target);
                    translated.Append(instruction, rightBracketPlusOne, instruction.Length - rightBracketPlusOne);
                    if (targetName != null)
                    {
                        AppendComment(translated, targetName);
                    }
                }

                translated.Append(instruction, rightBracketPlusOne, instruction.Length - rightBracketPlusOne);
                instruction = translated.ToString();
            }
            else
            {
                ProbeCommonIntelQuirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction);
            }
        }
Example #8
0
        /// <summary>
        /// Dumps disassembly and register liveness
        /// </summary>
        internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset)
        {
            int    indent       = (_options.Naked ? 11 : 32);
            string indentString = new string(' ', indent);
            int    rtfOffset    = 0;
            int    codeOffset   = rtf.CodeOffset;

            while (rtfOffset < rtf.Size)
            {
                string instr;
                int    instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr);

                if (_r2r.Machine == Machine.Amd64 && ((ILCompiler.Reflection.ReadyToRun.Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes.ContainsKey(codeOffset))
                {
                    List <ILCompiler.Reflection.ReadyToRun.Amd64.UnwindCode> codes = ((ILCompiler.Reflection.ReadyToRun.Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes[codeOffset];
                    foreach (ILCompiler.Reflection.ReadyToRun.Amd64.UnwindCode code in codes)
                    {
                        _writer.Write($"{indentString}{code.UnwindOp} {code.OpInfoStr}");
                        if (code.NextFrameOffset != -1)
                        {
                            _writer.WriteLine($"{indentString}{code.NextFrameOffset}");
                        }
                        _writer.WriteLine();
                    }
                }

                if (rtf.Method.GcInfo?.Transitions != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset))
                {
                    foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])
                    {
                        _writer.WriteLine($"{indentString}{transition}");
                    }
                }

                /* According to https://msdn.microsoft.com/en-us/library/ck9asaa9.aspx and src/vm/gcinfodecoder.cpp
                 * UnwindCode and GcTransition CodeOffsets are encoded with a -1 adjustment (that is, it's the offset of the start of the next instruction)
                 */
                _writer.Write(instr);

                CoreDisTools.ClearOutputBuffer();
                rtfOffset  += instrSize;
                codeOffset += instrSize;
            }
        }
Example #9
0
        /// <summary>
        /// Improves disassembler output for ARM64.
        /// </summary>
        /// <param name="rtf">Runtime function</param>
        /// <param name="imageOffset">Offset within the image byte array</param>
        /// <param name="rtfOffset">Offset within the runtime function</param>
        /// <param name="instruction">Textual representation of the instruction</param>
        private void ProbeArm64Quirks(RuntimeFunction rtf, int imageOffset, int rtfOffset, ref string instruction)
        {
            const int InstructionSize = 4;

            // The list of PC-relative instructions: ADR, ADRP, B.cond, B, BL, CBNZ, CBZ, TBNZ, TBZ.

            // Handle an ADR instruction
            if (IsArm64AdrInstruction(imageOffset + rtfOffset, out int adrOffset))
            {
                ReplaceRelativeOffset(ref instruction, rtf.StartAddress + rtfOffset + adrOffset, rtf);
            }
            // Handle the ADRP instruction of an ADRP+ADD pair
            else if (IsArm64AdrpInstruction(imageOffset + rtfOffset, out uint adrpRegister, out long pageOffset))
            {
                int  pc         = rtf.StartAddress + rtfOffset;
                long targetPage = (pc & ~0xfff) + pageOffset;

                if ((0 <= targetPage) && (targetPage <= int.MaxValue) &&
                    IsArm64AddImmediate64NoShiftInstruction(imageOffset + rtfOffset + InstructionSize, out uint addSrcRegister, out uint offset) &&
                    (addSrcRegister == adrpRegister))
                {
                    int target = (int)targetPage + (int)offset;
                    _addInstructionOffset = imageOffset + rtfOffset + 4;
                    _addInstructionTarget = target;

                    int hashPos    = instruction.LastIndexOf('#');
                    var translated = new StringBuilder();
                    translated.Append(instruction, 0, hashPos);

                    _reader.ImportCellNames.TryGetValue(target, out string targetName);

                    if (_options.Naked && (targetName != null))
                    {
                        translated.Append("import_hi21{").Append(targetName).Append('}');
                    }
                    else
                    {
                        translated.AppendFormat("#0x{0:x4}", targetPage);
                    }

                    instruction = translated.ToString();
                }
            }
Example #10
0
        // <summary>
        /// For each query in the list of queries, dump a runtime function by id.
        /// The method containing the runtime function gets outputted, along with the single runtime function that was searched
        /// </summary>
        /// <param name="r2r">Contains all the extracted info about the ReadyToRun image</param>
        /// <param name="queries">The ids to search for</param>
        private void QueryRuntimeFunction(ReadyToRunReader r2r, IEnumerable <string> queries)
        {
            if (queries.Any())
            {
                _dumper.WriteDivider("Runtime Functions");
            }
            foreach (string q in queries)
            {
                RuntimeFunction rtf = FindRuntimeFunction(r2r, ArgStringToInt(q));

                if (rtf == null)
                {
                    WriteWarning("Unable to find by id " + q);
                    continue;
                }
                _dumper.DumpQueryCount(q.ToString(), "Runtime Function", 1);
                _dumper.DumpRuntimeFunction(rtf);
            }
        }
Example #11
0
        // <summary>
        /// For each query in the list of queries, search for a runtime function by id.
        /// The method containing the runtime function gets outputted, along with the single runtime function that was searched
        /// </summary>
        /// <param name="r2r">Contains all the extracted info about the ReadyToRun image</param>
        /// <param name="queries">The ids to search for</param>
        private void QueryRuntimeFunction(R2RReader r2r, IReadOnlyList <int> queries)
        {
            if (queries.Count > 0)
            {
                _dumper.WriteDivider("Runtime Functions");
            }
            foreach (int q in queries)
            {
                RuntimeFunction rtf = FindRuntimeFunction(r2r, q);

                if (rtf == null)
                {
                    WriteWarning("Unable to find by id " + q);
                    continue;
                }
                XmlNode queryNode = _dumper.DumpQueryCount(q.ToString(), "Runtime Function", 1);
                _dumper.DumpRuntimeFunction(rtf, queryNode);
            }
        }
Example #12
0
        // <summary>
        /// For each query in the list of queries, search for a runtime function by id.
        /// The method containing the runtime function gets outputted, along with the single runtime function that was searched
        /// </summary>
        /// <param name="r2r">Contains all the extracted info about the ReadyToRun image</param>
        /// <param name="queries">The ids to search for</param>
        private void QueryRuntimeFunction(R2RReader r2r, IReadOnlyList <int> queries)
        {
            if (queries.Count > 0)
            {
                WriteDivider("Runtime Functions");
            }
            foreach (int q in queries)
            {
                RuntimeFunction rtf = FindRuntimeFunction(r2r, q);

                if (rtf == null)
                {
                    WriteWarning("Unable to find by id " + q);
                    continue;
                }
                _writer.WriteLine(rtf.Method.SignatureString);
                DumpRuntimeFunction(r2r, rtf);
            }
        }
Example #13
0
        /// <summary>
        /// Translate RIP-relative offsets to RVA's and convert cell addresses to symbol names
        /// </summary>
        /// <param name="rtf">Runtime function</param>
        /// <param name="imageOffset">Offset within the image byte array</param>
        /// <param name="rtfOffset">Offset within the runtime function</param>
        /// <param name="instrSize">Instruction size</param>
        /// <param name="instruction">Textual representation of the instruction</param>
        private void ProbeX64Quirks(RuntimeFunction rtf, int imageOffset, int rtfOffset, int instrSize, ref string instruction)
        {
            int leftBracket;
            int rightBracketPlusOne;
            int displacement;

            if (TryParseRipRelative(instruction, out leftBracket, out rightBracketPlusOne, out displacement))
            {
                int           target     = rtf.StartAddress + rtfOffset + instrSize + displacement;
                int           newline    = instruction.LastIndexOf('\n');
                StringBuilder translated = new StringBuilder();
                translated.Append(instruction, 0, leftBracket);
                if (_options.Naked)
                {
                    String targetName;
                    if (_reader.ImportCellNames.TryGetValue(target, out targetName))
                    {
                        translated.AppendFormat("[{0}]", targetName);
                    }
                    else
                    {
                        translated.AppendFormat("[0x{0:x4}]", target);
                    }
                }
                else
                {
                    translated.AppendFormat("[0x{0:x4}]", target);

                    AppendImportCellName(translated, target);
                }

                translated.Append(instruction, rightBracketPlusOne, newline - rightBracketPlusOne);

                translated.Append(instruction, newline, instruction.Length - newline);
                instruction = translated.ToString();
            }
            else
            {
                ProbeCommonIntelQuirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction);
            }
        }
Example #14
0
        internal unsafe override void DumpDisasm(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, byte[] image, XmlNode parentNode = null)
        {
            int rtfOffset  = 0;
            int codeOffset = rtf.CodeOffset;

            while (rtfOffset < rtf.Size)
            {
                string instr;
                int    instrSize = CoreDisTools.GetInstruction(Disasm, rtf, imageOffset, rtfOffset, image, out instr);

                _writer.Write(instr);
                if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset))
                {
                    _writer.WriteLine($"\t\t\t\t{rtf.Method.GcInfo.Transitions[codeOffset].GetSlotState(rtf.Method.GcInfo.SlotTable)}");
                }

                CoreDisTools.ClearOutputBuffer();
                rtfOffset  += instrSize;
                codeOffset += instrSize;
            }
        }
Example #15
0
 /// <summary>
 /// Probe quirks that have the same behavior for X86 and X64.
 /// </summary>
 /// <param name="rtf">Runtime function</param>
 /// <param name="imageOffset">Offset within the image byte array</param>
 /// <param name="rtfOffset">Offset within the runtime function</param>
 /// <param name="instrSize">Instruction size</param>
 /// <param name="instruction">Textual representation of the instruction</param>
 private void ProbeCommonIntelQuirks(RuntimeFunction rtf, int imageOffset, int rtfOffset, int instrSize, ref string instruction)
 {
     if (instrSize == 2 && IsIntelJumpInstructionWithByteOffset(imageOffset + rtfOffset))
     {
         sbyte offset = (sbyte)_reader.Image[imageOffset + rtfOffset + 1];
         int   target = rtf.StartAddress + rtfOffset + instrSize + offset;
         ReplaceRelativeOffset(ref instruction, target, rtf);
     }
     else if (instrSize == 5 && IsIntel1ByteJumpInstructionWithIntOffset(imageOffset + rtfOffset))
     {
         int offset = BitConverter.ToInt32(_reader.Image, imageOffset + rtfOffset + 1);
         int target = rtf.StartAddress + rtfOffset + instrSize + offset;
         ReplaceRelativeOffset(ref instruction, target, rtf);
     }
     else if (instrSize == 6 && IsIntel2ByteJumpInstructionWithIntOffset(imageOffset + rtfOffset))
     {
         int offset = BitConverter.ToInt32(_reader.Image, imageOffset + rtfOffset + 2);
         int target = rtf.StartAddress + rtfOffset + instrSize + offset;
         ReplaceRelativeOffset(ref instruction, target, rtf);
     }
 }
Example #16
0
        /// <summary>
        /// Dumps disassembly and register liveness
        /// </summary>
        internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null)
        {
            int rtfOffset  = 0;
            int codeOffset = rtf.CodeOffset;

            while (rtfOffset < rtf.Size)
            {
                string instr;
                int    instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr);

                _writer.Write(instr);

                if (_r2r.Machine == Machine.Amd64 && ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes.ContainsKey(codeOffset))
                {
                    List <Amd64.UnwindCode> codes = ((Amd64.UnwindInfo)rtf.UnwindInfo).UnwindCodes[codeOffset];
                    foreach (Amd64.UnwindCode code in codes)
                    {
                        _writer.Write($"\t\t\t\t{code.UnwindOp} {code.OpInfoStr}");
                        if (code.NextFrameOffset != -1)
                        {
                            _writer.WriteLine($" - {code.NextFrameOffset}");
                        }
                        _writer.WriteLine();
                    }
                }

                if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset))
                {
                    foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])
                    {
                        _writer.WriteLine($"\t\t\t\t{transition.ToString()}");
                    }
                }

                CoreDisTools.ClearOutputBuffer();
                rtfOffset  += instrSize;
                codeOffset += instrSize;
            }
        }
Example #17
0
        internal unsafe override void DumpDisasm(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, byte[] image, XmlNode parentNode)
        {
            int rtfOffset  = 0;
            int codeOffset = rtf.CodeOffset;
            Dictionary <int, GcInfo.GcTransition> transitions = rtf.Method.GcInfo.Transitions;
            GcSlotTable slotTable = rtf.Method.GcInfo.SlotTable;

            while (rtfOffset < rtf.Size)
            {
                string instr;
                int    instrSize = CoreDisTools.GetInstruction(Disasm, rtf, imageOffset, rtfOffset, image, out instr);

                AddXMLNode("offset" + codeOffset, instr, parentNode, $"{codeOffset}");
                if (transitions.ContainsKey(codeOffset))
                {
                    AddXMLNode("Transition", transitions[codeOffset].GetSlotState(slotTable), parentNode, $"{codeOffset}");
                }

                CoreDisTools.ClearOutputBuffer();
                rtfOffset  += instrSize;
                codeOffset += instrSize;
            }
        }
Example #18
0
        /// <summary>
        /// Parse a single instruction and return the RVA of the next instruction.
        /// </summary>
        /// <param name="rtf">Runtime function to parse</param>
        /// <param name="imageOffset">Offset within the PE image byte array</param>
        /// <param name="rtfOffset">Instruction offset within the runtime function</param>
        /// <param name="instruction">Output text representation of the instruction</param>
        /// <returns>Instruction size in bytes - i.o.w. the next instruction starts at rtfOffset + (the return value)</returns>
        public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, out string instruction)
        {
            if (_disasm == IntPtr.Zero)
            {
                instruction = "";
                return(rtf.Size);
            }

            int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _reader.Image, out instruction);

            instruction = instruction.Replace('\t', ' ');

            switch (_reader.Machine)
            {
            case Machine.Amd64:
            case Machine.IA64:
                ProbeX64Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction);
                break;

            case Machine.I386:
                ProbeX86Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction);
                break;

            case Machine.ArmThumb2:
            case Machine.Thumb:
                break;

            case Machine.Arm64:
                break;

            default:
                throw new NotImplementedException();
            }

            instruction = instruction.Replace("\n", Environment.NewLine);
            return(instrSize);
        }
Example #19
0
        /// <summary>
        /// Dumps disassembly and register liveness
        /// </summary>
        internal override void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null)
        {
            int rtfOffset  = 0;
            int codeOffset = rtf.CodeOffset;

            while (rtfOffset < rtf.Size)
            {
                string instr;
                int    instrSize = _disassembler.GetInstruction(rtf, imageOffset, rtfOffset, out instr);

                _writer.Write(instr);
                if (rtf.Method.GcInfo != null && rtf.Method.GcInfo.Transitions.ContainsKey(codeOffset))
                {
                    foreach (BaseGcTransition transition in rtf.Method.GcInfo.Transitions[codeOffset])
                    {
                        _writer.WriteLine($"\t\t\t\t{transition.ToString()}");
                    }
                }

                CoreDisTools.ClearOutputBuffer();
                rtfOffset  += instrSize;
                codeOffset += instrSize;
            }
        }
Example #20
0
        /// <summary>
        /// Dumps one runtime function.
        /// </summary>
        private void DumpRuntimeFunction(R2RReader r2r, RuntimeFunction rtf)
        {
            _writer.Write($"{rtf}");
            if (_disasm)
            {
                _writer.Write(CoreDisTools.GetCodeBlock(_disassembler, rtf, r2r.GetOffset(rtf.StartAddress), r2r.Image));
            }

            if (_raw)
            {
                _writer.WriteLine("Raw Bytes:");
                DumpBytes(r2r, rtf.StartAddress, (uint)rtf.Size);
            }
            if (_unwind)
            {
                _writer.WriteLine("UnwindInfo:");
                _writer.Write(rtf.UnwindInfo);
                if (_raw)
                {
                    DumpBytes(r2r, rtf.UnwindRVA, (uint)rtf.UnwindInfo.Size);
                }
            }
            _writer.WriteLine();
        }
Example #21
0
 abstract internal unsafe void DumpDisasm(IntPtr Disasm, RuntimeFunction rtf, int imageOffset, byte[] image, XmlNode parentNode = null);
Example #22
0
 abstract internal void DumpRuntimeFunction(RuntimeFunction rtf, XmlNode parentNode = null);
Example #23
0
        /// <summary>
        /// Get the RVAs of the runtime functions for each method
        /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/zap/zapcode.cpp">ZapUnwindInfo::Save</a>
        /// </summary>
        private void ParseRuntimeFunctions(bool[] isEntryPoint, int runtimeFunctionOffset, int runtimeFunctionSize)
        {
            int curOffset = 0;

            foreach (R2RMethod method in R2RMethods)
            {
                int runtimeFunctionId = method.EntryPointRuntimeFunctionId;
                if (runtimeFunctionId == -1)
                {
                    continue;
                }
                curOffset = runtimeFunctionOffset + runtimeFunctionId * runtimeFunctionSize;
                BaseGcInfo gcInfo     = null;
                int        codeOffset = 0;
                do
                {
                    int startRva = NativeReader.ReadInt32(Image, ref curOffset);
                    int endRva   = -1;
                    if (Machine == Machine.Amd64)
                    {
                        endRva = NativeReader.ReadInt32(Image, ref curOffset);
                    }
                    int unwindRva    = NativeReader.ReadInt32(Image, ref curOffset);
                    int unwindOffset = GetOffset(unwindRva);

                    BaseUnwindInfo unwindInfo = null;
                    if (Machine == Machine.Amd64)
                    {
                        unwindInfo = new Amd64.UnwindInfo(Image, unwindOffset);
                        if (isEntryPoint[runtimeFunctionId])
                        {
                            gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
                        }
                    }
                    else if (Machine == Machine.I386)
                    {
                        unwindInfo = new x86.UnwindInfo(Image, unwindOffset);
                        if (isEntryPoint[runtimeFunctionId])
                        {
                            gcInfo = new x86.GcInfo(Image, unwindOffset, Machine, R2RHeader.MajorVersion);
                        }
                    }
                    else if (Machine == Machine.ArmThumb2)
                    {
                        unwindInfo = new Arm.UnwindInfo(Image, unwindOffset);
                        if (isEntryPoint[runtimeFunctionId])
                        {
                            gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion); // Arm and Arm64 use the same GcInfo format as x64
                        }
                    }
                    else if (Machine == Machine.Arm64)
                    {
                        unwindInfo = new Arm64.UnwindInfo(Image, unwindOffset);
                        if (isEntryPoint[runtimeFunctionId])
                        {
                            gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, R2RHeader.MajorVersion);
                        }
                    }

                    EHInfo ehInfo = null;

                    EHInfoLocation ehInfoLocation;
                    if (EHLookupTable != null && EHLookupTable.RuntimeFunctionToEHInfoMap.TryGetValue(startRva, out ehInfoLocation))
                    {
                        ehInfo = new EHInfo(this, ehInfoLocation.EHInfoRVA, startRva, GetOffset(ehInfoLocation.EHInfoRVA), ehInfoLocation.ClauseCount);
                    }

                    RuntimeFunction rtf = new RuntimeFunction(
                        runtimeFunctionId,
                        startRva,
                        endRva,
                        unwindRva,
                        codeOffset,
                        method,
                        unwindInfo,
                        gcInfo,
                        ehInfo,
                        _runtimeFunctionToDebugInfo.GetValueOrDefault(runtimeFunctionId));

                    method.RuntimeFunctions.Add(rtf);
                    runtimeFunctionId++;
                    codeOffset += rtf.Size;
                }while (runtimeFunctionId < isEntryPoint.Length && !isEntryPoint[runtimeFunctionId]);
            }
        }
Example #24
0
        /// <summary>
        /// Parse and dump a single instruction and return its size in bytes.
        /// </summary>
        /// <param name="rtf">Runtime function to parse</param>
        /// <param name="imageOffset">Offset within the PE image byte array</param>
        /// <param name="rtfOffset">Instruction offset within the runtime function</param>
        /// <param name="instruction">Output text representation of the instruction</param>
        /// <returns>Instruction size in bytes - i.o.w. the next instruction starts at rtfOffset + (the return value)</returns>
        public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, out string instruction)
        {
            if (_disasm == IntPtr.Zero)
            {
                instruction = "";
                return(rtf.Size);
            }

            int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _reader.Image, out instruction);

            // CoreDisTools dumps instructions in the following format:
            //
            //      address: bytes [padding] \t mnemonic [\t operands] \n
            //
            // However, due to an LLVM issue regarding instruction prefixes (https://bugs.llvm.org/show_bug.cgi?id=7709),
            // multiple lines may be returned for a single x86/x64 instruction.

            var builder = new StringBuilder();
            int lineNum = 0;
            // The start index of the last line in builder
            int lineStartIndex = 0;

            // Remove this foreach wrapper and line* variables after the aforementioned LLVM issue is fixed
            foreach (string line in instruction.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries))
            {
                int colonIndex = line.IndexOf(':');
                int tab1Index  = line.IndexOf('\t');

                if ((0 < colonIndex) && (colonIndex < tab1Index))
                {
                    // First handle the address and the byte dump
                    if (_options.Naked)
                    {
                        if (!_options.HideOffsets)
                        {
                            // All lines but the last one must represent single-byte prefixes, so add lineNum to the offset
                            builder.Append($"{rtf.CodeOffset + rtfOffset + lineNum,8:x4}:");
                        }
                    }
                    else
                    {
                        if (_reader.Machine == Machine.Arm64)
                        {
                            // Replace " hh hh hh hh " byte dump with " hhhhhhhh ".
                            // CoreDisTools should be fixed to dump bytes this way for ARM64.
                            uint instructionBytes = BitConverter.ToUInt32(_reader.Image, imageOffset + rtfOffset);
                            builder.Append(line, 0, colonIndex + 1);
                            builder.Append(' ');
                            builder.Append(instructionBytes.ToString("x8"));
                        }
                        else
                        {
                            // Copy the offset and the byte dump
                            int byteDumpEndIndex = tab1Index;
                            do
                            {
                                byteDumpEndIndex--;
                            }while (line[byteDumpEndIndex] == ' ');
                            builder.Append(line, 0, byteDumpEndIndex + 1);
                        }
                        builder.Append(' ');
                    }

                    // Now handle the mnemonic and operands. Ensure proper indentation for the mnemonic.
                    EnsureIndentation(builder, lineStartIndex, MnemonicIndentation);

                    int tab2Index = line.IndexOf('\t', tab1Index + 1);
                    if (tab2Index >= 0)
                    {
                        // Copy everything between the first and the second tabs
                        builder.Append(line, tab1Index + 1, tab2Index - tab1Index - 1);
                        // Ensure proper indentation for the operands
                        EnsureIndentation(builder, lineStartIndex, OperandsIndentation);
                        int afterTab2Index = tab2Index + 1;

                        // Work around an LLVM issue causing an extra space to be output before operands;
                        // see https://reviews.llvm.org/D35946.
                        if ((afterTab2Index < line.Length) &&
                            ((line[afterTab2Index] == ' ') || (line[afterTab2Index] == '\t')))
                        {
                            afterTab2Index++;
                        }

                        // Copy everything after the second tab
                        int savedLength = builder.Length;
                        builder.Append(line, afterTab2Index, line.Length - afterTab2Index);
                        // There should be no extra tabs. Should we encounter them, replace them with a single space.
                        if (line.IndexOf('\t', afterTab2Index) >= 0)
                        {
                            builder.Replace('\t', ' ', savedLength, builder.Length - savedLength);
                        }
                    }
                    else
                    {
                        // Copy everything after the first tab
                        builder.Append(line, tab1Index + 1, line.Length - tab1Index - 1);
                    }
                }
                else
                {
                    // Should not happen. Just replace tabs with spaces.
                    builder.Append(line.Replace('\t', ' '));
                }

                string translatedLine      = builder.ToString(lineStartIndex, builder.Length - lineStartIndex);
                string fixedTranslatedLine = translatedLine;

                switch (_reader.Machine)
                {
                case Machine.Amd64:
                    ProbeX64Quirks(rtf, imageOffset, rtfOffset, instrSize, ref fixedTranslatedLine);
                    break;

                case Machine.I386:
                    ProbeX86Quirks(rtf, imageOffset, rtfOffset, instrSize, ref fixedTranslatedLine);
                    break;

                case Machine.Arm64:
                    ProbeArm64Quirks(rtf, imageOffset, rtfOffset, ref fixedTranslatedLine);
                    break;

                case Machine.ArmThumb2:
                    break;

                default:
                    break;
                }

                // If the translated line has been changed, replace it in the builder
                if (!object.ReferenceEquals(fixedTranslatedLine, translatedLine))
                {
                    builder.Length = lineStartIndex;
                    builder.Append(fixedTranslatedLine);
                }

                builder.Append(Environment.NewLine);
                lineNum++;
                lineStartIndex = builder.Length;
            }

            instruction = builder.ToString();
            return(instrSize);
        }
Example #25
0
 abstract internal void DumpDisasm(RuntimeFunction rtf, int imageOffset, XmlNode parentNode = null);
Example #26
0
 abstract internal void DumpDisasm(RuntimeFunction rtf, int imageOffset);
Example #27
0
 abstract internal void DumpRuntimeFunction(RuntimeFunction rtf);
Example #28
0
        /// <summary>
        /// Parse a single instruction and return the RVA of the next instruction.
        /// </summary>
        /// <param name="rtf">Runtime function to parse</param>
        /// <param name="imageOffset">Offset within the PE image byte array</param>
        /// <param name="rtfOffset">Instruction offset within the runtime function</param>
        /// <param name="instruction">Output text representation of the instruction</param>
        /// <returns>Instruction size in bytes - i.o.w. the next instruction starts at rtfOffset + (the return value)</returns>
        public int GetInstruction(RuntimeFunction rtf, int imageOffset, int rtfOffset, out string instruction)
        {
            if (_disasm == IntPtr.Zero)
            {
                instruction = "";
                return(rtf.Size);
            }

            int instrSize = CoreDisTools.GetInstruction(_disasm, rtf, imageOffset, rtfOffset, _reader.Image, out instruction);

            CoreDisTools.ClearOutputBuffer();

            instruction = instruction.Replace('\t', ' ');

            if (_options.Naked)
            {
                StringBuilder nakedInstruction = new StringBuilder();
                foreach (string line in instruction.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    int colon = line.IndexOf(':');
                    if (colon >= 0)
                    {
                        colon += 2;
                        while (colon + 3 <= line.Length &&
                               IsXDigit(line[colon]) &&
                               IsXDigit(line[colon + 1]) &&
                               line[colon + 2] == ' ')
                        {
                            colon += 3;
                        }

                        if (!_options.HideOffsets)
                        {
                            nakedInstruction.Append($"{(rtfOffset + rtf.CodeOffset),8:x4}:");
                            nakedInstruction.Append("  ");
                        }
                        else
                        {
                            nakedInstruction.Append("    ");
                        }
                        nakedInstruction.Append(line.Substring(colon).TrimStart());
                        nakedInstruction.Append('\n');
                    }
                    else
                    {
                        nakedInstruction.Append(' ', 7);
                        nakedInstruction.Append(line.TrimStart());
                        nakedInstruction.Append('\n');
                    }
                }
                instruction = nakedInstruction.ToString();
            }

            switch (_reader.Machine)
            {
            case Machine.Amd64:
                ProbeX64Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction);
                break;

            case Machine.I386:
                ProbeX86Quirks(rtf, imageOffset, rtfOffset, instrSize, ref instruction);
                break;

            case Machine.ArmThumb2:
            case Machine.Thumb:
                break;

            case Machine.Arm64:
                break;

            default:
                throw new NotImplementedException();
            }

            instruction = instruction.Replace("\n", Environment.NewLine);
            return(instrSize);
        }
Example #29
0
        /// <summary>
        /// Replace relative offset in the disassembled instruction with an arbitrary string.
        /// </summary>
        /// <param name="instruction">Disassembled instruction to modify</param>
        /// <param name="replacementString">String to replace offset with</param>
        /// <param name="rtf">Runtime function being disassembled</param>
        private void ReplaceRelativeOffset(ref string instruction, string replacementString, RuntimeFunction rtf)
        {
            int numberEnd = instruction.IndexOf('\n');
            int number    = numberEnd;

            while (number > 0)
            {
                char c = instruction[number - 1];
                if (c >= ' ' && !Char.IsDigit(c) && c != '-')
                {
                    break;
                }
                number--;
            }

            StringBuilder translated = new StringBuilder();

            translated.Append(instruction, 0, number);
            translated.Append(replacementString);
            translated.Append(instruction, numberEnd, instruction.Length - numberEnd);
            instruction = translated.ToString();
        }
Example #30
0
        /// <summary>
        /// Probe quirks that have the same behavior for X86 and X64.
        /// </summary>
        /// <param name="rtf">Runtime function</param>
        /// <param name="imageOffset">Offset within the image byte array</param>
        /// <param name="rtfOffset">Offset within the runtime function</param>
        /// <param name="instrSize">Instruction size</param>
        /// <param name="instruction">Textual representation of the instruction</param>
        private void ProbeCommonIntelQuirks(RuntimeFunction rtf, int imageOffset, int rtfOffset, int instrSize, ref string instruction)
        {
            int instructionRVA     = rtf.StartAddress + rtfOffset;
            int nextInstructionRVA = instructionRVA + instrSize;

            if (instrSize == 2 && IsIntelJumpInstructionWithByteOffset(imageOffset + rtfOffset))
            {
                sbyte offset = (sbyte)_reader.Image[imageOffset + rtfOffset + 1];
                ReplaceRelativeOffset(ref instruction, nextInstructionRVA + offset, rtf);
            }
            else if (instrSize == 5 && IsIntel1ByteJumpInstructionWithIntOffset(imageOffset + rtfOffset))
            {
                int offset = BitConverter.ToInt32(_reader.Image, imageOffset + rtfOffset + 1);
                ReplaceRelativeOffset(ref instruction, nextInstructionRVA + offset, rtf);
            }
            else if (instrSize == 5 && IsIntelCallInstructionWithIntOffset(imageOffset + rtfOffset))
            {
                int  offset                       = BitConverter.ToInt32(_reader.Image, imageOffset + rtfOffset + 1);
                int  targetRVA                    = nextInstructionRVA + offset;
                int  targetImageOffset            = _reader.GetOffset(targetRVA);
                bool pointsOutsideRuntimeFunction = (targetRVA < rtf.StartAddress || targetRVA >= rtf.StartAddress + rtf.Size);
                if (pointsOutsideRuntimeFunction && IsIntel2ByteIndirectJumpPCRelativeInstruction(targetImageOffset, out int instructionRelativeOffset))
                {
                    int  thunkTargetRVA = targetRVA + instructionRelativeOffset;
                    bool haveImportCell = _reader.ImportCellNames.TryGetValue(thunkTargetRVA, out string importCellName);

                    if (_options.Naked && haveImportCell)
                    {
                        ReplaceRelativeOffset(ref instruction, $@"qword ptr [{importCellName}]", rtf);
                    }
                    else
                    {
                        ReplaceRelativeOffset(ref instruction, targetRVA, rtf);
                        if (haveImportCell)
                        {
                            int           instructionEnd = instruction.IndexOf('\n');
                            StringBuilder builder        = new StringBuilder(instruction, 0, instructionEnd, capacity: 256);
                            AppendComment(builder, @$ "JMP [0x{thunkTargetRVA:X4}]: {importCellName}");
                            builder.AppendLine();
                            instruction = builder.ToString();
                        }
                    }
                }
                else if (pointsOutsideRuntimeFunction && IsAnotherRuntimeFunctionWithinMethod(targetRVA, rtf, out int runtimeFunctionIndex))
                {
                    string runtimeFunctionName = string.Format("RUNTIME_FUNCTION[{0}]", runtimeFunctionIndex);

                    if (_options.Naked)
                    {
                        ReplaceRelativeOffset(ref instruction, runtimeFunctionName, rtf);
                    }
                    else
                    {
                        ReplaceRelativeOffset(ref instruction, targetRVA, rtf);
                        int           instructionEnd = instruction.IndexOf('\n');
                        StringBuilder builder        = new StringBuilder(instruction, 0, instructionEnd, capacity: 256);
                        AppendComment(builder, runtimeFunctionName);
                        builder.AppendLine();
                        instruction = builder.ToString();
                    }
                }
                else
                {
                    ReplaceRelativeOffset(ref instruction, nextInstructionRVA + offset, rtf);
                }
            }
            else if (instrSize == 6 && IsIntel2ByteJumpInstructionWithIntOffset(imageOffset + rtfOffset))
            {
                int offset = BitConverter.ToInt32(_reader.Image, imageOffset + rtfOffset + 2);
                ReplaceRelativeOffset(ref instruction, nextInstructionRVA + offset, rtf);
            }
        }