init commit

vs2017
_xeroxz 3 years ago
commit 095f1b17d0

10
.NOTE

@ -0,0 +1,10 @@
Checklist for a new release:
- update ChangeLog
- cd bindings; make
- update API version in keystone.h
- update API version in CMakeLists.txt
- change version + dev status in bindings/python/setup.py
- push all local commits
- tag + push -- tags -> check for new Pypi package

@ -0,0 +1,33 @@
version: 1.0-{build}
os:
- Visual Studio 2015
before_build:
- call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
build_script:
- mkdir build
- cd build
- ..\nmake-dll.bat
- ..\nmake-lib.bat
after_build:
- mkdir keystone-windows-latest
- copy llvm\bin\keystone.dll keystone-windows-latest
- copy llvm\lib\keystone.lib keystone-windows-latest
- copy kstool\kstool.exe keystone-windows-latest
- 7z a keystone-windows-latest.zip keystone-windows-latest\*
artifacts:
- path: build\keystone-windows-latest.zip
name: All Windows binaries
- path: build\llvm\bin\keystone.dll
name: keystone.dll
- path: build\llvm\lib\keystone.lib
name: keystone.lib
- path: build\kstool\kstool.exe
name: kstool.exe

@ -0,0 +1,80 @@
name: PyPI 📦 Distribution
on: [push]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
platform: [x32, x64]
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Set up MSVC x86
if: matrix.os == 'windows-latest' && matrix.platform == 'x32'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x86
- name: Set up MSVC x64
if: matrix.os == 'windows-latest' && matrix.platform == 'x64'
uses: ilammy/msvc-dev-cmd@v1
- name: Install dependencies
run: |
pip install setuptools wheel
- name: Build distribution 📦
shell: bash
run: |
if [ ${{ matrix.platform }} == 'x32' ] && [ ${{ matrix.os }} == 'windows-latest' ]; then
cd bindings/python && python setup.py build -p win32 bdist_wheel -p win32
elif [ ${{ matrix.platform }} == 'x32' ] && [ ${{ matrix.os }} == 'ubuntu-latest' ]; then
docker run --rm -v `pwd`/:/work dockcross/manylinux1-x86 > ./dockcross
chmod +x ./dockcross
./dockcross bindings/python/build_wheel.sh
elif [ ${{ matrix.platform }} == 'x64' ] && [ ${{ matrix.os }} == 'ubuntu-latest' ]; then
docker run --rm -v `pwd`/:/work dockcross/manylinux1-x64 > ./dockcross
chmod +x ./dockcross
./dockcross bindings/python/build_wheel.sh
elif [ ${{ matrix.platform }} == 'x32' ] && [ ${{ matrix.os }} == 'macos-latest' ]; then
cd bindings/python && python setup.py sdist
else
cd bindings/python && python setup.py bdist_wheel
fi
- uses: actions/upload-artifact@v2
with:
path: ${{ github.workspace }}/bindings/python/dist/*
publish:
needs: [build]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/download-artifact@v2
with:
name: artifact
path: dist
- name: Publish distribution 📦 to test PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.testpypi_pass }}
repository_url: https://test.pypi.org/legacy/
- name: Publish distribution 📦 to PyPI
if: ${{ success() }}
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_pass }}

29
.gitignore vendored

@ -0,0 +1,29 @@
.DS_Store
*.swp
*.d
*.o
*.a
*.dSYM
*.so
*.so.*
*.exe
*.dll
*.dylib
*.class
*.jar
*.pyc
*.egg-info
*.tgz
*.tar.gz
*.github.io
_*.diff
build*/
samples/sample
_sample*.txt
tmp
MANIFEST
bindings/python/dist/
bindings/python/src/

@ -0,0 +1,67 @@
language: cpp
sudo: false
script:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLLVM_TARGETS_TO_BUILD="all" -G "Unix Makefiles" ..
- make -j 8
compiler:
- clang
- gcc
os:
- linux
- osx
matrix:
fast_finish: true
include:
- if: branch = master
os: osx
osx_image: xcode10.1
compiler: clang
before_cache:
- brew cleanup
- find /usr/local/Homebrew \! -regex ".+\.git.+" -delete;
cache:
directories:
- $HOME/Library/Caches/Homebrew
- /usr/local/Homebrew
before_install:
- cd /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core && git stash && git clean -d -f
script: brew update && brew install --HEAD keystone
- if: branch = master
os: osx
osx_image: xcode10.1
compiler: gcc
before_cache:
- brew cleanup
- find /usr/local/Homebrew \! -regex ".+\.git.+" -delete;
cache:
directories:
- $HOME/Library/Caches/Homebrew
- /usr/local/Homebrew
before_install:
- cd /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core && git stash && git clean -d -f
script: brew update && brew install --HEAD keystone
- name: "Windows nmake 32bit"
os: windows
language: shell
script:
- mkdir build
- cd build
- cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat' x86 '&' cmd.exe //C '..\nmake-dll.bat' X86 '&' cmd.exe //c '..\nmake-lib.bat' X86
- name: "Windows nmake 64bit"
os: windows
language: shell
script:
- mkdir build
- cd build
- cmd.exe //C 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat' amd64 '&' cmd.exe //C '..\nmake-dll.bat' '&' cmd.exe //c '..\nmake-lib.bat'

@ -0,0 +1 @@
Nguyen Anh Quynh <aquynh -at- gmail.com>

@ -0,0 +1,123 @@
# Keystone Assembler Engine (www.keystone-engine.org)
# By Nguyen Anh Quynh, 2016
cmake_minimum_required(VERSION 2.8.7)
project(keystone)
set(KEYSTONE_VERSION_MAJOR 0)
set(KEYSTONE_VERSION_MINOR 9)
option(KEYSTONE_BUILD_STATIC_RUNTIME "Embed static runtime" ON)
option(BUILD_LIBS_ONLY "Only build keystone library" 0)
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "No build type selected, default to Debug")
set(CMAKE_BUILD_TYPE "Debug")
endif()
if (POLICY CMP0022)
cmake_policy(SET CMP0022 NEW) # automatic when 2.8.12 is required
endif()
if (POLICY CMP0051)
# CMake 3.1 and higher include generator expressions of the form
# $<TARGETLIB:obj> in the SOURCES property. These need to be
# stripped everywhere that access the SOURCES property, so we just
# defer to the OLD behavior of not including generator expressions
# in the output for now.
cmake_policy(SET CMP0051 OLD)
endif()
if (POLICY CMP0063)
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # automatic when 3.3.2 is required
endif()
if (CMAKE_VERSION VERSION_LESS 3.1.20141117)
set(cmake_3_2_USES_TERMINAL)
else()
set(cmake_3_2_USES_TERMINAL USES_TERMINAL)
endif()
if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /EHsc")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /EHsc")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /EHsc")
else()
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
# Use GNUInstallDirs to set LLVM_LIBDIR_SUFFIX. This should automatically
# handle cases where the libdir should be lib/, lib64/, or lib/<arch>/
# depending on the target system and cmake options.
include(GNUInstallDirs)
string(REGEX REPLACE "lib(.*)$" "\\1" LLVM_LIBDIR_SUFFIX "${CMAKE_INSTALL_LIBDIR}")
set(LLVM_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING "Define suffix of library directory name (32/64)")
endif()
# Force static runtime libraries
if (KEYSTONE_BUILD_STATIC_RUNTIME)
FOREACH(flag
CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_DEBUG_INIT
CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG_INIT)
if (MSVC)
STRING(REPLACE "/MD" "/MT" "${flag}" "${${flag}}")
SET("${flag}" "${${flag}} /EHsc")
endif (MSVC)
ENDFOREACH()
endif ()
add_subdirectory(llvm)
# for Windows, do not build kstool if buiding DLL
# TODO: fix this
if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
if (NOT BUILD_LIBS_ONLY AND NOT BUILD_SHARED_LIBS)
add_subdirectory(kstool)
endif()
else()
if (NOT BUILD_LIBS_ONLY)
add_subdirectory(kstool)
endif()
endif()
# generate and install pkg-config.pc
FIND_PACKAGE(PkgConfig)
SET(PKG_CONFIG_FILE_PATH
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
)
SET(PKG_CONFIG_LIBDIR
"${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}"
)
SET(PKG_CONFIG_INCLUDEDIR
"${CMAKE_INSTALL_PREFIX}/include"
)
SET(PKG_CONFIG_LIBS
"-L\${libdir} -lkeystone"
)
SET(PKG_CONFIG_CFLAGS
"-I\${includedir}"
)
CONFIGURE_FILE(
"${CMAKE_CURRENT_SOURCE_DIR}/pkg-config.pc.cmake"
"${PKG_CONFIG_FILE_PATH}"
)
INSTALL(FILES "${PKG_CONFIG_FILE_PATH}"
DESTINATION lib${LLVM_LIBDIR_SUFFIX}/pkgconfig)
# uninstall target
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/CMakeUninstall.in"
"${CMAKE_CURRENT_BINARY_DIR}/CMakeUninstall.cmake"
IMMEDIATE @ONLY)
if(NOT BUILD_LIBS_ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/CMakeUninstall.cmake)
endif()
if(NOT BUILD_LIBS_ONLY)
add_subdirectory(suite/fuzz)
endif()

@ -0,0 +1,21 @@
if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif(NOT "${rm_retval}" STREQUAL 0)
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
endforeach(file)

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

@ -0,0 +1,54 @@
This file credits all the contributors of Keystone Engine (keystone-engine.org).
Beta testers (in no particular order)
=====================================
Mike Guidry
Eloi Vanderbeken
Tim "diff" Strazzere
Matt Graeber
Edgar Barbosa
Loi Anh Tuan
Sascha Schirra
hugsy
Alexander Tereshkin
Sébastien Duquette
Sergi Alvarez, aka pancake (author of radare)
Richo Healey
Remco Verhoef (honeytrap.io)
Duncan Ogilvie
Bruce Dang
Dan Caselden
Jaime Peñalba
Daniel Collin
Vitaly Osipov
Dang Hoang Vu
Paul Rascagneres
postmodern
Fotis Loukos
Peter Geissler
Ingmar Steen (CED)
Matteo Favaro
Julien Legras
Anton Kochkov
Francisco Alonso
rdmz
practicalswift
Jurriaan Bremer
Contributors (in no particular order)
=====================================
Ingmar Steen: NodeJS binding.
Sascha Schirra: Ruby binding.
Remco Verhoef: Go & Rust bindings.
Adrian Herrera: Haskell binding.
practicalswift: Various cool bugs found by fuzzing.
Aziem Chawdhary: OCaml binding.
Ruben Boonen: PowerShell binding.
Marco Fornaro: C# binding.
David Zimmer: VB6 binding.
Michael Mohr: Debian packaging.
Jämes Ménétrey (ZenLulz): Java binding.
Philippe Antoine (Catena cyber): fuzzing.
Kevin Foo (chfl4gs): PyPI packaging, Travis-CI, fixes for Python binding.

@ -0,0 +1,96 @@
This file details the changelog of Keystone.
------------------------------
Version 0.9.2: June 21st, 2020
- Fix for Python binding (Pypi package)
----------------------------------
Version 0.9.2-rc1: June 13th, 2020
[ Core & tool ]
- Default radix set to 16
- kstool accepts -b option to print out encoding binary to output
- Do not build Universal binaries for Mac
- Better installer for Linux
- Add Ethereum VM architecture
- Better support for older compiler
- Add Masm binding
- Rename namespace llvm to llvm_ks
- Better cross compile with Android NDK
- Add KS_VERSION_{MAJOR, MINOR, EXTRA}
- Add new option KS_OPT_SYM_RESOLVER
- Fix memory leaks in ks_asm()
[ X86 ]
- Fix X86 prefix ordering
[ Arm ]
- Fix Thumb backward branch
- Fix Thumb2 ADR, B.W
- Fix BL, BLX in Thumb mode
[ Arm64 ]
- Fix ADRP
- Fix PC-relative offset for branch instructions
[ Mips ]
- Fix JAL IMM
[ PowerPC ]
- Remove bound check on branch instructions
[ Python binding ]
- Fix module loading issue
- Load library versioning
- Add as_bytes=True param to asm()
[ Bindings ]
- Multiple fixes for Python, Go, Rust, .NET
- Add Java, VB6, C#, Powershell, Perl bindings
------------------------------
Version 0.9.1: July 27th, 2016
[ Core & tool ]
- Fix a segfault in kstool (on missing assembly input).
- kstool now allows to specify instruction address.
- Build Mac libraries in universal format by default.
- Add "lib32" option to cross-compile to 32-bit *nix (on 64-bit system).
- Add "lib_only" option to only build libraries (skip kstool).
- New bindings: Haskell & OCaml.
[ X86 ]
- Fix instructions: LJMP, LCALL, CDQE, SHR, SHL, SAR, SAL, LOOP, LOOPE, LOOPNE
- Better handling a lot of tricky input caught by assert() before.
- Better support for Nasm syntax.
[ Arm ]
- Fix BLX instruction.
[ Python binding ]
- Better Python3 support.
- Expose @stat_count in KsError class when ks_asm() returns with error.
See sample code in bindings/python/sample_asm_count.py
[ Go binding ]
- Fix Go binding for 32-bit
---------------------------
Version 0.9: May 31th, 2016
- Initial public release.

@ -0,0 +1,132 @@
FOSS License Exception
What is the FOSS License Exception?
This is based on the Oracle's Free and Open Source Software ("FOSS")
License Exception.
The Free and Open Source Software ("FOSS") License Exception allows
developers of FOSS applications to include Keystone Libraries with their
FOSS applications. This exception permits distribution of Keystone libraries
with a developer's FOSS applications licensed under the terms of another
FOSS license listed below, even though such other FOSS license may be
incompatible with the GPL.
The following terms and conditions describe the circumstances under which
Oracle's FOSS License Exception applies.
FOSS License Exception Terms and Conditions
1. Definitions. "Derivative Work" means a derivative work, as defined
under applicable copyright law, formed entirely from the Program and
one or more FOSS Applications.
"FOSS Application" means a free and open source software application
distributed subject to a license listed in the section below titled
"FOSS License List."
"FOSS Notice" means a notice put in a copy of Keystone Libraries
stating that such copy of Keystone Libraries may be distributed under
FOSS License Exception.
"Independent Work" means portions of the Derivative Work that are not
derived from the Program and can reasonably be considered independent
and separate works.
"Program" means a copy of Keystone Libraries that contains
a FOSS Notice.
2. A FOSS application developer ("you" or "your") may distribute a
Derivative Work provided that you and the Derivative Work meet all of
the following conditions:
a. You obey the GPL in all respects for the Program and all portions
(including modifications) of the Program included in the
Derivative Work (provided that this condition does not apply to
Independent Works);
b. The Derivative Work does not include any work licensed under the
GPL other than the Program;
c. You distribute Independent Works subject to a license listed in
the section below titled "FOSS License List";
d. You distribute Independent Works in object code or executable
form with the complete corresponding machine-readable source code
on the same medium and under the same FOSS license applying to
the object code or executable forms;
e. All works that are aggregated with the Program or the Derivative
Work on a medium or volume of storage are not derivative works of
the Program, Derivative Work or FOSS Application, and must
reasonably be considered independent and separate works.
3. Keystone's authors reserves all rights not expressly granted in these
terms and conditions. If all of the above conditions are not met, then
this FOSS License Exception does not apply to you/your Derivative Work.
FOSS License List
+------------------------------------------------------------------------+
|License Name |Version(s)/Copyright Date|
|----------------------------------------------+-------------------------|
|Academic Free License |2.0 |
|----------------------------------------------+-------------------------|
|Apache Software License |1.0/1.1/2.0 |
|----------------------------------------------+-------------------------|
|Apple Public Source License |2.0 |
|----------------------------------------------+-------------------------|
|Artistic license |From Perl 5.8.0 |
|----------------------------------------------+-------------------------|
|BSD license |"July 22 1999" |
|----------------------------------------------+-------------------------|
|Common Development and Distribution License |1.0 |
|(CDDL) | |
|----------------------------------------------+-------------------------|
|Common Public License |1.0 |
|----------------------------------------------+-------------------------|
|Eclipse Public License |1.0 |
|----------------------------------------------+-------------------------|
|European Union Public License (EUPL)¹ |1.1 |
|----------------------------------------------+-------------------------|
|GNU Affero General Public License (AGPL) |3.0 |
|----------------------------------------------+-------------------------|
|GNU Library or "Lesser" General Public License|2.0/2.1/3.0 |
|(LGPL) | |
|----------------------------------------------+-------------------------|
|GNU General Public License (GPL) |3.0 |
|----------------------------------------------+-------------------------|
|IBM Public License |1.0 |
|----------------------------------------------+-------------------------|
|Jabber Open Source License |1.0 |
|----------------------------------------------+-------------------------|
|MIT License (As listed in file |- |
|MIT-License.txt) | |
|----------------------------------------------+-------------------------|
|Mozilla Public License (MPL) |1.0/1.1 |
|----------------------------------------------+-------------------------|
|Open Software License |2.0 |
|----------------------------------------------+-------------------------|
|OpenSSL license (with original SSLeay license)|"2003" ("1998") |
|----------------------------------------------+-------------------------|
|PHP License |3.0/3.01 |
|----------------------------------------------+-------------------------|
|Python license (CNRI Python License) |- |
|----------------------------------------------+-------------------------|
|Python Software Foundation License |2.1.1 |
|----------------------------------------------+-------------------------|
|Sleepycat License |"1999" |
|----------------------------------------------+-------------------------|
|University of Illinois/NCSA Open Source |- |
|License | |
|----------------------------------------------+-------------------------|
|W3C License |"2001" |
|----------------------------------------------+-------------------------|
|X11 License |"2001" |
|----------------------------------------------+-------------------------|
|Zlib/libpng License |- |
|----------------------------------------------+-------------------------|
|Zope Public License |2.0 |
+------------------------------------------------------------------------+
¹) When an Independent Work is licensed under a "Compatible License"
pursuant to the EUPL, the Compatible License rather than the EUPL is the
applicable license for purposes of these FOSS License Exception Terms and
Conditions.

