Last active
November 5, 2018 09:35
-
-
Save hadi77ir/e27f525fa24ba5077ba7c82ae0ce3234 to your computer and use it in GitHub Desktop.
Basic P/Invoke wrapper written in C# for Keystone Assembler Engine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Runtime.InteropServices; | |
| namespace NKeystone | |
| { | |
| /// <summary> | |
| /// Represents a higher-level access to Keystone API. | |
| /// </summary> | |
| public class AssemblerEngine : IDisposable | |
| { | |
| /// <summary> | |
| /// Dispose state | |
| /// </summary> | |
| protected bool _disposed; | |
| /// <summary> | |
| /// Underlying unmanaged Keystone instance. | |
| /// </summary> | |
| protected IntPtr _engine; | |
| #region Version Info | |
| /// <summary> | |
| /// Version of the implemented API | |
| /// </summary> | |
| public static CombinedVersion ApiVersion { get; } | |
| /// <summary> | |
| /// Version of the underlying Shared Library / Keystone Runtime | |
| /// </summary> | |
| public static CombinedVersion LibraryVersion { get; } | |
| #endregion | |
| static AssemblerEngine() | |
| { | |
| ApiVersion = new CombinedVersion(NativeWrapper.KS_API_MAJOR, NativeWrapper.KS_API_MINOR); | |
| uint major, minor; | |
| NativeWrapper.ks_version(out major, out minor); | |
| LibraryVersion = new CombinedVersion(major, minor); | |
| } | |
| /// <summary> | |
| /// Initializes a new instance of Keystone. | |
| /// </summary> | |
| /// <param name="arch">Target architecture</param> | |
| /// <param name="modes">Operating mode and flags</param> | |
| /// <exception cref="KeystoneException">In case any errors occurred, the exception will be raised.</exception> | |
| public AssemblerEngine(Architecture arch, Mode modes) | |
| { | |
| ErrorCode result; | |
| if ((result = NativeWrapper.ks_open(arch, (int) modes, out _engine)) != ErrorCode.KS_ERR_OK) | |
| { | |
| throw new KeystoneException(result); | |
| } | |
| } | |
| /// <summary> | |
| /// Sets value for an option. | |
| /// </summary> | |
| /// <param name="key">Option key</param> | |
| /// <param name="value">Option value</param> | |
| /// <exception cref="KeystoneException">In case any errors occurred, the exception will be raised.</exception> | |
| public void SetOption(OptionType key, OptionValue value) | |
| { | |
| ErrorCode result; | |
| if ((result = NativeWrapper.ks_option(_engine, key, (int)value)) != ErrorCode.KS_ERR_OK) | |
| { | |
| throw new KeystoneException(result); | |
| } | |
| } | |
| /// <summary> | |
| /// Checks whether the specified architecture is supported by Keystone or not. | |
| /// </summary> | |
| /// <param name="arch">Architecture</param> | |
| /// <returns>True if supported.</returns> | |
| public static bool IsArchSupported(Architecture arch) | |
| { | |
| return NativeWrapper.ks_arch_supported(arch); | |
| } | |
| /// <summary> | |
| /// Assembles the given assembly string. | |
| /// </summary> | |
| /// <param name="asm">Assembly input string</param> | |
| /// <param name="address">Address of first assembly instruction. Set to 0 to ignore.</param> | |
| /// <param name="statCount">Count of successfully processed statements.</param> | |
| /// <returns>Assembled bytes</returns> | |
| /// <exception cref="KeystoneException">In case any errors occurred, the exception will be raised.</exception> | |
| public virtual byte[] Assemble(string asm, ulong address, out int statCount) | |
| { | |
| ErrorCode error; | |
| // null-terminated string marshalling | |
| IntPtr cStr = Marshal.StringToHGlobalAnsi(asm); | |
| int result = NativeWrapper.ks_asm(_engine, cStr, address, out byte[] assembled, out int assembledSize, | |
| out statCount); | |
| // free unmanaged allocated memory | |
| Marshal.FreeHGlobal(cStr); | |
| if (result != 0) | |
| { | |
| error = NativeWrapper.ks_errno(_engine); | |
| if (error != ErrorCode.KS_ERR_OK) | |
| { | |
| throw new KeystoneException(error); | |
| } | |
| } | |
| byte[] output = new byte[assembledSize]; | |
| assembled.CopyTo(output, 0); | |
| // free unmanaged pointer | |
| NativeWrapper.ks_free(assembled); | |
| return output; | |
| } | |
| public virtual byte[] Assemble(string asm, ulong address = 0) | |
| { | |
| return Assemble(asm, address, out _); | |
| } | |
| /// <summary> | |
| /// Instance Finalizer | |
| /// </summary> | |
| ~AssemblerEngine() | |
| { | |
| // only release unmanaged resources | |
| Dispose(false); | |
| } | |
| /// <summary> | |
| /// Releases resources and closes the underlying unmanaged engine. | |
| /// </summary> | |
| public void Dispose() | |
| { | |
| // release all resources and remove object from finalizing queue | |
| Dispose(true); | |
| GC.SuppressFinalize(this); | |
| } | |
| protected virtual void Dispose(bool disposing) | |
| { | |
| if(_disposed) | |
| return; | |
| if (disposing) | |
| { | |
| // dispose managed resources if any. | |
| } | |
| // dispose unmanaged resources | |
| ErrorCode result; | |
| if ((result = NativeWrapper.ks_close(_engine)) != ErrorCode.KS_ERR_OK) | |
| { | |
| throw new KeystoneException(result); | |
| } | |
| _disposed = true; | |
| } | |
| } | |
| public sealed class CombinedVersion : IComparable<CombinedVersion> | |
| { | |
| public uint Major { get; } | |
| public uint Minor { get; } | |
| public uint Combined { get; } | |
| public CombinedVersion(uint combined) | |
| { | |
| Combined = combined; | |
| Major = combined >> 8; | |
| Minor = combined - (Major << 8); | |
| } | |
| public CombinedVersion(uint major, uint minor) | |
| { | |
| Major = major; | |
| Minor = minor; | |
| Combined = (major << 8) + minor; | |
| } | |
| public int CompareTo(CombinedVersion other) | |
| { | |
| if (ReferenceEquals(this, other)) return 0; | |
| if (ReferenceEquals(null, other)) return 1; | |
| return Combined.CompareTo(other.Combined); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Keystone Assembler Engine (www.keystone-engine.org) | |
| // By Nguyen Anh Quynh (aquynh@gmail.com), 2016 | |
| // Bindings by @hadi77ir | |
| using System; | |
| using System.Runtime.InteropServices; | |
| namespace NKeystone | |
| { | |
| public static class NativeWrapper | |
| { | |
| ///<summary>Keystone API version (major)</summary> | |
| public const uint KS_API_MAJOR = 0; | |
| ///<summary>Keystone API version (minor)</summary> | |
| public const uint KS_API_MINOR = 9; | |
| /// <summary> | |
| /// Constant that contains combined version which can be compared to result of ks_version() API. | |
| /// </summary> | |
| public const uint KS_VERSION = ((KS_API_MAJOR << 8) + KS_API_MINOR); | |
| /// <summary> | |
| /// All generic errors related to input assembly >= KS_ERR_ASM | |
| /// </summary> | |
| public const uint KS_ERR_ASM = 128; | |
| /// <summary> | |
| /// All architecture-specific errors related to input assembly >= KS_ERR_ASM_ARCH | |
| /// </summary> | |
| public const uint KS_ERR_ASM_ARCH = 512; | |
| /// <summary> | |
| /// Shared Library file name | |
| /// </summary> | |
| public const string LibraryName = "keystone"; | |
| /// <summary> | |
| ///Return combined API version & major and minor version numbers. | |
| /// NOTE: This returned value can be compared with version number in <see cref="KS_VERSION"/> constant | |
| /// NOTE: if you only care about returned value, but not major and minor values, | |
| /// set both <paramref name="major"/> & <paramref name="minor"/> arguments to NULL. | |
| ///<param name="major">major number of API version</param> | |
| ///<param name="minor">minor number of API version</param> | |
| ///<returns> | |
| /// hexical number, which contains both major & minor versions. | |
| /// For example, for API version with 1 in @major, and 1 in @minor, the return value would be 0x0101 | |
| /// </returns> | |
| [DllImport(LibraryName, CharSet = CharSet.Ansi)] | |
| public static extern uint ks_version(out uint major, out uint minor); | |
| /// <summary> | |
| /// Determine if the given architecture is supported by this library. | |
| /// </summary> | |
| ///<param name="arch">architecture type (KS_ARCH_*)>/param> | |
| /// <returns> True if this library supports the given arch.</returns> | |
| [DllImport(LibraryName, CharSet = CharSet.Ansi)] | |
| public static extern bool ks_arch_supported(Architecture arch); | |
| /// <summary> | |
| /// Create new instance of Keystone engine. | |
| /// </summary> | |
| /// <param name="arch">architecture type (KS_ARCH_*)</param> | |
| /// <param name="mode">hardware mode. This is combined of KS_MODE_*</param> | |
| /// <param name="ks">pointer to ks_engine, which will be updated at return time</param> | |
| /// <returns>KS_ERR_OK on success, or other value on failure (see <see cref="ErrorCode"/>).</returns> | |
| [DllImport(LibraryName, CharSet = CharSet.Ansi)] | |
| public static extern ErrorCode ks_open(Architecture arch, int mode, out IntPtr ks_engine_handle); | |
| /// <summary> | |
| ///Close KS instance: MUST do to release the handle when it is not used anymore. | |
| /// NOTE: this must be called only when there is no longer usage of Keystone. | |
| /// The reason is the this API releases some cached memory, thus access to any | |
| /// Keystone API after ks_close() might crash your application. | |
| /// After this, @ks is invalid, and nolonger usable. | |
| /// </summary> | |
| /// <param name="ks"> @ks: pointer to a handle returned by ks_open()</param> | |
| /// <returns>KS_ERR_OK on success, or other value on failure (<see cref="ErrorCode"/>).</returns> | |
| [DllImport(LibraryName, CharSet = CharSet.Ansi)] | |
| public static extern ErrorCode ks_close(IntPtr ks); | |
| /// <summary> | |
| /// Report the last error number when some API function fail. | |
| /// Like glibc's errno, ks_errno might not retain its old error once accessed. | |
| /// </summary> | |
| /// <param name="ks">handle returned by ks_open()</param> | |
| ///<returns>error code of ks_err enum type (see <see cref="ErrorCode"/>).</returns> | |
| [DllImport(LibraryName, CharSet = CharSet.Ansi)] | |
| public static extern ErrorCode ks_errno(IntPtr ks); | |
| /// <summary> | |
| /// Return a string describing given error code. | |
| /// </summary> | |
| /// <param name="code">error code (see <see cref="ErrorCode"/>)</param> | |
| /// <returns>returns a pointer to a string that describes the error code passed in the argument <paramref name="code"/></returns> | |
| [DllImport(LibraryName, CharSet = CharSet.Ansi)] | |
| public static extern string ks_strerror(ErrorCode code); | |
| /// <summary> | |
| /// Set option for Keystone engine at runtime | |
| /// Refer to ks_err enum for detailed error. | |
| /// </summary> | |
| /// <param name="handle">returned by ks_open()</param> | |
| /// <param name="type">type of option to be set</param> | |
| /// <param name="value">option value corresponding with <paramref name="type"/></param> | |
| /// <returns>KS_ERR_OK on success, or other value on failure.</returns> | |
| [DllImport(LibraryName, CharSet = CharSet.Ansi)] | |
| public static extern ErrorCode ks_option(IntPtr ks, OptionType type, int value); | |
| /// <summary> | |
| /// Assemble a string given its the buffer, size, start address and number | |
| /// of instructions to be decoded. | |
| /// This API dynamically allocate memory to contain assembled instruction. | |
| /// Resulted array of bytes containing the machine code is put into @*encoding | |
| /// | |
| /// NOTE 1: this API will automatically determine memory needed to contain | |
| /// output bytes in *encoding. | |
| /// | |
| /// NOTE 2: caller must free the allocated memory itself to avoid memory leaking. | |
| /// | |
| /// On failure, call ks_errno() for error code. | |
| /// </summary> | |
| /// <param name="ks">handle returned by ks_open()</param> | |
| /// <param name="str">NULL-terminated assembly string. Use ; or \n to separate statements.</param> | |
| /// <param name="address">address of the first assembly instruction, or 0 to ignore.</param> | |
| /// <param name="encoding"> | |
| /// array of bytes containing encoding of input assembly string. | |
| /// NOTE: *encoding will be allocated by this function, and should be freed | |
| /// with ks_free() function. | |
| /// </param> | |
| /// <param name="encoding_size">size of *encoding</param> | |
| /// <param name="stat_count">number of statements successfully processed</param> | |
| /// <returns>0 on success, or -1 on failure.</returns> | |
| [DllImport(LibraryName, CharSet = CharSet.Ansi)] | |
| public static extern int ks_asm(IntPtr ks, IntPtr str, ulong address, | |
| [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] | |
| out byte[] encoding, | |
| out int encoding_size, out int stat_count); | |
| /// <summary> | |
| /// Free memory allocated by ks_asm() | |
| /// </summary> | |
| /// <param name="p">memory allocated in @encoding argument of ks_asm()</param> | |
| [DllImport(LibraryName, CharSet = CharSet.Ansi)] | |
| public static extern void ks_free(byte[] p); | |
| } | |
| /// <summary> | |
| /// Runtime option value (associated with ks_opt_type above) | |
| /// </summary> | |
| [Flags] | |
| public enum OptionValue | |
| { | |
| #region KS_OPT_SYNTAX | |
| KS_OPT_SYNTAX_INTEL = 1 << 0, // X86 Intel syntax - default on X86 (KS_OPT_SYNTAX). | |
| KS_OPT_SYNTAX_ATT = 1 << 1, // X86 ATT asm syntax (KS_OPT_SYNTAX). | |
| KS_OPT_SYNTAX_NASM = 1 << 2, // X86 Nasm syntax (KS_OPT_SYNTAX). | |
| KS_OPT_SYNTAX_MASM = 1 << 3, // X86 Masm syntax (KS_OPT_SYNTAX) - unsupported yet. | |
| KS_OPT_SYNTAX_GAS = 1 << 4, // X86 GNU GAS syntax (KS_OPT_SYNTAX). | |
| #endregion | |
| } | |
| /// <summary> | |
| /// Runtime option for the Keystone engine | |
| /// </summary> | |
| public enum OptionType | |
| { | |
| KS_OPT_SYNTAX = 1, // Choose syntax for input assembly | |
| } | |
| /// <summary> | |
| /// Architecture type | |
| /// </summary> | |
| public enum Architecture | |
| { | |
| KS_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2) | |
| KS_ARCH_ARM64, // ARM-64, also called AArch64 | |
| KS_ARCH_MIPS, // Mips architecture | |
| KS_ARCH_X86, // X86 architecture (including x86 & x86-64) | |
| KS_ARCH_PPC, // PowerPC architecture (currently unsupported) | |
| KS_ARCH_SPARC, // Sparc architecture | |
| KS_ARCH_SYSTEMZ, // SystemZ architecture (S390X) | |
| KS_ARCH_HEXAGON, // Hexagon architecture | |
| KS_ARCH_MAX, | |
| } | |
| /// <summary> | |
| /// Mode type | |
| /// </summary> | |
| [Flags] | |
| public enum Mode | |
| { | |
| #region Generic | |
| KS_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode) | |
| KS_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode | |
| #endregion | |
| #region ARM / ARM64 | |
| KS_MODE_ARM = 1 << 0, // ARM mode | |
| KS_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2) | |
| KS_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM | |
| #endregion | |
| #region MIPS | |
| KS_MODE_MICRO = 1 << 4, // MicroMips mode | |
| KS_MODE_MIPS3 = 1 << 5, // Mips III ISA | |
| KS_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA | |
| KS_MODE_MIPS32 = 1 << 2, // Mips32 ISA | |
| KS_MODE_MIPS64 = 1 << 3, // Mips64 ISA | |
| #endregion | |
| #region x86 / x64 | |
| KS_MODE_16 = 1 << 1, // 16-bit mode | |
| KS_MODE_32 = 1 << 2, // 32-bit mode | |
| KS_MODE_64 = 1 << 3, // 64-bit mode | |
| #endregion | |
| #region PowerPC | |
| KS_MODE_PPC32 = 1 << 2, // 32-bit mode | |
| KS_MODE_PPC64 = 1 << 3, // 64-bit mode | |
| KS_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode | |
| #endregion | |
| #region SPARC | |
| KS_MODE_SPARC32 = 1 << 2, // 32-bit mode | |
| KS_MODE_SPARC64 = 1 << 3, // 64-bit mode | |
| KS_MODE_V9 = 1 << 4, // SparcV9 mode | |
| #endregion | |
| } | |
| /// <summary> | |
| /// All type of errors encountered by Keystone API. | |
| /// </summary> | |
| public enum ErrorCode : uint | |
| { | |
| KS_ERR_OK = 0, // No error: everything was fine | |
| KS_ERR_NOMEM, // Out-Of-Memory error: ks_open(), ks_emulate() | |
| KS_ERR_ARCH, // Unsupported architecture: ks_open() | |
| KS_ERR_HANDLE, // Invalid handle | |
| KS_ERR_MODE, // Invalid/unsupported mode: ks_open() | |
| KS_ERR_VERSION, // Unsupported version (bindings) | |
| KS_ERR_OPT_INVALID, // Unsupported option | |
| #region generic input assembly errors - parser specific | |
| KS_ERR_ASM_EXPR_TOKEN = NativeWrapper.KS_ERR_ASM, // unknown token in expression | |
| KS_ERR_ASM_DIRECTIVE_VALUE_RANGE, // literal value out of range for directive | |
| KS_ERR_ASM_DIRECTIVE_ID, // expected identifier in directive | |
| KS_ERR_ASM_DIRECTIVE_TOKEN, // unexpected token in directive | |
| KS_ERR_ASM_DIRECTIVE_STR, // expected string in directive | |
| KS_ERR_ASM_DIRECTIVE_COMMA, // expected comma in directive | |
| KS_ERR_ASM_DIRECTIVE_RELOC_NAME, // expected relocation name in directive | |
| KS_ERR_ASM_DIRECTIVE_RELOC_TOKEN, // unexpected token in .reloc directive | |
| KS_ERR_ASM_DIRECTIVE_FPOINT, // invalid floating point in directive | |
| KS_ERR_ASM_DIRECTIVE_UNKNOWN, // unknown directive | |
| KS_ERR_ASM_DIRECTIVE_EQU, // invalid equal directive | |
| KS_ERR_ASM_DIRECTIVE_INVALID, // (generic) invalid directive | |
| KS_ERR_ASM_VARIANT_INVALID, // invalid variant | |
| KS_ERR_ASM_EXPR_BRACKET, // brackets expression not supported on this target | |
| KS_ERR_ASM_SYMBOL_MODIFIER, // unexpected symbol modifier following '@' | |
| KS_ERR_ASM_SYMBOL_REDEFINED, // invalid symbol redefinition | |
| KS_ERR_ASM_SYMBOL_MISSING, // cannot find a symbol | |
| KS_ERR_ASM_RPAREN, // expected ')' in parentheses expression | |
| KS_ERR_ASM_STAT_TOKEN, // unexpected token at start of statement | |
| KS_ERR_ASM_UNSUPPORTED, // unsupported token yet | |
| KS_ERR_ASM_MACRO_TOKEN, // unexpected token in macro instantiation | |
| KS_ERR_ASM_MACRO_PAREN, // unbalanced parentheses in macro argument | |
| KS_ERR_ASM_MACRO_EQU, // expected '=' after formal parameter identifier | |
| KS_ERR_ASM_MACRO_ARGS, // too many positional arguments | |
| KS_ERR_ASM_MACRO_LEVELS_EXCEED, // macros cannot be nested more than 20 levels deep | |
| KS_ERR_ASM_MACRO_STR, // invalid macro string | |
| KS_ERR_ASM_MACRO_INVALID, // invalid macro (generic error) | |
| KS_ERR_ASM_ESC_BACKSLASH, // unexpected backslash at end of escaped string | |
| KS_ERR_ASM_ESC_OCTAL, // invalid octal escape sequence (out of range) | |
| KS_ERR_ASM_ESC_SEQUENCE, // invalid escape sequence (unrecognized character) | |
| KS_ERR_ASM_ESC_STR, // broken escape string | |
| KS_ERR_ASM_TOKEN_INVALID, // invalid token | |
| KS_ERR_ASM_INSN_UNSUPPORTED, // this instruction is unsupported in this mode | |
| KS_ERR_ASM_FIXUP_INVALID, // invalid fixup | |
| KS_ERR_ASM_LABEL_INVALID, // invalid label | |
| KS_ERR_ASM_FRAGMENT_INVALID, // invalid fragment | |
| #endregion | |
| #region generic input assembly errors - architecture specific | |
| KS_ERR_ASM_INVALIDOPERAND = NativeWrapper.KS_ERR_ASM_ARCH, | |
| KS_ERR_ASM_MISSINGFEATURE, | |
| KS_ERR_ASM_MNEMONICFAIL, | |
| #endregion | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment