Add Rust bindings to master branch (#1401)
* Rust bindings (#1295) * rust bindings init * updated bindings/README * Improved Rust bindings: (#1309) * Added basic block hooking * Changed confusing struct naming. Before: Protection::All -> R,W,X, Now: Permission::All -> R,W,X * Fixed issue with remove_hook(..). Implementation tried to remove hook from incorrect hashmap. * Made unused private vmmap(..) public. * Improve Rust bindings (#1367) * fixed tests * constant readability * HookType as bitflags * Mode as bitflags * improve bitflags * cargo fmt * removed unnecessary "as usize" * fixed buggy deallocation of uc_context structs * Remove data field in rust bindings Co-authored-by: Lukas Seidel <pr0me@users.noreply.github.com> Co-authored-by: Nikolas Eller <nikolas.e@mail.de> Co-authored-by: Simon Wörner <git@simon-woerner.de> Co-authored-by: floesen <floesen@users.noreply.github.com>
This commit is contained in:
parent
668c43c94d
commit
9c6134ca91
2
.gitignore
vendored
2
.gitignore
vendored
@ -67,6 +67,8 @@ bindings/python/unicorn.egg-info/
|
|||||||
bindings/python/unicorn/lib/
|
bindings/python/unicorn/lib/
|
||||||
bindings/python/unicorn/include/
|
bindings/python/unicorn/include/
|
||||||
bindings/python/MANIFEST
|
bindings/python/MANIFEST
|
||||||
|
bindings/rust/target/
|
||||||
|
bindings/rust/Cargo.lock
|
||||||
config.log
|
config.log
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
This directory contains bindings & test code for Python, Java, Go and .NET.
|
This directory contains bindings & test code for Python, Java, Go, .NET and Rust.
|
||||||
See <language>/README or <language>/README.TXT or <language>/README.md for how to install each binding.
|
See <language>/README or <language>/README.TXT or <language>/README.md for how to install each binding.
|
||||||
|
|
||||||
The following bindings are contributed by community.
|
The following bindings are contributed by community.
|
||||||
@ -10,13 +10,14 @@ The following bindings are contributed by community.
|
|||||||
- Haskell binding: by Adrian Herrera.
|
- Haskell binding: by Adrian Herrera.
|
||||||
- VB6 binding: David Zimmer.
|
- VB6 binding: David Zimmer.
|
||||||
- FreePascal/Delphi binding: Mohamed Osama.
|
- FreePascal/Delphi binding: Mohamed Osama.
|
||||||
|
- Rust binding: Lukas Seidel.
|
||||||
|
|
||||||
More bindings created & maintained externally by community are available as follows.
|
More bindings created & maintained externally by community are available as follows.
|
||||||
|
|
||||||
- UnicornPascal: Delphi/Free Pascal binding (by Stievie).
|
- UnicornPascal: Delphi/Free Pascal binding (by Stievie).
|
||||||
https://github.com/stievie/UnicornPascal
|
https://github.com/stievie/UnicornPascal
|
||||||
|
|
||||||
- Unicorn-Rs: Rust binding (by Sébastien Duquette)
|
- Unicorn-Rs: Rust binding (by Sébastien Duquette, unmaintained)
|
||||||
https://github.com/ekse/unicorn-rs
|
https://github.com/ekse/unicorn-rs
|
||||||
|
|
||||||
- UnicornEngine: Perl binding (by Vikas Naresh Kumar)
|
- UnicornEngine: Perl binding (by Vikas Naresh Kumar)
|
||||||
|
339
bindings/rust/COPYING
Normal file
339
bindings/rust/COPYING
Normal 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.
|
27
bindings/rust/Cargo.toml
Normal file
27
bindings/rust/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "unicorn"
|
||||||
|
version = "1.0.0"
|
||||||
|
authors = ["Lukas Seidel"]
|
||||||
|
documentation = ""
|
||||||
|
edition = "2018"
|
||||||
|
include = [
|
||||||
|
"/.gitmodules",
|
||||||
|
"/COPYING",
|
||||||
|
"/Cargo.toml",
|
||||||
|
"/README.md",
|
||||||
|
"/src/*",
|
||||||
|
]
|
||||||
|
license = "GPL-2.0"
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/unicorn-engine/unicorn/"
|
||||||
|
description = "Rust bindings for the Unicorn emulator with utility functions"
|
||||||
|
build = "build.rs"
|
||||||
|
links = "unicorn"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitflags = "1.0"
|
||||||
|
libc = "0.2"
|
||||||
|
capstone="0.6.0"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
build-helper = "0.1"
|
45
bindings/rust/README.md
Normal file
45
bindings/rust/README.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# unicorn-rs
|
||||||
|
|
||||||
|
Rust bindings for the [Unicorn](http://www.unicorn-engine.org/) emulator with utility functions.
|
||||||
|
|
||||||
|
An extended version for fuzzing with AFL++ support can be found in https://github.com/aflplusplus/unicornafl.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use unicorn::RegisterARM;
|
||||||
|
use unicorn::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page");
|
||||||
|
emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions");
|
||||||
|
|
||||||
|
emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0");
|
||||||
|
emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5");
|
||||||
|
|
||||||
|
let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000);
|
||||||
|
assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100));
|
||||||
|
assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Further sample code can be found in ```tests/unicorn.rs```.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This project has been tested on Linux, OS X and Windows.
|
||||||
|
|
||||||
|
To use unicorn-rs, simply add it as a dependency to the Cargo.toml of your program.
|
||||||
|
|
||||||
|
```
|
||||||
|
[dependencies]
|
||||||
|
unicorn = { path = "/path/to/bindings/rust", version="1.0.0" }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
These bindings are based on Sébastien Duquette's (@ekse) [unicorn-rs](https://github.com/unicorn-rs/unicorn-rs).
|
||||||
|
We picked up the project, as it is no longer maintained.
|
||||||
|
Thanks to all contributers.
|
||||||
|
|
20
bindings/rust/build.rs
Normal file
20
bindings/rust/build.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use std::{env, process::Command};
|
||||||
|
|
||||||
|
use build_helper::rustc::{link_lib, link_search};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("cargo:rerun-if-changed=unicorn");
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let unicorn = "libunicorn.a";
|
||||||
|
let _ = Command::new("cp")
|
||||||
|
.current_dir("../..")
|
||||||
|
.arg(&unicorn)
|
||||||
|
.arg(&out_dir)
|
||||||
|
.status()
|
||||||
|
.unwrap();
|
||||||
|
link_search(
|
||||||
|
Some(build_helper::SearchKind::Native),
|
||||||
|
build_helper::out_dir(),
|
||||||
|
);
|
||||||
|
link_lib(Some(build_helper::LibKind::Static), "unicorn");
|
||||||
|
}
|
146
bindings/rust/src/arm.rs
Normal file
146
bindings/rust/src/arm.rs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum RegisterARM {
|
||||||
|
// ARM registers
|
||||||
|
INVALID = 0,
|
||||||
|
APSR = 1,
|
||||||
|
APSR_NZCV = 2,
|
||||||
|
CPSR = 3,
|
||||||
|
FPEXC = 4,
|
||||||
|
FPINST = 5,
|
||||||
|
FPSCR = 6,
|
||||||
|
FPSCR_NZCV = 7,
|
||||||
|
FPSID = 8,
|
||||||
|
ITSTATE = 9,
|
||||||
|
LR = 10,
|
||||||
|
PC = 11,
|
||||||
|
SP = 12,
|
||||||
|
SPSR = 13,
|
||||||
|
D0 = 14,
|
||||||
|
D1 = 15,
|
||||||
|
D2 = 16,
|
||||||
|
D3 = 17,
|
||||||
|
D4 = 18,
|
||||||
|
D5 = 19,
|
||||||
|
D6 = 20,
|
||||||
|
D7 = 21,
|
||||||
|
D8 = 22,
|
||||||
|
D9 = 23,
|
||||||
|
D10 = 24,
|
||||||
|
D11 = 25,
|
||||||
|
D12 = 26,
|
||||||
|
D13 = 27,
|
||||||
|
D14 = 28,
|
||||||
|
D15 = 29,
|
||||||
|
D16 = 30,
|
||||||
|
D17 = 31,
|
||||||
|
D18 = 32,
|
||||||
|
D19 = 33,
|
||||||
|
D20 = 34,
|
||||||
|
D21 = 35,
|
||||||
|
D22 = 36,
|
||||||
|
D23 = 37,
|
||||||
|
D24 = 38,
|
||||||
|
D25 = 39,
|
||||||
|
D26 = 40,
|
||||||
|
D27 = 41,
|
||||||
|
D28 = 42,
|
||||||
|
D29 = 43,
|
||||||
|
D30 = 44,
|
||||||
|
D31 = 45,
|
||||||
|
FPINST2 = 46,
|
||||||
|
MVFR0 = 47,
|
||||||
|
MVFR1 = 48,
|
||||||
|
MVFR2 = 49,
|
||||||
|
Q0 = 50,
|
||||||
|
Q1 = 51,
|
||||||
|
Q2 = 52,
|
||||||
|
Q3 = 53,
|
||||||
|
Q4 = 54,
|
||||||
|
Q5 = 55,
|
||||||
|
Q6 = 56,
|
||||||
|
Q7 = 57,
|
||||||
|
Q8 = 58,
|
||||||
|
Q9 = 59,
|
||||||
|
Q10 = 60,
|
||||||
|
Q11 = 61,
|
||||||
|
Q12 = 62,
|
||||||
|
Q13 = 63,
|
||||||
|
Q14 = 64,
|
||||||
|
Q15 = 65,
|
||||||
|
R0 = 66,
|
||||||
|
R1 = 67,
|
||||||
|
R2 = 68,
|
||||||
|
R3 = 69,
|
||||||
|
R4 = 70,
|
||||||
|
R5 = 71,
|
||||||
|
R6 = 72,
|
||||||
|
R7 = 73,
|
||||||
|
R8 = 74,
|
||||||
|
R9 = 75,
|
||||||
|
R10 = 76,
|
||||||
|
R11 = 77,
|
||||||
|
R12 = 78,
|
||||||
|
S0 = 79,
|
||||||
|
S1 = 80,
|
||||||
|
S2 = 81,
|
||||||
|
S3 = 82,
|
||||||
|
S4 = 83,
|
||||||
|
S5 = 84,
|
||||||
|
S6 = 85,
|
||||||
|
S7 = 86,
|
||||||
|
S8 = 87,
|
||||||
|
S9 = 88,
|
||||||
|
S10 = 89,
|
||||||
|
S11 = 90,
|
||||||
|
S12 = 91,
|
||||||
|
S13 = 92,
|
||||||
|
S14 = 93,
|
||||||
|
S15 = 94,
|
||||||
|
S16 = 95,
|
||||||
|
S17 = 96,
|
||||||
|
S18 = 97,
|
||||||
|
S19 = 98,
|
||||||
|
S20 = 99,
|
||||||
|
S21 = 100,
|
||||||
|
S22 = 101,
|
||||||
|
S23 = 102,
|
||||||
|
S24 = 103,
|
||||||
|
S25 = 104,
|
||||||
|
S26 = 105,
|
||||||
|
S27 = 106,
|
||||||
|
S28 = 107,
|
||||||
|
S29 = 108,
|
||||||
|
S30 = 109,
|
||||||
|
S31 = 110,
|
||||||
|
C1_C0_2 = 111,
|
||||||
|
C13_C0_2 = 112,
|
||||||
|
C13_C0_3 = 113,
|
||||||
|
IPSR = 114,
|
||||||
|
MSP = 115,
|
||||||
|
PSP = 116,
|
||||||
|
CONTROL = 117,
|
||||||
|
XPSR = 118,
|
||||||
|
ENDING = 119,
|
||||||
|
// alias registers
|
||||||
|
// (assoc) R13 = 12,
|
||||||
|
// (assoc) R14 = 10,
|
||||||
|
// (assoc) R15 = 11,
|
||||||
|
// (assoc) SB = 75,
|
||||||
|
// (assoc) SL = 76,
|
||||||
|
// (assoc) FP = 77,
|
||||||
|
// (assoc) IP = 78,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterARM {
|
||||||
|
pub const R13: RegisterARM = RegisterARM::SP;
|
||||||
|
pub const R14: RegisterARM = RegisterARM::LR;
|
||||||
|
pub const R15: RegisterARM = RegisterARM::PC;
|
||||||
|
pub const SB: RegisterARM = RegisterARM::R9;
|
||||||
|
pub const SL: RegisterARM = RegisterARM::R10;
|
||||||
|
pub const FP: RegisterARM = RegisterARM::R11;
|
||||||
|
pub const IP: RegisterARM = RegisterARM::R12;
|
||||||
|
}
|
268
bindings/rust/src/arm64.rs
Normal file
268
bindings/rust/src/arm64.rs
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
// ARM64 registers
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum RegisterARM64 {
|
||||||
|
INVALID = 0,
|
||||||
|
FP = 1,
|
||||||
|
LR = 2,
|
||||||
|
NZCV = 3,
|
||||||
|
SP = 4,
|
||||||
|
WSP = 5,
|
||||||
|
WZR = 6,
|
||||||
|
XZR = 7,
|
||||||
|
B0 = 8,
|
||||||
|
B1 = 9,
|
||||||
|
B2 = 10,
|
||||||
|
B3 = 11,
|
||||||
|
B4 = 12,
|
||||||
|
B5 = 13,
|
||||||
|
B6 = 14,
|
||||||
|
B7 = 15,
|
||||||
|
B8 = 16,
|
||||||
|
B9 = 17,
|
||||||
|
B10 = 18,
|
||||||
|
B11 = 19,
|
||||||
|
B12 = 20,
|
||||||
|
B13 = 21,
|
||||||
|
B14 = 22,
|
||||||
|
B15 = 23,
|
||||||
|
B16 = 24,
|
||||||
|
B17 = 25,
|
||||||
|
B18 = 26,
|
||||||
|
B19 = 27,
|
||||||
|
B20 = 28,
|
||||||
|
B21 = 29,
|
||||||
|
B22 = 30,
|
||||||
|
B23 = 31,
|
||||||
|
B24 = 32,
|
||||||
|
B25 = 33,
|
||||||
|
B26 = 34,
|
||||||
|
B27 = 35,
|
||||||
|
B28 = 36,
|
||||||
|
B29 = 37,
|
||||||
|
B30 = 38,
|
||||||
|
B31 = 39,
|
||||||
|
D0 = 40,
|
||||||
|
D1 = 41,
|
||||||
|
D2 = 42,
|
||||||
|
D3 = 43,
|
||||||
|
D4 = 44,
|
||||||
|
D5 = 45,
|
||||||
|
D6 = 46,
|
||||||
|
D7 = 47,
|
||||||
|
D8 = 48,
|
||||||
|
D9 = 49,
|
||||||
|
D10 = 50,
|
||||||
|
D11 = 51,
|
||||||
|
D12 = 52,
|
||||||
|
D13 = 53,
|
||||||
|
D14 = 54,
|
||||||
|
D15 = 55,
|
||||||
|
D16 = 56,
|
||||||
|
D17 = 57,
|
||||||
|
D18 = 58,
|
||||||
|
D19 = 59,
|
||||||
|
D20 = 60,
|
||||||
|
D21 = 61,
|
||||||
|
D22 = 62,
|
||||||
|
D23 = 63,
|
||||||
|
D24 = 64,
|
||||||
|
D25 = 65,
|
||||||
|
D26 = 66,
|
||||||
|
D27 = 67,
|
||||||
|
D28 = 68,
|
||||||
|
D29 = 69,
|
||||||
|
D30 = 70,
|
||||||
|
D31 = 71,
|
||||||
|
H0 = 72,
|
||||||
|
H1 = 73,
|
||||||
|
H2 = 74,
|
||||||
|
H3 = 75,
|
||||||
|
H4 = 76,
|
||||||
|
H5 = 77,
|
||||||
|
H6 = 78,
|
||||||
|
H7 = 79,
|
||||||
|
H8 = 80,
|
||||||
|
H9 = 81,
|
||||||
|
H10 = 82,
|
||||||
|
H11 = 83,
|
||||||
|
H12 = 84,
|
||||||
|
H13 = 85,
|
||||||
|
H14 = 86,
|
||||||
|
H15 = 87,
|
||||||
|
H16 = 88,
|
||||||
|
H17 = 89,
|
||||||
|
H18 = 90,
|
||||||
|
H19 = 91,
|
||||||
|
H20 = 92,
|
||||||
|
H21 = 93,
|
||||||
|
H22 = 94,
|
||||||
|
H23 = 95,
|
||||||
|
H24 = 96,
|
||||||
|
H25 = 97,
|
||||||
|
H26 = 98,
|
||||||
|
H27 = 99,
|
||||||
|
H28 = 100,
|
||||||
|
H29 = 101,
|
||||||
|
H30 = 102,
|
||||||
|
H31 = 103,
|
||||||
|
Q0 = 104,
|
||||||
|
Q1 = 105,
|
||||||
|
Q2 = 106,
|
||||||
|
Q3 = 107,
|
||||||
|
Q4 = 108,
|
||||||
|
Q5 = 109,
|
||||||
|
Q6 = 110,
|
||||||
|
Q7 = 111,
|
||||||
|
Q8 = 112,
|
||||||
|
Q9 = 113,
|
||||||
|
Q10 = 114,
|
||||||
|
Q11 = 115,
|
||||||
|
Q12 = 116,
|
||||||
|
Q13 = 117,
|
||||||
|
Q14 = 118,
|
||||||
|
Q15 = 119,
|
||||||
|
Q16 = 120,
|
||||||
|
Q17 = 121,
|
||||||
|
Q18 = 122,
|
||||||
|
Q19 = 123,
|
||||||
|
Q20 = 124,
|
||||||
|
Q21 = 125,
|
||||||
|
Q22 = 126,
|
||||||
|
Q23 = 127,
|
||||||
|
Q24 = 128,
|
||||||
|
Q25 = 129,
|
||||||
|
Q26 = 130,
|
||||||
|
Q27 = 131,
|
||||||
|
Q28 = 132,
|
||||||
|
Q29 = 133,
|
||||||
|
Q30 = 134,
|
||||||
|
Q31 = 135,
|
||||||
|
S0 = 136,
|
||||||
|
S1 = 137,
|
||||||
|
S2 = 138,
|
||||||
|
S3 = 139,
|
||||||
|
S4 = 140,
|
||||||
|
S5 = 141,
|
||||||
|
S6 = 142,
|
||||||
|
S7 = 143,
|
||||||
|
S8 = 144,
|
||||||
|
S9 = 145,
|
||||||
|
S10 = 146,
|
||||||
|
S11 = 147,
|
||||||
|
S12 = 148,
|
||||||
|
S13 = 149,
|
||||||
|
S14 = 150,
|
||||||
|
S15 = 151,
|
||||||
|
S16 = 152,
|
||||||
|
S17 = 153,
|
||||||
|
S18 = 154,
|
||||||
|
S19 = 155,
|
||||||
|
S20 = 156,
|
||||||
|
S21 = 157,
|
||||||
|
S22 = 158,
|
||||||
|
S23 = 159,
|
||||||
|
S24 = 160,
|
||||||
|
S25 = 161,
|
||||||
|
S26 = 162,
|
||||||
|
S27 = 163,
|
||||||
|
S28 = 164,
|
||||||
|
S29 = 165,
|
||||||
|
S30 = 166,
|
||||||
|
S31 = 167,
|
||||||
|
W0 = 168,
|
||||||
|
W1 = 169,
|
||||||
|
W2 = 170,
|
||||||
|
W3 = 171,
|
||||||
|
W4 = 172,
|
||||||
|
W5 = 173,
|
||||||
|
W6 = 174,
|
||||||
|
W7 = 175,
|
||||||
|
W8 = 176,
|
||||||
|
W9 = 177,
|
||||||
|
W10 = 178,
|
||||||
|
W11 = 179,
|
||||||
|
W12 = 180,
|
||||||
|
W13 = 181,
|
||||||
|
W14 = 182,
|
||||||
|
W15 = 183,
|
||||||
|
W16 = 184,
|
||||||
|
W17 = 185,
|
||||||
|
W18 = 186,
|
||||||
|
W19 = 187,
|
||||||
|
W20 = 188,
|
||||||
|
W21 = 189,
|
||||||
|
W22 = 190,
|
||||||
|
W23 = 191,
|
||||||
|
W24 = 192,
|
||||||
|
W25 = 193,
|
||||||
|
W26 = 194,
|
||||||
|
W27 = 195,
|
||||||
|
W28 = 196,
|
||||||
|
W29 = 197,
|
||||||
|
W30 = 198,
|
||||||
|
X0 = 199,
|
||||||
|
X1 = 200,
|
||||||
|
X2 = 201,
|
||||||
|
X3 = 202,
|
||||||
|
X4 = 203,
|
||||||
|
X5 = 204,
|
||||||
|
X6 = 205,
|
||||||
|
X7 = 206,
|
||||||
|
X8 = 207,
|
||||||
|
X9 = 208,
|
||||||
|
X10 = 209,
|
||||||
|
X11 = 210,
|
||||||
|
X12 = 211,
|
||||||
|
X13 = 212,
|
||||||
|
X14 = 213,
|
||||||
|
X15 = 214,
|
||||||
|
IP1 = 215,
|
||||||
|
IP0 = 216,
|
||||||
|
X18 = 217,
|
||||||
|
X19 = 218,
|
||||||
|
X20 = 219,
|
||||||
|
X21 = 220,
|
||||||
|
X22 = 221,
|
||||||
|
X23 = 222,
|
||||||
|
X24 = 223,
|
||||||
|
X25 = 224,
|
||||||
|
X26 = 225,
|
||||||
|
X27 = 226,
|
||||||
|
X28 = 227,
|
||||||
|
V0 = 228,
|
||||||
|
V1 = 229,
|
||||||
|
V2 = 230,
|
||||||
|
V3 = 231,
|
||||||
|
V4 = 232,
|
||||||
|
V5 = 233,
|
||||||
|
V6 = 234,
|
||||||
|
V7 = 235,
|
||||||
|
V8 = 236,
|
||||||
|
V9 = 237,
|
||||||
|
V10 = 238,
|
||||||
|
V11 = 239,
|
||||||
|
V12 = 240,
|
||||||
|
V13 = 241,
|
||||||
|
V14 = 242,
|
||||||
|
V15 = 243,
|
||||||
|
V16 = 244,
|
||||||
|
V17 = 245,
|
||||||
|
V18 = 246,
|
||||||
|
V19 = 247,
|
||||||
|
V20 = 248,
|
||||||
|
V21 = 249,
|
||||||
|
V22 = 250,
|
||||||
|
V23 = 251,
|
||||||
|
V24 = 252,
|
||||||
|
V25 = 253,
|
||||||
|
V26 = 254,
|
||||||
|
V27 = 255,
|
||||||
|
V28 = 256,
|
||||||
|
V29 = 257,
|
||||||
|
V30 = 258,
|
||||||
|
V31 = 259,
|
||||||
|
|
||||||
|
// pseudo registers
|
||||||
|
PC = 260,
|
||||||
|
}
|
230
bindings/rust/src/ffi.rs
Normal file
230
bindings/rust/src/ffi.rs
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use super::unicorn_const::*;
|
||||||
|
use libc::{c_char, c_int};
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
pub type uc_handle = *mut c_void;
|
||||||
|
pub type uc_hook = *mut c_void;
|
||||||
|
pub type uc_context = libc::size_t;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub fn uc_version(major: *mut u32, minor: *mut u32) -> u32;
|
||||||
|
pub fn uc_arch_supported(arch: Arch) -> bool;
|
||||||
|
pub fn uc_open(arch: Arch, mode: Mode, engine: *mut uc_handle) -> uc_error;
|
||||||
|
pub fn uc_close(engine: uc_handle) -> uc_error;
|
||||||
|
pub fn uc_context_free(mem: uc_context) -> uc_error;
|
||||||
|
pub fn uc_errno(engine: uc_handle) -> uc_error;
|
||||||
|
pub fn uc_strerror(error_code: uc_error) -> *const c_char;
|
||||||
|
pub fn uc_reg_write(engine: uc_handle, regid: c_int, value: *const c_void) -> uc_error;
|
||||||
|
pub fn uc_reg_read(engine: uc_handle, regid: c_int, value: *mut c_void) -> uc_error;
|
||||||
|
pub fn uc_mem_write(
|
||||||
|
engine: uc_handle,
|
||||||
|
address: u64,
|
||||||
|
bytes: *const u8,
|
||||||
|
size: libc::size_t,
|
||||||
|
) -> uc_error;
|
||||||
|
pub fn uc_mem_read(
|
||||||
|
engine: uc_handle,
|
||||||
|
address: u64,
|
||||||
|
bytes: *mut u8,
|
||||||
|
size: libc::size_t,
|
||||||
|
) -> uc_error;
|
||||||
|
pub fn uc_mem_map(engine: uc_handle, address: u64, size: libc::size_t, perms: u32) -> uc_error;
|
||||||
|
pub fn uc_mem_map_ptr(
|
||||||
|
engine: uc_handle,
|
||||||
|
address: u64,
|
||||||
|
size: libc::size_t,
|
||||||
|
perms: u32,
|
||||||
|
ptr: *mut c_void,
|
||||||
|
) -> uc_error;
|
||||||
|
pub fn uc_mem_unmap(engine: uc_handle, address: u64, size: libc::size_t) -> uc_error;
|
||||||
|
pub fn uc_mem_protect(
|
||||||
|
engine: uc_handle,
|
||||||
|
address: u64,
|
||||||
|
size: libc::size_t,
|
||||||
|
perms: u32,
|
||||||
|
) -> uc_error;
|
||||||
|
pub fn uc_mem_regions(
|
||||||
|
engine: uc_handle,
|
||||||
|
regions: *const *const MemRegion,
|
||||||
|
count: *mut u32,
|
||||||
|
) -> uc_error;
|
||||||
|
pub fn uc_emu_start(
|
||||||
|
engine: uc_handle,
|
||||||
|
begin: u64,
|
||||||
|
until: u64,
|
||||||
|
timeout: u64,
|
||||||
|
count: libc::size_t,
|
||||||
|
) -> uc_error;
|
||||||
|
pub fn uc_emu_stop(engine: uc_handle) -> uc_error;
|
||||||
|
pub fn uc_hook_add(
|
||||||
|
engine: uc_handle,
|
||||||
|
hook: *mut uc_hook,
|
||||||
|
hook_type: HookType,
|
||||||
|
callback: *mut c_void,
|
||||||
|
user_data: *mut c_void,
|
||||||
|
begin: u64,
|
||||||
|
end: u64,
|
||||||
|
...
|
||||||
|
) -> uc_error;
|
||||||
|
pub fn uc_hook_del(engine: uc_handle, hook: uc_hook) -> uc_error;
|
||||||
|
pub fn uc_query(engine: uc_handle, query_type: Query, result: *mut libc::size_t) -> uc_error;
|
||||||
|
pub fn uc_context_alloc(engine: uc_handle, context: *mut uc_context) -> uc_error;
|
||||||
|
pub fn uc_context_save(engine: uc_handle, context: uc_context) -> uc_error;
|
||||||
|
pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CodeHook {
|
||||||
|
pub unicorn: *mut crate::UnicornInner,
|
||||||
|
pub callback: Box<dyn FnMut(crate::UnicornHandle, u64, u32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BlockHook {
|
||||||
|
pub unicorn: *mut crate::UnicornInner,
|
||||||
|
pub callback: Box<dyn FnMut(crate::UnicornHandle, u64, u32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemHook {
|
||||||
|
pub unicorn: *mut crate::UnicornInner,
|
||||||
|
pub callback: Box<dyn FnMut(crate::UnicornHandle, MemType, u64, usize, i64)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InterruptHook {
|
||||||
|
pub unicorn: *mut crate::UnicornInner,
|
||||||
|
pub callback: Box<dyn FnMut(crate::UnicornHandle, u32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InstructionInHook {
|
||||||
|
pub unicorn: *mut crate::UnicornInner,
|
||||||
|
pub callback: Box<dyn FnMut(crate::UnicornHandle, u32, usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InstructionOutHook {
|
||||||
|
pub unicorn: *mut crate::UnicornInner,
|
||||||
|
pub callback: Box<dyn FnMut(crate::UnicornHandle, u32, usize, u32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InstructionSysHook {
|
||||||
|
pub unicorn: *mut crate::UnicornInner,
|
||||||
|
pub callback: Box<dyn FnMut(crate::UnicornHandle)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn code_hook_proxy(
|
||||||
|
uc: uc_handle,
|
||||||
|
address: u64,
|
||||||
|
size: u32,
|
||||||
|
user_data: *mut CodeHook,
|
||||||
|
) {
|
||||||
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
|
assert_eq!(uc, unicorn.uc);
|
||||||
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn block_hook_proxy(
|
||||||
|
uc: uc_handle,
|
||||||
|
address: u64,
|
||||||
|
size: u32,
|
||||||
|
user_data: *mut BlockHook,
|
||||||
|
) {
|
||||||
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
|
assert_eq!(uc, unicorn.uc);
|
||||||
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
address,
|
||||||
|
size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn mem_hook_proxy(
|
||||||
|
uc: uc_handle,
|
||||||
|
mem_type: MemType,
|
||||||
|
address: u64,
|
||||||
|
size: u32,
|
||||||
|
value: i64,
|
||||||
|
user_data: *mut MemHook,
|
||||||
|
) {
|
||||||
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
|
assert_eq!(uc, unicorn.uc);
|
||||||
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
mem_type,
|
||||||
|
address,
|
||||||
|
size as usize,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn intr_hook_proxy(uc: uc_handle, value: u32, user_data: *mut InterruptHook) {
|
||||||
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
|
assert_eq!(uc, unicorn.uc);
|
||||||
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn insn_in_hook_proxy(
|
||||||
|
uc: uc_handle,
|
||||||
|
port: u32,
|
||||||
|
size: usize,
|
||||||
|
user_data: *mut InstructionInHook,
|
||||||
|
) {
|
||||||
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
|
assert_eq!(uc, unicorn.uc);
|
||||||
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
port,
|
||||||
|
size,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn insn_out_hook_proxy(
|
||||||
|
uc: uc_handle,
|
||||||
|
port: u32,
|
||||||
|
size: usize,
|
||||||
|
value: u32,
|
||||||
|
user_data: *mut InstructionOutHook,
|
||||||
|
) {
|
||||||
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
|
assert_eq!(uc, unicorn.uc);
|
||||||
|
callback(
|
||||||
|
crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
},
|
||||||
|
port,
|
||||||
|
size,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn insn_sys_hook_proxy(uc: uc_handle, user_data: *mut InstructionSysHook) {
|
||||||
|
let unicorn = unsafe { &mut *(*user_data).unicorn };
|
||||||
|
let callback = &mut unsafe { &mut *(*user_data).callback };
|
||||||
|
assert_eq!(uc, unicorn.uc);
|
||||||
|
callback(crate::UnicornHandle {
|
||||||
|
inner: unsafe { Pin::new_unchecked(unicorn) },
|
||||||
|
});
|
||||||
|
}
|
784
bindings/rust/src/lib.rs
Normal file
784
bindings/rust/src/lib.rs
Normal file
@ -0,0 +1,784 @@
|
|||||||
|
//! Bindings for the Unicorn emulator.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! # Example use
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//!
|
||||||
|
//! use unicorn::RegisterARM;
|
||||||
|
//! use unicorn::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE};
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23
|
||||||
|
//!
|
||||||
|
//! let mut unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance");
|
||||||
|
//! let mut emu = unicorn.borrow();
|
||||||
|
//! emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page");
|
||||||
|
//! emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions");
|
||||||
|
//!
|
||||||
|
//! emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0");
|
||||||
|
//! emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5");
|
||||||
|
//!
|
||||||
|
//! let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000);
|
||||||
|
//! assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100));
|
||||||
|
//! assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337));
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
|
||||||
|
mod ffi;
|
||||||
|
pub mod unicorn_const;
|
||||||
|
|
||||||
|
mod arm;
|
||||||
|
mod arm64;
|
||||||
|
mod m68k;
|
||||||
|
mod mips;
|
||||||
|
mod ppc;
|
||||||
|
mod sparc;
|
||||||
|
mod x86;
|
||||||
|
pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, sparc::*, x86::*};
|
||||||
|
|
||||||
|
use ffi::uc_handle;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::marker::PhantomPinned;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use unicorn_const::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Context {
|
||||||
|
context: ffi::uc_context,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Context { context: 0 }
|
||||||
|
}
|
||||||
|
pub fn is_initialized(&self) -> bool {
|
||||||
|
self.context != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Context {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { ffi::uc_context_free(self.context) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// A Unicorn emulator instance.
|
||||||
|
pub struct Unicorn {
|
||||||
|
inner: Pin<Box<UnicornInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// Handle used to safely access exposed functions and data of a Unicorn instance.
|
||||||
|
pub struct UnicornHandle<'a> {
|
||||||
|
inner: Pin<&'a mut UnicornInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal Management struct
|
||||||
|
pub struct UnicornInner {
|
||||||
|
pub uc: uc_handle,
|
||||||
|
pub arch: Arch,
|
||||||
|
pub code_hooks: HashMap<*mut libc::c_void, Box<ffi::CodeHook>>,
|
||||||
|
pub block_hooks: HashMap<*mut libc::c_void, Box<ffi::BlockHook>>,
|
||||||
|
pub mem_hooks: HashMap<*mut libc::c_void, Box<ffi::MemHook>>,
|
||||||
|
pub intr_hooks: HashMap<*mut libc::c_void, Box<ffi::InterruptHook>>,
|
||||||
|
pub insn_in_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionInHook>>,
|
||||||
|
pub insn_out_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionOutHook>>,
|
||||||
|
pub insn_sys_hooks: HashMap<*mut libc::c_void, Box<ffi::InstructionSysHook>>,
|
||||||
|
_pin: PhantomPinned,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unicorn {
|
||||||
|
/// Create a new instance of the unicorn engine for the specified architecture
|
||||||
|
/// and hardware mode.
|
||||||
|
pub fn new(arch: Arch, mode: Mode) -> Result<Unicorn, uc_error> {
|
||||||
|
let mut handle = std::ptr::null_mut();
|
||||||
|
let err = unsafe { ffi::uc_open(arch, mode, &mut handle) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(Unicorn {
|
||||||
|
inner: Box::pin(UnicornInner {
|
||||||
|
uc: handle,
|
||||||
|
arch: arch,
|
||||||
|
code_hooks: HashMap::new(),
|
||||||
|
block_hooks: HashMap::new(),
|
||||||
|
mem_hooks: HashMap::new(),
|
||||||
|
intr_hooks: HashMap::new(),
|
||||||
|
insn_in_hooks: HashMap::new(),
|
||||||
|
insn_out_hooks: HashMap::new(),
|
||||||
|
insn_sys_hooks: HashMap::new(),
|
||||||
|
_pin: std::marker::PhantomPinned,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a> {
|
||||||
|
UnicornHandle {
|
||||||
|
inner: self.inner.as_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Unicorn {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { ffi::uc_close(self.inner.uc) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for UnicornInner {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(formatter, "Unicorn {{ uc: {:p} }}", self.uc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> UnicornHandle<'a> {
|
||||||
|
|
||||||
|
/// Return the architecture of the current emulator.
|
||||||
|
pub fn get_arch(&self) -> Arch {
|
||||||
|
self.inner.arch
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a vector with the memory regions that are mapped in the emulator.
|
||||||
|
pub fn mem_regions(&self) -> Result<Vec<MemRegion>, uc_error> {
|
||||||
|
let mut nb_regions: u32 = 0;
|
||||||
|
let mut p_regions: *const MemRegion = std::ptr::null_mut();
|
||||||
|
let err = unsafe { ffi::uc_mem_regions(self.inner.uc, &mut p_regions, &mut nb_regions) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
let mut regions = Vec::new();
|
||||||
|
for i in 0..nb_regions {
|
||||||
|
regions.push(unsafe { std::mem::transmute_copy(&*p_regions.offset(i as isize)) });
|
||||||
|
}
|
||||||
|
unsafe { libc::free(p_regions as _) };
|
||||||
|
Ok(regions)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a range of bytes from memory at the specified address.
|
||||||
|
pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_mem_read(self.inner.uc, address, buf.as_mut_ptr(), buf.len()) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a range of bytes from memory at the specified address as vector.
|
||||||
|
pub fn mem_read_as_vec(&self, address: u64, size: usize) -> Result<Vec<u8>, uc_error> {
|
||||||
|
let mut buf = vec![0; size];
|
||||||
|
let err = unsafe { ffi::uc_mem_read(self.inner.uc, address, buf.as_mut_ptr(), size) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(buf)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mem_write(&mut self, address: u64, bytes: &[u8]) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_mem_write(self.inner.uc, address, bytes.as_ptr(), bytes.len()) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map an existing memory region in the emulator at the specified address.
|
||||||
|
///
|
||||||
|
/// This function is marked unsafe because it is the responsibility of the caller to
|
||||||
|
/// ensure that `size` matches the size of the passed buffer, an invalid `size` value will
|
||||||
|
/// likely cause a crash in unicorn.
|
||||||
|
///
|
||||||
|
/// `address` must be aligned to 4kb or this will return `Error::ARG`.
|
||||||
|
///
|
||||||
|
/// `size` must be a multiple of 4kb or this will return `Error::ARG`.
|
||||||
|
///
|
||||||
|
/// `ptr` is a pointer to the provided memory region that will be used by the emulator.
|
||||||
|
pub fn mem_map_ptr(
|
||||||
|
&mut self,
|
||||||
|
address: u64,
|
||||||
|
size: usize,
|
||||||
|
perms: Permission,
|
||||||
|
ptr: *mut c_void,
|
||||||
|
) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_mem_map_ptr(self.inner.uc, address, size, perms.bits(), ptr) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map a memory region in the emulator at the specified address.
|
||||||
|
///
|
||||||
|
/// `address` must be aligned to 4kb or this will return `Error::ARG`.
|
||||||
|
/// `size` must be a multiple of 4kb or this will return `Error::ARG`.
|
||||||
|
pub fn mem_map(
|
||||||
|
&mut self,
|
||||||
|
address: u64,
|
||||||
|
size: libc::size_t,
|
||||||
|
perms: Permission,
|
||||||
|
) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_mem_map(self.inner.uc, address, size, perms.bits()) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmap a memory region.
|
||||||
|
///
|
||||||
|
/// `address` must be aligned to 4kb or this will return `Error::ARG`.
|
||||||
|
/// `size` must be a multiple of 4kb or this will return `Error::ARG`.
|
||||||
|
pub fn mem_unmap(&mut self, address: u64, size: libc::size_t) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_mem_unmap(self.inner.uc, address, size) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the memory permissions for an existing memory region.
|
||||||
|
///
|
||||||
|
/// `address` must be aligned to 4kb or this will return `Error::ARG`.
|
||||||
|
/// `size` must be a multiple of 4kb or this will return `Error::ARG`.
|
||||||
|
pub fn mem_protect(
|
||||||
|
&mut self,
|
||||||
|
address: u64,
|
||||||
|
size: libc::size_t,
|
||||||
|
perms: Permission,
|
||||||
|
) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_mem_protect(self.inner.uc, address, size, perms.bits()) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write an unsigned value from a register.
|
||||||
|
pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> {
|
||||||
|
let err =
|
||||||
|
unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), &value as *const _ as _) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write variable sized values into registers.
|
||||||
|
///
|
||||||
|
/// The user has to make sure that the buffer length matches the register size.
|
||||||
|
/// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
|
||||||
|
pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: Box<[u8]>) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), value.as_ptr() as _) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read an unsigned value from a register.
|
||||||
|
///
|
||||||
|
/// Not to be used with registers larger than 64 bit.
|
||||||
|
pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> {
|
||||||
|
let mut value: u64 = 0;
|
||||||
|
let err =
|
||||||
|
unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut u64 as _) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(value)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read 128, 256 or 512 bit register value into heap allocated byte array.
|
||||||
|
///
|
||||||
|
/// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)).
|
||||||
|
pub fn reg_read_long<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, uc_error> {
|
||||||
|
let err: uc_error;
|
||||||
|
let boxed: Box<[u8]>;
|
||||||
|
let mut value: Vec<u8>;
|
||||||
|
let curr_reg_id = regid.into();
|
||||||
|
let curr_arch = self.get_arch();
|
||||||
|
|
||||||
|
if curr_arch == Arch::X86 {
|
||||||
|
if curr_reg_id >= x86::RegisterX86::XMM0 as i32
|
||||||
|
&& curr_reg_id <= x86::RegisterX86::XMM31 as i32
|
||||||
|
{
|
||||||
|
value = vec![0; 16];
|
||||||
|
} else if curr_reg_id >= x86::RegisterX86::YMM0 as i32
|
||||||
|
&& curr_reg_id <= x86::RegisterX86::YMM31 as i32
|
||||||
|
{
|
||||||
|
value = vec![0; 32];
|
||||||
|
} else if curr_reg_id >= x86::RegisterX86::ZMM0 as i32
|
||||||
|
&& curr_reg_id <= x86::RegisterX86::ZMM31 as i32
|
||||||
|
{
|
||||||
|
value = vec![0; 64];
|
||||||
|
} else if curr_reg_id == x86::RegisterX86::GDTR as i32
|
||||||
|
|| curr_reg_id == x86::RegisterX86::IDTR as i32
|
||||||
|
{
|
||||||
|
value = vec![0; 10]; // 64 bit base address in IA-32e mode
|
||||||
|
} else {
|
||||||
|
return Err(uc_error::ARG);
|
||||||
|
}
|
||||||
|
} else if curr_arch == Arch::ARM64 {
|
||||||
|
if (curr_reg_id >= arm64::RegisterARM64::Q0 as i32
|
||||||
|
&& curr_reg_id <= arm64::RegisterARM64::Q31 as i32)
|
||||||
|
|| (curr_reg_id >= arm64::RegisterARM64::V0 as i32
|
||||||
|
&& curr_reg_id <= arm64::RegisterARM64::V31 as i32)
|
||||||
|
{
|
||||||
|
value = vec![0; 16];
|
||||||
|
} else {
|
||||||
|
return Err(uc_error::ARG);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(uc_error::ARCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = unsafe { ffi::uc_reg_read(self.inner.uc, curr_reg_id, value.as_mut_ptr() as _) };
|
||||||
|
|
||||||
|
if err == uc_error::OK {
|
||||||
|
boxed = value.into_boxed_slice();
|
||||||
|
Ok(boxed)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a signed 32-bit value from a register.
|
||||||
|
pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> {
|
||||||
|
let mut value: i32 = 0;
|
||||||
|
let err =
|
||||||
|
unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut i32 as _) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(value)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a code hook.
|
||||||
|
pub fn add_code_hook<F: 'static>(
|
||||||
|
&mut self,
|
||||||
|
begin: u64,
|
||||||
|
end: u64,
|
||||||
|
callback: F,
|
||||||
|
) -> Result<ffi::uc_hook, uc_error>
|
||||||
|
where
|
||||||
|
F: FnMut(UnicornHandle, u64, u32),
|
||||||
|
{
|
||||||
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
|
let mut user_data = Box::new(ffi::CodeHook {
|
||||||
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
|
callback: Box::new(callback),
|
||||||
|
});
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
ffi::uc_hook_add(
|
||||||
|
self.inner.uc,
|
||||||
|
&mut hook_ptr,
|
||||||
|
HookType::CODE,
|
||||||
|
ffi::code_hook_proxy as _,
|
||||||
|
user_data.as_mut() as *mut _ as _,
|
||||||
|
begin,
|
||||||
|
end,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if err == uc_error::OK {
|
||||||
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.code_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
|
Ok(hook_ptr)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a block hook.
|
||||||
|
pub fn add_block_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
|
||||||
|
where
|
||||||
|
F: FnMut(UnicornHandle, u64, u32),
|
||||||
|
{
|
||||||
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
|
let mut user_data = Box::new(ffi::BlockHook {
|
||||||
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
|
callback: Box::new(callback),
|
||||||
|
});
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
ffi::uc_hook_add(
|
||||||
|
self.inner.uc,
|
||||||
|
&mut hook_ptr,
|
||||||
|
HookType::BLOCK,
|
||||||
|
ffi::block_hook_proxy as _,
|
||||||
|
user_data.as_mut() as *mut _ as _,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if err == uc_error::OK {
|
||||||
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.block_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
|
Ok(hook_ptr)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a memory hook.
|
||||||
|
pub fn add_mem_hook<F: 'static>(
|
||||||
|
&mut self,
|
||||||
|
hook_type: HookType,
|
||||||
|
begin: u64,
|
||||||
|
end: u64,
|
||||||
|
callback: F,
|
||||||
|
) -> Result<ffi::uc_hook, uc_error>
|
||||||
|
where
|
||||||
|
F: FnMut(UnicornHandle, MemType, u64, usize, i64),
|
||||||
|
{
|
||||||
|
if !(HookType::MEM_ALL | HookType::MEM_READ_AFTER).contains(hook_type) {
|
||||||
|
return Err(uc_error::ARG);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
|
let mut user_data = Box::new(ffi::MemHook {
|
||||||
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
|
callback: Box::new(callback),
|
||||||
|
});
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
ffi::uc_hook_add(
|
||||||
|
self.inner.uc,
|
||||||
|
&mut hook_ptr,
|
||||||
|
hook_type,
|
||||||
|
ffi::mem_hook_proxy as _,
|
||||||
|
user_data.as_mut() as *mut _ as _,
|
||||||
|
begin,
|
||||||
|
end,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if err == uc_error::OK {
|
||||||
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.mem_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
|
Ok(hook_ptr)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an interrupt hook.
|
||||||
|
pub fn add_intr_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
|
||||||
|
where
|
||||||
|
F: FnMut(UnicornHandle, u32),
|
||||||
|
{
|
||||||
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
|
let mut user_data = Box::new(ffi::InterruptHook {
|
||||||
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
|
callback: Box::new(callback),
|
||||||
|
});
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
ffi::uc_hook_add(
|
||||||
|
self.inner.uc,
|
||||||
|
&mut hook_ptr,
|
||||||
|
HookType::INTR,
|
||||||
|
ffi::intr_hook_proxy as _,
|
||||||
|
user_data.as_mut() as *mut _ as _,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if err == uc_error::OK {
|
||||||
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.intr_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
|
Ok(hook_ptr)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add hook for x86 IN instruction.
|
||||||
|
pub fn add_insn_in_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
|
||||||
|
where
|
||||||
|
F: FnMut(UnicornHandle, u32, usize),
|
||||||
|
{
|
||||||
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
|
let mut user_data = Box::new(ffi::InstructionInHook {
|
||||||
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
|
callback: Box::new(callback),
|
||||||
|
});
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
ffi::uc_hook_add(
|
||||||
|
self.inner.uc,
|
||||||
|
&mut hook_ptr,
|
||||||
|
HookType::INSN,
|
||||||
|
ffi::insn_in_hook_proxy as _,
|
||||||
|
user_data.as_mut() as *mut _ as _,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
x86::InsnX86::IN,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if err == uc_error::OK {
|
||||||
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.insn_in_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
|
Ok(hook_ptr)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add hook for x86 OUT instruction.
|
||||||
|
pub fn add_insn_out_hook<F: 'static>(&mut self, callback: F) -> Result<ffi::uc_hook, uc_error>
|
||||||
|
where
|
||||||
|
F: FnMut(UnicornHandle, u32, usize, u32),
|
||||||
|
{
|
||||||
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
|
let mut user_data = Box::new(ffi::InstructionOutHook {
|
||||||
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
|
callback: Box::new(callback),
|
||||||
|
});
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
ffi::uc_hook_add(
|
||||||
|
self.inner.uc,
|
||||||
|
&mut hook_ptr,
|
||||||
|
HookType::INSN,
|
||||||
|
ffi::insn_out_hook_proxy as _,
|
||||||
|
user_data.as_mut() as *mut _ as _,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
x86::InsnX86::OUT,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if err == uc_error::OK {
|
||||||
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.insn_out_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
|
Ok(hook_ptr)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add hook for x86 SYSCALL or SYSENTER.
|
||||||
|
pub fn add_insn_sys_hook<F: 'static>(
|
||||||
|
&mut self,
|
||||||
|
insn_type: x86::InsnSysX86,
|
||||||
|
begin: u64,
|
||||||
|
end: u64,
|
||||||
|
callback: F,
|
||||||
|
) -> Result<ffi::uc_hook, uc_error>
|
||||||
|
where
|
||||||
|
F: FnMut(UnicornHandle),
|
||||||
|
{
|
||||||
|
let mut hook_ptr = std::ptr::null_mut();
|
||||||
|
let mut user_data = Box::new(ffi::InstructionSysHook {
|
||||||
|
unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _,
|
||||||
|
callback: Box::new(callback),
|
||||||
|
});
|
||||||
|
|
||||||
|
let err = unsafe {
|
||||||
|
ffi::uc_hook_add(
|
||||||
|
self.inner.uc,
|
||||||
|
&mut hook_ptr,
|
||||||
|
HookType::INSN,
|
||||||
|
ffi::insn_sys_hook_proxy as _,
|
||||||
|
user_data.as_mut() as *mut _ as _,
|
||||||
|
begin,
|
||||||
|
end,
|
||||||
|
insn_type,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if err == uc_error::OK {
|
||||||
|
unsafe { self.inner.as_mut().get_unchecked_mut() }
|
||||||
|
.insn_sys_hooks
|
||||||
|
.insert(hook_ptr, user_data);
|
||||||
|
Ok(hook_ptr)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a hook.
|
||||||
|
///
|
||||||
|
/// `hook` is the value returned by `add_*_hook` functions.
|
||||||
|
pub fn remove_hook(&mut self, hook: ffi::uc_hook) -> Result<(), uc_error> {
|
||||||
|
let handle = unsafe { self.inner.as_mut().get_unchecked_mut() };
|
||||||
|
let err: uc_error;
|
||||||
|
let mut in_one_hashmap = false;
|
||||||
|
|
||||||
|
if handle.code_hooks.contains_key(&hook) {
|
||||||
|
in_one_hashmap = true;
|
||||||
|
handle.code_hooks.remove(&hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle.mem_hooks.contains_key(&hook) {
|
||||||
|
in_one_hashmap = true;
|
||||||
|
handle.mem_hooks.remove(&hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle.block_hooks.contains_key(&hook) {
|
||||||
|
in_one_hashmap = true;
|
||||||
|
handle.block_hooks.remove(&hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle.intr_hooks.contains_key(&hook) {
|
||||||
|
in_one_hashmap = true;
|
||||||
|
handle.intr_hooks.remove(&hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle.insn_in_hooks.contains_key(&hook) {
|
||||||
|
in_one_hashmap = true;
|
||||||
|
handle.insn_in_hooks.remove(&hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle.insn_out_hooks.contains_key(&hook) {
|
||||||
|
in_one_hashmap = true;
|
||||||
|
handle.insn_out_hooks.remove(&hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle.insn_sys_hooks.contains_key(&hook) {
|
||||||
|
in_one_hashmap = true;
|
||||||
|
handle.insn_sys_hooks.remove(&hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
if in_one_hashmap {
|
||||||
|
err = unsafe { ffi::uc_hook_del(handle.uc, hook) };
|
||||||
|
} else {
|
||||||
|
err = uc_error::HOOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate and return an empty Unicorn context.
|
||||||
|
///
|
||||||
|
/// To be populated via context_save.
|
||||||
|
pub fn context_alloc(&self) -> Result<Context, uc_error> {
|
||||||
|
let mut empty_context: ffi::uc_context = Default::default();
|
||||||
|
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(Context {
|
||||||
|
context: empty_context,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Save current Unicorn context to previously allocated Context struct.
|
||||||
|
pub fn context_save(&self, context: &mut Context) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_context_save(self.inner.uc, context.context) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate and return a Context struct initialized with the current CPU context.
|
||||||
|
///
|
||||||
|
/// This can be used for fast rollbacks with context_restore.
|
||||||
|
/// In case of many non-concurrent context saves, use context_alloc and *_save
|
||||||
|
/// individually to avoid unnecessary allocations.
|
||||||
|
pub fn context_init(&self) -> Result<Context, uc_error> {
|
||||||
|
let mut new_context: ffi::uc_context = Default::default();
|
||||||
|
let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut new_context) };
|
||||||
|
if err != uc_error::OK {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
let err = unsafe { ffi::uc_context_save(self.inner.uc, new_context) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(Context {
|
||||||
|
context: new_context,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
unsafe { ffi::uc_context_free(new_context) };
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restore a previously saved Unicorn context.
|
||||||
|
///
|
||||||
|
/// Perform a quick rollback of the CPU context, including registers and some
|
||||||
|
/// internal metadata. Contexts may not be shared across engine instances with
|
||||||
|
/// differing arches or modes. Memory has to be restored manually, if needed.
|
||||||
|
pub fn context_restore(&self, context: &Context) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_context_restore(self.inner.uc, context.context) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emulate machine code for a specified duration.
|
||||||
|
///
|
||||||
|
/// `begin` is the address where to start the emulation. The emulation stops if `until`
|
||||||
|
/// is hit. `timeout` specifies a duration in microseconds after which the emulation is
|
||||||
|
/// stopped (infinite execution if set to 0). `count` is the maximum number of instructions
|
||||||
|
/// to emulate (emulate all the available instructions if set to 0).
|
||||||
|
pub fn emu_start(
|
||||||
|
&mut self,
|
||||||
|
begin: u64,
|
||||||
|
until: u64,
|
||||||
|
timeout: u64,
|
||||||
|
count: usize,
|
||||||
|
) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_emu_start(self.inner.uc, begin, until, timeout, count as _) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop the emulation.
|
||||||
|
///
|
||||||
|
/// This is usually called from callback function in hooks.
|
||||||
|
/// NOTE: For now, this will stop the execution only after the current block.
|
||||||
|
pub fn emu_stop(&mut self) -> Result<(), uc_error> {
|
||||||
|
let err = unsafe { ffi::uc_emu_stop(self.inner.uc) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query the internal status of the engine.
|
||||||
|
///
|
||||||
|
/// supported: MODE, PAGE_SIZE, ARCH
|
||||||
|
pub fn query(&self, query: Query) -> Result<usize, uc_error> {
|
||||||
|
let mut result: libc::size_t = Default::default();
|
||||||
|
let err = unsafe { ffi::uc_query(self.inner.uc, query, &mut result) };
|
||||||
|
if err == uc_error::OK {
|
||||||
|
Ok(result)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
bindings/rust/src/m68k.rs
Normal file
24
bindings/rust/src/m68k.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// M68K registers
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum RegisterM68K {
|
||||||
|
INVALID = 0,
|
||||||
|
A0,
|
||||||
|
A1,
|
||||||
|
A2,
|
||||||
|
A3,
|
||||||
|
A4,
|
||||||
|
A5,
|
||||||
|
A6,
|
||||||
|
A7,
|
||||||
|
D0,
|
||||||
|
D1,
|
||||||
|
D2,
|
||||||
|
D3,
|
||||||
|
D4,
|
||||||
|
D5,
|
||||||
|
D6,
|
||||||
|
D7,
|
||||||
|
SR,
|
||||||
|
PC,
|
||||||
|
}
|
156
bindings/rust/src/mips.rs
Normal file
156
bindings/rust/src/mips.rs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
// MIPS registers
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum RegisterMIPS {
|
||||||
|
INVALID = 0,
|
||||||
|
|
||||||
|
// General purpose registers
|
||||||
|
PC = 1,
|
||||||
|
ZERO = 2,
|
||||||
|
AT = 3,
|
||||||
|
V0 = 4,
|
||||||
|
V1 = 5,
|
||||||
|
A0 = 6,
|
||||||
|
A1 = 7,
|
||||||
|
A2 = 8,
|
||||||
|
A3 = 9,
|
||||||
|
T0 = 10,
|
||||||
|
T1 = 11,
|
||||||
|
T2 = 12,
|
||||||
|
T3 = 13,
|
||||||
|
T4 = 14,
|
||||||
|
T5 = 15,
|
||||||
|
T6 = 16,
|
||||||
|
T7 = 17,
|
||||||
|
S0 = 18,
|
||||||
|
S1 = 19,
|
||||||
|
S2 = 20,
|
||||||
|
S3 = 21,
|
||||||
|
S4 = 22,
|
||||||
|
S5 = 23,
|
||||||
|
S6 = 24,
|
||||||
|
S7 = 25,
|
||||||
|
T8 = 26,
|
||||||
|
T9 = 27,
|
||||||
|
K0 = 28,
|
||||||
|
K1 = 29,
|
||||||
|
GP = 30,
|
||||||
|
SP = 31,
|
||||||
|
FP = 32,
|
||||||
|
RA = 33,
|
||||||
|
|
||||||
|
// DSP registers
|
||||||
|
DSPCCOND = 34,
|
||||||
|
DSPCARRY = 35,
|
||||||
|
DSPEFI = 36,
|
||||||
|
DSPOUTFLAG = 37,
|
||||||
|
DSPOUTFLAG16_19 = 38,
|
||||||
|
DSPOUTFLAG20 = 39,
|
||||||
|
DSPOUTFLAG21 = 40,
|
||||||
|
DSPOUTFLAG22 = 41,
|
||||||
|
DSPOUTFLAG23 = 42,
|
||||||
|
DSPPOS = 43,
|
||||||
|
DSPSCOUNT = 44,
|
||||||
|
|
||||||
|
// ACC registers
|
||||||
|
AC0 = 45,
|
||||||
|
AC1 = 46,
|
||||||
|
AC2 = 47,
|
||||||
|
AC3 = 48,
|
||||||
|
|
||||||
|
// COP registers
|
||||||
|
CC0 = 49,
|
||||||
|
CC1 = 50,
|
||||||
|
CC2 = 51,
|
||||||
|
CC3 = 52,
|
||||||
|
CC4 = 53,
|
||||||
|
CC5 = 54,
|
||||||
|
CC6 = 55,
|
||||||
|
CC7 = 56,
|
||||||
|
|
||||||
|
// FPU registers
|
||||||
|
F0 = 57,
|
||||||
|
F1 = 58,
|
||||||
|
F2 = 59,
|
||||||
|
F3 = 60,
|
||||||
|
F4 = 61,
|
||||||
|
F5 = 62,
|
||||||
|
F6 = 63,
|
||||||
|
F7 = 64,
|
||||||
|
F8 = 65,
|
||||||
|
F9 = 66,
|
||||||
|
F10 = 67,
|
||||||
|
F11 = 68,
|
||||||
|
F12 = 69,
|
||||||
|
F13 = 70,
|
||||||
|
F14 = 71,
|
||||||
|
F15 = 72,
|
||||||
|
F16 = 73,
|
||||||
|
F17 = 74,
|
||||||
|
F18 = 75,
|
||||||
|
F19 = 76,
|
||||||
|
F20 = 77,
|
||||||
|
F21 = 78,
|
||||||
|
F22 = 79,
|
||||||
|
F23 = 80,
|
||||||
|
F24 = 81,
|
||||||
|
F25 = 82,
|
||||||
|
F26 = 83,
|
||||||
|
F27 = 84,
|
||||||
|
F28 = 85,
|
||||||
|
F29 = 86,
|
||||||
|
F30 = 87,
|
||||||
|
F31 = 88,
|
||||||
|
FCC0 = 89,
|
||||||
|
FCC1 = 90,
|
||||||
|
FCC2 = 91,
|
||||||
|
FCC3 = 92,
|
||||||
|
FCC4 = 93,
|
||||||
|
FCC5 = 94,
|
||||||
|
FCC6 = 95,
|
||||||
|
FCC7 = 96,
|
||||||
|
|
||||||
|
// AFPR128
|
||||||
|
W0 = 97,
|
||||||
|
W1 = 98,
|
||||||
|
W2 = 99,
|
||||||
|
W3 = 100,
|
||||||
|
W4 = 101,
|
||||||
|
W5 = 102,
|
||||||
|
W6 = 103,
|
||||||
|
W7 = 104,
|
||||||
|
W8 = 105,
|
||||||
|
W9 = 106,
|
||||||
|
W10 = 107,
|
||||||
|
W11 = 108,
|
||||||
|
W12 = 109,
|
||||||
|
W13 = 110,
|
||||||
|
W14 = 111,
|
||||||
|
W15 = 112,
|
||||||
|
W16 = 113,
|
||||||
|
W17 = 114,
|
||||||
|
W18 = 115,
|
||||||
|
W19 = 116,
|
||||||
|
W20 = 117,
|
||||||
|
W21 = 118,
|
||||||
|
W22 = 119,
|
||||||
|
W23 = 120,
|
||||||
|
W24 = 121,
|
||||||
|
W25 = 122,
|
||||||
|
W26 = 123,
|
||||||
|
W27 = 124,
|
||||||
|
W28 = 125,
|
||||||
|
W29 = 126,
|
||||||
|
W30 = 127,
|
||||||
|
W31 = 128,
|
||||||
|
HI = 129,
|
||||||
|
LO = 130,
|
||||||
|
P0 = 131,
|
||||||
|
P1 = 132,
|
||||||
|
P2 = 133,
|
||||||
|
MPL0 = 134,
|
||||||
|
MPL1 = 135,
|
||||||
|
MPL2 = 136,
|
||||||
|
}
|
42
bindings/rust/src/ppc.rs
Normal file
42
bindings/rust/src/ppc.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
|
||||||
|
|
||||||
|
// PowerPC registers
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum RegisterPPC {
|
||||||
|
INVALID = 0,
|
||||||
|
PC = 1,
|
||||||
|
GPR0 = 2,
|
||||||
|
GPR1 = 3,
|
||||||
|
GPR2 = 4,
|
||||||
|
GPR3 = 5,
|
||||||
|
GPR4 = 6,
|
||||||
|
GPR5 = 7,
|
||||||
|
GPR6 = 8,
|
||||||
|
GPR7 = 9,
|
||||||
|
GPR8 = 10,
|
||||||
|
GPR9 = 11,
|
||||||
|
GPR10 = 12,
|
||||||
|
GPR11 = 13,
|
||||||
|
GPR12 = 14,
|
||||||
|
GPR13 = 15,
|
||||||
|
GPR14 = 16,
|
||||||
|
GPR15 = 17,
|
||||||
|
GPR16 = 18,
|
||||||
|
GPR17 = 19,
|
||||||
|
GPR18 = 20,
|
||||||
|
GPR19 = 21,
|
||||||
|
GPR20 = 22,
|
||||||
|
GPR21 = 23,
|
||||||
|
GPR22 = 24,
|
||||||
|
GPR23 = 25,
|
||||||
|
GPR24 = 26,
|
||||||
|
GPR25 = 27,
|
||||||
|
GPR26 = 28,
|
||||||
|
GPR27 = 29,
|
||||||
|
GPR28 = 30,
|
||||||
|
GPR29 = 31,
|
||||||
|
GPR30 = 32,
|
||||||
|
GPR31 = 33,
|
||||||
|
}
|
94
bindings/rust/src/sparc.rs
Normal file
94
bindings/rust/src/sparc.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// SPARC registers
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum RegisterSPARC {
|
||||||
|
INVALID = 0,
|
||||||
|
F0 = 1,
|
||||||
|
F1 = 2,
|
||||||
|
F2 = 3,
|
||||||
|
F3 = 4,
|
||||||
|
F4 = 5,
|
||||||
|
F5 = 6,
|
||||||
|
F6 = 7,
|
||||||
|
F7 = 8,
|
||||||
|
F8 = 9,
|
||||||
|
F9 = 10,
|
||||||
|
F10 = 11,
|
||||||
|
F11 = 12,
|
||||||
|
F12 = 13,
|
||||||
|
F13 = 14,
|
||||||
|
F14 = 15,
|
||||||
|
F15 = 16,
|
||||||
|
F16 = 17,
|
||||||
|
F17 = 18,
|
||||||
|
F18 = 19,
|
||||||
|
F19 = 20,
|
||||||
|
F20 = 21,
|
||||||
|
F21 = 22,
|
||||||
|
F22 = 23,
|
||||||
|
F23 = 24,
|
||||||
|
F24 = 25,
|
||||||
|
F25 = 26,
|
||||||
|
F26 = 27,
|
||||||
|
F27 = 28,
|
||||||
|
F28 = 29,
|
||||||
|
F29 = 30,
|
||||||
|
F30 = 31,
|
||||||
|
F31 = 32,
|
||||||
|
F32 = 33,
|
||||||
|
F34 = 34,
|
||||||
|
F36 = 35,
|
||||||
|
F38 = 36,
|
||||||
|
F40 = 37,
|
||||||
|
F42 = 38,
|
||||||
|
F44 = 39,
|
||||||
|
F46 = 40,
|
||||||
|
F48 = 41,
|
||||||
|
F50 = 42,
|
||||||
|
F52 = 43,
|
||||||
|
F54 = 44,
|
||||||
|
F56 = 45,
|
||||||
|
F58 = 46,
|
||||||
|
F60 = 47,
|
||||||
|
F62 = 48,
|
||||||
|
FCC0 = 49,
|
||||||
|
FCC1 = 50,
|
||||||
|
FCC2 = 51,
|
||||||
|
FCC3 = 52,
|
||||||
|
G0 = 53,
|
||||||
|
G1 = 54,
|
||||||
|
G2 = 55,
|
||||||
|
G3 = 56,
|
||||||
|
G4 = 57,
|
||||||
|
G5 = 58,
|
||||||
|
G6 = 59,
|
||||||
|
G7 = 60,
|
||||||
|
I0 = 61,
|
||||||
|
I1 = 62,
|
||||||
|
I2 = 63,
|
||||||
|
I3 = 64,
|
||||||
|
I4 = 65,
|
||||||
|
I5 = 66,
|
||||||
|
FP = 67,
|
||||||
|
I7 = 68,
|
||||||
|
ICC = 69,
|
||||||
|
L0 = 70,
|
||||||
|
L1 = 71,
|
||||||
|
L2 = 72,
|
||||||
|
L3 = 73,
|
||||||
|
L4 = 74,
|
||||||
|
L5 = 75,
|
||||||
|
L6 = 76,
|
||||||
|
L7 = 77,
|
||||||
|
O0 = 78,
|
||||||
|
O1 = 79,
|
||||||
|
O2 = 80,
|
||||||
|
O3 = 81,
|
||||||
|
O4 = 82,
|
||||||
|
O5 = 83,
|
||||||
|
SP = 84,
|
||||||
|
O7 = 85,
|
||||||
|
Y = 86,
|
||||||
|
XCC = 87,
|
||||||
|
PC = 88,
|
||||||
|
}
|
158
bindings/rust/src/unicorn_const.rs
Normal file
158
bindings/rust/src/unicorn_const.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
pub const API_MAJOR: u64 = 1;
|
||||||
|
pub const API_MINOR: u64 = 0;
|
||||||
|
pub const VERSION_MAJOR: u64 = 1;
|
||||||
|
pub const VERSION_MINOR: u64 = 0;
|
||||||
|
pub const VERSION_EXTRA: u64 = 2;
|
||||||
|
pub const SECOND_SCALE: u64 = 1_000_000;
|
||||||
|
pub const MILISECOND_SCALE: u64 = 1_000;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum uc_error {
|
||||||
|
OK = 0,
|
||||||
|
NOMEM = 1,
|
||||||
|
ARCH = 2,
|
||||||
|
HANDLE = 3,
|
||||||
|
MODE = 4,
|
||||||
|
VERSION = 5,
|
||||||
|
READ_UNMAPPED = 6,
|
||||||
|
WRITE_UNMAPPED = 7,
|
||||||
|
FETCH_UNMAPPED = 8,
|
||||||
|
HOOK = 9,
|
||||||
|
INSN_INVALID = 10,
|
||||||
|
MAP = 11,
|
||||||
|
WRITE_PROT = 12,
|
||||||
|
READ_PROT = 13,
|
||||||
|
FETCH_PROT = 14,
|
||||||
|
ARG = 15,
|
||||||
|
READ_UNALIGNED = 16,
|
||||||
|
WRITE_UNALIGNED = 17,
|
||||||
|
FETCH_UNALIGNED = 18,
|
||||||
|
HOOK_EXIST = 19,
|
||||||
|
RESOURCE = 20,
|
||||||
|
EXCEPTION = 21,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum MemType {
|
||||||
|
READ = 16,
|
||||||
|
WRITE = 17,
|
||||||
|
FETCH = 18,
|
||||||
|
READ_UNMAPPED = 19,
|
||||||
|
WRITE_UNMAPPED = 20,
|
||||||
|
FETCH_UNMAPPED = 21,
|
||||||
|
WRITE_PROT = 22,
|
||||||
|
READ_PROT = 23,
|
||||||
|
FETCH_PROT = 24,
|
||||||
|
READ_AFTER = 25,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct HookType: i32 {
|
||||||
|
const INTR = 1;
|
||||||
|
const INSN = 2;
|
||||||
|
const CODE = 4;
|
||||||
|
const BLOCK = 8;
|
||||||
|
|
||||||
|
const MEM_READ_UNMAPPED = 0x10;
|
||||||
|
const MEM_WRITE_UNMAPPED = 0x20;
|
||||||
|
const MEM_FETCH_UNMAPPED = 0x40;
|
||||||
|
const MEM_UNMAPPED = Self::MEM_READ_UNMAPPED.bits | Self::MEM_WRITE_UNMAPPED.bits | Self::MEM_FETCH_UNMAPPED.bits;
|
||||||
|
|
||||||
|
const MEM_READ_PROT = 0x80;
|
||||||
|
const MEM_WRITE_PROT = 0x100;
|
||||||
|
const MEM_FETCH_PROT = 0x200;
|
||||||
|
const MEM_PROT = Self::MEM_READ_PROT.bits | Self::MEM_WRITE_PROT.bits | Self::MEM_FETCH_PROT.bits;
|
||||||
|
|
||||||
|
const MEM_READ = 0x400;
|
||||||
|
const MEM_WRITE = 0x800;
|
||||||
|
const MEM_FETCH = 0x1000;
|
||||||
|
const MEM_VALID = Self::MEM_READ.bits | Self::MEM_WRITE.bits | Self::MEM_FETCH.bits;
|
||||||
|
|
||||||
|
const MEM_READ_AFTER = 0x2000;
|
||||||
|
|
||||||
|
const INSN_INVALID = 0x4000;
|
||||||
|
|
||||||
|
const MEM_READ_INVALID = Self::MEM_READ_UNMAPPED.bits | Self::MEM_READ_PROT.bits;
|
||||||
|
const MEM_WRITE_INVALID = Self::MEM_WRITE_UNMAPPED.bits | Self::MEM_WRITE_PROT.bits;
|
||||||
|
const MEM_FETCH_INVALID = Self::MEM_FETCH_UNMAPPED.bits | Self::MEM_FETCH_PROT.bits;
|
||||||
|
const MEM_INVALID = Self::MEM_READ_INVALID.bits | Self::MEM_WRITE_INVALID.bits | Self::MEM_FETCH_INVALID.bits;
|
||||||
|
|
||||||
|
const MEM_ALL = Self::MEM_VALID.bits | Self::MEM_INVALID.bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum Query {
|
||||||
|
MODE = 1,
|
||||||
|
PAGE_SIZE = 2,
|
||||||
|
ARCH = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Permission : u32 {
|
||||||
|
const NONE = 0;
|
||||||
|
const READ = 1;
|
||||||
|
const WRITE = 2;
|
||||||
|
const EXEC = 4;
|
||||||
|
const ALL = Self::READ.bits | Self::WRITE.bits | Self::EXEC.bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MemRegion {
|
||||||
|
pub begin: u64,
|
||||||
|
pub end: u64,
|
||||||
|
pub perms: Permission,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum Arch {
|
||||||
|
ARM = 1,
|
||||||
|
ARM64 = 2,
|
||||||
|
MIPS = 3,
|
||||||
|
X86 = 4,
|
||||||
|
PPC = 5,
|
||||||
|
SPARC = 6,
|
||||||
|
M68K = 7,
|
||||||
|
MAX = 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Mode: i32 {
|
||||||
|
const LITTLE_ENDIAN = 0;
|
||||||
|
const BIG_ENDIAN = 0x4000_0000;
|
||||||
|
|
||||||
|
const ARM = 0;
|
||||||
|
const THUMB = 0x10;
|
||||||
|
const MCLASS = 0x20;
|
||||||
|
const V8 = 0x40;
|
||||||
|
const ARM926 = 0x80;
|
||||||
|
const ARM946 = 0x100;
|
||||||
|
const ARM1176 = 0x200;
|
||||||
|
const MICRO = Self::THUMB.bits;
|
||||||
|
const MIPS3 = Self::MCLASS.bits;
|
||||||
|
const MIPS32R6 = Self::V8.bits;
|
||||||
|
const MIPS32 = 4;
|
||||||
|
const MIPS64 = 8;
|
||||||
|
const MODE_16 = 2;
|
||||||
|
const MODE_32 = Self::MIPS32.bits;
|
||||||
|
const MODE_64 = Self::MIPS64.bits;
|
||||||
|
const PPC32 = Self::MIPS32.bits;
|
||||||
|
const PPC64 = Self::MIPS64.bits;
|
||||||
|
const QPX = Self::THUMB.bits;
|
||||||
|
const SPARC32 = Self::MIPS32.bits;
|
||||||
|
const SPARC64 = Self::MIPS64.bits;
|
||||||
|
const V9 = Self::THUMB.bits;
|
||||||
|
}
|
||||||
|
}
|
281
bindings/rust/src/x86.rs
Normal file
281
bindings/rust/src/x86.rs
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
// X86 registers
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum RegisterX86 {
|
||||||
|
INVALID = 0,
|
||||||
|
AH,
|
||||||
|
AL,
|
||||||
|
AX,
|
||||||
|
BH,
|
||||||
|
BL,
|
||||||
|
BP,
|
||||||
|
BPL,
|
||||||
|
BX,
|
||||||
|
CH,
|
||||||
|
CL,
|
||||||
|
CS,
|
||||||
|
CX,
|
||||||
|
DH,
|
||||||
|
DI,
|
||||||
|
DIL,
|
||||||
|
DL,
|
||||||
|
DS,
|
||||||
|
DX,
|
||||||
|
EAX,
|
||||||
|
EBP,
|
||||||
|
EBX,
|
||||||
|
ECX,
|
||||||
|
EDI,
|
||||||
|
EDX,
|
||||||
|
EFLAGS,
|
||||||
|
EIP,
|
||||||
|
EIZ,
|
||||||
|
ES,
|
||||||
|
ESI,
|
||||||
|
ESP,
|
||||||
|
FPSW,
|
||||||
|
FS,
|
||||||
|
GS,
|
||||||
|
IP,
|
||||||
|
RAX,
|
||||||
|
RBP,
|
||||||
|
RBX,
|
||||||
|
RCX,
|
||||||
|
RDI,
|
||||||
|
RDX,
|
||||||
|
RIP,
|
||||||
|
RIZ,
|
||||||
|
RSI,
|
||||||
|
RSP,
|
||||||
|
SI,
|
||||||
|
SIL,
|
||||||
|
SP,
|
||||||
|
SPL,
|
||||||
|
SS,
|
||||||
|
CR0,
|
||||||
|
CR1,
|
||||||
|
CR2,
|
||||||
|
CR3,
|
||||||
|
CR4,
|
||||||
|
CR5,
|
||||||
|
CR6,
|
||||||
|
CR7,
|
||||||
|
CR8,
|
||||||
|
CR9,
|
||||||
|
CR10,
|
||||||
|
CR11,
|
||||||
|
CR12,
|
||||||
|
CR13,
|
||||||
|
CR14,
|
||||||
|
CR15,
|
||||||
|
DR0,
|
||||||
|
DR1,
|
||||||
|
DR2,
|
||||||
|
DR3,
|
||||||
|
DR4,
|
||||||
|
DR5,
|
||||||
|
DR6,
|
||||||
|
DR7,
|
||||||
|
DR8,
|
||||||
|
DR9,
|
||||||
|
DR10,
|
||||||
|
DR11,
|
||||||
|
DR12,
|
||||||
|
DR13,
|
||||||
|
DR14,
|
||||||
|
DR15,
|
||||||
|
FP0,
|
||||||
|
FP1,
|
||||||
|
FP2,
|
||||||
|
FP3,
|
||||||
|
FP4,
|
||||||
|
FP5,
|
||||||
|
FP6,
|
||||||
|
FP7,
|
||||||
|
K0,
|
||||||
|
K1,
|
||||||
|
K2,
|
||||||
|
K3,
|
||||||
|
K4,
|
||||||
|
K5,
|
||||||
|
K6,
|
||||||
|
K7,
|
||||||
|
MM0,
|
||||||
|
MM1,
|
||||||
|
MM2,
|
||||||
|
MM3,
|
||||||
|
MM4,
|
||||||
|
MM5,
|
||||||
|
MM6,
|
||||||
|
MM7,
|
||||||
|
R8,
|
||||||
|
R9,
|
||||||
|
R10,
|
||||||
|
R11,
|
||||||
|
R12,
|
||||||
|
R13,
|
||||||
|
R14,
|
||||||
|
R15,
|
||||||
|
ST0,
|
||||||
|
ST1,
|
||||||
|
ST2,
|
||||||
|
ST3,
|
||||||
|
ST4,
|
||||||
|
ST5,
|
||||||
|
ST6,
|
||||||
|
ST7,
|
||||||
|
XMM0,
|
||||||
|
XMM1,
|
||||||
|
XMM2,
|
||||||
|
XMM3,
|
||||||
|
XMM4,
|
||||||
|
XMM5,
|
||||||
|
XMM6,
|
||||||
|
XMM7,
|
||||||
|
XMM8,
|
||||||
|
XMM9,
|
||||||
|
XMM10,
|
||||||
|
XMM11,
|
||||||
|
XMM12,
|
||||||
|
XMM13,
|
||||||
|
XMM14,
|
||||||
|
XMM15,
|
||||||
|
XMM16,
|
||||||
|
XMM17,
|
||||||
|
XMM18,
|
||||||
|
XMM19,
|
||||||
|
XMM20,
|
||||||
|
XMM21,
|
||||||
|
XMM22,
|
||||||
|
XMM23,
|
||||||
|
XMM24,
|
||||||
|
XMM25,
|
||||||
|
XMM26,
|
||||||
|
XMM27,
|
||||||
|
XMM28,
|
||||||
|
XMM29,
|
||||||
|
XMM30,
|
||||||
|
XMM31,
|
||||||
|
YMM0,
|
||||||
|
YMM1,
|
||||||
|
YMM2,
|
||||||
|
YMM3,
|
||||||
|
YMM4,
|
||||||
|
YMM5,
|
||||||
|
YMM6,
|
||||||
|
YMM7,
|
||||||
|
YMM8,
|
||||||
|
YMM9,
|
||||||
|
YMM10,
|
||||||
|
YMM11,
|
||||||
|
YMM12,
|
||||||
|
YMM13,
|
||||||
|
YMM14,
|
||||||
|
YMM15,
|
||||||
|
YMM16,
|
||||||
|
YMM17,
|
||||||
|
YMM18,
|
||||||
|
YMM19,
|
||||||
|
YMM20,
|
||||||
|
YMM21,
|
||||||
|
YMM22,
|
||||||
|
YMM23,
|
||||||
|
YMM24,
|
||||||
|
YMM25,
|
||||||
|
YMM26,
|
||||||
|
YMM27,
|
||||||
|
YMM28,
|
||||||
|
YMM29,
|
||||||
|
YMM30,
|
||||||
|
YMM31,
|
||||||
|
ZMM0,
|
||||||
|
ZMM1,
|
||||||
|
ZMM2,
|
||||||
|
ZMM3,
|
||||||
|
ZMM4,
|
||||||
|
ZMM5,
|
||||||
|
ZMM6,
|
||||||
|
ZMM7,
|
||||||
|
ZMM8,
|
||||||
|
ZMM9,
|
||||||
|
ZMM10,
|
||||||
|
ZMM11,
|
||||||
|
ZMM12,
|
||||||
|
ZMM13,
|
||||||
|
ZMM14,
|
||||||
|
ZMM15,
|
||||||
|
ZMM16,
|
||||||
|
ZMM17,
|
||||||
|
ZMM18,
|
||||||
|
ZMM19,
|
||||||
|
ZMM20,
|
||||||
|
ZMM21,
|
||||||
|
ZMM22,
|
||||||
|
ZMM23,
|
||||||
|
ZMM24,
|
||||||
|
ZMM25,
|
||||||
|
ZMM26,
|
||||||
|
ZMM27,
|
||||||
|
ZMM28,
|
||||||
|
ZMM29,
|
||||||
|
ZMM30,
|
||||||
|
ZMM31,
|
||||||
|
R8B,
|
||||||
|
R9B,
|
||||||
|
R10B,
|
||||||
|
R11B,
|
||||||
|
R12B,
|
||||||
|
R13B,
|
||||||
|
R14B,
|
||||||
|
R15B,
|
||||||
|
R8D,
|
||||||
|
R9D,
|
||||||
|
R10D,
|
||||||
|
R11D,
|
||||||
|
R12D,
|
||||||
|
R13D,
|
||||||
|
R14D,
|
||||||
|
R15D,
|
||||||
|
R8W,
|
||||||
|
R9W,
|
||||||
|
R10W,
|
||||||
|
R11W,
|
||||||
|
R12W,
|
||||||
|
R13W,
|
||||||
|
R14W,
|
||||||
|
R15W,
|
||||||
|
IDTR,
|
||||||
|
GDTR,
|
||||||
|
LDTR,
|
||||||
|
TR,
|
||||||
|
FPCW,
|
||||||
|
FPTAG,
|
||||||
|
MSR,
|
||||||
|
MXCSR,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum InsnX86 {
|
||||||
|
IN = 218,
|
||||||
|
OUT = 500,
|
||||||
|
SYSCALL = 699,
|
||||||
|
SYSENTER = 700,
|
||||||
|
RET = 151,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum InsnSysX86 {
|
||||||
|
SYSCALL = InsnX86::SYSCALL as isize,
|
||||||
|
SYSENTER = InsnX86::SYSENTER as isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub struct X86Mmr {
|
||||||
|
pub selector: u64,
|
||||||
|
pub base: u64,
|
||||||
|
pub limit: u32,
|
||||||
|
pub flags: u32,
|
||||||
|
}
|
683
bindings/rust/tests/unicorn.rs
Normal file
683
bindings/rust/tests/unicorn.rs
Normal file
@ -0,0 +1,683 @@
|
|||||||
|
#![deny(rust_2018_idioms)]
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use unicorn::unicorn_const::{uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE};
|
||||||
|
use unicorn::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86};
|
||||||
|
|
||||||
|
pub static X86_REGISTERS: [RegisterX86; 145] = [
|
||||||
|
RegisterX86::AH,
|
||||||
|
RegisterX86::AL,
|
||||||
|
RegisterX86::AX,
|
||||||
|
RegisterX86::BH,
|
||||||
|
RegisterX86::BL,
|
||||||
|
RegisterX86::BP,
|
||||||
|
RegisterX86::BPL,
|
||||||
|
RegisterX86::BX,
|
||||||
|
RegisterX86::CH,
|
||||||
|
RegisterX86::CL,
|
||||||
|
RegisterX86::CS,
|
||||||
|
RegisterX86::CX,
|
||||||
|
RegisterX86::DH,
|
||||||
|
RegisterX86::DI,
|
||||||
|
RegisterX86::DIL,
|
||||||
|
RegisterX86::DL,
|
||||||
|
RegisterX86::DS,
|
||||||
|
RegisterX86::DX,
|
||||||
|
RegisterX86::EAX,
|
||||||
|
RegisterX86::EBP,
|
||||||
|
RegisterX86::EBX,
|
||||||
|
RegisterX86::ECX,
|
||||||
|
RegisterX86::EDI,
|
||||||
|
RegisterX86::EDX,
|
||||||
|
RegisterX86::EFLAGS,
|
||||||
|
RegisterX86::EIP,
|
||||||
|
RegisterX86::EIZ,
|
||||||
|
RegisterX86::ES,
|
||||||
|
RegisterX86::ESI,
|
||||||
|
RegisterX86::ESP,
|
||||||
|
RegisterX86::FPSW,
|
||||||
|
RegisterX86::FS,
|
||||||
|
RegisterX86::GS,
|
||||||
|
RegisterX86::IP,
|
||||||
|
RegisterX86::RAX,
|
||||||
|
RegisterX86::RBP,
|
||||||
|
RegisterX86::RBX,
|
||||||
|
RegisterX86::RCX,
|
||||||
|
RegisterX86::RDI,
|
||||||
|
RegisterX86::RDX,
|
||||||
|
RegisterX86::RIP,
|
||||||
|
RegisterX86::RIZ,
|
||||||
|
RegisterX86::RSI,
|
||||||
|
RegisterX86::RSP,
|
||||||
|
RegisterX86::SI,
|
||||||
|
RegisterX86::SIL,
|
||||||
|
RegisterX86::SP,
|
||||||
|
RegisterX86::SPL,
|
||||||
|
RegisterX86::SS,
|
||||||
|
RegisterX86::CR0,
|
||||||
|
RegisterX86::CR1,
|
||||||
|
RegisterX86::CR2,
|
||||||
|
RegisterX86::CR3,
|
||||||
|
RegisterX86::CR4,
|
||||||
|
RegisterX86::CR5,
|
||||||
|
RegisterX86::CR6,
|
||||||
|
RegisterX86::CR7,
|
||||||
|
RegisterX86::CR8,
|
||||||
|
RegisterX86::CR9,
|
||||||
|
RegisterX86::CR10,
|
||||||
|
RegisterX86::CR11,
|
||||||
|
RegisterX86::CR12,
|
||||||
|
RegisterX86::CR13,
|
||||||
|
RegisterX86::CR14,
|
||||||
|
RegisterX86::CR15,
|
||||||
|
RegisterX86::DR0,
|
||||||
|
RegisterX86::DR1,
|
||||||
|
RegisterX86::DR2,
|
||||||
|
RegisterX86::DR3,
|
||||||
|
RegisterX86::DR4,
|
||||||
|
RegisterX86::DR5,
|
||||||
|
RegisterX86::DR6,
|
||||||
|
RegisterX86::DR7,
|
||||||
|
RegisterX86::DR8,
|
||||||
|
RegisterX86::DR9,
|
||||||
|
RegisterX86::DR10,
|
||||||
|
RegisterX86::DR11,
|
||||||
|
RegisterX86::DR12,
|
||||||
|
RegisterX86::DR13,
|
||||||
|
RegisterX86::DR14,
|
||||||
|
RegisterX86::DR15,
|
||||||
|
RegisterX86::FP0,
|
||||||
|
RegisterX86::FP1,
|
||||||
|
RegisterX86::FP2,
|
||||||
|
RegisterX86::FP3,
|
||||||
|
RegisterX86::FP4,
|
||||||
|
RegisterX86::FP5,
|
||||||
|
RegisterX86::FP6,
|
||||||
|
RegisterX86::FP7,
|
||||||
|
RegisterX86::K0,
|
||||||
|
RegisterX86::K1,
|
||||||
|
RegisterX86::K2,
|
||||||
|
RegisterX86::K3,
|
||||||
|
RegisterX86::K4,
|
||||||
|
RegisterX86::K5,
|
||||||
|
RegisterX86::K6,
|
||||||
|
RegisterX86::K7,
|
||||||
|
RegisterX86::MM0,
|
||||||
|
RegisterX86::MM1,
|
||||||
|
RegisterX86::MM2,
|
||||||
|
RegisterX86::MM3,
|
||||||
|
RegisterX86::MM4,
|
||||||
|
RegisterX86::MM5,
|
||||||
|
RegisterX86::MM6,
|
||||||
|
RegisterX86::MM7,
|
||||||
|
RegisterX86::R8,
|
||||||
|
RegisterX86::R9,
|
||||||
|
RegisterX86::R10,
|
||||||
|
RegisterX86::R11,
|
||||||
|
RegisterX86::R12,
|
||||||
|
RegisterX86::R13,
|
||||||
|
RegisterX86::R14,
|
||||||
|
RegisterX86::R15,
|
||||||
|
RegisterX86::ST0,
|
||||||
|
RegisterX86::ST1,
|
||||||
|
RegisterX86::ST2,
|
||||||
|
RegisterX86::ST3,
|
||||||
|
RegisterX86::ST4,
|
||||||
|
RegisterX86::ST5,
|
||||||
|
RegisterX86::ST6,
|
||||||
|
RegisterX86::ST7,
|
||||||
|
RegisterX86::R8B,
|
||||||
|
RegisterX86::R9B,
|
||||||
|
RegisterX86::R10B,
|
||||||
|
RegisterX86::R11B,
|
||||||
|
RegisterX86::R12B,
|
||||||
|
RegisterX86::R13B,
|
||||||
|
RegisterX86::R14B,
|
||||||
|
RegisterX86::R15B,
|
||||||
|
RegisterX86::R8D,
|
||||||
|
RegisterX86::R9D,
|
||||||
|
RegisterX86::R10D,
|
||||||
|
RegisterX86::R11D,
|
||||||
|
RegisterX86::R12D,
|
||||||
|
RegisterX86::R13D,
|
||||||
|
RegisterX86::R14D,
|
||||||
|
RegisterX86::R15D,
|
||||||
|
RegisterX86::R8W,
|
||||||
|
RegisterX86::R9W,
|
||||||
|
RegisterX86::R10W,
|
||||||
|
RegisterX86::R11W,
|
||||||
|
RegisterX86::R12W,
|
||||||
|
RegisterX86::R13W,
|
||||||
|
RegisterX86::R14W,
|
||||||
|
RegisterX86::R15W,
|
||||||
|
];
|
||||||
|
|
||||||
|
type Unicorn<'a> = unicorn::UnicornHandle<'a>;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn emulate_x86() {
|
||||||
|
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(()));
|
||||||
|
assert_eq!(emu.reg_read(RegisterX86::EAX as i32), Ok(123));
|
||||||
|
|
||||||
|
// Attempt to write to memory before mapping it.
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_write(0x1000, &x86_code32),
|
||||||
|
(Err(uc_error::WRITE_UNMAPPED))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_read_as_vec(0x1000, x86_code32.len()),
|
||||||
|
Ok(x86_code32.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(()));
|
||||||
|
assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(()));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
(0x1000 + x86_code32.len()) as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11));
|
||||||
|
assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x86_code_callback() {
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct CodeExpectation(u64, u32);
|
||||||
|
let expects = vec![CodeExpectation(0x1000, 1), CodeExpectation(0x1001, 1)];
|
||||||
|
let codes: Vec<CodeExpectation> = Vec::new();
|
||||||
|
let codes_cell = Rc::new(RefCell::new(codes));
|
||||||
|
|
||||||
|
let callback_codes = codes_cell.clone();
|
||||||
|
let callback = move |_: Unicorn<'_>, address: u64, size: u32| {
|
||||||
|
let mut codes = callback_codes.borrow_mut();
|
||||||
|
codes.push(CodeExpectation(address, size));
|
||||||
|
};
|
||||||
|
|
||||||
|
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
|
let hook = emu
|
||||||
|
.add_code_hook(0x1000, 0x2000, callback)
|
||||||
|
.expect("failed to add code hook");
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(0x1000, 0x1002, 10 * SECOND_SCALE, 1000),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(expects, *codes_cell.borrow());
|
||||||
|
assert_eq!(emu.remove_hook(hook), Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x86_intr_callback() {
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct IntrExpectation(u32);
|
||||||
|
let expect = IntrExpectation(0x80);
|
||||||
|
let intr_cell = Rc::new(RefCell::new(IntrExpectation(0)));
|
||||||
|
|
||||||
|
let callback_intr = intr_cell.clone();
|
||||||
|
let callback = move |_: Unicorn<'_>, intno: u32| {
|
||||||
|
*callback_intr.borrow_mut() = IntrExpectation(intno);
|
||||||
|
};
|
||||||
|
|
||||||
|
let x86_code32: Vec<u8> = vec![0xcd, 0x80]; // INT 0x80;
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
|
let hook = emu
|
||||||
|
.add_intr_hook(callback)
|
||||||
|
.expect("failed to add intr hook");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
0x1000 + x86_code32.len() as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(expect, *intr_cell.borrow());
|
||||||
|
assert_eq!(emu.remove_hook(hook), Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x86_mem_callback() {
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct MemExpectation(MemType, u64, usize, i64);
|
||||||
|
let expects = vec![
|
||||||
|
MemExpectation(MemType::WRITE, 0x2000, 4, 0xdeadbeef),
|
||||||
|
MemExpectation(MemType::READ_UNMAPPED, 0x10000, 4, 0),
|
||||||
|
MemExpectation(MemType::READ, 0x10000, 4, 0),
|
||||||
|
];
|
||||||
|
let mems: Vec<MemExpectation> = Vec::new();
|
||||||
|
let mems_cell = Rc::new(RefCell::new(mems));
|
||||||
|
|
||||||
|
let callback_mems = mems_cell.clone();
|
||||||
|
let callback =
|
||||||
|
move |_: Unicorn<'_>, mem_type: MemType, address: u64, size: usize, value: i64| {
|
||||||
|
let mut mems = callback_mems.borrow_mut();
|
||||||
|
mems.push(MemExpectation(mem_type, address, size, value));
|
||||||
|
};
|
||||||
|
|
||||||
|
// mov eax, 0xdeadbeef;
|
||||||
|
// mov [0x2000], eax;
|
||||||
|
// mov eax, [0x10000];
|
||||||
|
let x86_code32: Vec<u8> = vec![
|
||||||
|
0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
|
let hook = emu
|
||||||
|
.add_mem_hook(HookType::MEM_ALL, 0, std::u64::MAX, callback)
|
||||||
|
.expect("failed to add memory hook");
|
||||||
|
assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 0x123), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
0x1000 + x86_code32.len() as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
0x1000
|
||||||
|
),
|
||||||
|
Err(uc_error::READ_UNMAPPED)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(expects, *mems_cell.borrow());
|
||||||
|
assert_eq!(emu.remove_hook(hook), Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x86_insn_in_callback() {
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct InsnInExpectation(u32, usize);
|
||||||
|
let expect = InsnInExpectation(0x10, 4);
|
||||||
|
let insn_cell = Rc::new(RefCell::new(InsnInExpectation(0, 0)));
|
||||||
|
|
||||||
|
let callback_insn = insn_cell.clone();
|
||||||
|
let callback = move |_: Unicorn<'_>, port: u32, size: usize| {
|
||||||
|
*callback_insn.borrow_mut() = InsnInExpectation(port, size);
|
||||||
|
};
|
||||||
|
|
||||||
|
let x86_code32: Vec<u8> = vec![0xe5, 0x10]; // IN eax, 0x10;
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
|
let hook = emu
|
||||||
|
.add_insn_in_hook(callback)
|
||||||
|
.expect("failed to add in hook");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
0x1000 + x86_code32.len() as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(expect, *insn_cell.borrow());
|
||||||
|
assert_eq!(emu.remove_hook(hook), Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x86_insn_out_callback() {
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct InsnOutExpectation(u32, usize, u32);
|
||||||
|
let expect = InsnOutExpectation(0x46, 1, 0x32);
|
||||||
|
let insn_cell = Rc::new(RefCell::new(InsnOutExpectation(0, 0, 0)));
|
||||||
|
|
||||||
|
let callback_insn = insn_cell.clone();
|
||||||
|
let callback = move |_: Unicorn<'_>, port: u32, size: usize, value: u32| {
|
||||||
|
*callback_insn.borrow_mut() = InsnOutExpectation(port, size, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
let x86_code32: Vec<u8> = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al;
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
|
let hook = emu
|
||||||
|
.add_insn_out_hook(callback)
|
||||||
|
.expect("failed to add out hook");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
0x1000 + x86_code32.len() as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(expect, *insn_cell.borrow());
|
||||||
|
assert_eq!(emu.remove_hook(hook), Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x86_insn_sys_callback() {
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct InsnSysExpectation(u64);
|
||||||
|
let expect = InsnSysExpectation(0xdeadbeef);
|
||||||
|
let insn_cell = Rc::new(RefCell::new(InsnSysExpectation(0)));
|
||||||
|
|
||||||
|
let callback_insn = insn_cell.clone();
|
||||||
|
let callback = move |uc: Unicorn<'_>| {
|
||||||
|
println!("!!!!");
|
||||||
|
let rax = uc.reg_read(RegisterX86::RAX as i32).unwrap();
|
||||||
|
*callback_insn.borrow_mut() = InsnSysExpectation(rax);
|
||||||
|
};
|
||||||
|
|
||||||
|
// MOV rax, 0xdeadbeef; SYSCALL;
|
||||||
|
let x86_code: Vec<u8> = vec![
|
||||||
|
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_64)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
|
||||||
|
|
||||||
|
let hook = emu
|
||||||
|
.add_insn_sys_hook(InsnSysX86::SYSCALL, 1, 0, callback)
|
||||||
|
.expect("failed to add syscall hook");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
0x1000 + x86_code.len() as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(expect, *insn_cell.borrow());
|
||||||
|
assert_eq!(emu.remove_hook(hook), Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn emulate_arm() {
|
||||||
|
let arm_code32: Vec<u8> = vec![0x83, 0xb0]; // sub sp, #0xc
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::ARM, Mode::THUMB)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(()));
|
||||||
|
assert_eq!(emu.reg_read(RegisterARM::R1 as i32), Ok(123));
|
||||||
|
|
||||||
|
// Attempt to write to memory before mapping it.
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_write(0x1000, &arm_code32),
|
||||||
|
(Err(uc_error::WRITE_UNMAPPED))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &arm_code32), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_read_as_vec(0x1000, arm_code32.len()),
|
||||||
|
Ok(arm_code32.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(emu.reg_write(RegisterARM::SP as i32, 12), Ok(()));
|
||||||
|
assert_eq!(emu.reg_write(RegisterARM::R0 as i32, 10), Ok(()));
|
||||||
|
|
||||||
|
// ARM checks the least significant bit of the address to know
|
||||||
|
// if the code is in Thumb mode.
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000 | 0x01,
|
||||||
|
(0x1000 | (0x01 + arm_code32.len())) as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.reg_read(RegisterARM::SP as i32), Ok(0));
|
||||||
|
assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn emulate_mips() {
|
||||||
|
let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456;
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::MIPS, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &mips_code32), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_read_as_vec(0x1000, mips_code32.len()),
|
||||||
|
Ok(mips_code32.clone())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.reg_write(RegisterMIPS::AT as i32, 0), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
(0x1000 + mips_code32.len()) as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.reg_read(RegisterMIPS::AT as i32), Ok(0x3456));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn emulate_ppc() {
|
||||||
|
let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::PPC, Mode::PPC32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_read_as_vec(0x1000, ppc_code32.len()),
|
||||||
|
Ok(ppc_code32.clone())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.reg_write(RegisterPPC::GPR3 as i32, 42), Ok(()));
|
||||||
|
assert_eq!(emu.reg_write(RegisterPPC::GPR6 as i32, 1337), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
(0x1000 + ppc_code32.len()) as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.reg_read(RegisterPPC::GPR26 as i32), Ok(1379));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mem_unmapping() {
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mem_map_ptr() {
|
||||||
|
// Use an array for the emulator memory.
|
||||||
|
let mut mem: [u8; 4000] = [0; 4000];
|
||||||
|
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
|
||||||
|
// Attempt to write to memory before mapping it.
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_write(0x1000, &x86_code32),
|
||||||
|
(Err(uc_error::WRITE_UNMAPPED))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_read_as_vec(0x1000, x86_code32.len()),
|
||||||
|
Ok(x86_code32.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(()));
|
||||||
|
assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(()));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
(0x1000 + x86_code32.len()) as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11));
|
||||||
|
assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49));
|
||||||
|
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
|
||||||
|
|
||||||
|
// Use a Vec for the emulator memory.
|
||||||
|
let mut mem: Vec<u8> = Vec::new();
|
||||||
|
mem.reserve(4000);
|
||||||
|
|
||||||
|
// Attempt to write to memory before mapping it.
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_write(0x1000, &x86_code32),
|
||||||
|
(Err(uc_error::WRITE_UNMAPPED))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
emu.mem_read_as_vec(0x1000, x86_code32.len()),
|
||||||
|
Ok(x86_code32.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(()));
|
||||||
|
assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(()));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
(0x1000 + x86_code32.len()) as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11));
|
||||||
|
assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49));
|
||||||
|
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x86_context_save_and_restore() {
|
||||||
|
for mode in vec![Mode::MODE_32, Mode::MODE_64] {
|
||||||
|
let x86_code: Vec<u8> = vec![
|
||||||
|
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
|
||||||
|
];
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, mode)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
|
||||||
|
let _ = emu.emu_start(
|
||||||
|
0x1000,
|
||||||
|
(0x1000 + x86_code.len()) as u64,
|
||||||
|
10 * SECOND_SCALE,
|
||||||
|
1000,
|
||||||
|
);
|
||||||
|
|
||||||
|
/* now, save the context... */
|
||||||
|
let context = emu.context_init();
|
||||||
|
let context = context.unwrap();
|
||||||
|
|
||||||
|
/* and create a new emulator, into which we will "restore" that context */
|
||||||
|
let mut unicorn2 = unicorn::Unicorn::new(Arch::X86, mode)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let emu2 = unicorn2.borrow();
|
||||||
|
assert_eq!(emu2.context_restore(&context), Ok(()));
|
||||||
|
for register in X86_REGISTERS.iter() {
|
||||||
|
println!("Testing register {:?}", register);
|
||||||
|
assert_eq!(
|
||||||
|
emu2.reg_read(*register as i32),
|
||||||
|
emu.reg_read(*register as i32)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn x86_block_callback() {
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct BlockExpectation(u64, u32);
|
||||||
|
let expects = vec![BlockExpectation(0x1000, 2), BlockExpectation(0x1000, 2)];
|
||||||
|
let blocks: Vec<BlockExpectation> = Vec::new();
|
||||||
|
let blocks_cell = Rc::new(RefCell::new(blocks));
|
||||||
|
|
||||||
|
let callback_blocks = blocks_cell.clone();
|
||||||
|
let callback = move |_: Unicorn<'_>, address: u64, size: u32| {
|
||||||
|
let mut blocks = callback_blocks.borrow_mut();
|
||||||
|
blocks.push(BlockExpectation(address, size));
|
||||||
|
};
|
||||||
|
|
||||||
|
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
|
||||||
|
|
||||||
|
let mut unicorn = unicorn::Unicorn::new(Arch::X86, Mode::MODE_32)
|
||||||
|
.expect("failed to initialize unicorn instance");
|
||||||
|
let mut emu = unicorn.borrow();
|
||||||
|
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
|
||||||
|
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
|
||||||
|
|
||||||
|
let hook = emu
|
||||||
|
.add_block_hook(callback)
|
||||||
|
.expect("failed to add block hook");
|
||||||
|
assert_eq!(
|
||||||
|
emu.emu_start(0x1000, 0x1002, 10 * SECOND_SCALE, 1000),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(expects, *blocks_cell.borrow());
|
||||||
|
assert_eq!(emu.remove_hook(hook), Ok(()));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user