@ -0,0 +1,8 @@
The commercial license of Keystone allows you to use Keystone libraries & tools
in commercial products.
- The Keystone commercial license is perpetual and royalty-free.
- You have the right to distribute the binary and modifications of Keystone.
- No source code redistribution is allowed in any way.
Contact keystone.engine@gmail.com for further information.

@ -0,0 +1,63 @@
Keystone Engine
==============
[![Build Status](https://travis-ci.org/keystone-engine/keystone.svg?branch=master)](https://travis-ci.org/keystone-engine/keystone)
[![Build Status](https://semaphoreci.com/api/v1/aquynh/keystone/branches/master/badge.svg)](https://semaphoreci.com/aquynh/keystone)
[![Build status](https://ci.appveyor.com/api/projects/status/c27slvyrijiejvqs?svg=true)](https://ci.appveyor.com/project/aquynh/keystone)
Keystone is a lightweight multi-platform, multi-architecture assembler framework.
It offers some unparalleled features:
- Multi-architecture, with support for Arm, Arm64 (AArch64/Armv8), Ethereum Virtual Machine, Hexagon, Mips, PowerPC, Sparc, SystemZ & X86 (include 16/32/64bit).
- Clean/simple/lightweight/intuitive architecture-neutral API.
- Implemented in C/C++ languages, with bindings for Java, Masm, C#, PowerShell, Perl, Python, NodeJS, Ruby, Go, Rust, Haskell, VB6 & OCaml available.
- Native support for Windows & \*nix (with Mac OSX, Linux, \*BSD & Solaris confirmed).
- Thread-safe by design.
- Open source - with a dual license.
Keystone is based on LLVM, but it goes much further with [a lot more to offer](/docs/beyond_llvm.md).
Further information is available at http://www.keystone-engine.org
License
-------
Keystone is available under a dual license:
- Version 2 of the GNU General Public License (GPLv2). (I.e. Without the "any later version" clause.).
License information can be found in the [COPYING file](COPYING) and the [EXCEPTIONS-CLIENT file](EXCEPTIONS-CLIENT).
This combination allows almost all of open source projects to use Keystone without conflicts.
- For commercial usage in production environments, contact the authors of Keystone to buy a royalty-free license.
See [LICENSE-COM.TXT](LICENSE-COM.TXT) for more information.
Compilation & Docs
------------------
See [COMPILE.md](docs/COMPILE.md) file for how to compile and install Keystone.
More documentation is available in [docs/README.md](docs/README.md).
Contact
-------
[Contact us](http://www.keystone-engine.org/contact/) via mailing list, email or twitter for any questions.
Contribute
----------
Keystone is impossible without generous support from [our sponsors](/SPONSORS.TXT). We cannot thank them enough!
[CREDITS.TXT](CREDITS.TXT) records other important contributors of our project.
If you want to contribute, please pick up something from our [Github issues](https://github.com/keystone-engine/keystone/issues).
We also maintain a list of more challenged problems in a [TODO list](https://github.com/keystone-engine/keystone/wiki/TODO).

@ -0,0 +1,6 @@
Version 0.9.2 works quite well for all architectures, but is known to not be able
to deal with some malformed craft input. In such a case, Keystone usually exits
with some error messages. Report if you experience this issue, so we can fix
that in the next release.
See details of open bugs at https://github.com/keystone-engine/keystone/issues

@ -0,0 +1,8 @@
This file lists all the sponsors of Keystone Engine.
Thanks a lot for your great support!
----------------------------------------------------
Mike Guidry
Synacktiv Digital Security (www.synacktiv.com)
Tim "diff" Strazzere
Veris Group (www.verisgroup.com)

@ -0,0 +1,87 @@
# Keystone Engine
# By Nguyen Anh Quynh & Dang Hoang Vu, 2015
TMPDIR = /tmp/keystone_sample
DIFF = diff -u -w
SAMPLE_ARM = $(TMPDIR)/sample_arm
SAMPLE_ARM64 = $(TMPDIR)/sample_arm64
SAMPLE_MIPS = $(TMPDIR)/sample_mips
SAMPLE_M68K = $(TMPDIR)/sample_m68k
SAMPLE_SPARC = $(TMPDIR)/sample_sparc
SAMPLE_X86 = $(TMPDIR)/sample_x86
.PHONY: all expected python rust go nodejs ruby
all:
cd python && $(MAKE) gen_const
cd nodejs && $(MAKE) gen_const
python const_generator.py rust
python const_generator.py go
cd ruby && $(MAKE) gen_const
python const_generator.py powershell
samples: expected python
sample_python: expected python
expected:
cd ../samples && $(MAKE)
mkdir -p $(TMPDIR)
../samples/sample_arm > $(SAMPLE_ARM)_e
../samples/sample_arm64 > $(SAMPLE_ARM64)_e
../samples/sample_mips > $(SAMPLE_MIPS)_e
../samples/sample_sparc > $(SAMPLE_SPARC)_e
../samples/sample_m68k > $(SAMPLE_M68K)_e
../samples/sample_x86 > $(SAMPLE_X86)_e
python: FORCE
cd python && $(MAKE)
python python/sample_arm.py > $(SAMPLE_ARM)_o
python python/sample_arm64.py > $(SAMPLE_ARM64)_o
python python/sample_mips.py > $(SAMPLE_MIPS)_o
python python/sample_sparc.py > $(SAMPLE_SPARC)_o
python python/sample_m68k.py > $(SAMPLE_M68K)_o
python python/sample_x86.py > $(SAMPLE_X86)_o
$(MAKE) sample_diff
ruby: FORCE
cd ruby && $(MAKE)
nodejs: FORCE
cd nodejs && $(MAKE)
rust: FORCE
cd rust && $(MAKE)
go: FORCE
cd go && $(MAKE)
ocaml: FORCE
cd ocaml && $(MAKE)
sample_diff: FORCE
$(DIFF) $(SAMPLE_ARM)_e $(SAMPLE_ARM)_o
$(DIFF) $(SAMPLE_ARM64)_e $(SAMPLE_ARM64)_o
$(DIFF) $(SAMPLE_MIPS)_e $(SAMPLE_MIPS)_o
$(DIFF) $(SAMPLE_SPARC)_e $(SAMPLE_SPARC)_o
$(DIFF) $(SAMPLE_M68K)_e $(SAMPLE_M68K)_o
$(DIFF) $(SAMPLE_X86)_e $(SAMPLE_X86)_o
clean:
rm -rf $(TMPDIR)
cd python && $(MAKE) clean
cd rust && $(MAKE) clean
cd go && $(MAKE) clean
cd nodejs && $(MAKE) clean
cd ruby && $(MAKE) clean
cd ocaml && $(MAKE) clean
check:
cd python && $(MAKE) check
cd rust && $(MAKE) check
cd go && $(MAKE) check
cd nodejs && $(MAKE) check
cd ruby && $(MAKE) check
FORCE:

@ -0,0 +1,23 @@
This directory contains bindings for Keystone.
See <language>/README* for how to install each binding.
Except Python, all other bindings are contributed by community.
- NodeJS binding: by Ingmar Steen
- Ruby binding: by Sascha Schirra
- Go binding: by Remco Verhoef
- Rust binding: by Remco Verhoef
- Haskell binding: by Adrian Herrera
- OCaml binding: by Aziem Chawdhary
- PowerShell binding: by Ruben Boonen
- C# binding: by Marco Fornaro
- VB6 binding: by David Zimmer
- Masm binding: by mrfearless
- Java binding: by Jämes Ménétrey (ZenLulz)
Other bindings maintained externally by community:
- Perl-Keystone: Perl binding made by @t00sh.
https://github.com/t00sh/perl-keystone
- keystone-rs: Rust binding by @tathanhdinh
https://github.com/tathanhdinh/keystone-rs

@ -0,0 +1,430 @@
# Keystone Engine
# Adapted from the code of Dang Hoang Vu for Capstone Engine, 2013
from __future__ import print_function
import sys, re, os
INCL_DIR = os.path.join('..', 'include', 'keystone')
# NOTE: this reflects the value of KS_ERR_ASM_xxx in keystone.h
ks_err_val = { 'KS_ERR_ASM': '128', 'KS_ERR_ASM_ARCH': '512' }
include = [ 'arm.h', 'arm64.h', 'mips.h', 'x86.h', 'sparc.h', 'ppc.h', 'systemz.h', 'hexagon.h', 'evm.h', 'keystone.h' ]
def CamelCase(s):
# return re.sub(r'(\w)+\_?', lambda m:m.group(0).capitalize(), s)
return ''.join(''.join([w[0].upper(), w[1:].lower()]) for w in s.split('_'))
template = {
'powershell': {
'header': "/// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_h.cs]\n",
'footer': "",
'out_file': './powershell/Keystone/Const/%s_h.cs',
# prefixes for constant filenames of all archs - case sensitive
'keystone.h': 'keystone',
'comment_open': '///',
'comment_close': '',
'rules': [
{
'regex': r'.*',
'line_format': 'KS_{0} = {1},\n',
'fn': (lambda x: x),
},
]
},
'rust': {
'header': "#![allow(non_camel_case_types)]\n// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.rs]\nuse ::libc::*;\n",
'footer': "",
# prefixes for constant filenames of all archs - case sensitive
'arm.h': 'keystone',
'arm64.h': 'keystone',
'mips.h': 'keystone',
'x86.h': 'keystone',
'sparc.h': 'keystone',
'systemz.h': 'keystone',
'ppc.h': 'keystone',
'hexagon.h': 'keystone',
'evm.h': 'keystone',
'keystone.h': 'keystone',
'comment_open': '/*',
'comment_close': '*/',
'out_file': './rust/keystone-sys/src/%s_const.rs',
'rules': [
{
'regex': r'(API)_.*',
'pre': '\n',
'line_format': 'pub const {0}: c_uint = {1};\n',
'fn': (lambda x: x),
},
{ 'regex': r'MODE_.*',
'pre': '\n' +
'bitflags! {{\n' +
'#[repr(C)]\n' +
' pub struct Mode: c_int {{\n',
'line_format': ' const {0} = {1};\n',
'fn': (lambda x: '_'.join(x.split('_')[1:]) if not re.match(r'MODE_\d+', x) else x),
'post': ' }\n}',
},
{
'regex': r'ARCH_.*',
'pre': '\n' +
'#[repr(C)]\n' +
'#[derive(Debug, PartialEq, Clone, Copy)]\n' +
'pub enum Arch {{\n',
'line_format': ' {0} = {1},\n',
'fn': (lambda x: '_'.join(x.split('_')[1:])),
'post': '}\n',
},
{
'regex': r'(OPT_([A-Z]+)|OPT_SYM_RESOLVER)$',
'pre': '#[repr(C)]\n' +
'#[derive(Debug, PartialEq, Clone, Copy)]\n' +
'pub enum OptionType {{\n',
'line_format': ' {0} = {1},\n',
'fn': (lambda x: '_'.join(x.split('_')[1:])),
'post': '}\n',
},
{
'regex': r'OPT_(?!SYM)([A-Z]+\_)+[A-Z]+',
'pre': 'bitflags! {{\n'
'#[repr(C)]\n' +
' pub struct OptionValue: size_t {{\n',
'line_format': ' const {0} = {1};\n',
'fn': (lambda x: '_'.join(x.split('_')[1:])),
'post': ' }\n}\n',
},
{
'regex': r'ERR_(.*)',
'pre': 'bitflags! {{\n' +
'#[repr(C)]\n' +
' pub struct Error: c_int {{\n',
'line_format': ' const {0} = {1};\n',
'fn': (lambda x: '_'.join(x.split('_')[1:])),
'post': ' }\n}',
},
],
},
'go': {
'header': "package keystone\n// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.go]\n\n",
'footer': "",
# 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',
'evm.h': 'evm',
'keystone.h': 'keystone',
'comment_open': '/*',
'comment_close': '*/',
'out_file': './go/keystone/%s_const.go',
'rules': [
{
'regex': r'API_.*',
'pre': 'const (\n',
'line_format': '\t{0} = {1}\n',
'fn': (lambda x: x),
'post': ')\n',
},
{ 'regex': r'MODE_.*',
'pre': 'const (\n',
'line_format': '\t{0} Mode = {1}\n',
'fn': (lambda x: x),
'post': ')\n',
},
{
'regex': r'ARCH_.*',
'pre': 'const (\n',
'line_format': '\t{0} Architecture = {1}\n',
'fn': (lambda x: x),
'post': ')\n',
},
{
'regex': r'OPT_([A-Z]+)$',
'pre': 'const (\n',
'line_format': '\t{0} OptionType = {1}\n',
'fn': (lambda x: x),
'post': ')\n',
},
{
'regex': r'OPT_([A-Z]+\_)+[A-Z]+',
'pre': 'const (\n',
'line_format': '\t{0} OptionValue = {1}\n',
'fn': (lambda x: x),
'post': ')\n',
},
{
'regex': r'ERR_.*',
'pre': 'const (\n',
'line_format': '\t{0} Error = {1}\n',
'fn': (lambda x: x),
'post': ')\n',
},
]
},
'python': {
'header': "# For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.py]\n",
'footer': "",
'out_file': './python/keystone/%s_const.py',
# 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',
'evm.h': 'evm',
'keystone.h': 'keystone',
'comment_open': '#',
'comment_close': '',
'rules': [
{
'regex': r'.*',
'line_format': 'KS_{0} = {1}\n',
'fn': (lambda x: x),
},
]
},
'nodejs': {
'header': "// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.js]\n",
'footer': "",
'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',
'evm.h': 'evm',
'keystone.h': 'keystone',
'comment_open': '//',
'comment_close': '',
'rules': [
{
'regex': r'.*',
'line_format': 'module.exports.{0} = {1}\n',
'fn': (lambda x: x),
},
]
},
'ruby': {
'header': "# For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.rb]\n\nmodule Keystone\n",
'footer': "end",
'out_file': './ruby/keystone_gem/lib/keystone/%s_const.rb',
# 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',
'evm.h': 'evm',
'keystone.h': 'keystone',
'comment_open': '#',
'comment_close': '',
'rules': [
{
'regex': r'.*',
'line_format': '\tKS_{0} = {1}\n',
'fn': (lambda x: x),
},
]
},
'csharp': {
'header': "// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%sConstants.cs]\nnamespace KeystoneNET\n{",
'footer': "}",
'out_file': './csharp/KeystoneNET/KeystoneNET/Constants/%sConstants.cs',
# prefixes for constant filenames of all archs - case sensitive
'keystone.h': 'keystone',
'arm.h': 'arm',
'arm64.h': 'arm64',
'mips.h': 'mips',
'x86.h': 'x86',
'sparc.h': 'sparc',
'systemz.h': 'systemz',
'ppc.h': 'ppc',
'hexagon.h': 'hexagon',
'evm.h': 'evm',
'keystone.h': 'keystone',
'comment_open': '//',
'comment_close': '',
'rules': [
{
'regex': r'(ARCH)_.*',
'pre': '\n\tpublic enum KeystoneArchitecture : int\n\t{{\n',
'post': '\t}',
'line_format': '\t\tKS_{0} = {1},\n',
'fn': (lambda x: x),
},
{
'regex': r'(MODE)_.*',
'pre': '\n\tpublic enum KeystoneMode : uint\n\t{{\n',
'post': '\t}',
'line_format': '\t\tKS_{0} = {1},\n',
'fn': (lambda x: x),
},
{
'regex': r'(ERR)_.*',
'pre': '\n\tpublic enum {0}Error : short\n\t{{\n',
'post': '\t}',
'line_format': '\t\tKS_{0} = {1},\n',
'fn': (lambda x: x),
},
{
'regex': r'((OPT_([A-Z]+))|(OPT_SYM_RESOLVER))$',
'pre': '\n\tpublic enum KeystoneOptionType : short\n\t{{\n',
'post': '\t}',
'line_format': '\t\tKS_{0} = {1},\n',
'fn': (lambda x: x),
},
{
'regex': r'OPT_(?!SYM)([A-Z]+\_)+[A-Z]+',
'pre': '\n\tpublic enum KeystoneOptionValue : short\n\t{{\n',
'post': '\t}',
'line_format': '\t\tKS_{0} = {1},\n',
'fn': (lambda x: x),
},
]
},
}
# markup for comments to be added to autogen files
MARKUP = '//>'
def gen(lang):
global include, INCL_DIR
consts = {}
templ = template[lang]
for target in include:
if target not in templ:
continue
prefix = templ[target]
if target == 'keystone.h':
prefix = 'keystone'
lines = open(os.path.join(INCL_DIR, target)).readlines()
consts[prefix] = []
previous = {}
count = 0
for line in lines:
line = line.strip()
if line.startswith(MARKUP): # markup for comments
outfile.write(("\n%s%s%s\n" %(templ['comment_open'], \
line.replace(MARKUP, ''), templ['comment_close'])).encode("utf-8"))
continue
if line == '' or line.startswith('//'):
continue
tmp = line.strip().split(',')
for t in tmp:
t = t.strip()
if not t or t.startswith('//'): continue
f = re.split('\s+', t)
# parse #define KS_TARGET (num)
define = False
if f[0] == '#define' and len(f) >= 3:
define = True
f.pop(0)
f.insert(1, '=')
# if f[0].startswith("KS_" + prefix.upper()):
if f[0].startswith("KS_"):
if len(f) > 1 and f[1] not in ('//', '='):
print("WARNING: Unable to convert %s" % f)
print(" Line =", line)
continue
elif len(f) > 1 and f[1] == '=':
rhs = ''.join(f[2:])
else:
rhs = str(count)
lhs = f[0].strip()
# evaluate bitshifts in constants e.g. "KS_X86 = 1 << 1"
match = re.match(r'(?P<rhs>\s*\d+\s*<<\s*\d+\s*)', rhs)
if match:
rhs = str(eval(match.group(1)))
else:
# evaluate references to other constants e.g. "KS_ARM_REG_X = KS_ARM_REG_SP"
match = re.match(r'^([^\d]\w+)$', rhs)
if match:
try:
rhs = previous[match.group(1)]
except:
rhs = match.group(1)
if not rhs.isdigit():
for k, v in previous.items():
rhs = re.sub(r'\b%s\b' % k, v, rhs)
try:
rhs = str(eval(rhs))
except:
rhs = ks_err_val[rhs]
lhs_strip = re.sub(r'^KS_', '', lhs)
consts[prefix].append((lhs_strip, rhs))
count = int(rhs) + 1
previous[lhs] = str(rhs)
rules = templ['rules']
for prefix in consts.keys():
outfile = open(templ['out_file'] % prefix, 'wb') # open as binary prevents windows newlines
outfile.write (templ['header'] % prefix)
for rule in rules:
regex = rule['regex']
consts2 = []
for const in consts.get(prefix):
if not (re.match(regex, const[0])):
continue
consts2.append(const)
if len(consts2) == 0:
continue
if rule.get('pre'):
outfile.write(rule.get('pre').format(CamelCase(prefix)))
for const in consts2:
lhs_strip = const[0]
rhs = const[1]
outfile.write(rule['line_format'].format(rule['fn'](lhs_strip), rhs, lhs_strip).encode("utf-8"))
if rule.get('post'):
outfile.write (rule.get('post'))
outfile.write ('\n')
outfile.write (templ['footer'])
outfile.close()
def main():
lang = sys.argv[1]
if not lang in template:
raise RuntimeError("Unsupported binding %s" % lang)
gen(sys.argv[1])
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage:", sys.argv[0], " <python>")
sys.exit(1)
main()

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

@ -0,0 +1,246 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Xx]64/
[Xx]86/
[Bb]uild/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Un-comment the next line if you do not want to checkin
# your web deploy settings because they may include unencrypted
# passwords
#*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# LightSwitch generated files
GeneratedArtifacts/
ModelManifest.xml
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
/keystone/keystone.dll

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RootNamespace>Keystone.Tests</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Keystone.Net/Keystone.Net.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="NUnit" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
<PackageReference Include="Shouldly" Version="3.0.0" />
</ItemGroup>
</Project>

@ -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<KeystoneException>(() => engine.Assemble("push eax, 0x42", 0));
Should.Throw<KeystoneException>(() => 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);
}
}
}
}

@ -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

@ -0,0 +1,10 @@
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm64Constants.cs]
namespace Keystone
{
public enum Arm64Error : short
{
KS_ERR_ASM_ARM64_INVALIDOPERAND = 512,
KS_ERR_ASM_ARM64_MISSINGFEATURE = 513,
KS_ERR_ASM_ARM64_MNEMONICFAIL = 514,
}
}

@ -0,0 +1,10 @@
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [armConstants.cs]
namespace Keystone
{
public enum ArmError : short
{
KS_ERR_ASM_ARM_INVALIDOPERAND = 512,
KS_ERR_ASM_ARM_MISSINGFEATURE = 513,
KS_ERR_ASM_ARM_MNEMONICFAIL = 514,
}
}

@ -0,0 +1,10 @@
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [hexagonConstants.cs]
namespace Keystone
{
public enum HexagonError : short
{
KS_ERR_ASM_HEXAGON_INVALIDOPERAND = 512,
KS_ERR_ASM_HEXAGON_MISSINGFEATURE = 513,
KS_ERR_ASM_HEXAGON_MNEMONICFAIL = 514,
}
}

@ -0,0 +1,107 @@
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystoneConstants.cs]
namespace Keystone
{
public enum Architecture : int
{
ARM = 1,
ARM64 = 2,
MIPS = 3,
X86 = 4,
PPC = 5,
SPARC = 6,
SYSTEMZ = 7,
HEXAGON = 8,
MAX = 9,
}
public enum Mode : uint
{
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
{
KS_ERR_ASM = 128,
KS_ERR_ASM_ARCH = 512,
KS_ERR_OK = 0,
KS_ERR_NOMEM = 1,
KS_ERR_ARCH = 2,
KS_ERR_HANDLE = 3,
KS_ERR_MODE = 4,
KS_ERR_VERSION = 5,
KS_ERR_OPT_INVALID = 6,
KS_ERR_ASM_EXPR_TOKEN = 128,
KS_ERR_ASM_DIRECTIVE_VALUE_RANGE = 129,
KS_ERR_ASM_DIRECTIVE_ID = 130,
KS_ERR_ASM_DIRECTIVE_TOKEN = 131,
KS_ERR_ASM_DIRECTIVE_STR = 132,
KS_ERR_ASM_DIRECTIVE_COMMA = 133,
KS_ERR_ASM_DIRECTIVE_RELOC_NAME = 134,
KS_ERR_ASM_DIRECTIVE_RELOC_TOKEN = 135,
KS_ERR_ASM_DIRECTIVE_FPOINT = 136,
KS_ERR_ASM_DIRECTIVE_UNKNOWN = 137,
KS_ERR_ASM_DIRECTIVE_EQU = 138,
KS_ERR_ASM_DIRECTIVE_INVALID = 139,
KS_ERR_ASM_VARIANT_INVALID = 140,
KS_ERR_ASM_EXPR_BRACKET = 141,
KS_ERR_ASM_SYMBOL_MODIFIER = 142,
KS_ERR_ASM_SYMBOL_REDEFINED = 143,
KS_ERR_ASM_SYMBOL_MISSING = 144,
KS_ERR_ASM_RPAREN = 145,
KS_ERR_ASM_STAT_TOKEN = 146,
KS_ERR_ASM_UNSUPPORTED = 147,
KS_ERR_ASM_MACRO_TOKEN = 148,
KS_ERR_ASM_MACRO_PAREN = 149,
KS_ERR_ASM_MACRO_EQU = 150,
KS_ERR_ASM_MACRO_ARGS = 151,
KS_ERR_ASM_MACRO_LEVELS_EXCEED = 152,
KS_ERR_ASM_MACRO_STR = 153,
KS_ERR_ASM_MACRO_INVALID = 154,
KS_ERR_ASM_ESC_BACKSLASH = 155,
KS_ERR_ASM_ESC_OCTAL = 156,
KS_ERR_ASM_ESC_SEQUENCE = 157,
KS_ERR_ASM_ESC_STR = 158,
KS_ERR_ASM_TOKEN_INVALID = 159,
KS_ERR_ASM_INSN_UNSUPPORTED = 160,
KS_ERR_ASM_FIXUP_INVALID = 161,
KS_ERR_ASM_LABEL_INVALID = 162,
KS_ERR_ASM_FRAGMENT_INVALID = 163,
KS_ERR_ASM_INVALIDOPERAND = 512,
KS_ERR_ASM_MISSINGFEATURE = 513,
KS_ERR_ASM_MNEMONICFAIL = 514
}
public enum OptionType : int
{
SYNTAX = 1,
SYM_RESOLVER = 2,
}
public enum OptionValue : short
{
SYNTAX_INTEL = 1,
SYNTAX_ATT = 2,
SYNTAX_NASM = 4,
SYNTAX_MASM = 8,
SYNTAX_GAS = 16,
SYNTAX_RADIX16 = 32
}
}

@ -0,0 +1,10 @@
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [mipsConstants.cs]
namespace Keystone
{
public enum MipsError : short
{
KS_ERR_ASM_MIPS_INVALIDOPERAND = 512,
KS_ERR_ASM_MIPS_MISSINGFEATURE = 513,
KS_ERR_ASM_MIPS_MNEMONICFAIL = 514,
}
}

@ -0,0 +1,10 @@
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [ppcConstants.cs]
namespace Keystone
{
public enum PpcError : short
{
KS_ERR_ASM_PPC_INVALIDOPERAND = 512,
KS_ERR_ASM_PPC_MISSINGFEATURE = 513,
KS_ERR_ASM_PPC_MNEMONICFAIL = 514,
}
}

@ -0,0 +1,10 @@
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [sparcConstants.cs]
namespace Keystone
{
public enum SparcError : short
{
KS_ERR_ASM_SPARC_INVALIDOPERAND = 512,
KS_ERR_ASM_SPARC_MISSINGFEATURE = 513,
KS_ERR_ASM_SPARC_MNEMONICFAIL = 514,
}
}

@ -0,0 +1,10 @@
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [systemzConstants.cs]
namespace Keystone
{
public enum SystemzError : short
{
KS_ERR_ASM_SYSTEMZ_INVALIDOPERAND = 512,
KS_ERR_ASM_SYSTEMZ_MISSINGFEATURE = 513,
KS_ERR_ASM_SYSTEMZ_MNEMONICFAIL = 514,
}
}

@ -0,0 +1,10 @@
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [x86Constants.cs]
namespace Keystone
{
public enum X86Error : short
{
KS_ERR_ASM_X86_INVALIDOPERAND = 512,
KS_ERR_ASM_X86_MISSINGFEATURE = 513,
KS_ERR_ASM_X86_MNEMONICFAIL = 514,
}
}

@ -0,0 +1,33 @@
namespace Keystone
{
/// <summary>
/// Defines an encoded instruction or group of instructions.
/// </summary>
public sealed class EncodedData
{
/// <summary>
/// Constructs the encoded data.
/// </summary>
internal EncodedData(byte[] buffer, int statementCount, ulong address)
{
Buffer = buffer;
Address = address;
StatementCount = statementCount;
}
/// <summary>
/// Gets the address of the first instruction for this operation.
/// </summary>
public ulong Address { get; }
/// <summary>
/// Gets the result of an assembly operation.
/// </summary>
public byte[] Buffer { get; }
/// <summary>
/// Gets the number of statements found.
/// </summary>
public int StatementCount { get; }
}
}

@ -0,0 +1,370 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
namespace Keystone
{
/// <summary>
/// Represents a Keystone engine.
/// </summary>
public sealed class Engine : IDisposable
{
private IntPtr engine = IntPtr.Zero;
private bool addedResolveSymbol;
private readonly ResolverInternal internalImpl;
private readonly List<Resolver> resolvers = new List<Resolver>();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool ResolverInternal(IntPtr symbol, ref ulong value);
/// <summary>
/// Gets or sets a value that represents whether a <see cref="KeystoneException" />
/// should be thrown on error.
/// </summary>
public bool ThrowOnError { get; set; }
/// <summary>
/// Delegate for defining symbol resolvers.
/// </summary>
/// <param name="symbol">Symbol to resolve.</param>
/// <param name="value">Address of taid symbol, if found.</param>
/// <returns>Whether the symbol was recognized.</returns>
public delegate bool Resolver(string symbol, ref ulong value);
/// <summary>
/// Event raised when keystone is resolving a symbol.
/// </summary>
/// <remarks>This event is only available on Keystone 0.9.2 or higher.</remarks>
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);
}
}
/// <summary>
/// Method used for symbol resolving.
/// </summary>
/// <param name="symbolPtr">Pointer to the name of the symbol.</param>
/// <param name="value">Address of the symbol, if found.</param>
/// <returns>Whether the symbol could be recognized.</returns>
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;
}
/// <summary>
/// Constructs the engine with a given architecture and a given mode.
/// </summary>
/// <param name="architecture">The target architecture.</param>
/// <param name="mode">The mode, i.e. endianness, word size etc.</param>
/// <remarks>
/// Some architectures are not supported.
/// Check with <see cref="IsArchitectureSupported(Architecture)"/> if the engine
/// supports the target architecture.
/// </remarks>
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);
}
/// <summary>
/// Sets an option in the engine.
/// </summary>
/// <param name="type">Type of the option.</param>
/// <param name="value">Value it the option.</param>
/// <returns>Whether the option was correctly set.</returns>
/// <exception cref="KeystoneException">An error encountered when setting the option.</exception>
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;
}
/// <summary>
/// Encodes the given statement(s).
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="size">Size of the buffer produced by the operation.</param>
/// <param name="statementCount">Number of statements found and encoded.</param>
/// <returns>Result of the operation, or <c>null</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
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;
}
/// <summary>
/// Encodes the given statement(s).
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <returns>Result of the operation, or <c>null</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
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);
}
/// <summary>
/// Encodes the given statement(s) into the given buffer.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="buffer">Buffer into which the data shall be written.</param>
/// <param name="index">Index into the buffer after which the data shall be written.</param>
/// <param name="statementCount">Number of statements found and encoded.</param>
/// <returns>Size of the data writen by the operation., or <c>0</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="ArgumentOutOfRangeException">The provided index is invalid.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
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;
}
/// <summary>
/// Encodes the given statement(s) into the given buffer.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="buffer">Buffer into which the data shall be written.</param>
/// <param name="index">Index into the buffer after which the data shall be written.</param>
/// <returns>Size of the data writen by the operation., or <c>0</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="ArgumentOutOfRangeException">The provided index is invalid.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public int Assemble(string toEncode, ulong address, byte[] buffer, int index)
{
return Assemble(toEncode, address, buffer, index, out _);
}
/// <summary>
/// Encodes the given statement(s) into the given stream.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="stream">Buffer into which the data shall be written.</param>
/// <param name="size">Size of the buffer produced by the operation.</param>
/// <param name="statementCount">Number of statements found and encoded.</param>
/// <returns><c>true</c> on success, or <c>false</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
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;
}
/// <summary>
/// Encodes the given statement(s) into the given stream.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="stream">Buffer into which the data shall be written.</param>
/// <param name="size">Size of the buffer produced by the operation.</param>
/// <returns><c>true</c> on success, or <c>false</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public bool Assemble(string toEncode, ulong address, Stream stream, out int size)
{
return Assemble(toEncode, address, stream, out size, out _);
}
/// <summary>
/// Encodes the given statement(s) into the given stream.
/// </summary>
/// <param name="toEncode">String that contains the statements to encode.</param>
/// <param name="address">Address of the first instruction to encode.</param>
/// <param name="stream">Buffer into which the data shall be written.</param>
/// <returns><c>true</c> on success, or <c>false</c> if it failed and <see cref="ThrowOnError" /> is <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">A null argument was given.</exception>
/// <exception cref="KeystoneException">An error encountered when encoding the instructions.</exception>
public bool Assemble(string toEncode, ulong address, Stream stream)
{
return Assemble(toEncode, address, stream, out _, out _);
}
/// <summary>
/// Gets the last error for this instance.
/// </summary>
/// <returns>The last error code.</returns>
/// <remarks>
/// It might not retain its old error once accessed.
/// </remarks>
public KeystoneError GetLastKeystoneError()
{
return NativeInterop.GetLastKeystoneError(engine);
}
/// <summary>
/// Returns the string associated with a given error code.
/// </summary>
public static string ErrorToString(KeystoneError code)
{
IntPtr error = NativeInterop.ErrorToString(code);
if (error != IntPtr.Zero)
return Marshal.PtrToStringAnsi(error);
return string.Empty;
}
/// <summary>
/// Checks if the given architecture is supported.
/// </summary>
public static bool IsArchitectureSupported(Architecture architecture)
{
return NativeInterop.IsArchitectureSupported(architecture);
}
/// <summary>
/// Gets the version of the engine.
/// </summary>
/// <param name="major">Major version number.</param>
/// <param name="minor">Minor version number.</param>
/// <returns>Unique identifier for this version.</returns>
public static uint GetKeystoneVersion(ref uint major, ref uint minor)
{
return NativeInterop.Version(ref major, ref minor);
}
/// <summary>
/// Releases the engine.
/// </summary>
public void Dispose()
{
IntPtr currentEngine = Interlocked.Exchange(ref engine, IntPtr.Zero);
if (currentEngine != IntPtr.Zero)
NativeInterop.Close(currentEngine);
}
}
}

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<RootNamespace>Keystone</RootNamespace>
<Version>1.1.0</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version).0</FileVersion>
<Description>.NET bindings to the Keystone Engine.</Description>
<Authors>Grégoire Geis</Authors>
<PackageId>Keystone.Net</PackageId>
<PackageVersion>$(Version)</PackageVersion>
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
<PackageReleaseNotes>- First release.</PackageReleaseNotes>
<PackageTags>assembler x86 arm keystone</PackageTags>
<PackageProjectUrl>https://github.com/keystone-engine/keystone</PackageProjectUrl>
<PackageLicenseUrl>$(PackageProjectUrl)/blob/master/COPYING</PackageLicenseUrl>
<PackageIconUrl>http://www.keystone-engine.org/images/keystone.png</PackageIconUrl>
<RepositoryUrl>$(PackageProjectUrl).git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
</Project>

