From 32c13df6f51e2ba2581bd08bc5373bc5df823801 Mon Sep 17 00:00:00 2001 From: Ingmar Steen <iksteen@gmail.com> Date: Tue, 10 May 2016 17:58:05 +0200 Subject: [PATCH] Add nodejs bindings --- bindings/const_generator.py | 18 ++++++ bindings/nodejs/LICENSE | 19 ++++++ bindings/nodejs/README.md | 53 +++++++++++++++ bindings/nodejs/consts/arm.js | 4 ++ bindings/nodejs/consts/arm64.js | 4 ++ bindings/nodejs/consts/hexagon.js | 4 ++ bindings/nodejs/consts/index.js | 9 +++ bindings/nodejs/consts/keystone.js | 75 ++++++++++++++++++++++ bindings/nodejs/consts/mips.js | 4 ++ bindings/nodejs/consts/ppc.js | 4 ++ bindings/nodejs/consts/sparc.js | 4 ++ bindings/nodejs/consts/systemz.js | 4 ++ bindings/nodejs/consts/x86.js | 4 ++ bindings/nodejs/index.js | 100 +++++++++++++++++++++++++++++ bindings/nodejs/package.json | 18 ++++++ bindings/nodejs/sample.js | 27 ++++++++ 16 files changed, 351 insertions(+) create mode 100644 bindings/nodejs/LICENSE create mode 100644 bindings/nodejs/README.md create mode 100644 bindings/nodejs/consts/arm.js create mode 100644 bindings/nodejs/consts/arm64.js create mode 100644 bindings/nodejs/consts/hexagon.js create mode 100644 bindings/nodejs/consts/index.js create mode 100644 bindings/nodejs/consts/keystone.js create mode 100644 bindings/nodejs/consts/mips.js create mode 100644 bindings/nodejs/consts/ppc.js create mode 100644 bindings/nodejs/consts/sparc.js create mode 100644 bindings/nodejs/consts/systemz.js create mode 100644 bindings/nodejs/consts/x86.js create mode 100644 bindings/nodejs/index.js create mode 100644 bindings/nodejs/package.json create mode 100644 bindings/nodejs/sample.js diff --git a/bindings/const_generator.py b/bindings/const_generator.py index 909f835..96db7cb 100644 --- a/bindings/const_generator.py +++ b/bindings/const_generator.py @@ -29,6 +29,24 @@ template = { 'comment_open': '#', 'comment_close': '', }, + 'nodejs': { + 'header': "// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.js]\n", + 'footer': "", + 'line_format': 'module.exports.%s = %s\n', + 'out_file': './nodejs/consts/%s.js', + # prefixes for constant filenames of all archs - case sensitive + 'arm.h': 'arm', + 'arm64.h': 'arm64', + 'mips.h': 'mips', + 'x86.h': 'x86', + 'sparc.h': 'sparc', + 'systemz.h': 'systemz', + 'ppc.h': 'ppc', + 'hexagon.h': 'hexagon', + 'keystone.h': 'keystone', + 'comment_open': '//', + 'comment_close': '', + }, } # markup for comments to be added to autogen files diff --git a/bindings/nodejs/LICENSE b/bindings/nodejs/LICENSE new file mode 100644 index 0000000..89cc025 --- /dev/null +++ b/bindings/nodejs/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Ingmar Steen + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/bindings/nodejs/README.md b/bindings/nodejs/README.md new file mode 100644 index 0000000..83ed4af --- /dev/null +++ b/bindings/nodejs/README.md @@ -0,0 +1,53 @@ +# node-keystone + +`node-keystone` provides Node.js bindings for the +[Keystone](http://www.keystone-engine.org) assembler library, allowing +text data in to be assembled into `Buffer` objects using any of Keystone's +supported architectures. + +### Install + +`npm install /path/to/keystone/bindings/nodejs` + +#### libkeystone + +These bindings require you to have the Keystone library installed as it is +not included. + +### Basic usage + +```javascript +var keystone = require("keystone"); +var assembly = "inc ecx; dec ebx" + +var ks = new keystone.Ks(keystone.ARCH_X86, keystone.MODE_64); +console.log(ks.asm(assembly)); +ks.close(); +``` + +For other examples, see the `example.js` file. + +### License + +The source code is hereby released under the MIT License. The full text of the +license appears below. + +Copyright (c) 2016 Ingmar Steen + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bindings/nodejs/consts/arm.js b/bindings/nodejs/consts/arm.js new file mode 100644 index 0000000..7fa6688 --- /dev/null +++ b/bindings/nodejs/consts/arm.js @@ -0,0 +1,4 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm_const.js] +module.exports.ERR_ASM_ARM_INVALIDOPERAND = 512 +module.exports.ERR_ASM_ARM_MISSINGFEATURE = 513 +module.exports.ERR_ASM_ARM_MNEMONICFAIL = 514 diff --git a/bindings/nodejs/consts/arm64.js b/bindings/nodejs/consts/arm64.js new file mode 100644 index 0000000..4c134b6 --- /dev/null +++ b/bindings/nodejs/consts/arm64.js @@ -0,0 +1,4 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm64_const.js] +module.exports.ERR_ASM_ARM64_INVALIDOPERAND = 512 +module.exports.ERR_ASM_ARM64_MISSINGFEATURE = 513 +module.exports.ERR_ASM_ARM64_MNEMONICFAIL = 514 diff --git a/bindings/nodejs/consts/hexagon.js b/bindings/nodejs/consts/hexagon.js new file mode 100644 index 0000000..7e94aeb --- /dev/null +++ b/bindings/nodejs/consts/hexagon.js @@ -0,0 +1,4 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [hexagon_const.js] +module.exports.ERR_ASM_HEXAGON_INVALIDOPERAND = 512 +module.exports.ERR_ASM_HEXAGON_MISSINGFEATURE = 513 +module.exports.ERR_ASM_HEXAGON_MNEMONICFAIL = 514 diff --git a/bindings/nodejs/consts/index.js b/bindings/nodejs/consts/index.js new file mode 100644 index 0000000..df58d5c --- /dev/null +++ b/bindings/nodejs/consts/index.js @@ -0,0 +1,9 @@ +var extend = require('util')._extend, + archs = ['arm64', 'arm', 'hexagon', 'mips', 'ppc', 'sparc', 'systemz', 'x86'], + i + +module.exports = require('./keystone') + +for (i = 0; i < archs.length; ++i) { + extend(module.exports, require('./' + archs[i])); +} diff --git a/bindings/nodejs/consts/keystone.js b/bindings/nodejs/consts/keystone.js new file mode 100644 index 0000000..d66b681 --- /dev/null +++ b/bindings/nodejs/consts/keystone.js @@ -0,0 +1,75 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.js] +module.exports.API_MAJOR = 1 + +module.exports.API_MINOR = 0 +module.exports.ARCH_ARM = 1 +module.exports.ARCH_ARM64 = 2 +module.exports.ARCH_MIPS = 3 +module.exports.ARCH_X86 = 4 +module.exports.ARCH_PPC = 5 +module.exports.ARCH_SPARC = 6 +module.exports.ARCH_SYSTEMZ = 7 +module.exports.ARCH_HEXAGON = 8 +module.exports.ARCH_MAX = 9 + +module.exports.MODE_LITTLE_ENDIAN = 0 +module.exports.MODE_BIG_ENDIAN = 1073741824 +module.exports.MODE_ARM = 1 +module.exports.MODE_THUMB = 16 +module.exports.MODE_V8 = 64 +module.exports.MODE_MICRO = 16 +module.exports.MODE_MIPS3 = 32 +module.exports.MODE_MIPS32R6 = 64 +module.exports.MODE_MIPS32 = 4 +module.exports.MODE_MIPS64 = 8 +module.exports.MODE_16 = 2 +module.exports.MODE_32 = 4 +module.exports.MODE_64 = 8 +module.exports.MODE_PPC32 = 4 +module.exports.MODE_PPC64 = 8 +module.exports.MODE_QPX = 16 +module.exports.MODE_SPARC32 = 4 +module.exports.MODE_SPARC64 = 8 +module.exports.MODE_V9 = 16 +module.exports.ERR_ASM = 128 +module.exports.ERR_ASM_ARCH = 512 + +module.exports.ERR_OK = 0 +module.exports.ERR_NOMEM = 1 +module.exports.ERR_ARCH = 2 +module.exports.ERR_HANDLE = 3 +module.exports.ERR_MODE = 4 +module.exports.ERR_VERSION = 5 +module.exports.ERR_OPT_INVALID = 6 +module.exports.ERR_ASM_EXPR_TOKEN = 128 +module.exports.ERR_ASM_DIRECTIVE_VALUE_RANGE = 129 +module.exports.ERR_ASM_DIRECTIVE_ID = 130 +module.exports.ERR_ASM_DIRECTIVE_TOKEN = 131 +module.exports.ERR_ASM_DIRECTIVE_STR = 132 +module.exports.ERR_ASM_DIRECTIVE_COMMA = 133 +module.exports.ERR_ASM_DIRECTIVE_RELOC_NAME = 134 +module.exports.ERR_ASM_DIRECTIVE_RELOC_TOKEN = 135 +module.exports.ERR_ASM_DIRECTIVE_FPOINT = 136 +module.exports.ERR_ASM_VARIANT_INVALID = 137 +module.exports.ERR_ASM_EXPR_BRACKET = 138 +module.exports.ERR_ASM_SYMBOL_MODIFIER = 139 +module.exports.ERR_ASM_RPAREN = 140 +module.exports.ERR_ASM_STAT_TOKEN = 141 +module.exports.ERR_ASM_UNSUPPORTED = 142 +module.exports.ERR_ASM_MACRO_TOKEN = 143 +module.exports.ERR_ASM_MACRO_PAREN = 144 +module.exports.ERR_ASM_MACRO_EQU = 145 +module.exports.ERR_ASM_MACRO_ARGS = 146 +module.exports.ERR_ASM_MACRO_LEVELS_EXCEED = 147 +module.exports.ERR_ASM_ESC_BACKSLASH = 148 +module.exports.ERR_ASM_ESC_OCTAL = 149 +module.exports.ERR_ASM_ESC_SEQUENCE = 150 +module.exports.ERR_ASM_INVALIDOPERAND = 512 +module.exports.ERR_ASM_MISSINGFEATURE = 513 +module.exports.ERR_ASM_MNEMONICFAIL = 514 +module.exports.OPT_SYNTAX = 1 +module.exports.OPT_SYNTAX_INTEL = 1 +module.exports.OPT_SYNTAX_ATT = 2 +module.exports.OPT_SYNTAX_NASM = 4 +module.exports.OPT_SYNTAX_MASM = 8 +module.exports.OPT_SYNTAX_GAS = 16 diff --git a/bindings/nodejs/consts/mips.js b/bindings/nodejs/consts/mips.js new file mode 100644 index 0000000..076f68b --- /dev/null +++ b/bindings/nodejs/consts/mips.js @@ -0,0 +1,4 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [mips_const.js] +module.exports.ERR_ASM_MIPS_INVALIDOPERAND = 512 +module.exports.ERR_ASM_MIPS_MISSINGFEATURE = 513 +module.exports.ERR_ASM_MIPS_MNEMONICFAIL = 514 diff --git a/bindings/nodejs/consts/ppc.js b/bindings/nodejs/consts/ppc.js new file mode 100644 index 0000000..63562f4 --- /dev/null +++ b/bindings/nodejs/consts/ppc.js @@ -0,0 +1,4 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [ppc_const.js] +module.exports.ERR_ASM_PPC_INVALIDOPERAND = 512 +module.exports.ERR_ASM_PPC_MISSINGFEATURE = 513 +module.exports.ERR_ASM_PPC_MNEMONICFAIL = 514 diff --git a/bindings/nodejs/consts/sparc.js b/bindings/nodejs/consts/sparc.js new file mode 100644 index 0000000..9287ce4 --- /dev/null +++ b/bindings/nodejs/consts/sparc.js @@ -0,0 +1,4 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [sparc_const.js] +module.exports.ERR_ASM_SPARC_INVALIDOPERAND = 512 +module.exports.ERR_ASM_SPARC_MISSINGFEATURE = 513 +module.exports.ERR_ASM_SPARC_MNEMONICFAIL = 514 diff --git a/bindings/nodejs/consts/systemz.js b/bindings/nodejs/consts/systemz.js new file mode 100644 index 0000000..4f9a611 --- /dev/null +++ b/bindings/nodejs/consts/systemz.js @@ -0,0 +1,4 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [systemz_const.js] +module.exports.ERR_ASM_SYSTEMZ_INVALIDOPERAND = 512 +module.exports.ERR_ASM_SYSTEMZ_MISSINGFEATURE = 513 +module.exports.ERR_ASM_SYSTEMZ_MNEMONICFAIL = 514 diff --git a/bindings/nodejs/consts/x86.js b/bindings/nodejs/consts/x86.js new file mode 100644 index 0000000..f356afe --- /dev/null +++ b/bindings/nodejs/consts/x86.js @@ -0,0 +1,4 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [x86_const.js] +module.exports.ERR_ASM_X86_INVALIDOPERAND = 512 +module.exports.ERR_ASM_X86_MISSINGFEATURE = 513 +module.exports.ERR_ASM_X86_MNEMONICFAIL = 514 diff --git a/bindings/nodejs/index.js b/bindings/nodejs/index.js new file mode 100644 index 0000000..04d8e6a --- /dev/null +++ b/bindings/nodejs/index.js @@ -0,0 +1,100 @@ +var ref = require('ref'), + ffi = require('ffi'), + consts = require('./consts'), + extend = require('util')._extend + +var ks_engine = 'void', + ks_enginePtr = ref.refType(ks_engine), + ks_enginePtrPtr = ref.refType(ks_enginePtr), + ks_arch = 'int', + ks_err = 'int', + ks_opt_type = 'int', + uintPtr = ref.refType('uint'), + ucharPtr = ref.refType('uchar'), + ucharPtrPtr = ref.refType(ucharPtr), + size_tPtr = ref.refType('size_t'), + stringPtr = ref.refType('string') + +var Keystone = ffi.Library('libkeystone', { + 'ks_version': [ 'uint', [ uintPtr, uintPtr ] ], + 'ks_arch_supported': [ 'bool', [ ks_arch ] ], + 'ks_open': [ ks_err, [ ks_arch, 'int', ks_enginePtrPtr ] ], + 'ks_close': [ ks_err, [ ks_enginePtr ] ], + 'ks_errno': [ 'int', [ ks_enginePtr ] ], + 'ks_strerror': [ 'string', [ ks_err ] ], + 'ks_option': [ ks_err, [ ks_enginePtr, ks_opt_type, 'size_t' ] ], + 'ks_asm': [ 'int', [ ks_enginePtr, 'string', 'uint64', ucharPtrPtr, size_tPtr, size_tPtr ] ], + 'ks_free': [ 'void', [ 'pointer' ] ] +}) + +function KsError(message, errno, count) { + this.message = message + this.errno = errno + this.count = count +} + +function Ks(arch, mode) { + var _ks = ref.alloc(ks_enginePtr), + err = Keystone.ks_open(arch, mode, _ks) + + if (err !== consts.ERR_OK) { + this._ks = null + throw new KsError('Error: failed on ks_open()') + } + + this._ks = _ks.deref() + + + this.__defineGetter__('errno', function() { + return Keystone.ks_errno(this._ks) + }) + + this.__defineSetter__('syntax', function(value) { + this.set_option(consts.OPT_SYNTAX, value) + }) +} + +Ks.prototype.asm = function(code, addr) { + var encoding = ref.alloc('uchar *'), + size = ref.alloc('size_t'), + count = ref.alloc('size_t'), + err, msg + + if (Keystone.ks_asm(this._ks, code, addr || 0, encoding, size, count) !== consts.ERR_OK) { + err = this.errno + msg = Keystone.ks_strerror(err) + throw new KsError(msg, err, count.deref()) + } + + return { + encoding: ref.reinterpret(encoding.deref(), size.deref(), 0), + count: count.deref() + } +} + +Ks.prototype.close = function() { + Keystone.ks_close(this._ks) + this._ks = null +} + +Ks.prototype.set_option = function(type, value) { + var err = Keystone.ks_option(this._ks, type, value) + if (err != consts.ERR_OK) { + throw new KsError(Keystone.ks_strerror(err), err) + } +} + +module.exports.Ks = Ks + +module.exports.is_arch_supported = function(arch) { + return Keystone.ks_arch_supported(arch) +} + +module.exports.__defineGetter__('version', function() { + var version = Keystone.ks_version(null, null) + return { + major: version >> 8, + minor: version & 255 + } +}) +extend(module.exports, consts) diff --git a/bindings/nodejs/package.json b/bindings/nodejs/package.json new file mode 100644 index 0000000..bf93ec9 --- /dev/null +++ b/bindings/nodejs/package.json @@ -0,0 +1,18 @@ +{ + "name": "keystone", + "version": "1.0.0", + "description": "Keystone assembler engine", + "homepage": "http://www.keystone-engine.org", + "main": "index.js", + "dependencies": { + "ffi": "^2.0.0", + "ref": "^1.3.2" + }, + "devDependencies": {}, + "scripts": { + "prepublish": "cd .. && python const_generator.py nodejs", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Ingmar Steen <iksteen@gmail.com>", + "license": "MIT" +} diff --git a/bindings/nodejs/sample.js b/bindings/nodejs/sample.js new file mode 100644 index 0000000..ddfa545 --- /dev/null +++ b/bindings/nodejs/sample.js @@ -0,0 +1,27 @@ +var keystone = require('.') // Or: require('keystone') if you have installed it + +console.log('Using keystone ' + keystone.version.major + '.' + keystone.version.minor) + +var ks, assembly, result + +// Check if architecture is supported +if (! keystone.is_arch_supported(keystone.ARCH_X86)) { + throw 'Warning: X86 architecture not supported by keystone.' +} + +// Create a new Keystone instance for X86 64bit +ks = new keystone.Ks(keystone.ARCH_X86, keystone.MODE_64) + +// Assemble some instructions +assembly = 'inc rcx; dec rbx' +result = ks.asm(assembly) +console.log('"' + assembly + '"', ':', result.encoding) + +// Change syntax, assemble some more instructions +assembly = 'lea rax, [label1]\nnop\nnop\nlabel1:' +ks.syntax = keystone.OPT_SYNTAX_NASM +result = ks.asm(assembly) +console.log('"' + assembly.replace(/\n/g, '; ') + '"', ':', result.encoding) + +// Close Keystone instance to free resources +ks.close() -- GitLab