From 29ecabbbbb33d4216861ce467376292d27a14a2f Mon Sep 17 00:00:00 2001 From: Justin Stenning Date: Thu, 24 May 2018 12:05:32 +1000 Subject: [PATCH 1/3] Generate documentation XML, and provide updated NuGet .nuspec file --- SharpDisasm.nuspec | 70 ++++++++++++++++++++++++++++++++++ SharpDisasm/SharpDisasm.csproj | 2 + SharpDisasmPack.bat | 2 + 3 files changed, 74 insertions(+) create mode 100644 SharpDisasm.nuspec create mode 100644 SharpDisasmPack.bat diff --git a/SharpDisasm.nuspec b/SharpDisasm.nuspec new file mode 100644 index 0000000..abd6522 --- /dev/null +++ b/SharpDisasm.nuspec @@ -0,0 +1,70 @@ + + + + SharpDisasm + $version$ + SharpDisasm + Justin Stenning + Justin Stenning + true + https://github.com/spazzarama/SharpDisasm/blob/master/LICENSE.md + https://github.com/spazzarama/SharpDisasm + SharpDisam is a disassembler written in C# able to decode the x86 and x86-64 instruction set architectures. + +It features: + * a full C# port of the libudis86 C library + * a set of simple C# classes wrapping the udis86 API + * support for x86 16-bit, 32-bit and 64-bit instruction set architectures + * support for outputting in Intel and AT&T syntax + * support for all x86 and x86-64 (AMD64) General purpose and System instructions. + * support for the following ISA extensions: + - MMX, FPU (x87), AMD 3DNow + - SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AES, + - AMD-V, INTEL-VMX, SMX + * instructions are defined in an XML document that is consumed by a T4 template to generate opcode tables for performance. + * the XML instructions document is exactly the same as that found within the udis86 project. The generated C# opcode tables is also very similar except in syntax to those generated by the Python script in the libudis86 C-library. + * able to decode more than 4 million 64-bit instructions per second (with an average instruction size of 7-bytes) + +Usage: http://sharpdisasm.codeplex.com/documentation + A C# port of the udis86 x86 disassembler. + $version$ +1. Added ability to resolve RIP relative addresses in ASM outputting +2. 64-bit definition fixes for a number of instructions +3. Support for .NET Standard target + +1. Added support for offset into IAssemblyCode +2. Fix exceptions on invalid instructions (contributed by ste-art) +3. Fix ATT syntax for enter/bound mnemonics being dropped +4. Translator internals refactored (better code reuse) +5. A few Debug.Asserts replaced with exceptions for Translator and Instruction.ToString +1.1.5 +1. Use of unsafe replaced with an assembly code reader interface +1.0.2 +1. Full port of udis86 C-library into C# +2. Wrapper class Disassembler for those not familiar with the libudis86 C-library + Copyright (c) 2015 Justin Stenning + ASM disassembler x86 x86-64 instructions opcodes decoder AMD Intel + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SharpDisasm/SharpDisasm.csproj b/SharpDisasm/SharpDisasm.csproj index d120d62..8eb17c4 100644 --- a/SharpDisasm/SharpDisasm.csproj +++ b/SharpDisasm/SharpDisasm.csproj @@ -3,6 +3,8 @@ netstandard2.0;net45;net4;net35 ..\bin\$(Configuration)\ + ..\bin\$(Configuration)\$(TargetFramework)\SharpDisasm.xml + diff --git a/SharpDisasmPack.bat b/SharpDisasmPack.bat new file mode 100644 index 0000000..c9ebed8 --- /dev/null +++ b/SharpDisasmPack.bat @@ -0,0 +1,2 @@ +nuget pack SharpDisasm.nuspec -Symbols -Version VERSIONHERE +pause \ No newline at end of file From af7484e9fbffffab93ea74587d7623d0aa738c35 Mon Sep 17 00:00:00 2001 From: shlomoartsi <40729917+shlomoartsi@users.noreply.github.com> Date: Thu, 5 Jul 2018 12:42:40 +0300 Subject: [PATCH 2/3] Interfacing Instruction class and introducing Instruction factories instead of using 'new Instruction()' --- SharpDisasm/Disassembler.cs | 71 ++++++++++++++++------ SharpDisasm/Factory/IInstructionFactory.cs | 22 +++++++ SharpDisasm/Factory/InstructionFactory.cs | 25 ++++++++ SharpDisasm/IInstruction.cs | 54 ++++++++++++++++ SharpDisasm/Instruction.cs | 18 +++--- 5 files changed, 164 insertions(+), 26 deletions(-) create mode 100644 SharpDisasm/Factory/IInstructionFactory.cs create mode 100644 SharpDisasm/Factory/InstructionFactory.cs create mode 100644 SharpDisasm/IInstruction.cs diff --git a/SharpDisasm/Disassembler.cs b/SharpDisasm/Disassembler.cs index 2a40ea5..82f1f26 100644 --- a/SharpDisasm/Disassembler.cs +++ b/SharpDisasm/Disassembler.cs @@ -40,6 +40,7 @@ using System.Text; using SharpDisasm.Helpers; +using SharpDisasm.Factory; namespace SharpDisasm { @@ -86,6 +87,8 @@ public sealed class Disassembler : IDisposable /// private Udis86.ud _u = new Udis86.ud(); + private readonly IInstructionFactory _instructionFactory; + #endregion /// @@ -98,8 +101,8 @@ public sealed class Disassembler : IDisposable /// public int BytesDecoded { get; private set; } - /// - /// Initializes a new instance of the class. + + /// Initializes a new instance of the class. /// /// The code. /// The architecture. @@ -107,7 +110,12 @@ public sealed class Disassembler : IDisposable /// The address. /// if set to true [copy binary to instruction]. /// The vendor. - public Disassembler(IAssemblyCode code, ArchitectureMode architecture, ulong offset = 0x0, ulong address = 0x0, bool copyBinaryToInstruction = false, Vendor vendor = Vendor.Any) + /// instruction factory, when null + /// class is created + public Disassembler(IAssemblyCode code, ArchitectureMode architecture, + ulong offset = 0x0, ulong address = 0x0, + bool copyBinaryToInstruction = false, Vendor vendor = Vendor.Any, + IInstructionFactory instructionFactory = null) { this.Code = code; @@ -115,13 +123,23 @@ public Disassembler(IAssemblyCode code, ArchitectureMode architecture, ulong off this.Address = address; this.CopyBinaryToInstruction = copyBinaryToInstruction; this.Vendor = vendor; - this.Offset = offset; + if (instructionFactory == null) + { + _instructionFactory = new InstructionFactory(); + } + else + { + _instructionFactory = instructionFactory; + } InitUdis86(); } /// - /// Prepares a new disassembler instance for the code provided. The instructions can then be disassembled with a call to . The base address used to resolve relative addresses should be provided in . + /// Prepares a new disassembler instance for the code provided.The instructions + /// can then be disassembled with a call to . + /// The base address used to resolve relative addresses should be provided + /// in . /// /// The code to be disassembled /// The target x86 instruction set architecture of the code (e.g. 64-bit, 32-bit or 16-bit). @@ -129,8 +147,15 @@ public Disassembler(IAssemblyCode code, ArchitectureMode architecture, ulong off /// Keeps a copy of the binary code for the instruction. This will increase the memory usage for each instruction. This is necessary if planning on using the option. /// What vendor instructions to support during disassembly, default is Any. Other options are AMD or Intel. /// The offset. - public Disassembler(byte[] code, ArchitectureMode architecture, ulong address = 0x0, bool copyBinaryToInstruction = false, Vendor vendor = Vendor.Any, ulong offset = 0) - : this(new AssemblyCodeArray(code), architecture, offset, address, copyBinaryToInstruction, vendor) + /// instruction factory, when null + /// class is created + public Disassembler(byte[] code, ArchitectureMode architecture, + ulong address = 0x0, bool copyBinaryToInstruction = false, + Vendor vendor = Vendor.Any, ulong offset = 0, + IInstructionFactory instructionFactory = null) + : this(new AssemblyCodeArray(code),architecture, offset, + address, copyBinaryToInstruction, + vendor,instructionFactory) { } @@ -139,12 +164,24 @@ public Disassembler(byte[] code, ArchitectureMode architecture, ulong address = /// /// A pointer to memory to be disassembled. /// The maximum length to be disassembled. - /// The architecture of the code (e.g. 64-bit, 32-bit or 16-bit). - /// The address of the first byte of code. This value is used to resolve relative addresses into absolute addresses while disassembling. - /// Keeps a copy of the binary code for the instruction. This will increase the memory usage for each instruction. This is necessary if planning on using the option. - /// What vendors to support for disassembly, default is Any. Other options are AMD or Intel. - public Disassembler(IntPtr codePtr, int codeLength, ArchitectureMode architecture, ulong address = 0x0, bool copyBinaryToInstruction = false, Vendor vendor = Vendor.Any) - : this(new AssemblyCodeMemory(codePtr, codeLength), architecture, 0, address, copyBinaryToInstruction, vendor) + /// The architecture of the code + /// + /// (e.g. 64-bit, 32-bit or 16-bit). + /// The address of the first byte of code. + /// This value is used to resolve relative addresses into absolute addresses while disassembling. + /// Keeps a copy of the binary code for the + /// instruction. This will increase the memory usage for each instruction. + /// This is necessary if planning on using the + /// option. + /// What vendors to support for disassembly, default is Any. + /// Other options are AMD or Intel. + /// instruction factory, when null + /// class is created + public Disassembler(IntPtr codePtr, int codeLength, ArchitectureMode architecture, + ulong address = 0x0, bool copyBinaryToInstruction = false, + Vendor vendor = Vendor.Any,IInstructionFactory instructionFactory = null) + : this(new AssemblyCodeMemory(codePtr, codeLength), architecture, + 0, address, copyBinaryToInstruction, vendor,instructionFactory) { if (codePtr == IntPtr.Zero) throw new ArgumentOutOfRangeException("codePtr"); @@ -175,10 +212,10 @@ private void InitUdis86() /// Disassemble instructions and yield the result. Breaking out of the enumerator will prevent further instructions being disassembled. /// /// An IEnumerable collection of disassembled instructions - public IEnumerable Disassemble() + public IEnumerable Disassemble() { Reset(); - Instruction instruction = null; + IInstruction instruction = null; while ((instruction = NextInstruction()) != null) { yield return instruction; @@ -199,12 +236,12 @@ public void Reset() /// Decodes a single instruction and increments buffer position. /// /// - public Instruction NextInstruction() + public IInstruction NextInstruction() { int length = 0; if ((length = Udis86.udis86.ud_disassemble(ref _u)) > 0) { - var instruction = new Instruction(ref _u, CopyBinaryToInstruction); + var instruction = _instructionFactory.Create(ref _u, CopyBinaryToInstruction); if (!instruction.Error) { BytesDecoded += length; diff --git a/SharpDisasm/Factory/IInstructionFactory.cs b/SharpDisasm/Factory/IInstructionFactory.cs new file mode 100644 index 0000000..860e75f --- /dev/null +++ b/SharpDisasm/Factory/IInstructionFactory.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace SharpDisasm.Factory +{ + /// + /// interface for instruction factory + /// + public interface IInstructionFactory + { + /// + /// The create method of the factory + /// + /// the internal instruction parser + /// To copy the binary bytes to instruction + /// Instructuion instance + IInstruction Create(ref Udis86.ud u, bool keepBinary); + } +} diff --git a/SharpDisasm/Factory/InstructionFactory.cs b/SharpDisasm/Factory/InstructionFactory.cs new file mode 100644 index 0000000..e422183 --- /dev/null +++ b/SharpDisasm/Factory/InstructionFactory.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace SharpDisasm.Factory +{ + /// + /// Instruction factory + /// + public class InstructionFactory : IInstructionFactory + { + /// + /// The create method of the factory + /// + /// the internal instruction parser + /// To copy the binary bytes to instruction + /// Instructuion instance + public IInstruction Create(ref Udis86.ud u, bool keepBinary) + { + return new Instruction( ref u, keepBinary); + } + } +} diff --git a/SharpDisasm/IInstruction.cs b/SharpDisasm/IInstruction.cs new file mode 100644 index 0000000..a9650b4 --- /dev/null +++ b/SharpDisasm/IInstruction.cs @@ -0,0 +1,54 @@ +using SharpDisasm.Udis86; + +namespace SharpDisasm +{ + /// + /// instruction interface + /// + public interface IInstruction + { + /// + /// Instruction Offset + /// + byte[] Bytes { get; } + + /// + /// Indicates whether the instruction was successfully decoded. + /// + bool Error { get; } + + /// + /// The reason an instruction was not successfully decoded. + /// + string ErrorMessage { get; } + + /// + /// The length of the instruction in bytes + /// + int Length { get; } + + /// + /// Mnemonic + /// + ud_mnemonic_code Mnemonic { get; } + + /// + /// Instruction offset + /// + ulong Offset { get; } + + /// + /// Instruction Operends (maximum 3) + /// + Operand[] Operands { get; } + + /// + /// Program counter + /// + ulong PC { get; } + + + + + } +} \ No newline at end of file diff --git a/SharpDisasm/Instruction.cs b/SharpDisasm/Instruction.cs index 3d74ce7..f87bea2 100644 --- a/SharpDisasm/Instruction.cs +++ b/SharpDisasm/Instruction.cs @@ -47,47 +47,47 @@ namespace SharpDisasm /// /// Represents a decoded instruction. /// - public class Instruction + public class Instruction : IInstruction { /// /// Instruction Offset /// - public ulong Offset { get; private set; } + public ulong Offset { get; protected set; } /// /// Program counter /// - public ulong PC { get; private set; } + public ulong PC { get; protected set; } /// /// Will contain a copy of the original binary instruction if is true. /// - public byte[] Bytes { get; private set; } + public byte[] Bytes { get; protected set; } /// /// Mnemonic /// - public SharpDisasm.Udis86.ud_mnemonic_code Mnemonic { get; private set; } + public SharpDisasm.Udis86.ud_mnemonic_code Mnemonic { get; protected set; } /// /// The instruction operands (maximum 3) /// - public Operand[] Operands { get; private set; } + public Operand[] Operands { get; protected set; } /// /// The length of the instruction in bytes /// - public int Length { get; private set; } + public int Length { get; protected set; } /// /// Indicates whether the instruction was successfully decoded. /// - public bool Error { get; private set; } + public bool Error { get; protected set; } /// /// The reason an instruction was not successfully decoded. /// - public string ErrorMessage { get; private set; } + public string ErrorMessage { get; protected set; } #region Low-level instruction information From 9410bac46bae1b1528f0c71f3a9496824938ccdb Mon Sep 17 00:00:00 2001 From: shlomoartsi <40729917+shlomoartsi@users.noreply.github.com> Date: Thu, 5 Jul 2018 12:45:15 +0300 Subject: [PATCH 3/3] Interfacing Instruction class and introducing Instruction factories instead of using 'new Instruction()' --- SharpDisasm.Tests/Decode64bitTests.cs | 6 ++---- SharpDisasm.Tests/DisassemblerTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/SharpDisasm.Tests/Decode64bitTests.cs b/SharpDisasm.Tests/Decode64bitTests.cs index 7b490f4..122ceca 100644 --- a/SharpDisasm.Tests/Decode64bitTests.cs +++ b/SharpDisasm.Tests/Decode64bitTests.cs @@ -31,9 +31,8 @@ public void Disp64Test() //0000000000000018 4c03849800000080 add r8, [rax+rbx*4-0x80000000] //0000000000000020 48a1000000000080 mov rax, [0x800000000000] - Instruction insn = null; - insn = disasm.NextInstruction(); + var insn = disasm.NextInstruction(); Assert.AreEqual("mov ax, [eax-0x10]", insn.ToString()); insn = disasm.NextInstruction(); @@ -61,9 +60,8 @@ public void NegativeRIPAddress() 0xFF, 0x15, 0xF7, 0xFF, 0xFF, 0xFF, // call qword [rip-0x9] }, ArchitectureMode.x86_64); - Instruction insn = null; - insn = disasm.NextInstruction(); + var insn = disasm.NextInstruction(); Assert.AreEqual("mov rax, [rip-0x9]", insn.ToString()); insn = disasm.NextInstruction(); diff --git a/SharpDisasm.Tests/DisassemblerTests.cs b/SharpDisasm.Tests/DisassemblerTests.cs index 792a356..e66a105 100644 --- a/SharpDisasm.Tests/DisassemblerTests.cs +++ b/SharpDisasm.Tests/DisassemblerTests.cs @@ -20,7 +20,7 @@ public void DisassembleBytesDecoded() 0x00, 0x67, // invalid }, ArchitectureMode.x86_32, 0, false); - foreach (SharpDisasm.Instruction instruction in disasm.Disassemble()) + foreach (SharpDisasm.IInstruction instruction in disasm.Disassemble()) { Assert.IsTrue(instruction.Length > 0); } @@ -55,7 +55,7 @@ where insn.Length > 5 select insn).First().ToString()); - foreach (SharpDisasm.Instruction instruction in results) + foreach (SharpDisasm.IInstruction instruction in results) { Assert.IsFalse(instruction.Error); Assert.IsTrue(instruction.Length > 0);