@ -0,0 +1,30 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Keystone
{
/// <summary>
/// Represents an error encountered while encoding one or more instructions.
/// </summary>
public sealed class KeystoneException : Exception
{
/// <summary>
/// Gets the value that represents the encountered error.
/// </summary>
public KeystoneError Error { get; }
internal KeystoneException(string message, KeystoneError error) : base(message + '.')
{
Debug.Assert(error != KeystoneError.KS_ERR_OK);
Error = error;
}
/// <inheritdoc />
public override string ToString()
{
return $"{Message}: {Engine.ErrorToString(Error)}.";
}
}
}

@ -0,0 +1,66 @@
using System;
using System.Runtime.InteropServices;
namespace Keystone
{
/// <summary>
/// Imported symbols for interop with keystone.dll.
/// </summary>
internal class NativeInterop
{
// This shouldn't be needed, even on Windows
// /// <summary>
// /// Taken from: http://stackoverflow.com/questions/10852634/using-a-32bit-or-64bit-dll-in-c-sharp-dllimport
// /// </summary>
// 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);
}
}

@ -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<byte>`. 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<byte>();
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);
}
```

@ -0,0 +1,32 @@
# Keystone.Net
.NET Standard bindings for Keystone.
## Usage
```csharp
using Keystone;
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);
}
```
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).

