private void Disassemble(ITextOutput output, ReadyToRunReader reader, ReadyToRunMethod readyToRunMethod, RuntimeFunction runtimeFunction, int bitness, ulong address) { WriteCommentLine(output, readyToRunMethod.SignatureString); byte[] codeBytes = new byte[runtimeFunction.Size]; for (int i = 0; i < runtimeFunction.Size; i++) { codeBytes[i] = reader.Image[reader.GetOffset(runtimeFunction.StartAddress) + i]; } // TODO: Decorate the disassembly with Unwind, GC and debug info var codeReader = new ByteArrayCodeReader(codeBytes); var decoder = Decoder.Create(bitness, codeReader); decoder.IP = address; ulong endRip = decoder.IP + (uint)codeBytes.Length; var instructions = new InstructionList(); while (decoder.IP < endRip) { decoder.Decode(out instructions.AllocUninitializedElement()); } string disassemblyFormat = ReadyToRunOptions.GetDisassemblyFormat(null); Formatter formatter = null; if (disassemblyFormat.Equals(ReadyToRunOptions.intel)) { formatter = new NasmFormatter(); } else { Debug.Assert(disassemblyFormat.Equals(ReadyToRunOptions.gas)); formatter = new GasFormatter(); } formatter.Options.DigitSeparator = "`"; formatter.Options.FirstOperandCharIndex = 10; var tempOutput = new StringBuilderFormatterOutput(); foreach (var instr in instructions) { int byteBaseIndex = (int)(instr.IP - address); formatter.Format(instr, tempOutput); output.Write(instr.IP.ToString("X16")); output.Write(" "); int instrLen = instr.ByteLength; for (int i = 0; i < instrLen; i++) { output.Write(codeBytes[byteBaseIndex + i].ToString("X2")); } int missingBytes = 10 - instrLen; for (int i = 0; i < missingBytes; i++) { output.Write(" "); } output.Write(" "); output.WriteLine(tempOutput.ToStringAndReset()); } output.WriteLine(); }
private static IEnumerable <Asm> Decode(ulong startAddress, uint size, State state, int depth, ClrMethod currentMethod) { byte[] code = new byte[size]; if (!state.Runtime.DataTarget.ReadProcessMemory(startAddress, code, code.Length, out int bytesRead) || bytesRead == 0) { yield break; } var reader = new ByteArrayCodeReader(code, 0, bytesRead); var decoder = Decoder.Create(state.Runtime.PointerSize * 8, reader); decoder.IP = startAddress; while (reader.CanReadByte) { decoder.Decode(out var instruction); TryTranslateAddressToName(instruction, state, depth, currentMethod); yield return(new Asm { InstructionPointer = instruction.IP, Instruction = instruction }); } }
Decoder CreateDecoder(int codeSize, string hexBytes) { Decoder decoder; var codeReader = new ByteArrayCodeReader(hexBytes); switch (codeSize) { case 16: decoder = Decoder.Create16(codeReader); decoder.InstructionPointer = DecoderConstants.DEFAULT_IP16; break; case 32: decoder = Decoder.Create32(codeReader); decoder.InstructionPointer = DecoderConstants.DEFAULT_IP32; break; case 64: decoder = Decoder.Create64(codeReader); decoder.InstructionPointer = DecoderConstants.DEFAULT_IP64; break; default: throw new ArgumentOutOfRangeException(nameof(codeSize)); } Assert.Equal(codeSize, decoder.Bitness); return(decoder); }
/// <summary> /// Directly adds a raw segment from an NE file segment /// </summary> /// <param name="segment"></param> public void AddSegment(Segment segment) { //Get Address for this Segment var segmentMemory = new byte[0x10000]; //Add the data to memory and record the segment offset in memory Array.Copy(segment.Data, 0, segmentMemory, 0, segment.Data.Length); _memorySegments.Add(segment.Ordinal, segmentMemory); if (segment.Flags.Contains(EnumSegmentFlags.Code)) { //Decode the Segment var instructionList = new InstructionList(); var codeReader = new ByteArrayCodeReader(segment.Data); var decoder = Decoder.Create(16, codeReader); decoder.IP = 0x0; while (decoder.IP < (ulong)segment.Data.Length) { decoder.Decode(out instructionList.AllocUninitializedElement()); } _decompiledSegments.Add(segment.Ordinal, new Dictionary <ushort, Instruction>()); foreach (var i in instructionList) { _decompiledSegments[segment.Ordinal].Add(i.IP16, i); } } _segments[segment.Ordinal] = segment; }
public static void Disassemble(ManualLogSource logSource, IntPtr memoryPtr, int size) { var data = new byte[size]; Marshal.Copy(memoryPtr, data, 0, size); var formatter = new NasmFormatter(); var output = new StringOutput(); var codeReader = new ByteArrayCodeReader(data); var decoder = Decoder.Create(64, codeReader); decoder.IP = (ulong)memoryPtr.ToInt64(); while (codeReader.CanReadByte) { decoder.Decode(out var instr); formatter.Format(instr, output); logSource.LogDebug($"{instr.IP:X16} {output.ToStringAndReset()}"); if (instr.Code == Code.Jmp_rm64 && instr.Immediate32 == 0 ) // && instr.IsIPRelativeMemoryOperand && instr.IPRelativeMemoryAddress = 6 { var address = new byte[8]; for (var i = 0; i < 8; i++) { address[i] = (byte)codeReader.ReadByte(); } logSource.LogDebug($"{instr.IP + (ulong) instr.Length:X16} db 0x{BitConverter.ToUInt64(address, 0):X16}"); decoder.IP += 8; } } }
Decoder CreateDecoder(int codeSize, byte[] hexBytes, DecoderOptions options) { var codeReader = new ByteArrayCodeReader(hexBytes); var decoder = Decoder.Create(codeSize, codeReader, options); switch (codeSize) { case 16: decoder.IP = DecoderConstants.DEFAULT_IP16; break; case 32: decoder.IP = DecoderConstants.DEFAULT_IP32; break; case 64: decoder.IP = DecoderConstants.DEFAULT_IP64; break; default: throw new ArgumentOutOfRangeException(nameof(codeSize)); } Assert.Equal(codeSize, decoder.Bitness); return(decoder); }
static void Main(string[] args) { int exampleCodeBitness = 32; if (args.Length == 2) { switch (args[0]) { case "32": { exampleCodeBitness = 32; break; } case "64": { exampleCodeBitness = 64; break; } default: break; } } else { Console.Write("Must use arguments: <bit-length[32|64]> <target file path>"); return; } string filePath = args[1]; BinaryReader reader = new BinaryReader(File.Open(filePath, FileMode.Open)); var fileSize = Convert.ToInt32(new FileInfo(filePath).Length); var codeBytes = reader.ReadBytes(fileSize); var sw = Stopwatch.StartNew(); var codeReader = new ByteArrayCodeReader(codeBytes); var decoder = Decoder.Create(exampleCodeBitness, codeReader); decoder.IP = exampleCodeRIP; ulong endRip = decoder.IP + (uint)codeBytes.Length; var instructions = new InstructionList(); while (decoder.IP < endRip) { decoder.Decode(out instructions.AllocUninitializedElement()); } var formatter = new MasmFormatter(); formatter.Options.DigitSeparator = "`"; formatter.Options.FirstOperandCharIndex = 10; var output = new StringOutput(); foreach (ref var instr in instructions) { formatter.Format(instr, output); Console.WriteLine(instr.IP.ToString("X8") + " " + output.ToStringAndReset()); } sw.Stop(); Console.Error.WriteLine("Total dump time: {0:F} sec.", sw.Elapsed.TotalSeconds); }
Decoder CreateDecoder(int bitness, byte[] hexBytes, ulong ip, DecoderOptions options) { var codeReader = new ByteArrayCodeReader(hexBytes); var decoder = Decoder.Create(bitness, codeReader, options); decoder.IP = ip; Assert.Equal(bitness, decoder.Bitness); return(decoder); }
public ModuleDisassembly(byte[] moduleData) { _moduleData = moduleData; _codeReader = new ByteArrayCodeReader(_moduleData); _decoder = Decoder.Create(16, _codeReader); _decoder.IP = 0x0; Instructions = new InstructionList(); }
static void ShellcodeDecoder(string shellcode, string RIP, bool x64) { string[] hexValues = shellcode.Split("\\x").Skip(1).ToArray(); int bitness; if (x64) { bitness = 64; } else { bitness = 32; } byte[] code = hexValues .Select(value => Convert.ToByte(value, 16)) .ToArray(); var codeBytes = code; var codeReader = new ByteArrayCodeReader(codeBytes); var decoder = Iced.Intel.Decoder.Create(bitness, codeReader); ulong CodeRIP = Convert.ToUInt64(RIP, 16); decoder.IP = CodeRIP; ulong endRip = decoder.IP + (uint)codeBytes.Length; var instructions = new InstructionList(); while (decoder.IP < endRip) { decoder.Decode(out instructions.AllocUninitializedElement()); } var formatter = new NasmFormatter(); formatter.Options.DigitSeparator = ""; formatter.Options.FirstOperandCharIndex = 10; var output = new StringBuilderFormatterOutput(); foreach (ref var instr in instructions) { formatter.Format(instr, output); int instrLen = instr.ByteLength; int byteBaseIndex = (int)(instr.IP - CodeRIP); for (int i = 0; i < instrLen; i++) { Console.Write(codeBytes[byteBaseIndex + i].ToString("X2")); } int missingBytes = 10 - instrLen; for (int i = 0; i < missingBytes; i++) { Console.Write(" "); } Console.Write(" "); Console.WriteLine(output.ToStringAndReset().ToUpper()); } }
static InstructionList DecodeAsm(CodeBlock src) { var dst = new InstructionList(); var reader = new ByteArrayCodeReader(src.Data); var decoder = Decoder.Create(IntPtr.Size * 8, reader); decoder.IP = src.Address; while (reader.CanReadByte) { ref var instruction = ref dst.AllocUninitializedElement(); decoder.Decode(out instruction); }
void Test_ByteArrayCodeReader_ctor(ByteArrayCodeReader reader, byte[] expectedData) { int i = 0; Assert.Equal(0, reader.Position); while (reader.CanReadByte) { Assert.Equal(i, reader.Position); Assert.True(i < expectedData.Length); Assert.Equal(expectedData[i], reader.ReadByte()); i++; } Assert.Equal(i, reader.Position); Assert.Equal(expectedData.Length, i); Assert.Equal(-1, reader.ReadByte()); Assert.Equal(i, reader.Position); reader.Position = 0; Assert.Equal(0, reader.Position); i = 0; while (reader.CanReadByte) { Assert.Equal(i, reader.Position); Assert.True(i < expectedData.Length); Assert.Equal(expectedData[i], reader.ReadByte()); i++; } Assert.Equal(i, reader.Position); Assert.Equal(expectedData.Length, i); Assert.Equal(-1, reader.ReadByte()); Assert.Equal(i, reader.Position); reader.Position = reader.Count; Assert.Equal(reader.Count, reader.Position); Assert.False(reader.CanReadByte); Assert.Equal(-1, reader.ReadByte()); for (i = expectedData.Length - 1; i >= 0; i--) { reader.Position = i; Assert.Equal(i, reader.Position); Assert.True(reader.CanReadByte); Assert.Equal(expectedData[i], reader.ReadByte()); Assert.Equal(i + 1, reader.Position); } Assert.Throws <ArgumentOutOfRangeException>(() => reader.Position = int.MinValue); Assert.Throws <ArgumentOutOfRangeException>(() => reader.Position = -1); Assert.Throws <ArgumentOutOfRangeException>(() => reader.Position = expectedData.Length + 1); Assert.Throws <ArgumentOutOfRangeException>(() => reader.Position = int.MaxValue); }
Decoder CreateDecoder(int bitness, byte[] hexBytes, DecoderOptions options) { var codeReader = new ByteArrayCodeReader(hexBytes); var decoder = Decoder.Create(bitness, codeReader, options); decoder.IP = bitness switch { 16 => DecoderConstants.DEFAULT_IP16, 32 => DecoderConstants.DEFAULT_IP32, 64 => DecoderConstants.DEFAULT_IP64, _ => throw new ArgumentOutOfRangeException(nameof(bitness)), }; Assert.Equal(bitness, decoder.Bitness); return(decoder); }
public Instruction Recompile(ushort segment, ushort instructionPointer) { //If it wasn't able to decompile linear through the data, there might have been //data in the path of the code that messed up decoding, in this case, we grab up to //6 bytes at the IP and decode the instruction manually. This works 9 times out of 10 Span <byte> segmentData = _segments[segment].Data; var reader = new ByteArrayCodeReader(segmentData.Slice(instructionPointer, 6).ToArray()); var decoder = Decoder.Create(16, reader); decoder.IP = instructionPointer; decoder.Decode(out var outputInstruction); _decompiledSegments[segment][instructionPointer] = outputInstruction; return(outputInstruction); }
private static void CreateCodeSegment(ReadOnlySpan <byte> byteCode, ushort segmentOrdinal = 1) { //Decode the Segment var instructionList = new InstructionList(); var codeReader = new ByteArrayCodeReader(byteCode.ToArray()); var decoder = Decoder.Create(16, codeReader); decoder.IP = 0x0; while (decoder.IP < (ulong)byteCode.Length) { decoder.Decode(out instructionList.AllocUninitializedElement()); } CreateCodeSegment(instructionList, segmentOrdinal); }
protected void CreateCodeSegment(ReadOnlySpan <byte> byteCode, ushort segmentOrdinal = 1) { //Decode the Segment var instructionList = new InstructionList(); var codeReader = new ByteArrayCodeReader(byteCode.ToArray()); var decoder = Decoder.Create(16, codeReader); decoder.IP = 0x0; while (decoder.IP < (ulong)byteCode.Length) { decoder.Decode(out instructionList.AllocUninitializedElement()); } mbbsEmuMemoryCore.AddSegment(segmentOrdinal, instructionList); }
public static InstructionList DisassembleBytesNew(bool is32Bit, byte[] bytes, ulong methodBase) { var codeReader = new ByteArrayCodeReader(bytes); var decoder = Decoder.Create(is32Bit ? 32 : 64, codeReader); decoder.IP = methodBase; var instructions = new InstructionList(); var endRip = decoder.IP + (uint)bytes.Length; while (decoder.IP < endRip) { decoder.Decode(out instructions.AllocUninitializedElement()); } return(instructions); }
private CodeByte Disamexe(string fileexe) { progressBar1.Maximum = 100; CodeByte codeByte = new CodeByte(); int exampleCodeBitness; var peHeader = new PeNet.PeFile(fileexe); if (peHeader.Is64Bit) { exampleCodeBitness = 64; } else { exampleCodeBitness = 32; } FileStream input = new FileStream(fileexe, FileMode.Open, FileAccess.Read); BinaryReader reader = new BinaryReader(input); reader.ReadBytes((int)peHeader.ImageSectionHeaders[0].PointerToRawData); byte[] buffer = reader.ReadBytes((int)peHeader.ImageSectionHeaders[0].SizeOfRawData); input.Close(); ulong exampleCodeRIP = peHeader.ImageNtHeaders.OptionalHeader.ImageBase + peHeader.ImageSectionHeaders[0].VirtualAddress; var codeBytes = buffer; var codeReader = new ByteArrayCodeReader(codeBytes); var decoder = Iced.Intel.Decoder.Create(exampleCodeBitness, codeReader); decoder.IP = exampleCodeRIP; ulong endRip = decoder.IP + (uint)codeBytes.Length; var instructions = new InstructionList(); while (decoder.IP < endRip) { decoder.Decode(out instructions.AllocUninitializedElement()); if (decoder.IP % PROGRESS_MODULO == 0) { progressBar1.Value = (int)(decoder.IP * 100 / endRip); } } codeByte.instructions = instructions; codeByte.hexcode = buffer; codeByte.CodeRIP = exampleCodeRIP; return(codeByte); }
private InstructionList DisassembleMethodInstructions(long methodOffset) { const int NativeBitness = 64; const int MaxMethodLength = 0xFFFF; var codeReader = new ByteArrayCodeReader(NativeDllCode, (int)methodOffset, (int)Math.Min(MaxMethodLength, NativeDllCode.Length - methodOffset)); var decoder = Decoder.Create(NativeBitness, codeReader); decoder.InstructionPointer = 0; ulong endRip = decoder.InstructionPointer + MaxMethodLength; var instructions = new InstructionList(); int int3count = 0; while (decoder.InstructionPointer < endRip) { if (decoder.InstructionPointer > 0) { long currentOffset = methodOffset + (long)decoder.InstructionPointer; if (Offsets.MethodsFromOffsets.ContainsKey(currentOffset)) { break; } } decoder.Decode(out Instruction instruction); if (instruction.Code == Code.INVALID) { break; } else if (instruction.Code == Code.Int3) { int3count++; if (int3count >= 2) { break; } } else { int3count = 0; } instructions.Add(instruction); } return(instructions); }
/// <summary> /// Returns a set of instructions represented by the prologue this type was instantiated with. /// </summary> private IList <Instruction> DecodePrologue() { var codeReader = new ByteArrayCodeReader(_bytes); var decoder = Decoder.Create(_bitness, codeReader); decoder.IP = _originalFunctionAddress; ulong endRip = decoder.IP + (uint)_bytes.Length; var instructions = new InstructionList(); while (decoder.IP < endRip) { decoder.Decode(out instructions.AllocUninitializedElement()); } return(instructions); }
public static void Main(string[] args) { var code = File.ReadAllBytes(args[0]); var reader = new ByteArrayCodeReader(code); var decoder = Decoder.Create(64, reader); decoder.InstructionPointer = 0x00007FFAC46ACDA4; var end = decoder.InstructionPointer + (uint)code.Length; var instructions = new InstructionList(); while (decoder.InstructionPointer < end) { decoder.Decode(out instructions.AllocUninitializedElement()); } }
public void TestNops() { foreach (var bitness in new[] { 16, 32, 64 }) { for (int i = 0; i <= 128; i++) { var a = new Assembler(bitness); a.nop(i); var writer = new CodeWriterImpl(); a.Assemble(writer, 0); var data = writer.ToArray(); Assert.Equal(i, data.Length); var reader = new ByteArrayCodeReader(data); var decoder = Decoder.Create(bitness, reader); while (reader.CanReadByte) { var instr = decoder.Decode(); switch (instr.Code) { case Code.Nopw: case Code.Nopd: case Code.Nopq: case Code.Nop_rm16: case Code.Nop_rm32: case Code.Nop_rm64: break; default: Assert.True(false, $"Expected a NOP but got {instr.Code}"); break; } } } { var a = new Assembler(bitness); Assert.Throws <ArgumentOutOfRangeException>(() => a.nop(-1)); } } }
private void CreateCodeSegment(ReadOnlySpan <byte> byteCode, ushort segmentOrdinal = 0x1000) { if (realModeMemoryCore != null) { realModeMemoryCore.SetArray(segmentOrdinal, 0, byteCode); return; } //Decode the Segment var instructionList = new InstructionList(); var codeReader = new ByteArrayCodeReader(byteCode.ToArray()); var decoder = Decoder.Create(16, codeReader); decoder.IP = 0x0; while (decoder.IP < (ulong)byteCode.Length) { decoder.Decode(out instructionList.AllocUninitializedElement()); } CreateCodeSegment(instructionList, segmentOrdinal); }
/// <summary> /// Returns the decompiled instruction from the specified segment:pointer /// </summary> /// <param name="segment"></param> /// <param name="instructionPointer"></param> /// <returns></returns> public Instruction GetInstruction(ushort segment, ushort instructionPointer) { //Prevents constant hash lookups for instructions from the same segment if (_currentCodeSegment != segment) { _currentCodeSegment = segment; _currentCodeSegmentInstructions = _decompiledSegments[segment]; } //If it wasn't able to decompile linear through the data, there might have been //data in the path of the code that messed up decoding, in this case, we grab up to //6 bytes at the IP and decode the instruction manually. This works 9 times out of 10 if (!_currentCodeSegmentInstructions.TryGetValue(instructionPointer, out var outputInstruction)) { Span <byte> segmentData = _segments[segment].Data; var reader = new ByteArrayCodeReader(segmentData.Slice(instructionPointer, 6).ToArray()); var decoder = Decoder.Create(16, reader); decoder.IP = instructionPointer; decoder.Decode(out outputInstruction); } return(outputInstruction); }
private static IEnumerable <Asm> Decode(ILToNativeMap map, State state, int depth, ClrMethod currentMethod) { ulong startAddress = map.StartAddress; uint size = (uint)(map.EndAddress - map.StartAddress); byte[] code = new byte[size]; int totalBytesRead = 0; do { int bytesRead = state.Runtime.DataTarget.DataReader.Read(startAddress + (ulong)totalBytesRead, new Span <byte>(code, totalBytesRead, (int)size - totalBytesRead)); if (bytesRead <= 0) { throw new EndOfStreamException($"Tried to read {size} bytes for {currentMethod.Signature}, got only {totalBytesRead}"); } totalBytesRead += bytesRead; } while (totalBytesRead != size); var reader = new ByteArrayCodeReader(code, 0, (int)size); var decoder = Decoder.Create(state.Runtime.DataTarget.DataReader.PointerSize * 8, reader); decoder.IP = startAddress; while (reader.CanReadByte) { decoder.Decode(out var instruction); TryTranslateAddressToName(instruction, state, depth, currentMethod); yield return(new Asm { InstructionPointer = instruction.IP, Instruction = instruction }); } }
private void Disassemble(PEFile currentFile, ITextOutput output, ReadyToRunReader reader, ReadyToRunMethod readyToRunMethod, RuntimeFunction runtimeFunction, int bitness, ulong address, bool showMetadataTokens, bool showMetadataTokensInBase10) { // TODO: Decorate the disassembly with GCInfo WriteCommentLine(output, readyToRunMethod.SignatureString); Dictionary <ulong, UnwindCode> unwindInfo = null; if (ReadyToRunOptions.GetIsShowUnwindInfo(null) && bitness == 64) { unwindInfo = WriteUnwindInfo(runtimeFunction, output); } bool isShowDebugInfo = ReadyToRunOptions.GetIsShowDebugInfo(null); Dictionary <VarLocType, HashSet <ValueTuple <DebugInfo, NativeVarInfo> > > debugInfo = null; if (isShowDebugInfo) { debugInfo = WriteDebugInfo(readyToRunMethod, output); } byte[] codeBytes = new byte[runtimeFunction.Size]; for (int i = 0; i < runtimeFunction.Size; i++) { codeBytes[i] = reader.Image[reader.GetOffset(runtimeFunction.StartAddress) + i]; } var codeReader = new ByteArrayCodeReader(codeBytes); var decoder = Decoder.Create(bitness, codeReader); decoder.IP = address; ulong endRip = decoder.IP + (uint)codeBytes.Length; var instructions = new InstructionList(); while (decoder.IP < endRip) { decoder.Decode(out instructions.AllocUninitializedElement()); } string disassemblyFormat = ReadyToRunOptions.GetDisassemblyFormat(null); Formatter formatter = null; if (disassemblyFormat.Equals(ReadyToRunOptions.intel)) { formatter = new NasmFormatter(); } else { Debug.Assert(disassemblyFormat.Equals(ReadyToRunOptions.gas)); formatter = new GasFormatter(); } formatter.Options.DigitSeparator = "`"; formatter.Options.FirstOperandCharIndex = 10; var tempOutput = new StringOutput(); ulong baseInstrIP = instructions[0].IP; foreach (var instr in instructions) { int byteBaseIndex = (int)(instr.IP - address); if (isShowDebugInfo && runtimeFunction.DebugInfo != null) { foreach (var bound in runtimeFunction.DebugInfo.BoundsList) { if (bound.NativeOffset == byteBaseIndex) { if (bound.ILOffset == (uint)DebugInfoBoundsType.Prolog) { WriteCommentLine(output, "Prolog"); } else if (bound.ILOffset == (uint)DebugInfoBoundsType.Epilog) { WriteCommentLine(output, "Epilog"); } else { WriteCommentLine(output, $"IL_{bound.ILOffset:x4}"); } } } } formatter.Format(instr, tempOutput); output.Write(instr.IP.ToString("X16")); output.Write(" "); int instrLen = instr.Length; for (int i = 0; i < instrLen; i++) { output.Write(codeBytes[byteBaseIndex + i].ToString("X2")); } int missingBytes = 10 - instrLen; for (int i = 0; i < missingBytes; i++) { output.Write(" "); } output.Write(" "); output.Write(tempOutput.ToStringAndReset()); DecorateUnwindInfo(output, unwindInfo, baseInstrIP, instr); DecorateDebugInfo(output, instr, debugInfo, baseInstrIP); DecorateCallSite(currentFile, output, reader, showMetadataTokens, showMetadataTokensInBase10, instr); } output.WriteLine(); }
/// <summary> /// Follow a JMP or Jcc instruction to the next logical location. /// </summary> /// <param name="address">Address of the instruction.</param> /// <returns>The address referenced by the jmp.</returns> private static IntPtr FollowJmp(IntPtr address) { while (true) { var hasOtherHooks = HookManager.Originals.ContainsKey(address); if (hasOtherHooks) { // This address has been hooked already. Do not follow a jmp into a trampoline of our own making. Log.Verbose($"Detected hook trampoline at {address.ToInt64():X}, stopping jump resolution."); return(address); } var bytes = MemoryHelper.ReadRaw(address, 8); var codeReader = new ByteArrayCodeReader(bytes); var decoder = Decoder.Create(64, codeReader); decoder.IP = (ulong)address.ToInt64(); decoder.Decode(out var inst); if (inst.Mnemonic == Mnemonic.Jmp) { var kind = inst.Op0Kind; IntPtr newAddress; switch (inst.Op0Kind) { case OpKind.NearBranch64: case OpKind.NearBranch32: case OpKind.NearBranch16: newAddress = (IntPtr)inst.NearBranchTarget; break; case OpKind.Immediate16: case OpKind.Immediate8to16: case OpKind.Immediate8to32: case OpKind.Immediate8to64: case OpKind.Immediate32to64: case OpKind.Immediate32 when IntPtr.Size == 4: case OpKind.Immediate64: newAddress = (IntPtr)inst.GetImmediate(0); break; case OpKind.Memory when inst.IsIPRelativeMemoryOperand: newAddress = (IntPtr)inst.IPRelativeMemoryAddress; newAddress = Marshal.ReadIntPtr(newAddress); break; case OpKind.Memory: newAddress = (IntPtr)inst.MemoryDisplacement64; newAddress = Marshal.ReadIntPtr(newAddress); break; default: var debugBytes = string.Join(" ", bytes.Take(inst.Length).Select(b => $"{b:X2}")); throw new Exception($"Unknown OpKind {inst.Op0Kind} from {debugBytes}"); } Log.Verbose($"Resolving assembly jump ({kind}) from {address.ToInt64():X} to {newAddress.ToInt64():X}"); address = newAddress; } else { break; } } return(address); }
public static Block[] Create(int bitness, NativeCodeBlock[] blocks) { var targets = new Dictionary <ulong, TargetKind>(); var instrInfo = new List <(Instruction instruction, int block, ArraySegment <byte> code)>(); for (int blockIndex = 0; blockIndex < blocks.Length; blockIndex++) { var block = blocks[blockIndex]; var reader = new ByteArrayCodeReader(block.Code); var decoder = Decoder.Create(bitness, reader); decoder.IP = block.Address; while (reader.CanReadByte) { decoder.Decode(out var instr); instrInfo.Add((instr, blockIndex, GetBytes(block.Code, block.Address, ref instr))); switch (instr.FlowControl) { case FlowControl.Next: case FlowControl.Interrupt: break; case FlowControl.UnconditionalBranch: Add(targets, instr.NextIP, TargetKind.Unknown); if (instr.Op0Kind == OpKind.NearBranch16 || instr.Op0Kind == OpKind.NearBranch32 || instr.Op0Kind == OpKind.NearBranch64) { Add(targets, instr.NearBranchTarget, TargetKind.Branch); } break; case FlowControl.ConditionalBranch: case FlowControl.XbeginXabortXend: if (instr.Op0Kind == OpKind.NearBranch16 || instr.Op0Kind == OpKind.NearBranch32 || instr.Op0Kind == OpKind.NearBranch64) { Add(targets, instr.NearBranchTarget, TargetKind.Branch); } break; case FlowControl.Call: if (instr.Op0Kind == OpKind.NearBranch16 || instr.Op0Kind == OpKind.NearBranch32 || instr.Op0Kind == OpKind.NearBranch64) { Add(targets, instr.NearBranchTarget, TargetKind.Call); } break; case FlowControl.IndirectBranch: Add(targets, instr.NextIP, TargetKind.Unknown); // Unknown target break; case FlowControl.IndirectCall: // Unknown target break; case FlowControl.Return: case FlowControl.Exception: Add(targets, instr.NextIP, TargetKind.Unknown); break; default: Debug.Fail($"Unknown flow control: {instr.FlowControl}"); break; } var baseReg = instr.MemoryBase; if (baseReg == Register.RIP || baseReg == Register.EIP) { int opCount = instr.OpCount; for (int i = 0; i < opCount; i++) { if (instr.GetOpKind(i) == OpKind.Memory) { if (IsCodeAddress(blocks, instr.IPRelativeMemoryAddress)) { Add(targets, instr.IPRelativeMemoryAddress, TargetKind.Branch); } break; } } } else if (instr.MemoryDisplSize >= 2) { ulong displ; switch (instr.MemoryDisplSize) { case 2: case 4: displ = instr.MemoryDisplacement; break; case 8: displ = (ulong)(int)instr.MemoryDisplacement; break; default: Debug.Fail($"Unknown mem displ size: {instr.MemoryDisplSize}"); goto case 8; } if (IsCodeAddress(blocks, displ)) { Add(targets, displ, TargetKind.Branch); } } } } foreach (var block in blocks) { if (targets.TryGetValue(block.Address, out var origKind)) { if (origKind < TargetKind.BlockStart && origKind != TargetKind.Unknown) { targets[block.Address] = TargetKind.BlockStart; } } else { targets.Add(block.Address, TargetKind.Unknown); } } var newBlocks = new List <BlockInfo>(); BlockInfo currentBlock = default; int labelIndex = 0, methodIndex = 0; for (int i = 0; i < instrInfo.Count; i++) { var info = instrInfo[i]; ref var instr = ref info.instruction; if (targets.TryGetValue(instr.IP, out var targetKind)) { var origBlock = blocks[info.block]; currentBlock = new BlockInfo(targetKind, origBlock.Kind, instr.IP, origBlock.Address == instr.IP ? origBlock.Comment : null); newBlocks.Add(currentBlock); } // The addr of each block is always in the dictionary so currentBlock is initialized Debug.Assert(!(currentBlock.Instructions is null)); currentBlock.Instructions.Add(new X86InstructionInfo(info.code, instr)); }
/// <summary> /// Reads assembly from <see cref="functionPtr" /> (at least <see cref="minimumTrampolineLength" /> bytes), and writes /// it to <see cref="trampolinePtr" /> plus a jmp to continue execution. /// </summary> /// <param name="instructionBuffer">The buffer to copy assembly from.</param> /// <param name="functionPtr">The pointer to the function to copy assembly from.</param> /// <param name="trampolinePtr">The pointer to write the trampoline assembly to.</param> /// <param name="arch">The architecture of the current platform.</param> /// <param name="minimumTrampolineLength">Copies at least this many bytes of assembly from <see cref="functionPtr" />.</param> /// <param name="trampolineLength">Returns the total length of the trampoline, in bytes.</param> /// <param name="jmpLength">Returns the length of the jmp at the end of the trampoline, in bytes.</param> public static void CreateTrampolineFromFunction(byte[] instructionBuffer, IntPtr functionPtr, IntPtr trampolinePtr, int minimumTrampolineLength, Architecture arch, out int trampolineLength, out int jmpLength) { // Decode original function up until we go past the needed bytes to write the jump to patchedFunctionPtr var codeReader = new ByteArrayCodeReader(instructionBuffer); var decoder = Decoder.Create(arch == Architecture.X64 ? 64 : 32, codeReader); decoder.IP = (ulong)functionPtr.ToInt64(); uint totalBytes = 0; var origInstructions = new InstructionList(); var readOverflowArea = false; while (codeReader.CanReadByte) { decoder.Decode(out var instr); origInstructions.Add(instr); totalBytes += (uint)instr.Length; if (instr.Code == Code.INVALID) { throw new Exception("Found garbage"); } if (totalBytes >= minimumTrampolineLength) { break; } if (readOverflowArea) { if (instr.Mnemonic != Mnemonic.Int3 && instr.Mnemonic != Mnemonic.Nop) { throw new Exception("Function is too short to hook"); } continue; } switch (instr.FlowControl) { case FlowControl.Next: break; // Interrupts are usually used for aligning case FlowControl.Interrupt: // eg. int n break; // Handled by BlockEncoder in most cases case FlowControl.UnconditionalBranch: case FlowControl.IndirectBranch: // eg. jmp reg/mem case FlowControl.ConditionalBranch: // eg. je, jno, etc break; case FlowControl.Return: // eg. ret // There is a possibility of an overflow area, attempt to allocate trampoline onto it readOverflowArea = true; break; case FlowControl.Call: // eg. call method case FlowControl.IndirectCall: // eg. call reg/mem case FlowControl.XbeginXabortXend: case FlowControl.Exception: // eg. ud0 default: throw new Exception("Not supported yet - " + instr.FlowControl); } } if (totalBytes < minimumTrampolineLength) { throw new Exception("Not enough bytes!"); } if (origInstructions.Count == 0) { throw new Exception("Not enough instructions!"); } ref readonly var lastInstr = ref origInstructions[origInstructions.Count - 1];
public void TrampolineTest(int bitness) { byte[] exampleCode = { 0x48, 0x89, 0x5C, 0x24, 0x10, 0x48, 0x89, 0x74, 0x24, 0x18, 0x55, 0x57, 0x41, 0x56, 0x48, 0x8D, 0xAC, 0x24, 0x00, 0xFF, 0xFF, 0xFF, 0x48, 0x81, 0xEC, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8B, 0x05, 0x18, 0x57, 0x0A, 0x00, 0x48, 0x33, 0xC4, 0x48, 0x89, 0x85, 0xF0, 0x00, 0x00, 0x00, 0x4C, 0x8B, 0x05, 0x2F, 0x24, 0x0A, 0x00, 0x48, 0x8D, 0x05, 0x78, 0x7C, 0x04, 0x00, 0x33, 0xFF }; var exampleCodePointer = Marshal.AllocHGlobal(80); var trampolineCodePointer = Marshal.AllocHGlobal(80); Marshal.Copy(exampleCode, 0, exampleCodePointer, exampleCode.Length); void Disassemble(byte[] data, ulong ip) { var formatter = new NasmFormatter(); var output = new StringOutput(); var codeReader = new ByteArrayCodeReader(data); var decoder = Decoder.Create(bitness, codeReader); decoder.IP = ip; while (codeReader.CanReadByte) { decoder.Decode(out var instr); formatter.Format(instr, output); Console.WriteLine($"{instr.IP:X16} {output.ToStringAndReset()}"); } Console.WriteLine(); } Console.WriteLine("Original:"); Console.WriteLine(); Disassemble(exampleCode, (ulong)exampleCodePointer.ToInt64()); DetourGenerator.CreateTrampolineFromFunction(exampleCodePointer, out var trampolineLength, out _); Console.WriteLine("Modified:"); Console.WriteLine(); Marshal.Copy(exampleCodePointer, exampleCode, 0, exampleCode.Length); Disassemble(exampleCode, (ulong)exampleCodePointer.ToInt64()); Console.WriteLine(); Console.WriteLine("Trampoline:"); Console.WriteLine(); var trampolineArray = new byte[trampolineLength]; Marshal.Copy(trampolineCodePointer, trampolineArray, 0, trampolineLength); Disassemble(trampolineArray, (ulong)trampolineCodePointer.ToInt64()); Marshal.FreeHGlobal(exampleCodePointer); Marshal.FreeHGlobal(trampolineCodePointer); Assert.IsFalse(false); }