From 0ab2b81261d02a3376de327d6214f93a620d2c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Geis?= Date: Thu, 2 Aug 2018 04:49:31 +0200 Subject: [PATCH] Refactor to target .NET Standard 1.1 (#344) * Refactor to target .NET Standard 1.1 * Added Nuget details to .csproj * Reviewed Engine methods and docs; added own Exception type. * Added migration guide for .NET bindings. --- .../csharp/{KeystoneNET => }/.gitattributes | 0 bindings/csharp/{KeystoneNET => }/.gitignore | 0 .../Keystone.Net.Tests.csproj | 20 + bindings/csharp/Keystone.Net.Tests/Tests.cs | 78 ++++ bindings/csharp/Keystone.Net.sln | 50 +++ .../Constants/ARM64Constants.cs | 2 +- .../Constants/ARMConstants.cs | 2 +- .../Constants/HexagonConstants.cs | 2 +- .../Constants/KeystoneConstants.cs | 84 ++-- .../Constants/MipsConstants.cs | 2 +- .../Constants/PPCConstants.cs | 2 +- .../Constants/SPARCConstants.cs | 2 +- .../Constants/SystemZConstants.cs | 2 +- .../Constants/X86Constants.cs | 2 +- bindings/csharp/Keystone.Net/EncodedData.cs | 33 ++ bindings/csharp/Keystone.Net/Engine.cs | 370 ++++++++++++++++++ .../csharp/Keystone.Net/Keystone.Net.csproj | 28 ++ .../csharp/Keystone.Net/KeystoneException.cs | 30 ++ bindings/csharp/Keystone.Net/NativeInterop.cs | 66 ++++ .../KeystoneNET/KeystoneNET.Tests/App.config | 9 - .../KeystoneNET.Tests/Compilation.feature | 20 - .../KeystoneNET.Tests/Compilation.feature.cs | 127 ------ .../KeystoneNET.Tests/CompilationSteps.cs | 84 ---- .../KeystoneNET.Tests.csproj | 113 ------ .../Properties/AssemblyInfo.cs | 18 - .../KeystoneNET.Tests/packages.config | 6 - bindings/csharp/KeystoneNET/KeystoneNET.sln | 28 -- .../KeystoneNET/KeystoneNET/Keystone.cs | 303 -------------- .../KeystoneNET/KeystoneEncoded.cs | 36 -- .../KeystoneNET/KeystoneImports.cs | 66 ---- .../KeystoneNET/KeystoneNET.csproj | 70 ---- .../KeystoneNET/Properties/AssemblyInfo.cs | 18 - bindings/csharp/MIGRATION.md | 83 ++++ bindings/csharp/README.md | 37 +- 34 files changed, 831 insertions(+), 962 deletions(-) rename bindings/csharp/{KeystoneNET => }/.gitattributes (100%) rename bindings/csharp/{KeystoneNET => }/.gitignore (100%) create mode 100644 bindings/csharp/Keystone.Net.Tests/Keystone.Net.Tests.csproj create mode 100644 bindings/csharp/Keystone.Net.Tests/Tests.cs create mode 100644 bindings/csharp/Keystone.Net.sln rename bindings/csharp/{KeystoneNET/KeystoneNET => Keystone.Net}/Constants/ARM64Constants.cs (91%) rename bindings/csharp/{KeystoneNET/KeystoneNET => Keystone.Net}/Constants/ARMConstants.cs (91%) rename bindings/csharp/{KeystoneNET/KeystoneNET => Keystone.Net}/Constants/HexagonConstants.cs (91%) rename bindings/csharp/{KeystoneNET/KeystoneNET => Keystone.Net}/Constants/KeystoneConstants.cs (61%) rename bindings/csharp/{KeystoneNET/KeystoneNET => Keystone.Net}/Constants/MipsConstants.cs (91%) rename bindings/csharp/{KeystoneNET/KeystoneNET => Keystone.Net}/Constants/PPCConstants.cs (91%) rename bindings/csharp/{KeystoneNET/KeystoneNET => Keystone.Net}/Constants/SPARCConstants.cs (91%) rename bindings/csharp/{KeystoneNET/KeystoneNET => Keystone.Net}/Constants/SystemZConstants.cs (91%) rename bindings/csharp/{KeystoneNET/KeystoneNET => Keystone.Net}/Constants/X86Constants.cs (91%) create mode 100644 bindings/csharp/Keystone.Net/EncodedData.cs create mode 100644 bindings/csharp/Keystone.Net/Engine.cs create mode 100644 bindings/csharp/Keystone.Net/Keystone.Net.csproj create mode 100644 bindings/csharp/Keystone.Net/KeystoneException.cs create mode 100644 bindings/csharp/Keystone.Net/NativeInterop.cs delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET.Tests/App.config delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET.Tests/Compilation.feature delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET.Tests/Compilation.feature.cs delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET.Tests/CompilationSteps.cs delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET.Tests/KeystoneNET.Tests.csproj delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET.Tests/Properties/AssemblyInfo.cs delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET.Tests/packages.config delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET.sln delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET/Keystone.cs delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET/KeystoneEncoded.cs delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET/KeystoneImports.cs delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET/KeystoneNET.csproj delete mode 100644 bindings/csharp/KeystoneNET/KeystoneNET/Properties/AssemblyInfo.cs create mode 100644 bindings/csharp/MIGRATION.md diff --git a/bindings/csharp/KeystoneNET/.gitattributes b/bindings/csharp/.gitattributes similarity index 100% rename from bindings/csharp/KeystoneNET/.gitattributes rename to bindings/csharp/.gitattributes diff --git a/bindings/csharp/KeystoneNET/.gitignore b/bindings/csharp/.gitignore similarity index 100% rename from bindings/csharp/KeystoneNET/.gitignore rename to bindings/csharp/.gitignore diff --git a/bindings/csharp/Keystone.Net.Tests/Keystone.Net.Tests.csproj b/bindings/csharp/Keystone.Net.Tests/Keystone.Net.Tests.csproj new file mode 100644 index 00000000..6dd30b6d --- /dev/null +++ b/bindings/csharp/Keystone.Net.Tests/Keystone.Net.Tests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.0 + Keystone.Tests + false + + + + + + + + + + + + + + diff --git a/bindings/csharp/Keystone.Net.Tests/Tests.cs b/bindings/csharp/Keystone.Net.Tests/Tests.cs new file mode 100644 index 00000000..a3abc65c --- /dev/null +++ b/bindings/csharp/Keystone.Net.Tests/Tests.cs @@ -0,0 +1,78 @@ +using System; +using NUnit.Framework; +using Shouldly; + +namespace Keystone.Tests +{ + [TestFixture] + public class ExecutionTests + { + [OneTimeSetUp] + public static void InitializeKeystone() + { + // Ensures the lib could be loaded + Engine.IsArchitectureSupported(Architecture.X86).ShouldBeTrue(); + } + + [Test] + public void ShouldEmitValidX86Data() + { + using (Engine engine = new Engine(Architecture.X86, Mode.X32) { ThrowOnError = true }) + { + engine.Assemble("nop", 0).Buffer.ShouldBe(new byte[] { 0x90 }); + engine.Assemble("add eax, eax", 0).Buffer.ShouldBe(new byte[] { 0x01, 0xC0 }); + } + } + + [Test] + public void ShouldEmitValidARMData() + { + using (Engine engine = new Engine(Architecture.ARM, Mode.ARM) { ThrowOnError = true }) + { + engine.Assemble("mul r1, r0, r0", 0).Buffer.ShouldBe(new byte[] { 0x90, 0x00, 0x01, 0xE0 }); + } + } + + [Test] + public void ShouldThrowOnError() + { + using (Engine engine = new Engine(Architecture.ARM, Mode.ARM) { ThrowOnError = false }) + { + engine.Assemble("push eax, 0x42", 0).ShouldBeNull(); + engine.Assemble("doesntexist", 0).ShouldBeNull(); + } + + using (Engine engine = new Engine(Architecture.ARM, Mode.ARM) { ThrowOnError = true }) + { + Should.Throw(() => engine.Assemble("push eax, 0x42", 0)); + Should.Throw(() => engine.Assemble("doestexist", 0)); + } + } + + [Test, Ignore("Feature requires Keystone built after October 7th 2016.")] + public void ShouldHaveValidExample() + { + using (Engine keystone = new Engine(Architecture.X86, Mode.X32) { ThrowOnError = true }) + { + ulong address = 0; + + keystone.ResolveSymbol += (string s, ref ulong w) => + { + if (s == "_j1") + { + w = 0x1234abcd; + return true; + } + + return false; + }; + + EncodedData enc = keystone.Assemble("xor eax, eax; jmp _j1", address); + + enc.Buffer.ShouldBe(new byte[] { 0x00 }); + enc.Address.ShouldBe(address); + enc.StatementCount.ShouldBe(3); + } + } + } +} diff --git a/bindings/csharp/Keystone.Net.sln b/bindings/csharp/Keystone.Net.sln new file mode 100644 index 00000000..16da386d --- /dev/null +++ b/bindings/csharp/Keystone.Net.sln @@ -0,0 +1,50 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2036 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Keystone.Net", "Keystone.Net\Keystone.Net.csproj", "{E3579F77-600A-4146-9296-3834B81F16E2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Keystone.Net.Tests", "Keystone.Net.Tests\Keystone.Net.Tests.csproj", "{377B6FDC-156F-4B8B-B693-2A5C7B604A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E3579F77-600A-4146-9296-3834B81F16E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Debug|x64.Build.0 = Debug|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Debug|x86.ActiveCfg = Debug|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Debug|x86.Build.0 = Debug|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Release|Any CPU.Build.0 = Release|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Release|x64.ActiveCfg = Release|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Release|x64.Build.0 = Release|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Release|x86.ActiveCfg = Release|Any CPU + {E3579F77-600A-4146-9296-3834B81F16E2}.Release|x86.Build.0 = Release|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Debug|x64.ActiveCfg = Debug|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Debug|x64.Build.0 = Debug|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Debug|x86.ActiveCfg = Debug|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Debug|x86.Build.0 = Debug|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Release|Any CPU.Build.0 = Release|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Release|x64.ActiveCfg = Release|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Release|x64.Build.0 = Release|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Release|x86.ActiveCfg = Release|Any CPU + {377B6FDC-156F-4B8B-B693-2A5C7B604A97}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0DA5689F-C570-41D7-93A8-F33F20F4B618} + EndGlobalSection +EndGlobal diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/ARM64Constants.cs b/bindings/csharp/Keystone.Net/Constants/ARM64Constants.cs similarity index 91% rename from bindings/csharp/KeystoneNET/KeystoneNET/Constants/ARM64Constants.cs rename to bindings/csharp/Keystone.Net/Constants/ARM64Constants.cs index a344bb3a..33eb5458 100644 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/ARM64Constants.cs +++ b/bindings/csharp/Keystone.Net/Constants/ARM64Constants.cs @@ -1,5 +1,5 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm64Constants.cs] -namespace KeystoneNET +namespace Keystone { public enum Arm64Error : short { diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/ARMConstants.cs b/bindings/csharp/Keystone.Net/Constants/ARMConstants.cs similarity index 91% rename from bindings/csharp/KeystoneNET/KeystoneNET/Constants/ARMConstants.cs rename to bindings/csharp/Keystone.Net/Constants/ARMConstants.cs index 29bc5954..f8d0c273 100644 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/ARMConstants.cs +++ b/bindings/csharp/Keystone.Net/Constants/ARMConstants.cs @@ -1,5 +1,5 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [armConstants.cs] -namespace KeystoneNET +namespace Keystone { public enum ArmError : short { diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/HexagonConstants.cs b/bindings/csharp/Keystone.Net/Constants/HexagonConstants.cs similarity index 91% rename from bindings/csharp/KeystoneNET/KeystoneNET/Constants/HexagonConstants.cs rename to bindings/csharp/Keystone.Net/Constants/HexagonConstants.cs index aa6a8830..bfb0da9f 100644 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/HexagonConstants.cs +++ b/bindings/csharp/Keystone.Net/Constants/HexagonConstants.cs @@ -1,5 +1,5 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [hexagonConstants.cs] -namespace KeystoneNET +namespace Keystone { public enum HexagonError : short { diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/KeystoneConstants.cs b/bindings/csharp/Keystone.Net/Constants/KeystoneConstants.cs similarity index 61% rename from bindings/csharp/KeystoneNET/KeystoneNET/Constants/KeystoneConstants.cs rename to bindings/csharp/Keystone.Net/Constants/KeystoneConstants.cs index 29fd0d7a..124e5247 100644 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/KeystoneConstants.cs +++ b/bindings/csharp/Keystone.Net/Constants/KeystoneConstants.cs @@ -1,40 +1,40 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystoneConstants.cs] -namespace KeystoneNET +namespace Keystone { - public enum KeystoneArchitecture : int + public enum Architecture : int { - KS_ARCH_ARM = 1, - KS_ARCH_ARM64 = 2, - KS_ARCH_MIPS = 3, - KS_ARCH_X86 = 4, - KS_ARCH_PPC = 5, - KS_ARCH_SPARC = 6, - KS_ARCH_SYSTEMZ = 7, - KS_ARCH_HEXAGON = 8, - KS_ARCH_MAX = 9, + ARM = 1, + ARM64 = 2, + MIPS = 3, + X86 = 4, + PPC = 5, + SPARC = 6, + SYSTEMZ = 7, + HEXAGON = 8, + MAX = 9, } - public enum KeystoneMode : uint + public enum Mode : uint { - KS_MODE_LITTLE_ENDIAN = 0, - KS_MODE_BIG_ENDIAN = 1073741824, - KS_MODE_ARM = 1, - KS_MODE_THUMB = 16, - KS_MODE_V8 = 64, - KS_MODE_MICRO = 16, - KS_MODE_MIPS3 = 32, - KS_MODE_MIPS32R6 = 64, - KS_MODE_MIPS32 = 4, - KS_MODE_MIPS64 = 8, - KS_MODE_16 = 2, - KS_MODE_32 = 4, - KS_MODE_64 = 8, - KS_MODE_PPC32 = 4, - KS_MODE_PPC64 = 8, - KS_MODE_QPX = 16, - KS_MODE_SPARC32 = 4, - KS_MODE_SPARC64 = 8, - KS_MODE_V9 = 16, + LITTLE_ENDIAN = 0, + BIG_ENDIAN = 1073741824, + ARM = 1, + THUMB = 16, + V8 = 64, + MICRO = 16, + MIPS3 = 32, + MIPS32R6 = 64, + MIPS32 = 4, + MIPS64 = 8, + X16 = 2, + X32 = 4, + X64 = 8, + PPC32 = 4, + PPC64 = 8, + QPX = 16, + SPARC32 = 4, + SPARC64 = 8, + V9 = 16, } public enum KeystoneError : short @@ -86,22 +86,22 @@ public enum KeystoneError : short KS_ERR_ASM_FRAGMENT_INVALID = 163, KS_ERR_ASM_INVALIDOPERAND = 512, KS_ERR_ASM_MISSINGFEATURE = 513, - KS_ERR_ASM_MNEMONICFAIL = 514, + KS_ERR_ASM_MNEMONICFAIL = 514 } - public enum KeystoneOptionType : short + public enum OptionType : int { - KS_OPT_SYNTAX = 1, - KS_OPT_SYM_RESOLVER = 2, + SYNTAX = 1, + SYM_RESOLVER = 2, } - public enum KeystoneOptionValue : short + public enum OptionValue : short { - KS_OPT_SYNTAX_INTEL = 1, - KS_OPT_SYNTAX_ATT = 2, - KS_OPT_SYNTAX_NASM = 4, - KS_OPT_SYNTAX_MASM = 8, - KS_OPT_SYNTAX_GAS = 16, - KS_OPT_SYNTAX_RADIX16 = 32, + SYNTAX_INTEL = 1, + SYNTAX_ATT = 2, + SYNTAX_NASM = 4, + SYNTAX_MASM = 8, + SYNTAX_GAS = 16, + SYNTAX_RADIX16 = 32 } } \ No newline at end of file diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/MipsConstants.cs b/bindings/csharp/Keystone.Net/Constants/MipsConstants.cs similarity index 91% rename from bindings/csharp/KeystoneNET/KeystoneNET/Constants/MipsConstants.cs rename to bindings/csharp/Keystone.Net/Constants/MipsConstants.cs index 208422dc..99be03dc 100644 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/MipsConstants.cs +++ b/bindings/csharp/Keystone.Net/Constants/MipsConstants.cs @@ -1,5 +1,5 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [mipsConstants.cs] -namespace KeystoneNET +namespace Keystone { public enum MipsError : short { diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/PPCConstants.cs b/bindings/csharp/Keystone.Net/Constants/PPCConstants.cs similarity index 91% rename from bindings/csharp/KeystoneNET/KeystoneNET/Constants/PPCConstants.cs rename to bindings/csharp/Keystone.Net/Constants/PPCConstants.cs index e4d1672a..601585a7 100644 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/PPCConstants.cs +++ b/bindings/csharp/Keystone.Net/Constants/PPCConstants.cs @@ -1,5 +1,5 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [ppcConstants.cs] -namespace KeystoneNET +namespace Keystone { public enum PpcError : short { diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/SPARCConstants.cs b/bindings/csharp/Keystone.Net/Constants/SPARCConstants.cs similarity index 91% rename from bindings/csharp/KeystoneNET/KeystoneNET/Constants/SPARCConstants.cs rename to bindings/csharp/Keystone.Net/Constants/SPARCConstants.cs index 5af9e229..6de93d20 100644 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/SPARCConstants.cs +++ b/bindings/csharp/Keystone.Net/Constants/SPARCConstants.cs @@ -1,5 +1,5 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [sparcConstants.cs] -namespace KeystoneNET +namespace Keystone { public enum SparcError : short { diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/SystemZConstants.cs b/bindings/csharp/Keystone.Net/Constants/SystemZConstants.cs similarity index 91% rename from bindings/csharp/KeystoneNET/KeystoneNET/Constants/SystemZConstants.cs rename to bindings/csharp/Keystone.Net/Constants/SystemZConstants.cs index c8aa105d..d2b61320 100644 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/SystemZConstants.cs +++ b/bindings/csharp/Keystone.Net/Constants/SystemZConstants.cs @@ -1,5 +1,5 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [systemzConstants.cs] -namespace KeystoneNET +namespace Keystone { public enum SystemzError : short { diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/X86Constants.cs b/bindings/csharp/Keystone.Net/Constants/X86Constants.cs similarity index 91% rename from bindings/csharp/KeystoneNET/KeystoneNET/Constants/X86Constants.cs rename to bindings/csharp/Keystone.Net/Constants/X86Constants.cs index 9ddfe094..79743f7d 100644 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Constants/X86Constants.cs +++ b/bindings/csharp/Keystone.Net/Constants/X86Constants.cs @@ -1,5 +1,5 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [x86Constants.cs] -namespace KeystoneNET +namespace Keystone { public enum X86Error : short { diff --git a/bindings/csharp/Keystone.Net/EncodedData.cs b/bindings/csharp/Keystone.Net/EncodedData.cs new file mode 100644 index 00000000..675cb3aa --- /dev/null +++ b/bindings/csharp/Keystone.Net/EncodedData.cs @@ -0,0 +1,33 @@ +namespace Keystone +{ + /// + /// Defines an encoded instruction or group of instructions. + /// + public sealed class EncodedData + { + /// + /// Constructs the encoded data. + /// + internal EncodedData(byte[] buffer, int statementCount, ulong address) + { + Buffer = buffer; + Address = address; + StatementCount = statementCount; + } + + /// + /// Gets the address of the first instruction for this operation. + /// + public ulong Address { get; } + + /// + /// Gets the result of an assembly operation. + /// + public byte[] Buffer { get; } + + /// + /// Gets the number of statements found. + /// + public int StatementCount { get; } + } +} diff --git a/bindings/csharp/Keystone.Net/Engine.cs b/bindings/csharp/Keystone.Net/Engine.cs new file mode 100644 index 00000000..86f2a04f --- /dev/null +++ b/bindings/csharp/Keystone.Net/Engine.cs @@ -0,0 +1,370 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Keystone +{ + /// + /// Represents a Keystone engine. + /// + public sealed class Engine : IDisposable + { + private IntPtr engine = IntPtr.Zero; + private bool addedResolveSymbol; + + private readonly ResolverInternal internalImpl; + private readonly List resolvers = new List(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool ResolverInternal(IntPtr symbol, ref ulong value); + + + /// + /// Gets or sets a value that represents whether a + /// should be thrown on error. + /// + public bool ThrowOnError { get; set; } + + /// + /// Delegate for defining symbol resolvers. + /// + /// Symbol to resolve. + /// Address of taid symbol, if found. + /// Whether the symbol was recognized. + public delegate bool Resolver(string symbol, ref ulong value); + + /// + /// Event raised when keystone is resolving a symbol. + /// + /// This event is only available on Keystone 0.9.2 or higher. + public event Resolver ResolveSymbol + { + add + { + if (!addedResolveSymbol) + { + KeystoneError err = NativeInterop.SetOption(engine, (int)OptionType.SYM_RESOLVER, Marshal.GetFunctionPointerForDelegate(internalImpl)); + + if (err == KeystoneError.KS_ERR_OK) + addedResolveSymbol = true; + else + throw new KeystoneException("Could not add symbol resolver", err); + } + + resolvers.Add(value); + } + + remove + { + if (addedResolveSymbol && resolvers.Count == 0) + { + KeystoneError err = NativeInterop.SetOption(engine, (int)OptionType.SYM_RESOLVER, IntPtr.Zero); + + if (err == KeystoneError.KS_ERR_OK) + addedResolveSymbol = false; + else + throw new KeystoneException("Could not remove symbol resolver", err); + } + + resolvers.Remove(value); + } + } + + /// + /// Method used for symbol resolving. + /// + /// Pointer to the name of the symbol. + /// Address of the symbol, if found. + /// Whether the symbol could be recognized. + private bool ResolveSymbolInternal(IntPtr symbolPtr, ref ulong value) + { + string symbol = Marshal.PtrToStringAnsi(symbolPtr); + + foreach (Resolver item in resolvers) + { + bool result = item(symbol, ref value); + if (result) + return true; + } + + return false; + } + + /// + /// Constructs the engine with a given architecture and a given mode. + /// + /// The target architecture. + /// The mode, i.e. endianness, word size etc. + /// + /// Some architectures are not supported. + /// Check with if the engine + /// supports the target architecture. + /// + public Engine(Architecture architecture, Mode mode) + { + internalImpl = ResolveSymbolInternal; + + var result = NativeInterop.Open(architecture, (int)mode, ref engine); + + if (result != KeystoneError.KS_ERR_OK) + throw new KeystoneException("Error while initializing keystone", result); + } + + /// + /// Sets an option in the engine. + /// + /// Type of the option. + /// Value it the option. + /// Whether the option was correctly set. + /// An error encountered when setting the option. + public bool SetOption(OptionType type, uint value) + { + var result = NativeInterop.SetOption(engine, (int)type, (IntPtr)value); + + if (result != KeystoneError.KS_ERR_OK) + { + if (ThrowOnError) + throw new KeystoneException("Error while setting option", result); + + return false; + } + + return true; + } + + /// + /// Encodes the given statement(s). + /// + /// String that contains the statements to encode. + /// Address of the first instruction to encode. + /// Size of the buffer produced by the operation. + /// Number of statements found and encoded. + /// Result of the operation, or null if it failed and is false. + /// A null argument was given. + /// An error encountered when encoding the instructions. + public byte[] Assemble(string toEncode, ulong address, out int size, out int statementCount) + { + if (toEncode == null) + throw new ArgumentNullException(nameof(toEncode)); + + int result = NativeInterop.Assemble(engine, + toEncode, + address, + out IntPtr encoding, + out uint size_, + out uint statementCount_); + + if (result != 0) + { + if (ThrowOnError) + throw new KeystoneException("Error while assembling instructions", GetLastKeystoneError()); + + size = statementCount = 0; + + return null; + } + + size = (int)size_; + statementCount = (int)statementCount_; + + byte[] buffer = new byte[size]; + + Marshal.Copy(encoding, buffer, 0, size); + NativeInterop.Free(encoding); + + return buffer; + } + + /// + /// Encodes the given statement(s). + /// + /// String that contains the statements to encode. + /// Address of the first instruction to encode. + /// Result of the operation, or null if it failed and is false. + /// A null argument was given. + /// An error encountered when encoding the instructions. + public EncodedData Assemble(string toEncode, ulong address) + { + byte[] buffer = Assemble(toEncode, address, out int size, out int statementCount); + + if (buffer == null) + return null; + + return new EncodedData(buffer, statementCount, address); + } + + /// + /// Encodes the given statement(s) into the given buffer. + /// + /// String that contains the statements to encode. + /// Address of the first instruction to encode. + /// Buffer into which the data shall be written. + /// Index into the buffer after which the data shall be written. + /// Number of statements found and encoded. + /// Size of the data writen by the operation., or 0 if it failed and is false. + /// A null argument was given. + /// The provided index is invalid. + /// An error encountered when encoding the instructions. + public int Assemble(string toEncode, ulong address, byte[] buffer, int index, out int statementCount) + { + if (toEncode == null) + throw new ArgumentNullException(nameof(toEncode)); + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + if (index < 0 || index >= buffer.Length) + throw new ArgumentOutOfRangeException(nameof(buffer)); + + int result = NativeInterop.Assemble(engine, + toEncode, + address, + out IntPtr encoding, + out uint size_, + out uint statementCount_); + + int size = (int)size_; + + statementCount = (int)statementCount_; + + if (result != 0) + { + if (ThrowOnError) + throw new KeystoneException("Error while assembling instructions", GetLastKeystoneError()); + + return 0; + } + + Marshal.Copy(encoding, buffer, index, size); + NativeInterop.Free(encoding); + + return size; + } + + /// + /// Encodes the given statement(s) into the given buffer. + /// + /// String that contains the statements to encode. + /// Address of the first instruction to encode. + /// Buffer into which the data shall be written. + /// Index into the buffer after which the data shall be written. + /// Size of the data writen by the operation., or 0 if it failed and is false. + /// A null argument was given. + /// The provided index is invalid. + /// An error encountered when encoding the instructions. + public int Assemble(string toEncode, ulong address, byte[] buffer, int index) + { + return Assemble(toEncode, address, buffer, index, out _); + } + + /// + /// Encodes the given statement(s) into the given stream. + /// + /// String that contains the statements to encode. + /// Address of the first instruction to encode. + /// Buffer into which the data shall be written. + /// Size of the buffer produced by the operation. + /// Number of statements found and encoded. + /// true on success, or false if it failed and is false. + /// A null argument was given. + /// An error encountered when encoding the instructions. + public bool Assemble(string toEncode, ulong address, Stream stream, out int size, out int statementCount) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + byte[] enc = Assemble(toEncode, address, out size, out statementCount); + + if (enc == null) + return false; + + stream.Write(enc, 0, size); + + return true; + } + + /// + /// Encodes the given statement(s) into the given stream. + /// + /// String that contains the statements to encode. + /// Address of the first instruction to encode. + /// Buffer into which the data shall be written. + /// Size of the buffer produced by the operation. + /// true on success, or false if it failed and is false. + /// A null argument was given. + /// An error encountered when encoding the instructions. + public bool Assemble(string toEncode, ulong address, Stream stream, out int size) + { + return Assemble(toEncode, address, stream, out size, out _); + } + + /// + /// Encodes the given statement(s) into the given stream. + /// + /// String that contains the statements to encode. + /// Address of the first instruction to encode. + /// Buffer into which the data shall be written. + /// true on success, or false if it failed and is false. + /// A null argument was given. + /// An error encountered when encoding the instructions. + public bool Assemble(string toEncode, ulong address, Stream stream) + { + return Assemble(toEncode, address, stream, out _, out _); + } + + /// + /// Gets the last error for this instance. + /// + /// The last error code. + /// + /// It might not retain its old error once accessed. + /// + public KeystoneError GetLastKeystoneError() + { + return NativeInterop.GetLastKeystoneError(engine); + } + + /// + /// Returns the string associated with a given error code. + /// + public static string ErrorToString(KeystoneError code) + { + IntPtr error = NativeInterop.ErrorToString(code); + + if (error != IntPtr.Zero) + return Marshal.PtrToStringAnsi(error); + + return string.Empty; + } + + /// + /// Checks if the given architecture is supported. + /// + public static bool IsArchitectureSupported(Architecture architecture) + { + return NativeInterop.IsArchitectureSupported(architecture); + } + + /// + /// Gets the version of the engine. + /// + /// Major version number. + /// Minor version number. + /// Unique identifier for this version. + public static uint GetKeystoneVersion(ref uint major, ref uint minor) + { + return NativeInterop.Version(ref major, ref minor); + } + + /// + /// Releases the engine. + /// + public void Dispose() + { + IntPtr currentEngine = Interlocked.Exchange(ref engine, IntPtr.Zero); + + if (currentEngine != IntPtr.Zero) + NativeInterop.Close(currentEngine); + } + } +} diff --git a/bindings/csharp/Keystone.Net/Keystone.Net.csproj b/bindings/csharp/Keystone.Net/Keystone.Net.csproj new file mode 100644 index 00000000..ab55366f --- /dev/null +++ b/bindings/csharp/Keystone.Net/Keystone.Net.csproj @@ -0,0 +1,28 @@ + + + + netstandard1.1 + Keystone + + 1.1.0 + $(Version) + $(Version).0 + + .NET bindings to the Keystone Engine. + Grégoire Geis + + Keystone.Net + $(Version) + False + - First release. + assembler x86 arm keystone + + https://github.com/keystone-engine/keystone + $(PackageProjectUrl)/blob/master/COPYING + http://www.keystone-engine.org/images/keystone.png + + $(PackageProjectUrl).git + git + + + diff --git a/bindings/csharp/Keystone.Net/KeystoneException.cs b/bindings/csharp/Keystone.Net/KeystoneException.cs new file mode 100644 index 00000000..4b16247f --- /dev/null +++ b/bindings/csharp/Keystone.Net/KeystoneException.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Keystone +{ + /// + /// Represents an error encountered while encoding one or more instructions. + /// + public sealed class KeystoneException : Exception + { + /// + /// Gets the value that represents the encountered error. + /// + public KeystoneError Error { get; } + + internal KeystoneException(string message, KeystoneError error) : base(message + '.') + { + Debug.Assert(error != KeystoneError.KS_ERR_OK); + + Error = error; + } + + /// + public override string ToString() + { + return $"{Message}: {Engine.ErrorToString(Error)}."; + } + } +} diff --git a/bindings/csharp/Keystone.Net/NativeInterop.cs b/bindings/csharp/Keystone.Net/NativeInterop.cs new file mode 100644 index 00000000..b910e2fa --- /dev/null +++ b/bindings/csharp/Keystone.Net/NativeInterop.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.InteropServices; + +namespace Keystone +{ + /// + /// Imported symbols for interop with keystone.dll. + /// + internal class NativeInterop + { + // This shouldn't be needed, even on Windows + // /// + // /// Taken from: http://stackoverflow.com/questions/10852634/using-a-32bit-or-64bit-dll-in-c-sharp-dllimport + // /// + // static NativeInterop() + // { + // var myPath = new Uri(typeof(NativeInterop).Assembly.CodeBase).LocalPath; + // var myFolder = Path.GetDirectoryName(myPath); + + // var is64 = IntPtr.Size == 8; + // var subfolder = is64 ? "\\win64\\" : "\\win32\\"; + + // string dllPosition = myFolder + subfolder + "keystone.dll"; + + // // If this file exist, load it. + // // Otherwise let the marshaller load the appropriate file. + // if (File.Exists(dllPosition)) + // LoadLibrary(dllPosition); + // } + + // [DllImport("kernel32.dll")] + // private static extern IntPtr LoadLibrary(string dllToLoad); + + [DllImport("keystone", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_version" )] + internal static extern uint Version(ref uint major, ref uint minor); + + [DllImport("keystone", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_open")] + internal static extern KeystoneError Open(Architecture arch, int mode, ref IntPtr ks); + + [DllImport("keystone", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_close")] + internal static extern KeystoneError Close(IntPtr ks); + + [DllImport("keystone", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_free")] + internal static extern void Free(IntPtr buffer); + + [DllImport("keystone", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_strerror")] + internal static extern IntPtr ErrorToString(KeystoneError code); + + [DllImport("keystone", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_errno")] + internal static extern KeystoneError GetLastKeystoneError(IntPtr ks); + + [DllImport("keystone", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_arch_supported")] + internal static extern bool IsArchitectureSupported(Architecture arch); + + [DllImport("keystone", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_option")] + internal static extern KeystoneError SetOption(IntPtr ks, int type, IntPtr value); + + [DllImport("keystone", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_asm")] + internal static extern int Assemble(IntPtr ks, + [MarshalAs(UnmanagedType.LPStr)] string toEncode, + ulong baseAddress, + out IntPtr encoding, + out uint size, + out uint statements); + } +} diff --git a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/App.config b/bindings/csharp/KeystoneNET/KeystoneNET.Tests/App.config deleted file mode 100644 index f592fcc8..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/App.config +++ /dev/null @@ -1,9 +0,0 @@ - - - -
- - - - - \ No newline at end of file diff --git a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/Compilation.feature b/bindings/csharp/KeystoneNET/KeystoneNET.Tests/Compilation.feature deleted file mode 100644 index cab7829a..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/Compilation.feature +++ /dev/null @@ -1,20 +0,0 @@ -Feature: Compilation - -Scenario: Compile the nop instruction - Given An instance of Keystone built for X86 in mode 32 - And The statement(s) "nop" - When I compile the statement(s) with Keystone - Then the result is 90 - -Scenario: Compile an invalid instruction - Given An instance of Keystone built for X86 in mode 32 - And The statement(s) "invalid instruction" - When I compile the statement(s) with Keystone - Then The last error is ASM_MNEMONICFAIL - -Scenario: Compile a jump to label - Given An instance of Keystone built for X86 in mode 32 - And A dummy symbols resolver - And The statement(s) "jmp _unknownSymbol;nop;" - When I compile the statement(s) with Keystone - Then the result is e9,aa,ab,ab,00,90 diff --git a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/Compilation.feature.cs b/bindings/csharp/KeystoneNET/KeystoneNET.Tests/Compilation.feature.cs deleted file mode 100644 index f95f590b..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/Compilation.feature.cs +++ /dev/null @@ -1,127 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by SpecFlow (http://www.specflow.org/). -// SpecFlow Version:2.2.0.0 -// SpecFlow Generator Version:2.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -#region Designer generated code -#pragma warning disable -namespace KeystoneNET.Tests -{ - using TechTalk.SpecFlow; - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "2.2.0.0")] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [NUnit.Framework.TestFixtureAttribute()] - [NUnit.Framework.DescriptionAttribute("Compilation")] - public partial class CompilationFeature - { - - private TechTalk.SpecFlow.ITestRunner testRunner; - -#line 1 "Compilation.feature" -#line hidden - - [NUnit.Framework.OneTimeSetUpAttribute()] - public virtual void FeatureSetup() - { - testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); - TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Compilation", null, ProgrammingLanguage.CSharp, ((string[])(null))); - testRunner.OnFeatureStart(featureInfo); - } - - [NUnit.Framework.OneTimeTearDownAttribute()] - public virtual void FeatureTearDown() - { - testRunner.OnFeatureEnd(); - testRunner = null; - } - - [NUnit.Framework.SetUpAttribute()] - public virtual void TestInitialize() - { - } - - [NUnit.Framework.TearDownAttribute()] - public virtual void ScenarioTearDown() - { - testRunner.OnScenarioEnd(); - } - - public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) - { - testRunner.OnScenarioStart(scenarioInfo); - } - - public virtual void ScenarioCleanup() - { - testRunner.CollectScenarioErrors(); - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Compile the nop instruction")] - public virtual void CompileTheNopInstruction() - { - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Compile the nop instruction", ((string[])(null))); -#line 3 -this.ScenarioSetup(scenarioInfo); -#line 4 - testRunner.Given("An instance of Keystone built for X86 in mode 32", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); -#line 5 - testRunner.And("The statement(s) \"nop\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); -#line 6 - testRunner.When("I compile the statement(s) with Keystone", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); -#line 7 - testRunner.Then("the result is 90", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line hidden - this.ScenarioCleanup(); - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Compile an invalid instruction")] - public virtual void CompileAnInvalidInstruction() - { - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Compile an invalid instruction", ((string[])(null))); -#line 9 -this.ScenarioSetup(scenarioInfo); -#line 10 - testRunner.Given("An instance of Keystone built for X86 in mode 32", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); -#line 11 - testRunner.And("The statement(s) \"invalid instruction\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); -#line 12 - testRunner.When("I compile the statement(s) with Keystone", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); -#line 13 - testRunner.Then("The last error is ASM_MNEMONICFAIL", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line hidden - this.ScenarioCleanup(); - } - - [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Compile a jump to label")] - public virtual void CompileAJumpToLabel() - { - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Compile a jump to label", ((string[])(null))); -#line 15 -this.ScenarioSetup(scenarioInfo); -#line 16 - testRunner.Given("An instance of Keystone built for X86 in mode 32", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); -#line 17 - testRunner.And("A dummy symbols resolver", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); -#line 18 - testRunner.And("The statement(s) \"jmp _unknownSymbol;nop;\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); -#line 19 - testRunner.When("I compile the statement(s) with Keystone", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); -#line 20 - testRunner.Then("the result is e9,aa,ab,ab,00,90", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); -#line hidden - this.ScenarioCleanup(); - } - } -} -#pragma warning restore -#endregion diff --git a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/CompilationSteps.cs b/bindings/csharp/KeystoneNET/KeystoneNET.Tests/CompilationSteps.cs deleted file mode 100644 index 3421308e..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/CompilationSteps.cs +++ /dev/null @@ -1,84 +0,0 @@ -using NUnit.Framework; -using System; -using System.Globalization; -using System.Linq; -using TechTalk.SpecFlow; - -namespace KeystoneNET.Tests -{ - [Binding] - class CompilationSteps - { - [Given(@"An instance of Keystone built for (.*) in mode (.*)")] - public void GivenAnInstanceOfKeystoneBuiltForInMode(string p0, string p1) - { - var arch = (KeystoneArchitecture)Enum.Parse(typeof(KeystoneArchitecture), $"KS_ARCH_{p0}"); - var mode = (KeystoneMode)Enum.Parse(typeof(KeystoneMode), $"KS_MODE_{p1}"); - - var keystone = new Keystone(arch, mode, false); - ScenarioContext.Current.Add("keystoneInstance", keystone); - } - - [Given(@"The statement\(s\) ""(.*)""")] - public void GivenTheStatements(string p0) - { - ScenarioContext.Current.Add("statements", p0); - } - - [When(@"I compile the statement\(s\) with Keystone")] - public void WhenICompileTheStatementWithKeystone() - { - var statements = ScenarioContext.Current["statements"] as string; - var engine = ScenarioContext.Current["keystoneInstance"] as Keystone; - var result = engine.Assemble(statements, 0); - - ScenarioContext.Current.Add("assembleResult", result); - } - - [Given(@"A dummy symbols resolver")] - public void GivenADummySymbolsResolver() - { - var engine = ScenarioContext.Current["keystoneInstance"] as Keystone; - - engine.ResolveSymbol += (string symbol, ref ulong address) => - { - address = 0xababab; - return true; - }; - } - - - [Then(@"the result is (.*)")] - public void ThenTheResultIs(string p0) - { - string[] bytes = p0.Split(','); - var expectedBytes = bytes.Select(x => byte.Parse(x, NumberStyles.HexNumber)) - .ToList(); - - var result = ScenarioContext.Current["assembleResult"] as KeystoneEncoded; - var engine = ScenarioContext.Current["keystoneInstance"] as Keystone; - - Assert.AreEqual(KeystoneError.KS_ERR_OK, engine.GetLastKeystoneError()); - Assert.AreEqual(expectedBytes.Count, result.Buffer.Length); - - for(int i=0; i< expectedBytes.Count; i++) - Assert.AreEqual(result.Buffer[i], expectedBytes[i]); - } - - [Then(@"The last error is (.*)")] - public void ThenTheLastErrorIs(string error) - { - var errorType = (KeystoneError)Enum.Parse(typeof(KeystoneError), $"KS_ERR_{error}"); - var engine = ScenarioContext.Current["keystoneInstance"] as Keystone; - Assert.AreEqual(engine.GetLastKeystoneError(), errorType); - } - - [AfterScenario] - public void ReleaseKeystone() - { - var engine = ScenarioContext.Current["keystoneInstance"] as Keystone; - if (engine != null) - engine.Dispose(); - } - } -} diff --git a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/KeystoneNET.Tests.csproj b/bindings/csharp/KeystoneNET/KeystoneNET.Tests/KeystoneNET.Tests.csproj deleted file mode 100644 index d54472c0..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/KeystoneNET.Tests.csproj +++ /dev/null @@ -1,113 +0,0 @@ - - - - Debug - AnyCPU - {B9B98FF9-CA76-4DDA-868C-04E8EB151465} - Library - Properties - KeystoneNET.Tests - KeystoneNET.Tests - v4.5.2 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - x86 - true - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\NUnit.3.0.0\lib\net45\nunit.framework.dll - True - - - - ..\packages\SpecFlow.2.2.0-preview20161020\lib\net45\TechTalk.SpecFlow.dll - True - - - - - - - - - - - - - - - - - - True - True - Compilation.feature - - - - - - {2FE3B804-90F7-4632-B6F4-F72548E26984} - KeystoneNET - - - - - - - SpecFlowSingleFileGenerator - Compilation.feature.cs - - - - - - - False - - - False - - - False - - - False - - - - - - - - \ No newline at end of file diff --git a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/Properties/AssemblyInfo.cs b/bindings/csharp/KeystoneNET/KeystoneNET.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index df51abba..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("KeystoneBindingsTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("KeystoneBindingsTests")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] - -[assembly: Guid("b9b98ff9-ca76-4dda-868c-04e8eb151465")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/packages.config b/bindings/csharp/KeystoneNET/KeystoneNET.Tests/packages.config deleted file mode 100644 index 05ee7c5d..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET.Tests/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/bindings/csharp/KeystoneNET/KeystoneNET.sln b/bindings/csharp/KeystoneNET/KeystoneNET.sln deleted file mode 100644 index a3902026..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeystoneNET", "KeystoneNET\KeystoneNET.csproj", "{2FE3B804-90F7-4632-B6F4-F72548E26984}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeystoneNET.Tests", "KeystoneNET.Tests\KeystoneNET.Tests.csproj", "{B9B98FF9-CA76-4DDA-868C-04E8EB151465}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2FE3B804-90F7-4632-B6F4-F72548E26984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2FE3B804-90F7-4632-B6F4-F72548E26984}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2FE3B804-90F7-4632-B6F4-F72548E26984}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2FE3B804-90F7-4632-B6F4-F72548E26984}.Release|Any CPU.Build.0 = Release|Any CPU - {B9B98FF9-CA76-4DDA-868C-04E8EB151465}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B9B98FF9-CA76-4DDA-868C-04E8EB151465}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B9B98FF9-CA76-4DDA-868C-04E8EB151465}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B9B98FF9-CA76-4DDA-868C-04E8EB151465}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Keystone.cs b/bindings/csharp/KeystoneNET/KeystoneNET/Keystone.cs deleted file mode 100644 index bf1c8d0f..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Keystone.cs +++ /dev/null @@ -1,303 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Threading; - -namespace KeystoneNET -{ - /// - /// Manage a Keystone engine. - /// - public class Keystone : IDisposable - { - IntPtr engine = IntPtr.Zero; - bool throwOnError; - bool addedResolveSymbol; - - ResolverInternal internalImpl; - List resolvers = new List(); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - delegate bool ResolverInternal(IntPtr symbol, ref ulong value); - - /// - /// Delegate for defining symbol resolvers. - /// - /// Symbol to resolve - /// Address - /// True if the symbol was recognized. - public delegate bool Resolver(string symbol, ref ulong value); - - /// - /// Event raised when keystone is resolving a symbol. - /// - public event Resolver ResolveSymbol - { - add - { - resolvers.Add(value); - if (!addedResolveSymbol) - { - KeystoneImports.SetOption(engine, KeystoneOptionType.KS_OPT_SYM_RESOLVER, Marshal.GetFunctionPointerForDelegate(internalImpl)); - addedResolveSymbol = true; - } - } - remove - { - resolvers.Remove(value); - if (addedResolveSymbol && resolvers.Count == 0) - { - KeystoneImports.SetOption(engine, KeystoneOptionType.KS_OPT_SYM_RESOLVER, IntPtr.Zero); - addedResolveSymbol = false; - } - } - } - - - /// - /// Method used for symbol resolving. - /// - /// Name of the symbol - /// Address - /// True if the symbol is recognized - private bool SymbolResolver(IntPtr symbolPtr, ref ulong value) - { - string symbol = Marshal.PtrToStringAnsi(symbolPtr); - foreach (var item in resolvers) - { - bool result = item(symbol, ref value); - if (result) - return true; - } - return false; - } - - /// - /// Construct the object with a given architecture and a given mode. - /// - /// Architecture - /// Mode, i.e. endianess, word size etc. - /// Throw when there are errors - /// - /// Some architectures are not supported. - /// Check with if the engine - /// support the architecture. - /// - public Keystone(KeystoneArchitecture architecture, KeystoneMode mode, bool throwOnKeystoneError = true) - { - internalImpl = SymbolResolver; - throwOnError = throwOnKeystoneError; - var result = KeystoneImports.Open(architecture, (int)mode, ref engine); - - if (result != KeystoneError.KS_ERR_OK && throwOnKeystoneError) - throw new InvalidOperationException($"Error while initializing keystone: {ErrorToString(result)}"); - } - - /// - /// Set an option in the engine. - /// - /// Type of option - /// Value - /// True is the option is correctly setted, False otherwise && throwOnError is false. - /// If Keystone return an error && throwOnError is true - public bool SetOption(KeystoneOptionType type, uint value) - { - var result = KeystoneImports.SetOption(engine, type, (IntPtr)value); - - if (result != KeystoneError.KS_ERR_OK) - { - if (throwOnError) - throw new InvalidOperationException($"Error while setting option in keystone: {ErrorToString(result)}"); - return false; - } - - return true; - } - - /// - /// Return a string associated with a given error code. - /// - /// Error code - /// The string - public static string ErrorToString(KeystoneError result) - { - IntPtr error = KeystoneImports.ErrorToString(result); - if (error != IntPtr.Zero) - return Marshal.PtrToStringAnsi(error); - return string.Empty; - } - - /// - /// Encode given statements. - /// - /// String that contains the statements to encode - /// Address of the first instruction. - /// Result of the assemble operation or null if it failed && throwOnError is false. - /// If keystone return an error && throwOnError is true - public KeystoneEncoded Assemble(string toEncode, ulong address) - { - IntPtr encoding; - uint size; - uint statementCount; - byte[] buffer; - - int result = KeystoneImports.Assemble(engine, - toEncode, - address, - out encoding, - out size, - out statementCount); - - if (result != 0) - { - if (throwOnError) - throw new InvalidOperationException($"Error while assembling {toEncode}: {ErrorToString(GetLastKeystoneError())}"); - return null; - } - - buffer = new byte[size]; - - Marshal.Copy(encoding, buffer, 0, (int)size); - KeystoneImports.Free(encoding); - - return new KeystoneEncoded(buffer, statementCount, address); - } - - /// - /// Append the result of an assemble to an existing collection of bytes. - /// - /// String to encode - /// Collection of bytes - /// Address of the first instruction in input to this function - /// Size of the result of this operation - /// Number of statement found - /// True if the compilation is successful, False otherwise &&throwOnError is False. - /// String to encode is null or collection is null - /// Collection is read-only - /// If keystone return an error && throwOnError is true - public bool AppendAssemble(string toEncode, ICollection encoded, ulong address, out int size, out uint statements) - { - if (encoded == null) - throw new ArgumentNullException("encoded"); - if (toEncode == null) - throw new ArgumentNullException("toEncode"); - if (encoded.IsReadOnly) - throw new ArgumentException("encoded collection can't be read-only."); - - var result = Assemble(toEncode, address); - - if (result != null) - { - foreach (var v in result.Buffer) - encoded.Add(v); - - size = result.Buffer.Length; - statements = result.StatementCount; - return true; - } - else - { - size = 0; - statements = 0; - return false; - } - } - - /// - /// Append the result of an assemble to an existing collection of bytes. - /// - /// String to encode - /// Collection of bytes - /// Address of the first instruction in input to this function - /// Size of the result of this operation - /// True if the compilation is successful, False otherwise && throwOnError is True. - /// String to encode is null or collection is null - /// Collection is read-only - /// If keystone return an error && throwOnError is true - public bool AppendAssemble(string toEncode, ICollection encoded, ulong address, out int size) - { - uint unused; - return AppendAssemble(toEncode, encoded, address, out size, out unused); - } - - /// - /// Append the result of an assemble to an existing collection of bytes. - /// - /// String to encode - /// Collection of bytes - /// Address of the first instruction in input to this function - /// True if the compilation is successful, False otherwise &&throwOnError is True. - /// String to encode is null or collection is null - /// Collection is read-only - /// If keystone return an error && throwOnError is true - public bool AppendAssemble(string toEncode, ICollection encoded, ulong address) - { - uint unused1; - int unused2; - - return AppendAssemble(toEncode, encoded, address, out unused2, out unused1); - } - - /// - /// Append the result of an assemble to an existing collection of bytes. - /// - /// String to encode - /// Collection of bytes - /// True if the compilation is successful, False otherwise &&throwOnError is True. - /// String to encode is null or collection is null - /// Collection is read-only - /// If keystone return an error && throwOnError is true - public bool AppendAssemble(string toEncode, ICollection encoded) - { - uint unused1; - int unused2; - - return AppendAssemble(toEncode, encoded, 0, out unused2, out unused1); - } - - /// - /// Get the last error for this instance. - /// - /// Last error - /// - /// Might not retain its old error once accessed. - /// - public KeystoneError GetLastKeystoneError() - { - return KeystoneImports.GetLastKeystoneError(engine); - } - - /// - /// Check if an architecture is supported. - /// - /// Architecture - /// True if it is supported - public static bool IsArchitectureSupported(KeystoneArchitecture architecture) - { - return KeystoneImports.IsArchitectureSupported(architecture); - } - - /// - /// Get the version of the engine. - /// - /// Major - /// Minor - /// Unique identifier for this version. - public static uint GetKeystoneVersion(ref uint major, ref uint minor) - { - return KeystoneImports.Version(ref major, ref minor); - } - - /// - /// Release the engine. - /// - public void Dispose() - { - var currentEngine = Interlocked.Exchange(ref engine, IntPtr.Zero); - if (currentEngine != IntPtr.Zero) - KeystoneImports.Close(currentEngine); - - GC.SuppressFinalize(this); - } - } -} diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/KeystoneEncoded.cs b/bindings/csharp/KeystoneNET/KeystoneNET/KeystoneEncoded.cs deleted file mode 100644 index fc57e91d..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET/KeystoneEncoded.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace KeystoneNET -{ - /// - /// Wrap the result of an assemble. - /// - public class KeystoneEncoded - { - /// - /// Construct the object. - /// - /// Result of an assemble - /// Number of statements found - /// Address of the first instruction - public KeystoneEncoded(byte[] buffer, uint statementCount, ulong address) - { - Buffer = buffer; - StatementCount = statementCount; - Address = address; - } - - /// - /// Address of the first instruction for this operation. - /// - public ulong Address { get; private set; } - - /// - /// Result of an assemble operation. - /// - public byte[] Buffer { get; private set; } - - /// - /// Number of statements found. - /// - public uint StatementCount { get; private set; } - } -} diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/KeystoneImports.cs b/bindings/csharp/KeystoneNET/KeystoneNET/KeystoneImports.cs deleted file mode 100644 index 6c6815d6..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET/KeystoneImports.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace KeystoneNET -{ - /// - /// Imports for keystone.dll - /// - internal class KeystoneImports - { - /// - /// Taken from: http://stackoverflow.com/questions/10852634/using-a-32bit-or-64bit-dll-in-c-sharp-dllimport - /// - static KeystoneImports() - { - var myPath = new Uri(typeof(KeystoneImports).Assembly.CodeBase).LocalPath; - var myFolder = Path.GetDirectoryName(myPath); - - var is64 = IntPtr.Size == 8; - var subfolder = is64 ? "\\win64\\" : "\\win32\\"; - - string dllPosition = myFolder + subfolder + "keystone.dll"; - - // If this file exist, load it. - // Otherwise let the marshaller load the appropriate file. - if (File.Exists(dllPosition)) - LoadLibrary(dllPosition); - } - - [DllImport("kernel32.dll")] - private static extern IntPtr LoadLibrary(string dllToLoad); - - [DllImport("keystone.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_version" )] - internal extern static uint Version(ref uint major, ref uint minor); - - [DllImport("keystone.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_open")] - internal extern static KeystoneError Open(KeystoneArchitecture arch, int mode, ref IntPtr ks); - - [DllImport("keystone.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_close")] - internal extern static KeystoneError Close(IntPtr ks); - - [DllImport("keystone.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_free")] - internal extern static void Free(IntPtr buffer); - - [DllImport("keystone.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_strerror")] - internal extern static IntPtr ErrorToString(KeystoneError code); - - [DllImport("keystone.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_errno")] - internal extern static KeystoneError GetLastKeystoneError(IntPtr ks); - - [DllImport("keystone.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_arch_supported")] - internal extern static bool IsArchitectureSupported(KeystoneArchitecture arch); - - [DllImport("keystone.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_option")] - internal extern static KeystoneError SetOption(IntPtr ks, KeystoneOptionType type, IntPtr value); - - [DllImport("keystone.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ks_asm")] - internal extern static int Assemble(IntPtr ks, - [MarshalAs(UnmanagedType.LPStr)] string toEncode, - ulong baseAddress, - out IntPtr encoding, - out uint size, - out uint statements); - } -} diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/KeystoneNET.csproj b/bindings/csharp/KeystoneNET/KeystoneNET/KeystoneNET.csproj deleted file mode 100644 index 241f89cf..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET/KeystoneNET.csproj +++ /dev/null @@ -1,70 +0,0 @@ - - - - - Debug - AnyCPU - {2FE3B804-90F7-4632-B6F4-F72548E26984} - Library - Properties - KeystoneNET - KeystoneNET - v4.5.2 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - x86 - false - - - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/bindings/csharp/KeystoneNET/KeystoneNET/Properties/AssemblyInfo.cs b/bindings/csharp/KeystoneNET/KeystoneNET/Properties/AssemblyInfo.cs deleted file mode 100644 index fdd39ae3..00000000 --- a/bindings/csharp/KeystoneNET/KeystoneNET/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("KeystoneNET")] -[assembly: AssemblyDescription("Keystone bindings for .NET")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Keystone")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] - -[assembly: Guid("2fe3b804-90f7-4632-b6f4-f72548e26984")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/bindings/csharp/MIGRATION.md b/bindings/csharp/MIGRATION.md new file mode 100644 index 00000000..3ef4c5ff --- /dev/null +++ b/bindings/csharp/MIGRATION.md @@ -0,0 +1,83 @@ +# Changes + +### Names +- The `KeystoneNET` namespace was renamed to `Keystone`. +- The `Keystone` and `KeystoneEncoded` classes were respectively renamed to `Engine` and `EncodedData`. +- The `KeystoneArchitecture`, `KeystoneMode`, `KeystoneOptionType` and `KeystoneOptionValue` enums had their names changed, dropping the `Keystone` prefix (ie: `Architecture`, `Mode`, ...). Furthermore, their members were renamed, dropping the `KS_MODE_`, `KS_ARCH_`, and `KS_OPT_` prefixes. + +### The `Engine` class +- The `Engine` constructor no longer takes `bool throwOnError`. Instead, the public `ThrowOnError` property now has both a getter and a setter, making it possible to alter the error behavior after initialization. +- Errors are no longer reported as `InvalidOperationException`, but instead as `KeystoneException`, a custom class which stores the returned error code. +- An error encountered in the constructor or when setting `ResolveSymbol` will throw an exception, regardless of the value of `ThrowOnError`. +- `AppendAssemble` was renamed to `Assemble`, and no longer accepts `ICollection`. Instead it accepts a `byte[]` buffer and an `int` index, and writes much more efficiently into it. A new overload accepting a `Stream` has also been added. +- The `out uint statements` parameter has been replaced by an `out int statementCount` parameter. It will always be positive, but better integrates into the C# language. + +# Examples + +### Namespace +```csharp +using KeystoneNET; +``` +becomes +```csharp +using Keystone; +``` + +### Initialization +```csharp +using (var ks = new Keystone(KeystoneArchitecture.KS_ARCH_X86, + KeystoneMode.KS_MODE_32, + throwOnError: false)) +{ +} +``` +becomes +```csharp +using (var ks = new Engine(Architecture.X86, Mode.X32) + { ThrowOnError = true }) +{ +} +``` + +### Catching errors +```csharp +try +{ + ks.SymbolResolver += Callback; +} +catch (InvalidOperationException e) +{ + Console.WriteLine(e); +} +``` +becomes +```csharp +try +{ + ks.SymbolResolver += Callback; +} +catch (KeystoneException e) +{ + Console.WriteLine(e); +} +``` + +### Assembling data +```csharp +var data = new List(); + +ks.AppendAssemble("add eax, eax", data); +``` +becomes +```csharp +var data = new byte[1024]; + +ks.Assemble("add eax, eax", data); +``` +or +```csharp +using (var ms = new MemoryStream()) +{ + ks.Assemble("add eax, eax", ms); +} +``` diff --git a/bindings/csharp/README.md b/bindings/csharp/README.md index 7b5caf94..7e7e7688 100644 --- a/bindings/csharp/README.md +++ b/bindings/csharp/README.md @@ -1,23 +1,32 @@ # Keystone.Net -.Net bindings for Keystone +.NET Standard bindings for Keystone. ## Usage +```csharp +using Keystone; - ```csharp - using(var keystone = new Keystone(KeystoneArchitecture.KS_ARCH_X86, KeystoneMode.KS_MODE_32, false)) - { +using (Engine keystone = new Engine(Architecture.X86, Mode.X32) { ThrowOnError = true }) +{ ulong address = 0; + keystone.ResolveSymbol += (string s, ref ulong w) => { - if (s == "_j1") - { - w = 0x1234abcd; - return true; - } - return false; + if (s == "_j1") + { + w = 0x1234abcd; + return true; + } + + return false; }; - KeystoneEncoded enc = keystone.Assemble("xor eax, eax; jmp _j1", address); - - ... - } + EncodedData enc = keystone.Assemble("xor eax, eax; jmp _j1", address); + + enc.Buffer.ShouldBe(new byte[] { 0x00 }); + enc.Address.ShouldBe(address); + enc.StatementCount.ShouldBe(3); +} +``` + +For those who already used the bindings before their last update, many things have changed. +You can migrate your existing code easily using the [migration guide](./MIGRATON.md).