@ -0,0 +1,21 @@
# Go binding for Keystone engine. Remco Verhoef <remco@honeytrap.io>
ifndef BUILDDIR
OBJDIR = ./build
else
OBJDIR = $(abspath $(BUILDDIR))/obj/bindings/go
endif
.PHONY: gen_const install clean check
gen_const:
cd .. && python const_generator.py go
go fmt
install:
cd keystone && go build
clean:
check:
cd keystone && go test

@ -0,0 +1,43 @@
# keystone
Go bindings for the [keystone](http://www.keystone-engine.org/) engine.
## Sample
```go
package main
import (
"fmt"
"os"
"github.com/keystone-engine/keystone/bindings/go/keystone"
)
func main() {
assembly := os.Args[1]
ks, err := keystone.New(keystone.ARCH_X86, keystone.MODE_32)
if err != nil {
panic(err)
}
defer ks.Close()
if err := ks.Option(keystone.OPT_SYNTAX, keystone.OPT_SYNTAX_INTEL); err != nil {
panic(fmt.Errorf("Could not set syntax option to intel"))
}
if insn, _, ok := ks.Assemble(assembly, 0); !ok {
panic(fmt.Errorf("Could not assemble instruction"))
} else {
fmt.Printf("%s: [%x]", assembly, insn)
}
}
```
## Testing
```
go test
```
## Contributors
- Remco Verhoef (@remco_verhoef)

@ -0,0 +1,9 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm64_const.go]
const (
ERR_ASM_ARM64_INVALIDOPERAND Error = 512
ERR_ASM_ARM64_MISSINGFEATURE Error = 513
ERR_ASM_ARM64_MNEMONICFAIL Error = 514
)

@ -0,0 +1,9 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm_const.go]
const (
ERR_ASM_ARM_INVALIDOPERAND Error = 512
ERR_ASM_ARM_MISSINGFEATURE Error = 513
ERR_ASM_ARM_MNEMONICFAIL Error = 514
)

@ -0,0 +1,9 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [evm_const.go]
const (
ERR_ASM_EVM_INVALIDOPERAND Error = 512
ERR_ASM_EVM_MISSINGFEATURE Error = 513
ERR_ASM_EVM_MNEMONICFAIL Error = 514
)

@ -0,0 +1,9 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [hexagon_const.go]
const (
ERR_ASM_HEXAGON_INVALIDOPERAND Error = 512
ERR_ASM_HEXAGON_MISSINGFEATURE Error = 513
ERR_ASM_HEXAGON_MNEMONICFAIL Error = 514
)

@ -0,0 +1,65 @@
/* Keystone Assembler Engine (www.keystone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */
/* Golang bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */
// +build darwin,linux,cgo
package keystone
// #cgo LDFLAGS: -lkeystone -lstdc++ -lm
// #include <keystone/keystone.h>
import "C"
import "unsafe"
func ks_version() (uint, uint) {
major := C.uint(0)
minor := C.uint(0)
C.ks_version(&major, &minor)
return uint(major), uint(minor)
}
func ks_arch_supported(a Architecture) bool {
return bool(C.ks_arch_supported((C.ks_arch)(a)))
}
func ks_open(a Architecture, m Mode, engine **C.ks_engine) error {
if err := C.ks_open((C.ks_arch)(a), (C.int)(m), (**C.ks_engine)(unsafe.Pointer(engine))); err != 0 {
return Error(err)
}
return nil
}
func ks_option(engine *C.ks_engine, type_ OptionType, value OptionValue) error {
if err := C.ks_option(engine, C.ks_opt_type(type_), C.size_t(value)); err != 0 {
return Error(err)
}
return nil
}
func ks_errno(engine *C.ks_engine) error {
if err := C.ks_errno(engine); err != 0 {
return Error(err)
}
return nil
}
func ks_asm(engine *C.ks_engine, str string, address uint64, encoding *[]byte, stat_count *uint64) bool {
cstr := C.CString(str)
defer C.free(unsafe.Pointer(cstr))
var p_insn unsafe.Pointer
defer C.free(unsafe.Pointer(p_insn))
var count, l_insn C.size_t
err := C.ks_asm(engine, cstr, C.uint64_t(address), (**C.uchar)(unsafe.Pointer(&p_insn)), &l_insn, &count)
*encoding = C.GoBytes(p_insn, C.int(l_insn))
*stat_count = uint64(count)
return err == 0
}
func ks_close(engine *C.ks_engine) error {
if err := C.ks_close(engine); err != 0 {
return Error(err)
}
return nil
}

@ -0,0 +1,67 @@
/* Keystone Assembler Engine (www.keystone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */
/* Golang bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */
package keystone
// #include <keystone/keystone.h>
import "C"
type Architecture uint
type Mode uint
type OptionType uint
type OptionValue uint
type Error uint32
func (e Error) Error() string {
s := C.ks_strerror((C.ks_err)(e))
return C.GoString(s)
}
func (a Architecture) Supported() bool {
return ks_arch_supported(a)
}
func Version() (uint, uint) {
return ks_version()
}
type Keystone struct {
engine *C.ks_engine
}
func New(a Architecture, m Mode) (*Keystone, error) {
ks := &Keystone{}
if err := ks_open(a, m, &ks.engine); err != nil {
return nil, err
} else {
return ks, nil
}
}
func (ks *Keystone) LastError() error {
return ks_errno(ks.engine)
}
func (ks *Keystone) Option(type_ OptionType, value OptionValue) error {
if err := ks_option(ks.engine, type_, value); err != nil {
return err
}
return nil
}
func (ks *Keystone) Assemble(str string, address uint64) ([]byte, uint64, bool) {
encoding := []byte{}
stat_count := uint64(0)
ok := ks_asm(ks.engine, str, address, &encoding, &stat_count)
return encoding, stat_count, ok
}
func (ks *Keystone) Close() error {
return ks_close(ks.engine)
}

@ -0,0 +1,108 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.go]
const (
API_MAJOR = 0
API_MINOR = 9
)
const (
MODE_LITTLE_ENDIAN Mode = 0
MODE_BIG_ENDIAN Mode = 1073741824
MODE_ARM Mode = 1
MODE_THUMB Mode = 16
MODE_V8 Mode = 64
MODE_MICRO Mode = 16
MODE_MIPS3 Mode = 32
MODE_MIPS32R6 Mode = 64
MODE_MIPS32 Mode = 4
MODE_MIPS64 Mode = 8
MODE_16 Mode = 2
MODE_32 Mode = 4
MODE_64 Mode = 8
MODE_PPC32 Mode = 4
MODE_PPC64 Mode = 8
MODE_QPX Mode = 16
MODE_SPARC32 Mode = 4
MODE_SPARC64 Mode = 8
MODE_V9 Mode = 16
)
const (
ARCH_ARM Architecture = 1
ARCH_ARM64 Architecture = 2
ARCH_MIPS Architecture = 3
ARCH_X86 Architecture = 4
ARCH_PPC Architecture = 5
ARCH_SPARC Architecture = 6
ARCH_SYSTEMZ Architecture = 7
ARCH_HEXAGON Architecture = 8
ARCH_EVM Architecture = 9
ARCH_MAX Architecture = 10
)
const (
OPT_SYNTAX OptionType = 1
)
const (
OPT_SYM_RESOLVER OptionValue = 2
OPT_SYNTAX_INTEL OptionValue = 1
OPT_SYNTAX_ATT OptionValue = 2
OPT_SYNTAX_NASM OptionValue = 4
OPT_SYNTAX_MASM OptionValue = 8
OPT_SYNTAX_GAS OptionValue = 16
OPT_SYNTAX_RADIX16 OptionValue = 32
)
const (
ERR_ASM Error = 128
ERR_ASM_ARCH Error = 512
ERR_OK Error = 0
ERR_NOMEM Error = 1
ERR_ARCH Error = 2
ERR_HANDLE Error = 3
ERR_MODE Error = 4
ERR_VERSION Error = 5
ERR_OPT_INVALID Error = 6
ERR_ASM_EXPR_TOKEN Error = 128
ERR_ASM_DIRECTIVE_VALUE_RANGE Error = 129
ERR_ASM_DIRECTIVE_ID Error = 130
ERR_ASM_DIRECTIVE_TOKEN Error = 131
ERR_ASM_DIRECTIVE_STR Error = 132
ERR_ASM_DIRECTIVE_COMMA Error = 133
ERR_ASM_DIRECTIVE_RELOC_NAME Error = 134
ERR_ASM_DIRECTIVE_RELOC_TOKEN Error = 135
ERR_ASM_DIRECTIVE_FPOINT Error = 136
ERR_ASM_DIRECTIVE_UNKNOWN Error = 137
ERR_ASM_DIRECTIVE_EQU Error = 138
ERR_ASM_DIRECTIVE_INVALID Error = 139
ERR_ASM_VARIANT_INVALID Error = 140
ERR_ASM_EXPR_BRACKET Error = 141
ERR_ASM_SYMBOL_MODIFIER Error = 142
ERR_ASM_SYMBOL_REDEFINED Error = 143
ERR_ASM_SYMBOL_MISSING Error = 144
ERR_ASM_RPAREN Error = 145
ERR_ASM_STAT_TOKEN Error = 146
ERR_ASM_UNSUPPORTED Error = 147
ERR_ASM_MACRO_TOKEN Error = 148
ERR_ASM_MACRO_PAREN Error = 149
ERR_ASM_MACRO_EQU Error = 150
ERR_ASM_MACRO_ARGS Error = 151
ERR_ASM_MACRO_LEVELS_EXCEED Error = 152
ERR_ASM_MACRO_STR Error = 153
ERR_ASM_MACRO_INVALID Error = 154
ERR_ASM_ESC_BACKSLASH Error = 155
ERR_ASM_ESC_OCTAL Error = 156
ERR_ASM_ESC_SEQUENCE Error = 157
ERR_ASM_ESC_STR Error = 158
ERR_ASM_TOKEN_INVALID Error = 159
ERR_ASM_INSN_UNSUPPORTED Error = 160
ERR_ASM_FIXUP_INVALID Error = 161
ERR_ASM_LABEL_INVALID Error = 162
ERR_ASM_FRAGMENT_INVALID Error = 163
ERR_ASM_INVALIDOPERAND Error = 512
ERR_ASM_MISSINGFEATURE Error = 513
ERR_ASM_MNEMONICFAIL Error = 514
)

@ -0,0 +1,66 @@
/* Keystone Assembler Engine (www.keystone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */
/* Golang bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */
package keystone
import (
"fmt"
"reflect"
"testing"
)
func TestVersion(t *testing.T) {
major, minor := Version()
if major == API_MAJOR && minor == API_MINOR {
} else {
t.Error(fmt.Errorf("Unexpected version: got %d.%d expected %d.%d", major, minor, 1, 0))
}
}
func TestArchitectureSupported(t *testing.T) {
if !ARCH_ARM.Supported() {
t.Error(fmt.Errorf("ARM not supported"))
}
}
type Test struct {
Architecture Architecture
Mode Mode
Address uint64
Assembly string
ExpectedResult []byte
}
type Syntax struct {
Syntax OptionValue
Tests []Test
}
var tests = []Syntax{
Syntax{
OPT_SYNTAX_INTEL, []Test{
Test{ARCH_X86, MODE_32 | MODE_LITTLE_ENDIAN, 0, "mov ah, al", []byte{0x88, 0xc4}},
},
},
}
func TestRun(t *testing.T) {
for _, st := range tests {
for _, tr := range st.Tests {
if ks, err := New(tr.Architecture, tr.Mode); err != nil {
t.Error(err)
} else {
defer ks.Close()
if err := ks.Option(OPT_SYNTAX, st.Syntax); err != nil {
t.Error(fmt.Errorf("Could not set syntax option to intel"))
} else if insn, _, ok := ks.Assemble(tr.Assembly, tr.Address); !ok {
t.Error(fmt.Errorf("Could not assemble instruction"))
} else if !reflect.DeepEqual(insn, tr.ExpectedResult) {
t.Error(fmt.Errorf("Not expected result: expected %#v got %#v", tr.ExpectedResult, insn))
}
}
}
}
}

@ -0,0 +1,9 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [mips_const.go]
const (
ERR_ASM_MIPS_INVALIDOPERAND Error = 512
ERR_ASM_MIPS_MISSINGFEATURE Error = 513
ERR_ASM_MIPS_MNEMONICFAIL Error = 514
)

@ -0,0 +1,9 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [ppc_const.go]
const (
ERR_ASM_PPC_INVALIDOPERAND Error = 512
ERR_ASM_PPC_MISSINGFEATURE Error = 513
ERR_ASM_PPC_MNEMONICFAIL Error = 514
)

@ -0,0 +1,32 @@
/* Keystone Assembler Engine (www.keystone-engine.org) */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */
/* Golang bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */
package main
import (
"fmt"
"os"
"github.com/keystone-engine/keystone/bindings/go/keystone"
)
func main() {
assembly := os.Args[1]
ks, err := keystone.New(keystone.ARCH_X86, keystone.MODE_32)
if err != nil {
panic(err)
}
defer ks.Close()
if err := ks.Option(keystone.OPT_SYNTAX, keystone.OPT_SYNTAX_INTEL); err != nil {
panic(fmt.Errorf("Could not set syntax option to intel"))
}
if insn, _, ok := ks.Assemble(assembly, 0); !ok {
panic(fmt.Errorf("Could not assemble instruction"))
} else {
fmt.Printf("%s: [%x]", assembly, insn)
}
}

@ -0,0 +1,9 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [sparc_const.go]
const (
ERR_ASM_SPARC_INVALIDOPERAND Error = 512
ERR_ASM_SPARC_MISSINGFEATURE Error = 513
ERR_ASM_SPARC_MNEMONICFAIL Error = 514
)

@ -0,0 +1,9 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [systemz_const.go]
const (
ERR_ASM_SYSTEMZ_INVALIDOPERAND Error = 512
ERR_ASM_SYSTEMZ_MISSINGFEATURE Error = 513
ERR_ASM_SYSTEMZ_MNEMONICFAIL Error = 514
)

@ -0,0 +1,9 @@
package keystone
// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [x86_const.go]
const (
ERR_ASM_X86_INVALIDOPERAND Error = 512
ERR_ASM_X86_MISSINGFEATURE Error = 513
ERR_ASM_X86_MNEMONICFAIL Error = 514
)

@ -0,0 +1,17 @@
dist
cabal-dev
*.o
*.hi
*.chi
*.chs.h
*.dyn_o
*.dyn_hi
.virtualenv
.hpc
.hsenv
.cabal-sandbox/
cabal.sandbox.config
*.prof
*.aux
*.hp
Sample

@ -0,0 +1,27 @@
This documentation explains how to install the Keystone Haskell bindings from
source.
1. Install the core Keystone Assembler as a dependency:
Follow docs/COMPILE.md in the root directory to compile & install the core.
2. Change into the Haskell bindings directory, build and install:
```
$ cd bindings/haskell
$ cabal install
```
If you are installing into a sandbox, run `cabal sandbox init` before
installing Keystone's dependencies.
If the build fails, install c2hs manually `cabal install c2hs` (note that this
will probably also require you to run `cabal install alex` and `cabal install
happy` as well). If you are NOT using a sandbox, ensure that `$HOME/.cabal/bin`
is on your PATH.
To build a sample (after having built and installed the Haskell bindings):
```
$ cd bindings/haskell
$ ghc --make samples/Sample.hs
```

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

@ -0,0 +1,38 @@
-- Initial keystone.cabal generated by cabal init. For further
-- documentation, see http://haskell.org/cabal/users-guide/
name: keystone
version: 0.1.0.0
synopsis: Keystone lightweight multi-platform, multi-architecture assembler framework
description: Haskell bindings for the Keystone assembler
homepage: https://github.com/keystone-engine/keystone
license: GPL-2
author: Adrian Herrera
category: System
build-type: Simple
cabal-version: >= 1.10
library
exposed-modules: Keystone.Internal.Core
Keystone.Internal.Keystone
Keystone.CPU.Arm64
Keystone.CPU.Arm
Keystone.CPU.Hexagon
Keystone.CPU.Mips
Keystone.CPU.Ppc
Keystone.CPU.Sparc
Keystone.CPU.SystemZ
Keystone.CPU.X86
Keystone
other-modules: Keystone.Internal.Util
build-depends: base >= 4 && < 5
, bytestring >= 0.9.1
, transformers < 0.6
, either >= 4.4
hs-source-dirs: src
c-sources: src/cbits/keystone_wrapper.c
include-dirs: src/include
build-tools: c2hs
pkgconfig-depends: keystone
default-language: Haskell2010
ghc-options: -Wall

@ -0,0 +1,74 @@
-- Sample code for Keystone Assembler Engine.
import Keystone
import qualified Data.ByteString as BS
import Data.List (intercalate)
import qualified Numeric as N (showHex)
-- Pretty-print byte string as hex.
showHexBS :: BS.ByteString
-> String
showHexBS =
concatMap (flip N.showHex " ") . BS.unpack
testKs :: Architecture
-> [Mode]
-> [String]
-> Maybe OptionValue
-> IO ()
testKs arch mode assembly maybeSyntax = do
result <- runAssembler $ do
ks <- open arch mode
case maybeSyntax of
Just syntax -> option ks OptSyntax syntax
Nothing -> return ()
(encode, count) <- assemble ks assembly Nothing
return (encode, count)
case result of
Right (encode, count) -> let size = BS.length encode in do
putStr $ intercalate ";" assembly ++ " = "
putStrLn $ showHexBS encode
putStrLn $ "Assembled: " ++ show size ++ " bytes, " ++
show count ++ " statements\n"
Left err -> putStrLn $ "Failed with error: " ++ show err ++ " (" ++
strerror err ++ ")"
main :: IO ()
main = do
-- X86
testKs ArchX86 [Mode16] ["add eax, ecx"] Nothing
testKs ArchX86 [Mode32] ["add eax, ecx"] Nothing
testKs ArchX86 [Mode64] ["add rax, rcx"] Nothing
testKs ArchX86 [Mode32] ["add %ecx, %eax"] (Just SyntaxAtt)
testKs ArchX86 [Mode64] ["add %rcx, %rax"] (Just SyntaxAtt)
-- ARM
testKs ArchArm [ModeArm] ["sub r1, r2, r5"] Nothing
testKs ArchArm [ModeArm, ModeBigEndian] ["sub r1, r2, r5"] Nothing
testKs ArchArm [ModeThumb] ["movs r4, #0xf0"] Nothing
testKs ArchArm [ModeThumb, ModeBigEndian] ["movs r4, #0xf0"] Nothing
-- ARM64
testKs ArchArm64 [ModeLittleEndian] ["ldr w1, [sp, #0x8]"] Nothing
-- Hexagon
testKs ArchHexagon [ModeBigEndian] ["v23.w=vavg(v11.w,v2.w):rnd"] Nothing
-- MIPS
testKs ArchMips [ModeMips32] ["and $9, $6, $7"] Nothing
testKs ArchMips [ModeMips32, ModeBigEndian] ["and $9, $6, $7"] Nothing
testKs ArchMips [ModeMips64] ["and $9, $6, $7"] Nothing
testKs ArchMips [ModeMips64, ModeBigEndian] ["and $9, $6, $7"] Nothing
-- PowerPC
testKs ArchPpc [ModePpc32, ModeBigEndian] ["add 1, 2, 3"] Nothing
testKs ArchPpc [ModePpc64] ["add 1, 2, 3"] Nothing
testKs ArchPpc [ModePpc64, ModeBigEndian] ["add 1, 2, 3"] Nothing
-- SPARC
testKs ArchSparc [ModeSparc32, ModeLittleEndian] ["add %g1, %g2, %g3"] Nothing
testKs ArchSparc [ModeSparc32, ModeBigEndian] ["add %g1, %g2, %g3"] Nothing
-- SystemZ
testKs ArchSystemz [ModeBigEndian] ["a %r0, 4095(%r15,%r1)"] Nothing

@ -0,0 +1,128 @@
{-|
Module : Keystone
Description : The Keystone assembler engine.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Keystone is a lightweight multi-platform, multi-architecture assembler
framework.
Further information is available at <http://www.keystone-engine.org>.
-}
module Keystone
( -- * Assembler control
Assembler
, Engine
, Architecture(..)
, Mode(..)
, OptionType(..)
, OptionValue(..)
, runAssembler
, open
, option
, assemble
-- * Error handling
, Error(..)
, errno
, strerror
-- * Misc.
, version
) where
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Either (left, right, runEitherT)
import Data.ByteString (ByteString, packCStringLen)
import Data.List (intercalate)
import Foreign
import Keystone.Internal.Core
import Keystone.Internal.Keystone
-------------------------------------------------------------------------------
-- Assembler control
-------------------------------------------------------------------------------
-- | Run the Keystone assembler and return a result on success, or an 'Error'
-- on failure.
runAssembler :: Assembler a -- ^ The assembler code to execute
-> IO (Either Error a) -- ^ A result on success, or an 'Error' on
-- failure
runAssembler =
runEitherT
-- | Create a new instance of the Keystone assembler.
open :: Architecture -- ^ CPU architecture
-> [Mode] -- ^ CPU hardware mode
-> Assembler Engine -- ^ A 'Keystone' engine on success, or an 'Error'
-- on failure
open arch mode = do
(err, ksPtr) <- lift $ ksOpen arch mode
if err == ErrOk then
-- Return a pointer to the Keystone engine if ksOpen completed
-- successfully
lift $ mkEngine ksPtr
else
-- Otherwise return an error
left err
option :: Engine -- ^ 'Keystone' engine handle
-> OptionType -- ^ Type of option to set
-> OptionValue -- ^ Option value corresponding with the type
-> Assembler () -- ^ An 'Error' on failure
option ks optType optValue = do
err <- lift $ ksOption ks optType optValue
if err == ErrOk then
right ()
else
left err
-- | Assemble a list of statements.
assemble :: Engine -- ^ 'Keystone' engine handle
-> [String] -- ^ List of statements to assemble.
-> Maybe Word64 -- ^ Optional address of the first
-- assembly instruction
-> Assembler (ByteString, Int) -- ^ Returns the encoded input assembly
-- string and the number of statements
-- successfully processed. Returns an
-- 'Error' on failure
assemble ks stmts addr = do
let string = intercalate ";" stmts
(res, encPtr, encSize, statCount) <- lift $ ksAsm ks string (maybeZ addr)
if res == 0 then do
-- If ksAsm completed successfully, pack the encoded bytes into a
-- ByteString. Once the encoded bytes have been packed the original
-- encoded bytes can be freed. The ByteString is returned with the
-- statement count
bs <- lift $ packCStringLen (castPtr encPtr, encSize)
lift $ ksFree encPtr
right (bs, statCount)
else do
-- On failure, call errno for error code
err <- errno ks
left err
where maybeZ = maybe 0 id
-------------------------------------------------------------------------------
-- Misc.
-------------------------------------------------------------------------------
-- | Combined API version & major and minor version numbers. Returns a
-- hexadecimal number as (major << 8 | minor), which encodes both major and
-- minor versions.
version :: Int
version =
ksVersion nullPtr nullPtr
-- | Report the 'Error' number when some API function failed.
errno :: Engine -- ^ 'Keystone' engine handle
-> Assembler Error -- ^ The last 'Error' code
errno =
lift . ksErrno
-- | Return a string describing the given 'Error'.
strerror :: Error -- ^ The 'Error' code
-> String -- ^ Description of the error code
strerror =
ksStrerror

@ -0,0 +1,25 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Keystone.CPU.Arm
Description : Definitions for the ARM architecture.
Copyright : (C) Adrian Herrera, 2016
License : GPL-2
Definitions for the ARM architecture.
-}
module Keystone.CPU.Arm
(
Error(..)
) where
{# context lib = "keystone" #}
#include <keystone/arm.h>
-- | ARM errors.
{# enum ks_err_asm_arm as Error
{ underscoreToCase }
with prefix = "KS_ERR_ASM_ARM_"
deriving (Show, Eq, Bounded)
#}

@ -0,0 +1,25 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Keystone.CPU.Arm64
Description : Definitions for the ARM64 architecture.
Copyright : (C) Adrian Herrera, 2016
License : GPL-2
Definitions for the ARM64 architecture.
-}
module Keystone.CPU.Arm64
(
Error(..)
) where
{# context lib = "keystone" #}
#include <keystone/arm64.h>
-- | ARM64 errors.
{# enum ks_err_asm_arm64 as Error
{ underscoreToCase }
with prefix = "KS_ERR_ASM_ARM64_"
deriving (Show, Eq, Bounded)
#}

@ -0,0 +1,25 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Keystone.CPU.Hexagon
Description : Definitions for the Hexagon architecture.
Copyright : (C) Adrian Herrera, 2016
License : GPL-2
Definitions for the Hexagon architecture.
-}
module Keystone.CPU.Hexagon
(
Error(..)
) where
{# context lib = "keystone" #}
#include <keystone/hexagon.h>
-- | Hexagon errors.
{# enum ks_err_asm_hexagon as Error
{ underscoreToCase }
with prefix = "KS_ERR_ASM_HEXAGON_"
deriving (Show, Eq, Bounded)
#}

@ -0,0 +1,25 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Keystone.CPU.Mips
Description : Definitions for the MIPS architecture.
Copyright : (C) Adrian Herrera, 2016
License : GPL-2
Definitions for the MIPS architecture.
-}
module Keystone.CPU.Mips
(
Error(..)
) where
{# context lib = "keystone" #}
#include <keystone/mips.h>
-- | MIPS errors.
{# enum ks_err_asm_mips as Error
{ underscoreToCase }
with prefix = "KS_ERR_ASM_MIPS_"
deriving (Show, Eq, Bounded)
#}

@ -0,0 +1,25 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Keystone.CPU.Ppc
Description : Definitions for the PPC architecture.
Copyright : (C) Adrian Herrera, 2016
License : GPL-2
Definitions for the PPC architecture.
-}
module Keystone.CPU.Ppc
(
Error(..)
) where
{# context lib = "keystone" #}
#include <keystone/ppc.h>
-- | PPC errors.
{# enum ks_err_asm_ppc as Error
{ underscoreToCase }
with prefix = "KS_ERR_ASM_PPC_"
deriving (Show, Eq, Bounded)
#}

@ -0,0 +1,24 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Keystone.CPU.Sparc
Description : Definitions for the SPARC architecture.
Copyright : (C) Adrian Herrera, 2016
License : GPL-2
Definitions for the SPARC architecture.
-}
module Keystone.CPU.Sparc
(
Error(..)
) where
{# context lib = "keystone" #}
#include <keystone/sparc.h>
-- | SPARC errors.
{# enum ks_err_asm_sparc as Error
{ underscoreToCase }
with prefix = "KS_ERR_ASM_SPARC_"
deriving (Show, Eq, Bounded) #}

@ -0,0 +1,25 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Keystone.CPU.SystemZ
Description : Definitions for the SystemZ architecture.
Copyright : (C) Adrian Herrera, 2016
License : GPL-2
Definitions for the SystemZ architecture.
-}
module Keystone.CPU.SystemZ
(
Error(..)
) where
{# context lib = "keystone" #}
#include <keystone/systemz.h>
-- | SystemZ errors.
{# enum ks_err_asm_systemz as Error
{ underscoreToCase }
with prefix = "KS_ERR_ASM_SYSTEMZ_"
deriving (Show, Eq, Bounded)
#}

@ -0,0 +1,25 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Keystone.CPU.X86
Description : Definitions for the X86 architecture.
Copyright : (C) Adrian Herrera, 2016
License : GPL-2
Definitions for the X86 architecture.
-}
module Keystone.CPU.X86
(
Error(..)
) where
{# context lib = "keystone" #}
#include <keystone/x86.h>
-- | X86 errors.
{# enum ks_err_asm_x86 as Error
{ underscoreToCase }
with prefix = "KS_ERR_ASM_X86_"
deriving (Show, Eq, Bounded)
#}

@ -0,0 +1,51 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module
Description : Core Keystone components.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Defines core Keystone components.
This module should not be directly imported; it is only exposed because of the
way cabal handles ordering of chs files.
-}
module Keystone.Internal.Core where
import Control.Monad
import Control.Monad.Trans.Either (EitherT)
import Foreign
{# context lib = "keystone" #}
#include <keystone/keystone.h>
#include "keystone_wrapper.h"
-- | The Keystone engine.
{# pointer *ks_engine as Engine
foreign finalizer ks_close_wrapper as close
newtype
#}
-- | A pointer to the Keystone engine.
{# pointer *ks_engine as EnginePtr -> Engine #}
-- | Make a new Keystone engine out of an engine pointer. The returned Keystone
-- engine will automatically call 'ks_close_wrapper' when it goes out of scope.
mkEngine :: EnginePtr
-> IO Engine
mkEngine ptr =
liftM Engine (newForeignPtr close ptr)
-- | Errors encountered by the Keystone API. These values are returned by
-- 'errno'.
{# enum ks_err as Error
{ underscoreToCase }
with prefix = "KS_"
deriving (Show, Eq, Bounded)
#}
-- | The assembler runs in the IO monad and allows for the handling of errors
-- "under the hood".
type Assembler a = EitherT Error IO a

@ -0,0 +1,135 @@
{-# LANGUAGE ForeignFunctionInterface #-}
{-|
Module : Keystone.Internal.Keystone
Description : The Keystone assembler engine.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
Low-level bindings for the Keystone assembler engine.
This module should not be directly imported; it is only exposed because of the
way cabal handles ordering of chs files.
-}
module Keystone.Internal.Keystone
( -- * Types
Architecture(..)
, Mode(..)
, OptionType(..)
, OptionValue(..)
-- * Function bindings
, ksOpen
, ksOption
, ksFree
, ksAsm
, ksVersion
, ksErrno
, ksStrerror
) where
import Foreign
import Foreign.C
import Keystone.Internal.Util
{# import Keystone.Internal.Core #}
{# context lib = "keystone" #}
#include <keystone/keystone.h>
-------------------------------------------------------------------------------
-- Types
-------------------------------------------------------------------------------
-- | CPU architecture.
{# enum ks_arch as Architecture
{ underscoreToCase }
with prefix = "KS_"
deriving (Show, Eq, Bounded)
#}
-- | CPU hardware mode.
{# enum ks_mode as Mode
{ underscoreToCase }
with prefix = "KS_"
deriving (Show, Eq, Bounded)
#}
-- | Runtime option types.
{# enum ks_opt_type as OptionType
{ underscoreToCase }
with prefix = "KS_"
deriving (Show, Eq, Bounded)
#}
-- | Runtime option values.
{# enum ks_opt_value as OptionValue
{ underscoreToCase }
with prefix = "KS_OPT_"
deriving (Show, Eq, Bounded)
#}
-------------------------------------------------------------------------------
-- Assembler control
-------------------------------------------------------------------------------
{# fun ks_open as ^
{ `Architecture'
, combineEnums `[Mode]'
, alloca- `EnginePtr' peek*
} -> `Error'
#}
{# fun ks_option as ^
{ `Engine'
, `OptionType'
, `OptionValue'
} -> `Error'
#}
{# fun ks_asm as ^
{ `Engine'
, `String'
, `Word64'
, alloca- `Ptr CUChar' peek*
, alloca- `Int' peekToInt*
, alloca- `Int' peekToInt*
} -> `Int'
#}
{# fun ks_free as ^
{ castPtr `Ptr CUChar'
} -> `()'
#}
-------------------------------------------------------------------------------
-- Misc.
-------------------------------------------------------------------------------
{# fun pure unsafe ks_version as ^
{ id `Ptr CUInt'
, id `Ptr CUInt'
} -> `Int'
#}
{# fun unsafe ks_errno as ^
{ `Engine'
} -> `Error'
#}
{# fun pure unsafe ks_strerror as ^
{ `Error'
} -> `String'
#}
-------------------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------------------
peekToInt :: (Storable a, Integral a, Num b)
=> Ptr a
-> IO b
peekToInt ptr =
peek ptr >>= (return . fromIntegral)

@ -0,0 +1,23 @@
{-|
Module : Keystone.Internal.Util
Description : Utility (aka helper) functions for the Keystone assembler.
Copyright : (c) Adrian Herrera, 2016
License : GPL-2
-}
module Keystone.Internal.Util where
import Data.Bits
-- | Combine a list of Enums by performing a bitwise-OR.
combineEnums :: (Enum a, Num b, Bits b)
=> [a]
-> b
combineEnums =
foldr ((.|.) <$> enumToNum) 0
-- | Convert an 'Eum' to a 'Num'.
enumToNum :: (Enum a, Num b)
=> a
-> b
enumToNum =
fromIntegral . fromEnum

@ -0,0 +1,5 @@
#include "keystone_wrapper.h"
void ks_close_wrapper(ks_engine *ks) {
ks_close(ks);
}

@ -0,0 +1,11 @@
#ifndef KEYSTONE_WRAPPER_H
#define KEYSTONE_WRAPPER_H
#include <keystone/keystone.h>
/*
* Wrap Keystone's ks_close function and ignore the returned error code.
*/
void ks_close_wrapper(ks_engine *ks);
#endif

@ -0,0 +1,3 @@
.idea
*.iml
target/

@ -0,0 +1,5 @@
# Changelog of the Java bindings for Keystone
**Version 0.9.1-0: July 16th, 2018**:
- Initial public release.

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2018 Jämes Ménétrey (aka ZenLulz) <james@menetrey.me>
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.

@ -0,0 +1,100 @@
# Java bindings for Keystone
Java bindings for the [Keystone](http://www.keystone-engine.org/) engine. Require JDK 10+.
## Sample
```java
import keystone.Keystone;
import keystone.KeystoneArchitecture;
import keystone.KeystoneMode;
import keystone.exceptions.AssembleFailedKeystoneException;
public class App {
public static void main(String[] args) {
try (Keystone keystone = new Keystone(KeystoneArchitecture.X86, KeystoneMode.Mode32)) {
try {
var result = keystone.assemble("INC ecx; DEC edx");
System.out.println("Number of statements encoded: " + result.getNumberOfStatements());
System.out.println("Base address: " + String.format("0x%08X", result.getAddress()));
System.out.print("Encoded bytes: ");
for (byte b: result.getMachineCode()) {
System.out.print(String.format("%X ", b));
}
} catch (AssembleFailedKeystoneException e) {
System.out.println(e.getMessage());
}
}
}
}
```
Output:
> Number of statements encoded: 2
Base address: 0x00000000
Encoded bytes: 41 4A
Other samples are provided by the unit tests included with the library, which cover most of the code.
## Getting Started
1. Clone the repository locally.
2. Download or compile Keystone library and store it in the folder `src/main/resources/{os-prefix}/{keystone-lib}`, according [the specifications of JNA](https://java-native-access.github.io/jna/4.5.0/javadoc/index.html?com/sun/jna/NativeLibrary.html), or [the related unit test](https://github.com/java-native-access/jna/blob/7122be473e5f6179deb1c2b1c2fdeb77d8486fed/test/com/sun/jna/PlatformTest.java#L29).
3. Compile the Java bindings and issue the JAR using Maven `mvn package`. The unit tests are automatically while running the goal.
4. In your project, include `target/binding-java-{version}.jar` and [JNA](https://github.com/java-native-access/jna) using your favourite dependency manager, or include `target/binding-java-{version}-jar-with-dependencies.jar` that is packaged with JNA.
### Exception handling
Each operation that returns the native enumeration `ks_err` is wrapped in an instance of a subclass of `KeystoneException`, thrown when an error occurs. The Javadoc indicates the exceptions thrown by the Java bindings for Keystone.
### Garbage collection
Keystone library requires to open and close an handle, that must be collected once an instance of the class `Keystone` is disposed. For that purpose, the class implements the interface `AutoCloseable`. Nonetheless, the Java bindings also implement a cleaning mechanism that automatically collects the handle when the instance of the class is *phantom reachable*, meaning no memory leak can happen, even if the instance is not closed properly.
### Native function calls
The Java bindings for Keystone rely on [JNA Direct Mapping](https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md) to improve the performances of native calls, approaching that of custom JNI.
## Found an issue or bug ?
Feel free to open a GitHub issue on [the official repository of Keystone](https://github.com/keystone-engine/keystone/issues) and ping the contributors.
## Contributors
Author: Jämes Ménétrey ([@ZenLulz](https://github.com/ZenLulz/))
Maintainers:
- Need some people here :) Feel free to contribute !
### Want to contribute ?
Hey you! Your help is more than welcome! Things to keep in mind when working on the Java bindings for Keystone:
- Think about the backward compatibility; while code refactoring is a good practice, changing entirely the API *may* result in struggles.
- Elaborate the unit tests that prove your code is working. Test all the paths of the newly-added functions/classes (IntelliJ can show some metrics using Coverage). Keep the code coverage high!
- Please; write the required Javadoc, so every developer has the chance to understand your code.
- Update the changelog with a summary of your changes.
#### Version notation
The version of the Java bindings for Keystone is indicated in the file Maven configuration file (`pom.xml`). The major, minor and incremental versions (w.x.y) match the version of the library Keystone that the bindings is developed with. The build number (the -z in w.x.y-z) is incremented for each newer version of the Java bindings. Please, don't forget to increment this version when you submit a pull request.
On the last commit for a pull request, please create a tag called `java-bindings-w.x.y-z`.
#### Pull request submission
Ping the contributors of the Java bindings when submitting a pull request, so your changes can be peer reviewed.
## License
The Java bindings for Keystone is open-sourced software licensed under the MIT license.
The license of the library Keystone may be different and is available [at the root of the repository of Keystone](https://github.com/keystone-engine/keystone).

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
~
~ This file is part of the Keystone Java bindings which is released under MIT.
~ See file LICENSE in the Java bindings folder for full license details.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>keystone</groupId>
<artifactId>java-bindings</artifactId>
<version>0.9.1-0</version>
<name>Keystone Java bindings</name>
<description>Keystone assembler framework: Core (Arm, Arm64, Hexagon, Mips, PowerPC, Sparc, SystemZ and X86) orm.</description>
<url>https://github.com/keystone-engine/keystone/tree/master/bindings/java</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>10</java.version>
<jna.version>4.5.1</jna.version>
<junit.jupiter.version>5.2.0</junit.jupiter.version>
<junit.platform.version>1.2.0</junit.platform.version>
</properties>
<developers>
<developer>
<email>james@menetrey.me</email>
<name>Jämes Ménétrey</name>
<url>https://github.com/ZenLulz</url>
<roles>
<role>Developer</role>
</roles>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>${jna.version}</version>
</dependency>
<!-- Dependencies for testing. -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Configure Maven to compile using Java 10. -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<!-- Package the project into a JAR with dependencies. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>assemble-all</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Unit tests are runnable using maven. -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>${junit.platform.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,249 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import keystone.exceptions.AssembleFailedKeystoneException;
import keystone.exceptions.OpenFailedKeystoneException;
import keystone.exceptions.SetOptionFailedKeystoneException;
import keystone.natives.CleanerContainer;
import keystone.natives.DirectMappingKeystoneNative;
import keystone.natives.KeystoneCleanerContainer;
import keystone.utilities.Version;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* The Keystone engine.
*/
public class Keystone implements AutoCloseable {
/**
* The pointer to the Keystone native resource.
*/
private final Pointer ksEngine;
/**
* The cleaner container that frees up the native resource if this object is not properly closed and is
* candidate for garbage collection.
*/
private final CleanerContainer ksEngineCleaner;
/**
* Indicates whether the current instance of Keystone has been closed.
*/
private final AtomicBoolean hasBeenClosed;
/**
* The memory retention of the symbol resolver callback, in order to prevent the garbage collector
* to free up the callback, that would result in a crash, as the native library still has a reference to it.
*/
private SymbolResolverCallback symbolResolverCallback;
/**
* Initializes a new instance of the class {@link Keystone}.
* <p>
* Some architectures are not supported. Use the static method {@link #isArchitectureSupported} to determine
* whether the engine support the architecture.
*
* @param architecture The architecture of the code generated by Keystone.
* @param mode The mode type.
* @throws OpenFailedKeystoneException if the Keystone library cannot be opened properly.
*/
public Keystone(KeystoneArchitecture architecture, KeystoneMode mode) {
ksEngine = initializeKeystoneEngine(architecture, mode);
ksEngineCleaner = initializeKeystoneCleanerContainer();
hasBeenClosed = new AtomicBoolean(false);
}
/**
* Determines whether the given architecture is supported by Keystone.
*
* @param architecture The architecture type to check.
* @return The return value is {@code true} if the architecture is supported, otherwise {@code false}.
*/
public static boolean isArchitectureSupported(KeystoneArchitecture architecture) {
return DirectMappingKeystoneNative.ks_arch_supported(architecture);
}
/**
* Opens an handle of Keystone.
*
* @param architecture The architecture of the code generated by Keystone.
* @param mode The mode type.
* @return The return value is a pointer to the handle of Keystone.
* @throws OpenFailedKeystoneException if the Keystone library cannot be opened properly.
*/
private Pointer initializeKeystoneEngine(KeystoneArchitecture architecture, KeystoneMode mode) {
var pointerToEngine = new PointerByReference();
var openResult = DirectMappingKeystoneNative.ks_open(architecture, mode, pointerToEngine);
if (openResult != KeystoneError.Ok) {
throw new OpenFailedKeystoneException(openResult);
}
return pointerToEngine.getValue();
}
/**
* Initializes the cleaner object, that is going to close the native handle of Keystone if
* the instance is garbage collected.
*
* @return The return value is a cleaner container.
*/
private CleanerContainer initializeKeystoneCleanerContainer() {
return new KeystoneCleanerContainer(ksEngine);
}
/**
* Assembles a string that contains assembly code.
*
* @param assembly The assembly instructions. Use ; or \n to separate statements.
* @return The return value is the machine code of the assembly instructions.
* @throws AssembleFailedKeystoneException if the assembly code cannot be assembled properly.
*/
public KeystoneEncoded assemble(String assembly) {
return assemble(assembly, 0);
}
/**
* Assembles a string that contains assembly code, located at a given address location.
*
* @param assembly The assembly instructions. Use ; or \n to separate statements.
* @param address The address of the first assembly instruction.
* @return The return value is the machine code of the assembly instructions.
* @throws AssembleFailedKeystoneException if the assembly code cannot be assembled properly.
*/
public KeystoneEncoded assemble(String assembly, int address) {
var pointerToMachineCodeBuffer = new PointerByReference();
var pointerToMachineCodeSize = new IntByReference();
var pointerToNumberOfStatements = new IntByReference();
var result = DirectMappingKeystoneNative.ks_asm(ksEngine, assembly, address, pointerToMachineCodeBuffer,
pointerToMachineCodeSize, pointerToNumberOfStatements);
if (result != 0) {
var errorCode = DirectMappingKeystoneNative.ks_errno(ksEngine);
throw new AssembleFailedKeystoneException(errorCode, assembly);
}
var machineCodeBuffer = pointerToMachineCodeBuffer.getValue();
var machineCode = machineCodeBuffer.getByteArray(0, pointerToMachineCodeSize.getValue());
DirectMappingKeystoneNative.ks_free(machineCodeBuffer);
return new KeystoneEncoded(machineCode, address, pointerToNumberOfStatements.getValue());
}
/**
* Assembles a string that contains assembly code.
*
* @param assembly A collection of assembly instructions.
* @return The return value is the machine code of the assembly instructions.
* @throws AssembleFailedKeystoneException if the assembly code cannot be assembled properly.
*/
public KeystoneEncoded assemble(Iterable<String> assembly) {
return assemble(assembly, 0);
}
/**
* Assembles a string that contains assembly code, located at a given address location.
*
* @param assembly A collection of assembly instructions.
* @param address The address of the first assembly instruction.
* @return The return value is the machine code of the assembly instructions.
* @throws AssembleFailedKeystoneException if the assembly code cannot be assembled properly.
*/
public KeystoneEncoded assemble(Iterable<String> assembly, int address) {
return assemble(String.join(";", assembly), address);
}
/**
* Gets the major and minor version numbers.
*
* @return The returned value is an instance of the class {@link Version}, containing the major and minor version numbers.
*/
public Version version() {
var major = new IntByReference();
var minor = new IntByReference();
DirectMappingKeystoneNative.ks_version(major, minor);
return new Version(major.getValue(), minor.getValue());
}
/**
* Sets the syntax of the assembly code used in this instance of Keystone.
*
* @param syntax The syntax of the assembly code.
* @throws SetOptionFailedKeystoneException if the syntax is not supported.
* @throws SetOptionFailedKeystoneException if the pair of type and value is not valid.
*/
public void setAssemblySyntax(KeystoneOptionValue.KeystoneOptionSyntax syntax) {
setOption(KeystoneOptionType.Syntax, syntax.value());
}
/**
* Sets an option for Keystone engine at runtime using a not-strongly typed option value.
* <p>
* It is suggested to prefer the methods {@link #setAssemblySyntax(KeystoneOptionValue.KeystoneOptionSyntax)},
* {@link #setSymbolResolver(SymbolResolverCallback)} or {@link #unsetSymbolResolver()}.
*
* @param type The type of the option.
* @param value The value of the option.
* @throws SetOptionFailedKeystoneException if the pair of type and value is not valid.
*/
public void setOption(KeystoneOptionType type, int value) {
var result = DirectMappingKeystoneNative.ks_option(ksEngine, type, value);
if (result != KeystoneError.Ok) {
throw new SetOptionFailedKeystoneException(result, type, value);
}
}
/**
* Sets a symbol resolver callback, to resolve unrecognized symbols encountered in the assembly code.
* <p>
* If the method is called many times, only the last callback will be triggered.
*
* @param callback The symbol resolver callback.
* @throws SetOptionFailedKeystoneException if the pair of type and value is not valid.
*/
public void setSymbolResolver(SymbolResolverCallback callback) {
symbolResolverCallback = callback;
DirectMappingKeystoneNative.ks_option(ksEngine, KeystoneOptionType.SymbolResolver, callback);
}
/**
* Unsets the current symbol resolver. The symbol resolver instance can be freely collected afterwards.
*
* @throws SetOptionFailedKeystoneException if the pair of type and value is not valid.
*/
public void unsetSymbolResolver() {
DirectMappingKeystoneNative.ks_option(ksEngine, KeystoneOptionType.SymbolResolver, 0);
symbolResolverCallback = null;
}
/**
* Closes this resource, relinquishing any underlying resources.
* This method is invoked automatically on objects managed by the
* {@code try}-with-resources statement.
* <p>
* The call to this method is thread-safe.
*/
@Override
public void close() {
var hasBeenAlreadyClosed = hasBeenClosed.getAndSet(true);
if (!hasBeenAlreadyClosed) {
ksEngineCleaner.close();
}
}
}

@ -0,0 +1,112 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone;
import keystone.jna.JnaEnum;
import java.util.HashMap;
import java.util.Map;
/**
* The supported architectures of Keystone.
*/
public enum KeystoneArchitecture implements JnaEnum {
/**
* ARM architecture (including Thumb, Thumb-2).
*/
Arm(1),
/**
* ARM-64, also called AArch64.
*/
Arm64(2),
/**
* Mips architecture.
*/
Mips(3),
/**
* X86 architecture (including x86 & x86-64).
*/
X86(4),
/**
* PowerPC architecture (currently unsupported).
*/
Ppc(5),
/**
* Sparc architecture.
*/
Sparc(6),
/**
* SystemZ architecture (S390X).
*/
SystemZ(7),
/**
* Hexagon architecture
*/
Hexagon(8),
/**
* Ethereum Virtual Machine architecture.
*/
Evm(9),
Max(10);
/**
* Mapping table to determine an enumeration value based on an integer with a complexity of θ(1).
*/
private static Map<Integer, KeystoneArchitecture> intToEnumMapping = new HashMap<>();
static {
// Initializes the mapping table.
for (KeystoneArchitecture architecture : KeystoneArchitecture.values()) {
intToEnumMapping.put(architecture.value(), architecture);
}
}
/**
* Holds the integer value of the enumeration, that corresponds to the value used in the enumeration in C.
*/
private final int value;
/**
* Constructor of the enumeration.
*
* @param value The integer value, that corresponds to the value used in the enumeration in C.
*/
KeystoneArchitecture(int value) {
this.value = value;
}
/**
* Converts an integer value into its corresponding enumeration value.
* <p>
* The complexity of the conversion is θ(1).
*
* @param value The integer value.
* @return The return value is a value of the enumeration.
*/
public static KeystoneArchitecture fromValue(Integer value) {
return intToEnumMapping.get(value);
}
/**
* Retrieves the value of the enumeration, that corresponds to the value used in the enumeration in C.
*
* @return The return value is an integer value.
*/
public int value() {
return value;
}
}

@ -0,0 +1,62 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone;
/**
* Wrap the result of an assemble using Keystone.
*/
public class KeystoneEncoded {
/**
* The machine code generated by Keystone.
*/
private final byte[] machineCode;
/**
* The address of the first assembly instruction.
*/
private final int address;
/**
* The number of statements successfully processed.
*/
private final int numberOfStatements;
/**
* Creates a new instance of the class {@link KeystoneEncoded}.
*
* @param machineCode The machine code generated by Keystone.
* @param address The address of the first assembly instruction.
* @param numberOfStatements The number of statements successfully processed.
*/
public KeystoneEncoded(byte[] machineCode, int address, int numberOfStatements) {
this.machineCode = machineCode;
this.address = address;
this.numberOfStatements = numberOfStatements;
}
/**
* Gets the machine code generated by Keystone.
*/
public byte[] getMachineCode() {
return machineCode;
}
/**
* Gets the address of the first assembly instruction.
*/
public int getAddress() {
return address;
}
/**
* Gets the number of statements successfully processed.
*/
public int getNumberOfStatements() {
return numberOfStatements;
}
}

@ -0,0 +1,298 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone;
import keystone.jna.JnaEnum;
import java.util.HashMap;
import java.util.Map;
/**
* All type of errors encountered by Keystone API.
*/
public enum KeystoneError implements JnaEnum {
/**
* No error: everything was fine.
*/
Ok(0),
/**
* Out-Of-Memory error: ks_open(), ks_emulate().
*/
Nomem(1),
/**
* Unsupported architecture: ks_open().
*/
Arch(2),
/**
* Invalid handle.
*/
Handle(3),
/**
* Invalid/unsupported mode: ks_open().
*/
Mode(4),
/**
* Unsupported version (bindings).
*/
Version(5),
/**
* Unsupported option.
*/
OptInvalid(6),
// generic input assembly errors - parser specific
/**
* Unknown token in expression.
*/
AsmExprToken(Base.asm),
/**
* Literal value out of range for directive.
*/
AsmDirectiveValueRange(Base.asm + 1),
/**
* Expected identifier in directive.
*/
AsmDirectiveId(Base.asm + 2),
/**
* Unexpected token in directive.
*/
AsmDirectiveToken(Base.asm + 3),
/**
* Expected string in directive.
*/
AsmDirectiveStr(Base.asm + 4),
/**
* Expected comma in directive.
*/
AsmDirectiveComma(Base.asm + 5),
/**
* Expected relocation name in directive.
*/
AsmDirectiveRelocName(Base.asm + 6),
/**
* Unexpected token in .reloc directive.
*/
AsmDirectiveRelocToken(Base.asm + 7),
/**
* Invalid floating point in directive.
*/
AsmDirectiveFpoint(Base.asm + 8),
/**
* Unknown directive.
*/
AsmDirectiveUnknown(Base.asm + 9),
/**
* Invalid equal directive.
*/
AsmDirectiveEqu(Base.asm + 10),
/**
* (Generic) invalid directive.
*/
AsmDirectiveInvalid(Base.asm + 11),
/**
* Invalid variant.
*/
AsmVariantInvalid(Base.asm + 12),
/**
* Brackets expression not supported on this target.
*/
AsmExprBracket(Base.asm + 13),
/**
* Unexpected symbol modifier following '@'.
*/
AsmSymbolModifier(Base.asm + 14),
/**
* Invalid symbol redefinition.
*/
AsmSymbolRedefined(Base.asm + 15),
/**
* Cannot find a symbol.
*/
AsmSymbolMissing(Base.asm + 16),
/**
* Expected ')' in parentheses expression.
*/
AsmRparen(Base.asm + 17),
/**
* Unexpected token at start of statement.
*/
AsmStatToken(Base.asm + 18),
/**
* Unsupported token yet.
*/
AsmUnsupported(Base.asm + 19),
/**
* Unexpected token in macro instantiation.
*/
AsmMacroToken(Base.asm + 20),
/**
* Unbalanced parentheses in macro argument.
*/
AsmMacroParen(Base.asm + 21),
/**
* Expected '=' after formal parameter identifier.
*/
AsmMacroEqu(Base.asm + 22),
/**
* Too many positional arguments.
*/
AsmMacroArgs(Base.asm + 23),
/**
* Macros cannot be nested more than 20 levels deep.
*/
AsmMacroLevelsExceed(Base.asm + 24),
/**
* Invalid macro string.
*/
AsmMacroStr(Base.asm + 25),
/**
* Invalid macro (generic error).
*/
AsmMacroInvalid(Base.asm + 26),
/**
* Unexpected backslash at end of escaped string.
*/
AsmEscBackslash(Base.asm + 27),
/**
* Invalid octal escape sequence (out of range).
*/
AsmEscOctal(Base.asm + 28),
/**
* Invalid escape sequence (unrecognized character).
*/
AsmEscSequence(Base.asm + 29),
/**
* Broken escape string.
*/
AsmEscStr(Base.asm + 30),
/**
* Invalid token.
*/
AsmTokenInvalid(Base.asm + 31),
/**
* This instruction is unsupported in this mode.
*/
AsmInsnUnsupported(Base.asm + 32),
/**
* Invalid fixup.
*/
AsmFixupInvalid(Base.asm + 33),
/**
* Invalid label.
*/
AsmLabelInvalid(Base.asm + 34),
/**
* Invalid fragment.
*/
AsmFragmentInvalid(Base.asm + 35),
// generic input assembly errors - architecture specific
AsmInvalidOperand(Base.asmArch),
AsmMissingFeature(Base.asmArch + 1),
AsmMnemonicFail(Base.asmArch + 2);
/**
* Mapping table to determine an enumeration value based on an integer with a complexity of θ(1).
*/
private static Map<Integer, KeystoneError> intToEnumMapping = new HashMap<>();
static {
// Initializes the mapping table.
for (KeystoneError error : KeystoneError.values()) {
intToEnumMapping.put(error.value(), error);
}
}
/**
* Holds the integer value of the enumeration, that corresponds to the value used in the enumeration in C.
*/
private final int value;
/**
* Constructor of the enumeration.
*
* @param value The integer value, that corresponds to the value used in the enumeration in C.
*/
KeystoneError(int value) {
this.value = value;
}
/**
* Converts an integer value into its corresponding enumeration value.
* <p>
* The complexity of the conversion is θ(1).
*
* @param value The integer value.
* @return The return value is a value of the enumeration.
*/
public static KeystoneError fromValue(Integer value) {
return intToEnumMapping.get(value);
}
/**
* Retrieves the value of the enumeration, that corresponds to the value used in the enumeration in C.
*
* @return The return value is an integer value.
*/
public int value() {
return value;
}
/**
* Internal interface referencing the base error codes.
*/
interface Base {
int asm = 128;
int asmArch = 512;
}
}

@ -0,0 +1,170 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone;
import keystone.jna.JnaEnum;
import java.util.HashMap;
import java.util.Map;
/**
* The supported mode type by Keystone.
*/
public enum KeystoneMode implements JnaEnum {
/**
* Little-endian mode (default mode).
*/
LittleEndian(0),
/**
* Big-endian mode.
*/
BigEndian(1 << 30),
// arm / arm64
/**
* ARM mode.
*/
Arm(1),
/**
* THUMB mode (including Thumb-2).
*/
ArmThumb(1 << 4),
/**
* ARMv8 A32 encodings for ARM.
*/
ArmV8(1 << 6),
// mips
/**
* MicroMips mode.
*/
MipsMicro(1 << 4),
/**
* Mips III ISA.
*/
Mips3(1 << 5),
/**
* Mips32r6 ISA.
*/
Mips32r6(1 << 6),
/**
* Mips32 ISA.
*/
Mips32(1 << 2),
/**
* Mips64 ISA.
*/
Mips64(1 << 3),
// x86 / x64
/**
* 16-bit mode.
*/
Mode16(1 << 1),
/**
* 32-bit mode.
*/
Mode32(1 << 2),
/**
* 64-bit mode.
*/
Mode64(1 << 3),
// ppc
/**
* 32-bit mode.
*/
Ppc32(1 << 2),
/**
* 64-bit mode.
*/
Ppc64(1 << 3),
/**
* Quad Processing eXtensions mode.
*/
PpcQpx(1 << 4),
// sparc
/**
* 32-bit mode.
*/
Sparc32(1 << 2),
/**
* 64-bit mode.
*/
Sparc64(1 << 3),
/**
* SparcV9 mode.
*/
SparcV9(1 << 4);
/**
* Mapping table to determine an enumeration value based on an integer with a complexity of θ(1).
*/
private static Map<Integer, KeystoneMode> intToEnumMapping = new HashMap<>();
static {
// Initializes the mapping table.
for (KeystoneMode mode : KeystoneMode.values()) {
intToEnumMapping.put(mode.value(), mode);
}
}
/**
* Holds the integer value of the enumeration, that corresponds to the value used in the enumeration in C.
*/
private final int value;
/**
* Constructor of the enumeration.
*
* @param value The integer value, that corresponds to the value used in the enumeration in C.
*/
KeystoneMode(int value) {
this.value = value;
}
/**
* Converts an integer value into its corresponding enumeration value.
* <p>
* The complexity of the conversion is θ(1).
*
* @param value The integer value.
* @return The return value is a value of the enumeration.
*/
public static KeystoneMode fromValue(Integer value) {
return intToEnumMapping.get(value);
}
/**
* Retrieves the value of the enumeration, that corresponds to the value used in the enumeration in C.
*
* @return The return value is an integer value.
*/
public int value() {
return value;
}
}

@ -0,0 +1,72 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone;
import keystone.jna.JnaEnum;
import java.util.HashMap;
import java.util.Map;
public enum KeystoneOptionType implements JnaEnum {
/**
* Choose syntax for input assembly.
*/
Syntax(1),
/**
* Set symbol resolver callback.
*/
SymbolResolver(2);
/**
* Mapping table to determine an enumeration value based on an integer with a complexity of θ(1).
*/
private static Map<Integer, KeystoneOptionType> intToEnumMapping = new HashMap<>();
static {
// Initializes the mapping table.
for (KeystoneOptionType type : KeystoneOptionType.values()) {
intToEnumMapping.put(type.value(), type);
}
}
/**
* Holds the integer value of the enumeration, that corresponds to the value used in the enumeration in C.
*/
private final int value;
/**
* Constructor of the enumeration.
*
* @param value The integer value, that corresponds to the value used in the enumeration in C.
*/
KeystoneOptionType(int value) {
this.value = value;
}
/**
* Converts an integer value into its corresponding enumeration value.
* <p>
* The complexity of the conversion is θ(1).
*
* @param value The integer value.
* @return The return value is a value of the enumeration.
*/
public static KeystoneOptionType fromValue(Integer value) {
return intToEnumMapping.get(value);
}
/**
* Retrieves the value of the enumeration, that corresponds to the value used in the enumeration in C.
*
* @return The return value is an integer value.
*/
public int value() {
return value;
}
}

@ -0,0 +1,104 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone;
import keystone.jna.JnaEnum;
import java.util.HashMap;
import java.util.Map;
/**
* Runtime option values (associated with {@link KeystoneOptionType}).
*/
public class KeystoneOptionValue {
/**
* Runtime option values associated with the assembly syntax.
*
* @see KeystoneOptionType#Syntax
*/
public enum KeystoneOptionSyntax implements JnaEnum {
/**
* X86 Intel syntax - default on X86 (KS_OPT_SYNTAX).
*/
Intel(1),
/**
* X86 ATT asm syntax (KS_OPT_SYNTAX).
*/
Att(1 << 1),
/**
* X86 Nasm syntax (KS_OPT_SYNTAX).
*/
Nasm(1 << 2),
/**
* X86 Masm syntax (KS_OPT_SYNTAX) - unsupported yet.
*/
Masm(1 << 3),
/**
* X86 GNU GAS syntax (KS_OPT_SYNTAX).
*/
Gas(1 << 4),
/**
* All immediates are in hex format (i.e 12 is 0x12).
*/
Radix16(1 << 5);
/**
* Mapping table to determine an enumeration value based on an integer with a complexity of θ(1).
*/
private static Map<Integer, KeystoneOptionSyntax> intToEnumMapping = new HashMap<>();
static {
// Initializes the mapping table.
for (KeystoneOptionSyntax syntax : KeystoneOptionSyntax.values()) {
intToEnumMapping.put(syntax.value(), syntax);
}
}
/**
* Holds the integer value of the enumeration, that corresponds to the value used in the enumeration in C.
*/
private final int value;
/**
* Constructor of the enumeration.
*
* @param value The integer value, that corresponds to the value used in the enumeration in C.
*/
KeystoneOptionSyntax(int value) {
this.value = value;
}
/**
* Converts an integer value into its corresponding enumeration value.
* <p>
* The complexity of the conversion is θ(1).
*
* @param value The integer value.
* @return The return value is a value of the enumeration.
*/
public static KeystoneOptionSyntax fromValue(Integer value) {
return intToEnumMapping.get(value);
}
/**
* Retrieves the value of an element in the enumeration.
*
* @return The return value is an integer number, that represents the value in the native library.
*/
@Override
public int value() {
return value;
}
}
}

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone;
import com.sun.jna.Callback;
import com.sun.jna.ptr.LongByReference;
/**
* An interface that represents a callback to provide a value for a missing symbol.
*/
public interface SymbolResolverCallback extends Callback {
/**
* A callback triggered when a unrecognized symbol is found.
*
* @param symbol The symbol to resolve.
* @param value The value to modify if the symbol is resolved.
* @return The return value must be {@code true} if the symbol can be resolved; otherwise {@code false}.
*/
boolean onResolve(String symbol, LongByReference value);
}

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.exceptions;
import keystone.KeystoneError;
/**
* An exception that represents a failure while assembling code.
*/
public class AssembleFailedKeystoneException extends KeystoneException {
/**
* The assembly code that generates the error.
*/
private final String assembly;
public AssembleFailedKeystoneException(KeystoneError keystoneError, String assembly) {
super(keystoneError, "Error while assembling `" + assembly + "`");
this.assembly = assembly;
}
/**
* Gets the assembly code that generates the error.
*/
public String getAssembly() {
return assembly;
}
}

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.exceptions;
import keystone.KeystoneError;
import keystone.natives.DirectMappingKeystoneNative;
/**
* The base class for all the exceptions thrown by the library Keystone.
*/
public abstract class KeystoneException extends RuntimeException {
/**
* The error thrown by Keystone.
*/
private final KeystoneError keystoneError;
/**
* Creates a new instance of {@link KeystoneException}.
*
* @param keystoneError The error code of Keystone.
* @param message A human-readable message of the error.
*/
KeystoneException(KeystoneError keystoneError, String message) {
super(message + " : " + DirectMappingKeystoneNative.ks_strerror(keystoneError));
this.keystoneError = keystoneError;
}
/**
* Gets the error code of Keystone.
*/
public KeystoneError getKeystoneError() {
return keystoneError;
}
}

@ -0,0 +1,19 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.exceptions;
import keystone.KeystoneError;
/**
* An exception that represents a failure to open the library Keystone.
*/
public class OpenFailedKeystoneException extends KeystoneException {
public OpenFailedKeystoneException(KeystoneError keystoneError) {
super(keystoneError, "Keystone library could not be opened");
}
}

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.exceptions;
import keystone.KeystoneError;
import keystone.KeystoneOptionType;
/**
* An exception that represents an error while setting an option in the Keystone library.
*/
public class SetOptionFailedKeystoneException extends KeystoneException {
/**
* The type of the option that generated the exception.
*/
private final KeystoneOptionType optionType;
/**
* The value of the option that generated the exception.
*/
private final int optionValue;
public SetOptionFailedKeystoneException(KeystoneError keystoneError, KeystoneOptionType type, int value) {
super(keystoneError, createErrorMessage(type, value));
this.optionType = type;
this.optionValue = value;
}
private static String createErrorMessage(KeystoneOptionType type, int value) {
return "Error while setting the option `" + type +
"` with the value `" + value + "`";
}
/**
* Gets the type of the option that generated the exception.
*/
public KeystoneOptionType getOptionType() {
return optionType;
}
/**
* Gets the value of the option that generated the exception.
*/
public int getOptionValue() {
return optionValue;
}
}

@ -0,0 +1,65 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.jna;
import com.sun.jna.DefaultTypeMapper;
import com.sun.jna.FromNativeContext;
import com.sun.jna.ToNativeContext;
import com.sun.jna.TypeConverter;
import java.util.function.Function;
/**
* Extends the default type mapper of JNA in order to provide custom bindings for enumerations.
*/
class EnumTypeMapper extends DefaultTypeMapper {
/**
* Add a {@link TypeConverter} to provide bidirectional mapping between
* a native and Java type.
*
* @param enumerationType The type of the enumeration to bind.
* @param fromNativeInteger A function that converts the native enumeration into the Java enumeration.
* @param <T> The type of the Java enumeration.
*/
<T extends JnaEnum> void addTypeConverter(Class<T> enumerationType, Function<Integer, T> fromNativeInteger) {
addTypeConverter(enumerationType, new TypeConverter() {
/**
* Converts the native enumeration into the Java enumeration.
*
* @param nativeValue The native element.
* @param context The context for converting a native value into a Java type.
* @return The return value is a Java enumeration.
*/
@Override
public Object fromNative(Object nativeValue, FromNativeContext context) {
return fromNativeInteger.apply((int) nativeValue);
}
/**
* Returns the native type of the conversion. The enumeration are of type {@link Integer}.
*/
@Override
public Class<?> nativeType() {
return Integer.class;
}
/**
* Converts a Java enumeration into a native enumeration.
*
* @param value The Java element.
* @param context The context of converting a Java type into a native value.
* @return The return value is a native value from an enumeration.
*/
@Override
public Object toNative(Object value, ToNativeContext context) {
return ((JnaEnum) value).value();
}
});
}
}

@ -0,0 +1,20 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.jna;
/**
* A contract that represents an JNA enumeration.
*/
public interface JnaEnum {
/**
* Retrieves the value of an element in the enumeration.
*
* @return The return value is an integer number, that represents the value in the native library.
*/
int value();
}

@ -0,0 +1,23 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.jna;
import keystone.*;
/**
* Extends the numeration type mapper in order to register the enumeration used by Keystone.
*/
public class KeystoneTypeMapper extends EnumTypeMapper {
public KeystoneTypeMapper() {
addTypeConverter(KeystoneError.class, KeystoneError::fromValue);
addTypeConverter(KeystoneArchitecture.class, KeystoneArchitecture::fromValue);
addTypeConverter(KeystoneMode.class, KeystoneMode::fromValue);
addTypeConverter(KeystoneOptionType.class, KeystoneOptionType::fromValue);
addTypeConverter(KeystoneOptionValue.KeystoneOptionSyntax.class, KeystoneOptionValue.KeystoneOptionSyntax::fromValue);
}
}

@ -0,0 +1,48 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.natives;
import java.lang.ref.Cleaner;
/**
* An abstract class that automatically runs the action passed by argument in the constructor once
* phantom reachable, thanks to {@link Cleaner}.
*
* @see Cleaner
* @see <a href="https://www.linkedin.com/pulse/java-9-cleaner-illustration-good-encapsulation-pawel-wlodarski/">Java 9 Cleaner as an illustration of good encapsulation</a>
*/
public abstract class CleanerContainer implements AutoCloseable {
/**
* Creates a single instance of the cleaner for all the native resources.
*/
private static final Cleaner cleaner = Cleaner.create();
/**
* The instance of {@link java.lang.ref.Cleaner.Cleanable} that refers to the native resource.
*/
private final Cleaner.Cleanable cleanable;
/**
* Registers a cleaning action to run when the object becomes phantom reachable.
*
* @param action a {@code Runnable} to invoke when the object becomes phantom reachable.
*/
CleanerContainer(Runnable action) {
cleanable = cleaner.register(this, action);
}
/**
* Closes this resource, relinquishing any underlying resources.
* This method is invoked automatically on objects managed by the
* {@code try}-with-resources statement.
*/
@Override
public void close() {
cleanable.clean();
}
}

@ -0,0 +1,179 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.natives;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import keystone.*;
import keystone.jna.KeystoneTypeMapper;
import java.util.HashMap;
import java.util.Map;
/**
* The class providing the native functions of Keystone using the Direct Mapping of JNA for best performance.
* <p>
* The original function prototypes are declared in the header file <i>keystone.h</i>.
*
* @see <a href="https://github.com/java-native-access/jna/blob/master/www/DirectMapping.md">JNA: Direct Mapping</a>
*/
public final class DirectMappingKeystoneNative {
static {
Map<String, Object> options = new HashMap<>();
options.put(Library.OPTION_TYPE_MAPPER, new KeystoneTypeMapper());
// Direct Mapping using JNA
Native.register(NativeLibrary.getInstance("keystone", options));
}
/**
* Private constructor to prevent any instantiation of the class.
*/
private DirectMappingKeystoneNative() {
}
/**
* Determine if the given architecture is supported by this library.
* <p>
* Native function prototype: <i>bool ks_arch_supported(ks_arch arch);</i>
*
* @param architecture architecture type
* @return {@code true} if this library supports the given arch.
*/
public static native boolean ks_arch_supported(KeystoneArchitecture architecture);
/**
* 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 @machineCode.
* <p>
* On failure, call {@link DirectMappingKeystoneNative#ks_errno} for error code.
* <p>
* NOTE 1: this API will automatically determine memory needed to contain
* output bytes in *encoding.
* <p>
* NOTE 2: caller must free the allocated memory itself to avoid memory leaking.
* <p>
* Native function prototype: <i>int ks_asm(ks_engine *ks,
* const char *string,
* uint64_t address,
* unsigned char **encoding, size_t *encoding_size,
* size_t *stat_count);</i>
*
* @param engine handle returned by ks_open()
* @param assembly NULL-terminated assembly string. Use ; or \n to separate statements.
* @param address address of the first assembly instruction, or 0 to ignore.
* @param machineCodeBuffer 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 machineCodeSize size of machineCode
* @param numberOfStatements number of statements successfully processed
* @return 0 on success, or -1 on failure.
*/
public static native int ks_asm(Pointer engine, String assembly, long address, PointerByReference machineCodeBuffer,
IntByReference machineCodeSize, IntByReference numberOfStatements);
/**
* 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 this API releases some cached memory, thus access to any
* Keystone API after ks_close() might crash your application.
* After this, @engine is invalid, and no longer usable.
* <p>
* Native function prototype: <i>ks_err ks_close(ks_engine *ks);</i>
*
* @param engine pointer to a handle returned by ks_open().
* @return KS_ERR_OK on success, or other value on failure (refer to ks_err enum for detailed error).
*/
public static native KeystoneError ks_close(Pointer engine);
/**
* Report the last error number when some API function fail.
* Like glibc's errno, ks_errno might not retain its old error once accessed.
* <p>
* Native function prototype: <i>ks_err ks_errno(ks_engine *ks)</i>
*
* @param engine handle returned by ks_open()
* @return error code of ks_err enum type {@link KeystoneError}
*/
public static native KeystoneError ks_errno(Pointer engine);
/**
* Free memory allocated by ks_asm().
* <p>
* Native function prototype: <i>void ks_free(unsigned char *p)</i>
*
* @param machineCodeBuffer memory allocated in @encoding argument of ks_asm()
*/
public static native void ks_free(Pointer machineCodeBuffer);
/**
* Create new instance of Keystone engine.
* <p>
* Native function prototype: <i>ks_err ks_open(ks_arch arch, int mode, ks_engine **ks);</i>
*
* @param architecture architecture type (KS_ARCH_*).
* @param mode hardware mode. This is combined of KS_MODE_*.
* @param engine pointer to ks_engine, which will be updated at return time.
* @return KS_ERR_OK on success, or other value on failure (refer to ks_err enum for detailed error).
*/
public static native KeystoneError ks_open(KeystoneArchitecture architecture, KeystoneMode mode, PointerByReference engine);
/**
* Set option for Keystone engine at runtime.
* <p>
* Native function prototype: <i>err ks_option(ks_engine *ks, ks_opt_type type, size_t value);</i>
*
* @param engine handle returned by ks_open()
* @param type type of option to be set. See {@link KeystoneOptionType}
* @param value option value corresponding with @type
* @return {@link KeystoneError#Ok} on success, or other value on failure.
* Refer to {@link KeystoneError} enum for detailed error.
*/
public static native KeystoneError ks_option(Pointer engine, KeystoneOptionType type, int value);
/**
* Set option for Keystone engine at runtime
* <p>
* Native function prototype: <i>err ks_option(ks_engine *ks, ks_opt_type type, size_t value);</i>
*
* @param engine handle returned by ks_open()
* @param type ype of option to be set. See {@link KeystoneOptionType}
* @param callback callback to resolve a unrecognized symbol.
* @return {@link KeystoneError#Ok} on success, or other value on failure.
* Refer to {@link KeystoneError} enum for detailed error.
*/
public static native KeystoneError ks_option(Pointer engine, KeystoneOptionType type, SymbolResolverCallback callback);
/**
* Return a string describing given error code.
* <p>
* Native function prototype: <i>const char *ks_strerror(ks_err code);</i>
*
* @param errorCode error code.
* @return returns a pointer to a string that describes the error code passed in the argument @errorCode.
*/
public static native String ks_strerror(KeystoneError errorCode);
/**
* Returns combined API version & major and minor version numbers.
* <p>
* Native function prototype: <i>unsigned int ks_version(unsigned int *major, unsigned int *minor);</i>
*
* @param major The major number of API version.
* @param minor The minor number of API version.
* @return An hexadecimal number as (major << 8 | minor), which encodes both major & minor versions.
*/
public static native int ks_version(IntByReference major, IntByReference minor);
}

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.natives;
import com.sun.jna.Pointer;
/**
* The cleaner that automatically close the {@code Pointer} of Keystone once the managed object is phantom reachable.
*/
public class KeystoneCleanerContainer extends CleanerContainer {
/**
* Registers an object and a cleaning action to run when the object becomes phantom reachable.
*/
public KeystoneCleanerContainer(Pointer pointerToEngine) {
super(new KeystoneState(pointerToEngine));
}
/**
* The internal state that contains the logic to free the native resource.
*/
static class KeystoneState implements Runnable {
/**
* A pointer to the native resource.
*/
private final Pointer pointerToEngine;
/**
* Create a new instance of {@link KeystoneState}.
*
* @param pointerToEngine The pointer to the Keystone native resource.
*/
KeystoneState(Pointer pointerToEngine) {
this.pointerToEngine = pointerToEngine;
}
/**
* Calls the logic to collect the native resource.
*/
@Override
public void run() {
DirectMappingKeystoneNative.ks_close(pointerToEngine);
}
}
}

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018 Jämes Ménétrey <james@menetrey.me>
*
* This file is part of the Keystone Java bindings which is released under MIT.
* See file LICENSE in the Java bindings folder for full license details.
*/
package keystone.utilities;
/**
* Represents the version number of Keystone. This class cannot be inherited.
*/
public final class Version implements Comparable<Version> {
private final int major;
private final int minor;
/**
* Initializes a new instance of the {@link Version} class using the specified major and minor values.
*
* @param major The major version number.
* @param minor The minor version number.
*/
public Version(int major, int minor) {
this.major = major;
this.minor = minor;
}
/**
* Gets the value of the major component of the version number for the current {@link Version} object.
*
* @return The major version number.
*/
public int major() {
return major;
}
/**
* Gets the value of the minor component of the version number for the current {@link Version} object.
*
* @return The minor version number.
*/
public int minor() {
return minor;
}
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
*
* <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
* -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>. (This
* implies that <tt>x.compareTo(y)</tt> must throw an exception iff
* <tt>y.compareTo(x)</tt> throws an exception.)
*
* <p>The implementor must also ensure that the relation is transitive:
* <tt>(x.compareTo(y)&gt;0 &amp;&amp; y.compareTo(z)&gt;0)</tt> implies
* <tt>x.compareTo(z)&gt;0</tt>.
*
* <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
* implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
* all <tt>z</tt>.
*
* <p>It is strongly recommended, but <i>not</i> strictly required that
* <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>. Generally speaking, any
* class that implements the <tt>Comparable</tt> interface and violates
* this condition should clearly indicate this fact. The recommended
* language is "Note: this class has a natural ordering that is
* inconsistent with equals."
*
* <p>In the foregoing description, the notation
* <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
* <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
* <tt>0</tt>, or <tt>1</tt> according to whether the value of
* <i>expression</i> is negative, zero or positive.
*
* @param o the object to be compared.
* @return a negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
* @throws NullPointerException if the specified object is null
* @throws ClassCastException if the specified object's type prevents it
* from being compared to this object.
*/
@Override
public int compareTo(Version o) {
if (o == null) return 1;
if (major != o.major) {
if (major > o.major) {
return 1;
}
return -1;
}
return Integer.compare(minor, o.minor);
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save