Initial commit
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
**/__pycache__/
|
||||
**/.idea/
|
||||
**/.vscode/
|
||||
**/venv/
|
||||
**~
|
||||
**.pyc
|
||||
**.log
|
||||
3
AUTHORS.md
Normal file
3
AUTHORS.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Authors list
|
||||
|
||||
- Boris Batteux
|
||||
75
D810.py
Normal file
75
D810.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import os
|
||||
import idaapi
|
||||
import ida_hexrays
|
||||
import ida_kernwin
|
||||
|
||||
|
||||
from d810.conf import D810Configuration
|
||||
from d810.manager import D810State, D810_LOG_DIR_NAME
|
||||
from d810.log import configure_loggers, clear_logs
|
||||
|
||||
|
||||
class D810Plugin(idaapi.plugin_t):
|
||||
# variables required by IDA
|
||||
flags = 0 # normal plugin
|
||||
wanted_name = "D-810"
|
||||
wanted_hotkey = "Ctrl-Shift-D"
|
||||
comment = "Interface to the D-810 plugin"
|
||||
help = ""
|
||||
initialized = False
|
||||
|
||||
def __init__(self):
|
||||
super(D810Plugin, self).__init__()
|
||||
self.d810_config = None
|
||||
self.state = None
|
||||
self.initialized = False
|
||||
|
||||
|
||||
def reload_plugin(self):
|
||||
if self.initialized:
|
||||
self.term()
|
||||
|
||||
self.d810_config = D810Configuration()
|
||||
|
||||
#TO-DO: if [...].get raises an exception because log_dir is not found, handle exception
|
||||
real_log_dir = os.path.join(self.d810_config.get("log_dir"), D810_LOG_DIR_NAME)
|
||||
|
||||
#TO-DO: if [...].get raises an exception because erase_logs_on_reload is not found, handle exception
|
||||
if self.d810_config.get("erase_logs_on_reload"):
|
||||
clear_logs(real_log_dir)
|
||||
|
||||
configure_loggers(real_log_dir)
|
||||
self.state = D810State(self.d810_config)
|
||||
print("D-810 reloading...")
|
||||
self.state.start_plugin()
|
||||
self.initialized = True
|
||||
|
||||
|
||||
# IDA API methods: init, run, term
|
||||
def init(self):
|
||||
if not ida_hexrays.init_hexrays_plugin():
|
||||
print("D-810 need Hex-Rays decompiler. Skipping")
|
||||
return idaapi.PLUGIN_SKIP
|
||||
|
||||
kv = ida_kernwin.get_kernel_version().split(".")
|
||||
if (int(kv[0]) < 7) or (int(kv[1]) < 5):
|
||||
print("D-810 need IDA version >= 7.5. Skipping")
|
||||
return idaapi.PLUGIN_SKIP
|
||||
|
||||
return idaapi.PLUGIN_OK
|
||||
|
||||
|
||||
def run(self, args):
|
||||
self.reload_plugin()
|
||||
|
||||
|
||||
def term(self):
|
||||
print("Terminating D-810...")
|
||||
if self.state is not None:
|
||||
self.state.stop_plugin()
|
||||
|
||||
self.initialized = False
|
||||
|
||||
|
||||
def PLUGIN_ENTRY():
|
||||
return D810Plugin()
|
||||
661
LICENSE
Normal file
661
LICENSE
Normal file
@@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
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
|
||||
them 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.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey 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;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If 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 convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero 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 that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
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.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
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.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
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
|
||||
state 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 Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
67
README.md
Normal file
67
README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Introduction
|
||||
|
||||
## What is D-810
|
||||
|
||||
D-810 is an IDA Pro plugin which can be used to deobfuscate code at decompilation time by modifying IDA Pro microcode.
|
||||
It was designed with the following goals in mind:
|
||||
|
||||
* It should have as least as possible impact on our standard reverse engineering workflow
|
||||
* Fully integrated to IDA Pro
|
||||
* It should be easily extensible and configurable
|
||||
* Fast creation of new deobfuscation rules
|
||||
* Configurable so that we don't have to modify the source code to use rules for a specific project
|
||||
* Performance impact should be reasonable
|
||||
* Our goal is to be transparent for the reverse engineer
|
||||
* But we don't care if the decompilation of a function takes 1 more second if the resulting code is much more simplier.
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
**Only IDA v7.5 or later is supported** (since we need the microcode Python API)
|
||||
|
||||
Copy this repository in `.idapro/plugins`
|
||||
|
||||
We recommend to install Z3 to be able to use several features of D-810:
|
||||
```bash
|
||||
pip3 install z3-solver
|
||||
```
|
||||
|
||||
# Using D-810
|
||||
|
||||
* Load the plugin by using the `Ctrl-Shift-D` shortcut, you should see this configuration GUI
|
||||
|
||||

|
||||
|
||||
* Choose or create your project configuration
|
||||
* If you are not sure what to do here, leave *default_instruction_only.json*.
|
||||
* Click on the `Start` button to enable deobfuscation
|
||||
* Decompile an obfuscated function, the code should be simplified (hopefully)
|
||||
* When you want to disable deobfuscation, just click on the `Stop` button.
|
||||
|
||||
# Warnings
|
||||
|
||||
This plugin is still in early stage of development, so issues ~~may~~ will happen.
|
||||
|
||||
* Modifying incorrectly IDA microcode may lead IDA to crash. We try to detect that as much as possible to avoid crash, but since it may still happen **save you IDA database often**
|
||||
* We only tested this plugin on Linux, but it should work on Windows too.
|
||||
|
||||
# Documentation
|
||||
|
||||
Work in progress
|
||||
|
||||
Currently, you can read our [blog post](https://eshard.com/posts/) to get some information.
|
||||
|
||||
|
||||
# Licenses
|
||||
|
||||
This library is licensed under LGPL V3 license. See the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## Authors
|
||||
|
||||
See [AUTHORS](AUTHORS.md) for the list of contributors to the project.
|
||||
|
||||
# Acknowledgement
|
||||
|
||||
Rolf Rolles for the huge work he has done with his [HexRaysDeob plugin](https://github.com/RolfRolles/HexRaysDeob) and all the information about Hex-Rays microcode internals described in his [blog post](https://www.hex-rays.com/blog/hex-rays-microcode-api-vs-obfuscating-compiler/). We are still using some part of his plugin in D-810.
|
||||
|
||||
Dennis Elser for the [genmc plugin](https://github.com/patois/genmc) plugin which was very helpful for debugging D-810 errors.
|
||||
0
d810/__init__.py
Normal file
0
d810/__init__.py
Normal file
605
d810/ast.py
Normal file
605
d810/ast.py
Normal file
@@ -0,0 +1,605 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typing import List, Union, Dict, Tuple
|
||||
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.utils import unsigned_to_signed, signed_to_unsigned, \
|
||||
get_add_cf, get_add_of, get_sub_of, get_parity_flag
|
||||
from d810.hexrays_helpers import OPCODES_INFO, MBA_RELATED_OPCODES, Z3_SPECIAL_OPERANDS, MINSN_TO_AST_FORBIDDEN_OPCODES, \
|
||||
equal_mops_ignore_size, AND_TABLE
|
||||
from d810.hexrays_formatters import format_minsn_t, format_mop_t
|
||||
from d810.errors import AstEvaluationException
|
||||
|
||||
logger = logging.getLogger('D810')
|
||||
|
||||
|
||||
def check_and_add_to_list(new_ast: Union[AstNode, AstLeaf], known_ast_list: List[Union[AstNode, AstLeaf]]):
|
||||
is_new_ast_known = False
|
||||
for existing_elt in known_ast_list:
|
||||
if equal_mops_ignore_size(new_ast.mop, existing_elt.mop):
|
||||
new_ast.ast_index = existing_elt.ast_index
|
||||
is_new_ast_known = True
|
||||
break
|
||||
|
||||
if not is_new_ast_known:
|
||||
ast_index = len(known_ast_list)
|
||||
new_ast.ast_index = ast_index
|
||||
known_ast_list.append(new_ast)
|
||||
|
||||
|
||||
def mop_to_ast_internal(mop: mop_t, ast_list: List[Union[AstNode, AstLeaf]]) -> Union[None, AstNode, AstLeaf]:
|
||||
if mop is None:
|
||||
return None
|
||||
|
||||
if mop.t != mop_d or (mop.d.opcode not in MBA_RELATED_OPCODES):
|
||||
tree = AstLeaf(format_mop_t(mop))
|
||||
tree.mop = mop
|
||||
dest_size = mop.size if mop.t != mop_d else mop.d.d.size
|
||||
tree.dest_size = dest_size
|
||||
else:
|
||||
left_ast = mop_to_ast_internal(mop.d.l, ast_list)
|
||||
right_ast = mop_to_ast_internal(mop.d.r, ast_list)
|
||||
dst_ast = mop_to_ast_internal(mop.d.d, ast_list)
|
||||
tree = AstNode(mop.d.opcode, left_ast, right_ast, dst_ast)
|
||||
tree.mop = mop
|
||||
tree.dest_size = mop.d.d.size
|
||||
tree.ea = mop.d.ea
|
||||
|
||||
check_and_add_to_list(tree, ast_list)
|
||||
return tree
|
||||
|
||||
|
||||
def mop_to_ast(mop: mop_t) -> Union[None, AstNode, AstLeaf]:
|
||||
mop_ast = mop_to_ast_internal(mop, [])
|
||||
mop_ast.compute_sub_ast()
|
||||
return mop_ast
|
||||
|
||||
|
||||
def minsn_to_ast(instruction: minsn_t) -> Union[None, AstNode, AstLeaf]:
|
||||
try:
|
||||
if instruction.opcode in MINSN_TO_AST_FORBIDDEN_OPCODES:
|
||||
# To avoid error 50278
|
||||
return None
|
||||
|
||||
ins_mop = mop_t()
|
||||
ins_mop.create_from_insn(instruction)
|
||||
|
||||
if instruction.opcode == m_mov:
|
||||
tmp = AstNode(m_mov, mop_to_ast(ins_mop))
|
||||
tmp.mop = ins_mop
|
||||
tmp.dest_size = instruction.d.size
|
||||
tmp.ea = instruction.ea
|
||||
tmp.dst_mop = instruction.d
|
||||
return tmp
|
||||
|
||||
tmp = mop_to_ast(ins_mop)
|
||||
tmp.dst_mop = instruction.d
|
||||
return tmp
|
||||
except RuntimeError as e:
|
||||
logger.error("Error while transforming instruction {0}: {1}".format(format_minsn_t(instruction), e))
|
||||
return None
|
||||
|
||||
|
||||
class AstInfo(object):
|
||||
def __init__(self, ast: Union[AstNode, AstLeaf], number_of_use: int):
|
||||
self.ast = ast
|
||||
self.number_of_use = number_of_use
|
||||
|
||||
def __str__(self):
|
||||
return "{0} used {1} times: {2}".format(self.ast, self.number_of_use, format_mop_t(self.ast.mop))
|
||||
|
||||
|
||||
class AstNode(dict):
|
||||
def __init__(self, opcode, left=None, right=None, dst=None):
|
||||
super(dict, self).__init__()
|
||||
self.opcode = opcode
|
||||
self.left = left
|
||||
self.right = right
|
||||
self.dst = dst
|
||||
self.dst_mop = None
|
||||
|
||||
self.opcodes = []
|
||||
self.mop = None
|
||||
self.is_candidate_ok = False
|
||||
|
||||
self.leafs = []
|
||||
self.leafs_by_name = {}
|
||||
|
||||
self.ast_index = 0
|
||||
self.sub_ast_info_by_index = {}
|
||||
|
||||
self.dest_size = None
|
||||
self.ea = None
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self.mop.d.d.size
|
||||
|
||||
def compute_sub_ast(self):
|
||||
self.sub_ast_info_by_index = {}
|
||||
self.sub_ast_info_by_index[self.ast_index] = AstInfo(self, 1)
|
||||
|
||||
if self.left is not None:
|
||||
self.left.compute_sub_ast()
|
||||
for ast_index, ast_info in self.left.sub_ast_info_by_index.items():
|
||||
if ast_index not in self.sub_ast_info_by_index.keys():
|
||||
self.sub_ast_info_by_index[ast_index] = AstInfo(ast_info.ast, 0)
|
||||
self.sub_ast_info_by_index[ast_index].number_of_use += ast_info.number_of_use
|
||||
|
||||
if self.right is not None:
|
||||
self.right.compute_sub_ast()
|
||||
for ast_index, ast_info in self.right.sub_ast_info_by_index.items():
|
||||
if ast_index not in self.sub_ast_info_by_index.keys():
|
||||
self.sub_ast_info_by_index[ast_index] = AstInfo(ast_info.ast, 0)
|
||||
self.sub_ast_info_by_index[ast_index].number_of_use += ast_info.number_of_use
|
||||
|
||||
def get_information(self):
|
||||
leaf_info_list = []
|
||||
cst_list = []
|
||||
opcode_list = []
|
||||
self.compute_sub_ast()
|
||||
|
||||
for _, ast_info in self.sub_ast_info_by_index.items():
|
||||
if (ast_info.ast.mop is not None) and (ast_info.ast.mop.t != mop_z):
|
||||
if ast_info.ast.is_leaf():
|
||||
if ast_info.ast.is_constant():
|
||||
cst_list.append(ast_info.ast.mop.nnn.value)
|
||||
else:
|
||||
leaf_info_list.append(ast_info)
|
||||
else:
|
||||
opcode_list += [ast_info.ast.opcode] * ast_info.number_of_use
|
||||
|
||||
return leaf_info_list, cst_list, opcode_list
|
||||
|
||||
def __getitem__(self, k) -> AstLeaf:
|
||||
return self.leafs_by_name[k]
|
||||
|
||||
def get_leaf_list(self) -> List[AstLeaf]:
|
||||
leafs = []
|
||||
if self.left is not None:
|
||||
leafs += self.left.get_leaf_list()
|
||||
if self.right is not None:
|
||||
leafs += self.right.get_leaf_list()
|
||||
return leafs
|
||||
|
||||
def is_leaf(self) -> bool:
|
||||
# An AstNode is not a leaf, so returns False
|
||||
return False
|
||||
|
||||
def add_leaf(self, leaf_name: str, leaf_mop: mop_t):
|
||||
leaf = AstLeaf(leaf_name)
|
||||
leaf.mop = leaf_mop
|
||||
self.leafs.append(leaf)
|
||||
self.leafs_by_name[leaf_name] = leaf
|
||||
|
||||
def add_constant_leaf(self, leaf_name: str, cst_value: int, cst_size: int):
|
||||
cst_mop = mop_t()
|
||||
cst_mop.make_number(cst_value & AND_TABLE[cst_size], cst_size)
|
||||
self.add_leaf(leaf_name, cst_mop)
|
||||
|
||||
def check_pattern_and_copy_mops(self, ast: Union[AstNode, AstLeaf]) -> bool:
|
||||
self.reset_mops()
|
||||
is_matching_shape = self._copy_mops_from_ast(ast)
|
||||
if not is_matching_shape:
|
||||
return False
|
||||
return self._check_implicit_equalities()
|
||||
|
||||
def reset_mops(self):
|
||||
self.mop = None
|
||||
if self.left is not None:
|
||||
self.left.reset_mops()
|
||||
if self.right is not None:
|
||||
self.right.reset_mops()
|
||||
|
||||
def _copy_mops_from_ast(self, other: Union[AstNode, AstLeaf]) -> bool:
|
||||
self.mop = other.mop
|
||||
self.dst_mop = other.dst_mop
|
||||
self.dest_size = other.dest_size
|
||||
self.ea = other.ea
|
||||
|
||||
if not isinstance(other, AstNode):
|
||||
return False
|
||||
if self.opcode != other.opcode:
|
||||
return False
|
||||
if self.left is not None:
|
||||
if not self.left._copy_mops_from_ast(other.left):
|
||||
return False
|
||||
if self.right is not None:
|
||||
if not self.right._copy_mops_from_ast(other.right):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_implicit_equalities(self) -> bool:
|
||||
self.leafs = self.get_leaf_list()
|
||||
self.leafs_by_name = {}
|
||||
self.is_candidate_ok = True
|
||||
|
||||
for leaf in self.leafs:
|
||||
ref_leaf = self.leafs_by_name.get(leaf.name)
|
||||
if ref_leaf is not None:
|
||||
if not equal_mops_ignore_size(ref_leaf.mop, leaf.mop):
|
||||
self.is_candidate_ok = False
|
||||
self.leafs_by_name[leaf.name] = leaf
|
||||
return self.is_candidate_ok
|
||||
|
||||
def update_leafs_mop(self, other: Union[AstNode, AstLeaf], other2: Union[None, AstNode, AstLeaf] = None) -> bool:
|
||||
self.leafs = self.get_leaf_list()
|
||||
all_leafs_found = True
|
||||
for leaf in self.leafs:
|
||||
if leaf.name in other.leafs_by_name.keys():
|
||||
leaf.mop = other.leafs_by_name[leaf.name].mop
|
||||
elif (other2 is not None) and (leaf.name in other2.leafs_by_name.keys()):
|
||||
leaf.mop = other2.leafs_by_name[leaf.name].mop
|
||||
else:
|
||||
all_leafs_found = False
|
||||
return all_leafs_found
|
||||
|
||||
def create_mop(self, ea: int) -> mop_t:
|
||||
new_ins = self.create_minsn(ea)
|
||||
new_ins_mop = mop_t()
|
||||
new_ins_mop.create_from_insn(new_ins)
|
||||
return new_ins_mop
|
||||
|
||||
def create_minsn(self, ea: int, dest=None) -> minsn_t:
|
||||
new_ins = minsn_t(ea)
|
||||
new_ins.opcode = self.opcode
|
||||
|
||||
if self.left is not None:
|
||||
new_ins.l = self.left.create_mop(ea)
|
||||
if self.right is not None:
|
||||
new_ins.r = self.right.create_mop(ea)
|
||||
|
||||
new_ins.d = mop_t()
|
||||
|
||||
if self.left is not None:
|
||||
new_ins.d.size = new_ins.l.size
|
||||
if dest is not None:
|
||||
new_ins.d = dest
|
||||
return new_ins
|
||||
|
||||
def get_pattern(self) -> str:
|
||||
nb_operands = OPCODES_INFO[self.opcode]["nb_operands"]
|
||||
if nb_operands == 0:
|
||||
return "AstNode({0})".format(OPCODES_INFO[self.opcode]["name"])
|
||||
elif nb_operands == 1:
|
||||
return "AstNode(m_{0}, {1})".format(OPCODES_INFO[self.opcode]["name"], self.left.get_pattern())
|
||||
elif nb_operands == 2:
|
||||
return "AstNode(m_{0}, {1}, {2})" \
|
||||
.format(OPCODES_INFO[self.opcode]["name"], self.left.get_pattern(), self.right.get_pattern())
|
||||
|
||||
def evaluate_with_leaf_info(self, leafs_info, leafs_value):
|
||||
dict_index_to_value = {leaf_info.ast.ast_index: leaf_value for leaf_info, leaf_value in
|
||||
zip(leafs_info, leafs_value)}
|
||||
res = self.evaluate(dict_index_to_value)
|
||||
return res
|
||||
|
||||
def evaluate(self, dict_index_to_value):
|
||||
if self.ast_index in dict_index_to_value:
|
||||
return dict_index_to_value[self.ast_index]
|
||||
res_mask = AND_TABLE[self.dest_size]
|
||||
if self.opcode == m_mov:
|
||||
return (self.left.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_neg:
|
||||
return (- self.left.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_lnot:
|
||||
return self.left.evaluate(dict_index_to_value) != 0
|
||||
elif self.opcode == m_bnot:
|
||||
return (self.left.evaluate(dict_index_to_value) ^ res_mask) & res_mask
|
||||
elif self.opcode == m_xds:
|
||||
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
|
||||
return signed_to_unsigned(left_value_signed, self.dest_size) & res_mask
|
||||
elif self.opcode == m_xdu:
|
||||
return (self.left.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_low:
|
||||
return (self.left.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_add:
|
||||
return (self.left.evaluate(dict_index_to_value) + self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_sub:
|
||||
return (self.left.evaluate(dict_index_to_value) - self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_mul:
|
||||
return (self.left.evaluate(dict_index_to_value) * self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_udiv:
|
||||
return (self.left.evaluate(dict_index_to_value) // self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_sdiv:
|
||||
return (self.left.evaluate(dict_index_to_value) // self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_umod:
|
||||
return (self.left.evaluate(dict_index_to_value) % self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_smod:
|
||||
return (self.left.evaluate(dict_index_to_value) % self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_or:
|
||||
return (self.left.evaluate(dict_index_to_value) | self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_and:
|
||||
return (self.left.evaluate(dict_index_to_value) & self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_xor:
|
||||
return (self.left.evaluate(dict_index_to_value) ^ self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_shl:
|
||||
return (self.left.evaluate(dict_index_to_value) << self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_shr:
|
||||
return (self.left.evaluate(dict_index_to_value) >> self.right.evaluate(dict_index_to_value)) & res_mask
|
||||
elif self.opcode == m_sar:
|
||||
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
|
||||
res_signed = left_value_signed >> self.right.evaluate(dict_index_to_value)
|
||||
return signed_to_unsigned(res_signed, self.dest_size) & res_mask
|
||||
elif self.opcode == m_cfadd:
|
||||
tmp = get_add_cf(self.left.evaluate(dict_index_to_value), self.right.evaluate(dict_index_to_value),
|
||||
self.left.dest_size)
|
||||
return tmp & res_mask
|
||||
elif self.opcode == m_ofadd:
|
||||
tmp = get_add_of(self.left.evaluate(dict_index_to_value), self.right.evaluate(dict_index_to_value),
|
||||
self.left.dest_size)
|
||||
return tmp & res_mask
|
||||
elif self.opcode == m_sets:
|
||||
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
|
||||
res = 1 if left_value_signed < 0 else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_seto:
|
||||
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
|
||||
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
|
||||
sub_overflow = get_sub_of(left_value_signed, right_value_signed, self.left.dest_size)
|
||||
return sub_overflow & res_mask
|
||||
elif self.opcode == m_setnz:
|
||||
res = 1 if self.left.evaluate(dict_index_to_value) != self.right.evaluate(dict_index_to_value) else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_setz:
|
||||
res = 1 if self.left.evaluate(dict_index_to_value) == self.right.evaluate(dict_index_to_value) else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_setae:
|
||||
res = 1 if self.left.evaluate(dict_index_to_value) >= self.right.evaluate(dict_index_to_value) else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_setb:
|
||||
res = 1 if self.left.evaluate(dict_index_to_value) < self.right.evaluate(dict_index_to_value) else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_seta:
|
||||
res = 1 if self.left.evaluate(dict_index_to_value) > self.right.evaluate(dict_index_to_value) else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_setbe:
|
||||
res = 1 if self.left.evaluate(dict_index_to_value) <= self.right.evaluate(dict_index_to_value) else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_setg:
|
||||
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
|
||||
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
|
||||
res = 1 if left_value_signed > right_value_signed else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_setge:
|
||||
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
|
||||
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
|
||||
res = 1 if left_value_signed >= right_value_signed else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_setl:
|
||||
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
|
||||
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
|
||||
res = 1 if left_value_signed < right_value_signed else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_setle:
|
||||
left_value_signed = unsigned_to_signed(self.left.evaluate(dict_index_to_value), self.left.dest_size)
|
||||
right_value_signed = unsigned_to_signed(self.right.evaluate(dict_index_to_value), self.right.dest_size)
|
||||
res = 1 if left_value_signed <= right_value_signed else 0
|
||||
return res & res_mask
|
||||
elif self.opcode == m_setp:
|
||||
res = get_parity_flag(self.left.evaluate(dict_index_to_value), self.right.evaluate(dict_index_to_value),
|
||||
self.left.dest_size)
|
||||
return res & res_mask
|
||||
else:
|
||||
raise AstEvaluationException("Can't evaluate opcode: {0}".format(self.opcode))
|
||||
|
||||
def get_depth_signature(self, depth):
|
||||
if depth == 1:
|
||||
return ["{0}".format(self.opcode)]
|
||||
tmp = []
|
||||
nb_operands = OPCODES_INFO[self.opcode]["nb_operands"]
|
||||
if (nb_operands >= 1) and self.left is not None:
|
||||
tmp += self.left.get_depth_signature(depth - 1)
|
||||
else:
|
||||
tmp += ["N"] * (2 ** (depth - 2))
|
||||
if (nb_operands >= 2) and self.right is not None:
|
||||
tmp += self.right.get_depth_signature(depth - 1)
|
||||
else:
|
||||
tmp += ["N"] * (2 ** (depth - 2))
|
||||
return tmp
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
nb_operands = OPCODES_INFO[self.opcode]["nb_operands"]
|
||||
if "symbol" in OPCODES_INFO[self.opcode].keys():
|
||||
if nb_operands == 0:
|
||||
return "{0}()".format(OPCODES_INFO[self.opcode]["symbol"])
|
||||
elif nb_operands == 1:
|
||||
return "{0}({1})".format(OPCODES_INFO[self.opcode]["symbol"], self.left)
|
||||
elif nb_operands == 2:
|
||||
if OPCODES_INFO[self.opcode]["symbol"] not in Z3_SPECIAL_OPERANDS:
|
||||
return "({1} {0} {2})".format(OPCODES_INFO[self.opcode]["symbol"], self.left, self.right)
|
||||
else:
|
||||
return "{0}({1}, {2})".format(OPCODES_INFO[self.opcode]["symbol"], self.left, self.right)
|
||||
else:
|
||||
if nb_operands == 0:
|
||||
return "{0}()".format(OPCODES_INFO[self.opcode]["name"])
|
||||
elif nb_operands == 1:
|
||||
return "{0}({1})".format(OPCODES_INFO[self.opcode]["name"], self.left)
|
||||
elif nb_operands == 2:
|
||||
return "{0}({1}, {2})".format(OPCODES_INFO[self.opcode]["name"], self.left, self.right)
|
||||
return "Error_AstNode"
|
||||
except RuntimeError as e:
|
||||
logger.info("Error while calling __str__ on AstNode: {0}".format(e))
|
||||
return "Error_AstNode"
|
||||
|
||||
|
||||
class AstLeaf(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.ast_index = None
|
||||
|
||||
self.mop = None
|
||||
self.z3_var = None
|
||||
self.z3_var_name = None
|
||||
|
||||
self.dest_size = None
|
||||
self.ea = None
|
||||
|
||||
self.sub_ast_info_by_index = {}
|
||||
|
||||
def __getitem__(self, name):
|
||||
if name == self.name:
|
||||
return self
|
||||
raise KeyError
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
return self.mop.size
|
||||
|
||||
@property
|
||||
def dst_mop(self):
|
||||
return self.mop
|
||||
|
||||
@dst_mop.setter
|
||||
def dst_mop(self, mop):
|
||||
self.mop = mop
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
if self.is_constant():
|
||||
return self.mop.nnn.value
|
||||
else:
|
||||
return None
|
||||
|
||||
def compute_sub_ast(self):
|
||||
self.sub_ast_info_by_index = {}
|
||||
self.sub_ast_info_by_index[self.ast_index] = AstInfo(self, 1)
|
||||
|
||||
def get_information(self):
|
||||
# Just here to allow calling get_information on either a AstNode or AstLeaf
|
||||
return [], [], []
|
||||
|
||||
def get_leaf_list(self):
|
||||
return [self]
|
||||
|
||||
def is_leaf(self):
|
||||
return True
|
||||
|
||||
def is_constant(self):
|
||||
if self.mop is None:
|
||||
return False
|
||||
return self.mop.t == mop_n
|
||||
|
||||
def create_mop(self, ea):
|
||||
# Currently, we are not creating a new mop but returning the one defined
|
||||
return self.mop
|
||||
|
||||
def update_leafs_mop(self, other, other2=None):
|
||||
if self.name in other.leafs_by_name.keys():
|
||||
self.mop = other.leafs_by_name[self.name].mop
|
||||
return True
|
||||
elif (other2 is not None) and (self.name in other2.leafs_by_name.keys()):
|
||||
self.mop = other2.leafs_by_name[self.name].mop
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_pattern_and_copy_mops(self, ast):
|
||||
self.reset_mops()
|
||||
is_matching_shape = self._copy_mops_from_ast(ast)
|
||||
|
||||
if not is_matching_shape:
|
||||
return False
|
||||
return self._check_implicit_equalities()
|
||||
|
||||
def reset_mops(self):
|
||||
self.z3_var = None
|
||||
self.z3_var_name = None
|
||||
self.mop = None
|
||||
|
||||
def _copy_mops_from_ast(self, other):
|
||||
self.mop = other.mop
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _check_implicit_equalities():
|
||||
# An AstLeaf does not have any implicit equalities to be checked, so we always returns True
|
||||
return True
|
||||
|
||||
def get_pattern(self):
|
||||
if self.is_constant():
|
||||
return "AstConstant('{0}', {0})".format(self.mop.nnn.value)
|
||||
if self.ast_index is not None:
|
||||
return "AstLeaf('x_{0}')".format(self.ast_index)
|
||||
if self.name is not None:
|
||||
return "AstLeaf('{0}')".format(self.name)
|
||||
|
||||
def evaluate_with_leaf_info(self, leafs_info, leafs_value):
|
||||
dict_index_to_value = {leaf_info.ast.ast_index: leaf_value for leaf_info, leaf_value in
|
||||
zip(leafs_info, leafs_value)}
|
||||
res = self.evaluate(dict_index_to_value)
|
||||
return res
|
||||
|
||||
def evaluate(self, dict_index_to_value):
|
||||
if self.is_constant():
|
||||
return self.mop.nnn.value
|
||||
return dict_index_to_value.get(self.ast_index)
|
||||
|
||||
def get_depth_signature(self, depth):
|
||||
if depth == 1:
|
||||
if self.is_constant():
|
||||
return ["C"]
|
||||
return ["L"]
|
||||
else:
|
||||
return ["N"] * (2 ** (depth - 1))
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
if self.is_constant():
|
||||
return "{0}".format(self.mop.nnn.value)
|
||||
if self.z3_var_name is not None:
|
||||
return self.z3_var_name
|
||||
if self.ast_index is not None:
|
||||
return "x_{0}".format(self.ast_index)
|
||||
if self.mop is not None:
|
||||
return format_mop_t(self.mop)
|
||||
return self.name
|
||||
except RuntimeError as e:
|
||||
logger.info("Error while calling __str__ on AstLeaf: {0}".format(e))
|
||||
return "Error_AstLeaf"
|
||||
|
||||
|
||||
class AstConstant(AstLeaf):
|
||||
def __init__(self, name, expected_value=None, expected_size=None):
|
||||
super().__init__(name)
|
||||
self.expected_value = expected_value
|
||||
self.expected_size = expected_size
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.mop.nnn.value
|
||||
|
||||
def is_constant(self):
|
||||
# An AstConstant is always constant, so return True
|
||||
return True
|
||||
|
||||
def _copy_mops_from_ast(self, other):
|
||||
if other.mop is not None and other.mop.t != mop_n:
|
||||
return False
|
||||
|
||||
self.mop = other.mop
|
||||
if self.expected_value is None:
|
||||
return True
|
||||
return self.expected_value == other.mop.nnn.value
|
||||
|
||||
def evaluate(self, dict_index_to_value=None):
|
||||
if self.mop is not None and self.mop.t == mop_n:
|
||||
return self.mop.nnn.value
|
||||
return self.expected_value
|
||||
|
||||
def get_depth_signature(self, depth):
|
||||
if depth == 1:
|
||||
return ["C"]
|
||||
else:
|
||||
return ["N"] * (2 ** (depth - 1))
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
if self.mop is not None and self.mop.t == mop_n:
|
||||
return "0x{0:x}".format(self.mop.nnn.value)
|
||||
if self.expected_value is not None:
|
||||
return "0x{0:x}".format(self.expected_value)
|
||||
return self.name
|
||||
except RuntimeError as e:
|
||||
logger.info("Error while calling __str__ on AstConstant: {0}".format(e))
|
||||
return "Error_AstConstant"
|
||||
473
d810/cfg_utils.py
Normal file
473
d810/cfg_utils.py
Normal file
@@ -0,0 +1,473 @@
|
||||
import logging
|
||||
from ida_hexrays import *
|
||||
from typing import List, Union, Dict, Tuple
|
||||
|
||||
from d810.errors import ControlFlowException
|
||||
from d810.hexrays_helpers import CONDITIONAL_JUMP_OPCODES
|
||||
from d810.hexrays_formatters import block_printer
|
||||
|
||||
|
||||
helper_logger = logging.getLogger('D810.helper')
|
||||
|
||||
|
||||
def log_block_info(blk: mblock_t, logger_func=helper_logger.info):
|
||||
if blk is None:
|
||||
logger_func("Block is None")
|
||||
return
|
||||
vp = block_printer()
|
||||
blk._print(vp)
|
||||
logger_func("Block {0} with successors {1} and predecessors {2}:\n{3}"
|
||||
.format(blk.serial, [x for x in blk.succset], [x for x in blk.predset], vp.get_block_mc()))
|
||||
|
||||
|
||||
def insert_goto_instruction(blk: mblock_t, goto_blk_serial: int, nop_previous_instruction=False):
|
||||
if blk.tail is not None:
|
||||
goto_ins = minsn_t(blk.tail)
|
||||
else:
|
||||
goto_ins = minsn_t(blk.start)
|
||||
|
||||
if nop_previous_instruction:
|
||||
blk.make_nop(blk.tail)
|
||||
blk.insert_into_block(goto_ins, blk.tail)
|
||||
|
||||
# We nop instruction before setting it to goto to avoid error 52123
|
||||
blk.make_nop(blk.tail)
|
||||
goto_ins.opcode = m_goto
|
||||
goto_ins.l = mop_t()
|
||||
goto_ins.l.make_blkref(goto_blk_serial)
|
||||
|
||||
|
||||
def change_1way_call_block_successor(call_blk: mblock_t, call_blk_successor_serial: int) -> bool:
|
||||
if call_blk.nsucc() != 1:
|
||||
return False
|
||||
|
||||
mba = call_blk.mba
|
||||
previous_call_blk_successor_serial = call_blk.succset[0]
|
||||
previous_call_blk_successor = mba.get_mblock(previous_call_blk_successor_serial)
|
||||
|
||||
nop_blk = insert_nop_blk(call_blk)
|
||||
insert_goto_instruction(nop_blk, call_blk_successor_serial, nop_previous_instruction=True)
|
||||
is_ok = change_1way_block_successor(nop_blk, call_blk_successor_serial)
|
||||
if not is_ok:
|
||||
return False
|
||||
|
||||
# Bookkeeping
|
||||
call_blk.succset._del(previous_call_blk_successor_serial)
|
||||
call_blk.succset.push_back(nop_blk.serial)
|
||||
call_blk.mark_lists_dirty()
|
||||
|
||||
previous_call_blk_successor.predset._del(call_blk.serial)
|
||||
if previous_call_blk_successor.serial != mba.qty - 1:
|
||||
previous_call_blk_successor.mark_lists_dirty()
|
||||
|
||||
mba.mark_chains_dirty()
|
||||
try:
|
||||
mba.verify(True)
|
||||
return True
|
||||
except RuntimeError as e:
|
||||
helper_logger.error("Error in change_1way_block_successor: {0}".format(e))
|
||||
log_block_info(call_blk, helper_logger.error)
|
||||
log_block_info(nop_blk, helper_logger.error)
|
||||
raise e
|
||||
|
||||
|
||||
def change_1way_block_successor(blk: mblock_t, blk_successor_serial: int) -> bool:
|
||||
if blk.nsucc() != 1:
|
||||
return False
|
||||
|
||||
mba: mbl_array_t = blk.mba
|
||||
previous_blk_successor_serial = blk.succset[0]
|
||||
previous_blk_successor = mba.get_mblock(previous_blk_successor_serial)
|
||||
|
||||
if blk.tail is None:
|
||||
# We add a goto instruction
|
||||
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=False)
|
||||
elif blk.tail.opcode == m_goto:
|
||||
# We change goto target directly
|
||||
blk.tail.l.make_blkref(blk_successor_serial)
|
||||
elif blk.tail.opcode == m_ijmp:
|
||||
# We replace ijmp instruction with goto instruction
|
||||
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=True)
|
||||
elif blk.tail.opcode == m_call:
|
||||
# Before maturity MMAT_CALLS, we can't add a goto after a call instruction
|
||||
if mba.maturity < MMAT_CALLS:
|
||||
return change_1way_call_block_successor(blk, blk_successor_serial)
|
||||
else:
|
||||
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=False)
|
||||
else:
|
||||
# We add a goto instruction
|
||||
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=False)
|
||||
|
||||
# Update block properties
|
||||
blk.type = BLT_1WAY
|
||||
blk.flags |= MBL_GOTO
|
||||
|
||||
# Bookkeeping
|
||||
blk.succset._del(previous_blk_successor_serial)
|
||||
blk.succset.push_back(blk_successor_serial)
|
||||
blk.mark_lists_dirty()
|
||||
|
||||
previous_blk_successor.predset._del(blk.serial)
|
||||
if previous_blk_successor.serial != mba.qty - 1:
|
||||
previous_blk_successor.mark_lists_dirty()
|
||||
|
||||
new_blk_successor = blk.mba.get_mblock(blk_successor_serial)
|
||||
new_blk_successor.predset.push_back(blk.serial)
|
||||
|
||||
if new_blk_successor.serial != mba.qty - 1:
|
||||
new_blk_successor.mark_lists_dirty()
|
||||
|
||||
mba.mark_chains_dirty()
|
||||
try:
|
||||
mba.verify(True)
|
||||
return True
|
||||
except RuntimeError as e:
|
||||
helper_logger.error("Error in change_1way_block_successor: {0}".format(e))
|
||||
log_block_info(blk, helper_logger.error)
|
||||
log_block_info(new_blk_successor, helper_logger.error)
|
||||
log_block_info(previous_blk_successor, helper_logger.error)
|
||||
raise e
|
||||
|
||||
|
||||
def change_0way_block_successor(blk: mblock_t, blk_successor_serial: int) -> bool:
|
||||
if blk.nsucc() != 0:
|
||||
return False
|
||||
mba = blk.mba
|
||||
|
||||
if blk.tail.opcode == m_ijmp:
|
||||
# We replace ijmp instruction with goto instruction
|
||||
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=True)
|
||||
else:
|
||||
# We add a goto instruction
|
||||
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=False)
|
||||
|
||||
# Update block properties
|
||||
blk.type = BLT_1WAY
|
||||
blk.flags |= MBL_GOTO
|
||||
|
||||
# Bookkeeping
|
||||
blk.succset.push_back(blk_successor_serial)
|
||||
blk.mark_lists_dirty()
|
||||
|
||||
new_blk_successor = blk.mba.get_mblock(blk_successor_serial)
|
||||
new_blk_successor.predset.push_back(blk.serial)
|
||||
if new_blk_successor.serial != mba.qty - 1:
|
||||
new_blk_successor.mark_lists_dirty()
|
||||
|
||||
mba.mark_chains_dirty()
|
||||
try:
|
||||
mba.verify(True)
|
||||
return True
|
||||
except RuntimeError as e:
|
||||
helper_logger.error("Error in change_0way_block_successor: {0}".format(e))
|
||||
log_block_info(blk, helper_logger.error)
|
||||
log_block_info(new_blk_successor, helper_logger.error)
|
||||
raise e
|
||||
|
||||
|
||||
def change_2way_block_conditional_successor(blk: mblock_t, blk_successor_serial: int) -> bool:
|
||||
if blk.nsucc() != 2:
|
||||
return False
|
||||
|
||||
mba = blk.mba
|
||||
previous_blk_conditional_successor_serial = blk.tail.d.b
|
||||
previous_blk_conditional_successor = mba.get_mblock(previous_blk_conditional_successor_serial)
|
||||
|
||||
blk.tail.d = mop_t()
|
||||
blk.tail.d.make_blkref(blk_successor_serial)
|
||||
|
||||
# Bookkeeping
|
||||
blk.succset._del(previous_blk_conditional_successor_serial)
|
||||
blk.succset.push_back(blk_successor_serial)
|
||||
blk.mark_lists_dirty()
|
||||
|
||||
previous_blk_conditional_successor.predset._del(blk.serial)
|
||||
if previous_blk_conditional_successor.serial != mba.qty - 1:
|
||||
previous_blk_conditional_successor.mark_lists_dirty()
|
||||
|
||||
new_blk_conditional_successor = blk.mba.get_mblock(blk_successor_serial)
|
||||
new_blk_conditional_successor.predset.push_back(blk.serial)
|
||||
if new_blk_conditional_successor.serial != mba.qty - 1:
|
||||
new_blk_conditional_successor.mark_lists_dirty()
|
||||
|
||||
# Step4: Final stuff and checks
|
||||
mba.mark_chains_dirty()
|
||||
try:
|
||||
mba.verify(True)
|
||||
except RuntimeError as e:
|
||||
helper_logger.error("Error in change_2way_block_conditional_successor: {0}".format(e))
|
||||
log_block_info(blk, helper_logger.error)
|
||||
log_block_info(new_blk_conditional_successor, helper_logger.error)
|
||||
raise e
|
||||
|
||||
|
||||
def update_blk_successor(blk: mblock_t, old_successor_serial: int, new_successor_serial: int) -> int:
|
||||
if blk.nsucc() == 1:
|
||||
change_1way_block_successor(blk, new_successor_serial)
|
||||
elif blk.nsucc() == 2:
|
||||
if old_successor_serial == blk.serial + 1:
|
||||
helper_logger.info("Can't update direct block successor: {0} - {1} - {2}"
|
||||
.format(blk.serial, old_successor_serial, new_successor_serial))
|
||||
return 0
|
||||
else:
|
||||
change_2way_block_conditional_successor(blk, new_successor_serial)
|
||||
else:
|
||||
helper_logger.info("Can't update block successor: {0} ".format(blk.serial))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
def make_2way_block_goto(blk: mblock_t, blk_successor_serial: int) -> bool:
|
||||
if blk.nsucc() != 2:
|
||||
return False
|
||||
mba = blk.mba
|
||||
previous_blk_successor_serials = [x for x in blk.succset]
|
||||
previous_blk_successors = [mba.get_mblock(x) for x in previous_blk_successor_serials]
|
||||
|
||||
insert_goto_instruction(blk, blk_successor_serial, nop_previous_instruction=True)
|
||||
|
||||
# Update block properties
|
||||
blk.type = BLT_1WAY
|
||||
blk.flags |= MBL_GOTO
|
||||
|
||||
# Bookkeeping
|
||||
for prev_serial in previous_blk_successor_serials:
|
||||
blk.succset._del(prev_serial)
|
||||
blk.succset.push_back(blk_successor_serial)
|
||||
blk.mark_lists_dirty()
|
||||
|
||||
for prev_blk in previous_blk_successors:
|
||||
prev_blk.predset._del(blk.serial)
|
||||
if prev_blk.serial != mba.qty - 1:
|
||||
prev_blk.mark_lists_dirty()
|
||||
|
||||
new_blk_successor = blk.mba.get_mblock(blk_successor_serial)
|
||||
new_blk_successor.predset.push_back(blk.serial)
|
||||
if new_blk_successor.serial != mba.qty - 1:
|
||||
new_blk_successor.mark_lists_dirty()
|
||||
|
||||
mba.mark_chains_dirty()
|
||||
try:
|
||||
mba.verify(True)
|
||||
return True
|
||||
except RuntimeError as e:
|
||||
helper_logger.error("Error in make_2way_block_goto: {0}".format(e))
|
||||
log_block_info(blk, helper_logger.error)
|
||||
log_block_info(new_blk_successor, helper_logger.error)
|
||||
raise e
|
||||
|
||||
|
||||
def create_block(blk: mblock_t, blk_ins: List[minsn_t], is_0_way: bool = False) -> mblock_t:
|
||||
mba = blk.mba
|
||||
new_blk = insert_nop_blk(blk)
|
||||
for ins in blk_ins:
|
||||
tmp_ins = minsn_t(ins)
|
||||
new_blk.insert_into_block(tmp_ins, new_blk.tail)
|
||||
|
||||
if is_0_way:
|
||||
new_blk.type = BLT_0WAY
|
||||
# Bookkeeping
|
||||
prev_successor_serial = new_blk.succset[0]
|
||||
new_blk.succset._del(prev_successor_serial)
|
||||
prev_succ = mba.get_mblock(prev_successor_serial)
|
||||
prev_succ.predset._del(new_blk.serial)
|
||||
if prev_succ.serial != mba.qty - 1:
|
||||
prev_succ.mark_lists_dirty()
|
||||
|
||||
new_blk.mark_lists_dirty()
|
||||
mba.mark_chains_dirty()
|
||||
try:
|
||||
mba.verify(True)
|
||||
return new_blk
|
||||
except RuntimeError as e:
|
||||
helper_logger.error("Error in create_block: {0}".format(e))
|
||||
log_block_info(new_blk, helper_logger.error)
|
||||
raise e
|
||||
|
||||
|
||||
def update_block_successors(blk: mblock_t, blk_succ_serial_list: List[int]):
|
||||
mba = blk.mba
|
||||
if len(blk_succ_serial_list) == 0:
|
||||
blk.type = BLT_0WAY
|
||||
elif len(blk_succ_serial_list) == 1:
|
||||
blk.type = BLT_1WAY
|
||||
elif len(blk_succ_serial_list) == 2:
|
||||
blk.type = BLT_2WAY
|
||||
else:
|
||||
raise
|
||||
|
||||
# Remove old successors
|
||||
prev_successor_serials = [x for x in blk.succset]
|
||||
for prev_successor_serial in prev_successor_serials:
|
||||
blk.succset._del(prev_successor_serial)
|
||||
prev_succ = mba.get_mblock(prev_successor_serial)
|
||||
prev_succ.predset._del(blk.serial)
|
||||
if prev_succ.serial != mba.qty - 1:
|
||||
prev_succ.mark_lists_dirty()
|
||||
# Add new successors
|
||||
for blk_succ_serial in blk_succ_serial_list:
|
||||
blk.succset.push_back(blk_succ_serial)
|
||||
new_blk_successor = mba.get_mblock(blk_succ_serial)
|
||||
new_blk_successor.predset.push_back(blk.serial)
|
||||
if new_blk_successor.serial != mba.qty - 1:
|
||||
new_blk_successor.mark_lists_dirty()
|
||||
|
||||
blk.mark_lists_dirty()
|
||||
|
||||
|
||||
def insert_nop_blk(blk: mblock_t) -> mblock_t:
|
||||
mba = blk.mba
|
||||
nop_block = mba.copy_block(blk, blk.serial + 1)
|
||||
cur_ins = nop_block.head
|
||||
while cur_ins is not None:
|
||||
nop_block.make_nop(cur_ins)
|
||||
cur_ins = cur_ins.next
|
||||
|
||||
nop_block.type = BLT_1WAY
|
||||
|
||||
# We might have clone a block with multiple or no successor, thus we need to clean all
|
||||
prev_successor_serials = [x for x in nop_block.succset]
|
||||
|
||||
# Bookkeeping
|
||||
for prev_successor_serial in prev_successor_serials:
|
||||
nop_block.succset._del(prev_successor_serial)
|
||||
prev_succ = mba.get_mblock(prev_successor_serial)
|
||||
prev_succ.predset._del(nop_block.serial)
|
||||
if prev_succ.serial != mba.qty - 1:
|
||||
prev_succ.mark_lists_dirty()
|
||||
|
||||
nop_block.succset.push_back(nop_block.serial + 1)
|
||||
nop_block.mark_lists_dirty()
|
||||
|
||||
new_blk_successor = mba.get_mblock(nop_block.serial + 1)
|
||||
new_blk_successor.predset.push_back(nop_block.serial)
|
||||
if new_blk_successor.serial != mba.qty - 1:
|
||||
new_blk_successor.mark_lists_dirty()
|
||||
|
||||
mba.mark_chains_dirty()
|
||||
try:
|
||||
mba.verify(True)
|
||||
return nop_block
|
||||
except RuntimeError as e:
|
||||
helper_logger.error("Error in insert_nop_blk: {0}".format(e))
|
||||
log_block_info(nop_block, helper_logger.error)
|
||||
raise e
|
||||
|
||||
|
||||
def ensure_last_block_is_goto(mba: mbl_array_t) -> int:
|
||||
last_blk = mba.get_mblock(mba.qty - 2)
|
||||
if last_blk.nsucc() == 1:
|
||||
change_1way_block_successor(last_blk, last_blk.succset[0])
|
||||
return 1
|
||||
elif last_blk.nsucc() == 0:
|
||||
return 0
|
||||
else:
|
||||
raise ControlFlowException("Last block {0} is not one way (not supported yet)".format(last_blk.serial))
|
||||
|
||||
|
||||
def duplicate_block(block_to_duplicate: mblock_t) -> Tuple[mblock_t, mblock_t]:
|
||||
mba = block_to_duplicate.mba
|
||||
duplicated_blk = mba.copy_block(block_to_duplicate, mba.qty - 1)
|
||||
helper_logger.debug(" Duplicated {0} -> {1}".format(block_to_duplicate.serial, duplicated_blk.serial))
|
||||
duplicated_blk_default = None
|
||||
if (block_to_duplicate.tail is not None) and is_mcode_jcond(block_to_duplicate.tail.opcode):
|
||||
block_to_duplicate_default_successor = mba.get_mblock(block_to_duplicate.serial + 1)
|
||||
duplicated_blk_default = insert_nop_blk(duplicated_blk)
|
||||
change_1way_block_successor(duplicated_blk_default, block_to_duplicate.serial + 1)
|
||||
helper_logger.debug(" {0} is conditional, so created a default child {1} for {2} which goto {3}"
|
||||
.format(block_to_duplicate.serial, duplicated_blk_default.serial, duplicated_blk.serial,
|
||||
block_to_duplicate_default_successor.serial))
|
||||
elif duplicated_blk.nsucc() == 1:
|
||||
helper_logger.debug(" Making {0} goto {1}".format(duplicated_blk.serial, block_to_duplicate.succset[0]))
|
||||
change_1way_block_successor(duplicated_blk, block_to_duplicate.succset[0])
|
||||
elif duplicated_blk.nsucc() == 0:
|
||||
helper_logger.debug(" Duplicated block {0} has no successor => Nothing to do".format(duplicated_blk.serial))
|
||||
|
||||
return duplicated_blk, duplicated_blk_default
|
||||
|
||||
|
||||
def change_block_address(block: mblock_t, new_ea: int):
|
||||
# Can be used to fix error 50357
|
||||
mb_curr = block.head
|
||||
while mb_curr:
|
||||
mb_curr.ea = new_ea
|
||||
mb_curr = mb_curr.next
|
||||
|
||||
|
||||
def is_conditional_jump(blk: mblock_t) -> bool:
|
||||
if (blk is not None) and (blk.tail is not None):
|
||||
return blk.tail.opcode in CONDITIONAL_JUMP_OPCODES
|
||||
return False
|
||||
|
||||
|
||||
def is_indirect_jump(blk: mblock_t) -> bool:
|
||||
if (blk is not None) and (blk.tail is not None):
|
||||
return blk.tail.opcode == m_ijmp
|
||||
return False
|
||||
|
||||
|
||||
def get_block_serials_by_address(mba: mbl_array_t, address: int) -> List[int]:
|
||||
blk_serial_list = []
|
||||
for i in range(mba.qty):
|
||||
blk = mba.get_mblock(i)
|
||||
if blk.start == address:
|
||||
blk_serial_list.append(i)
|
||||
return blk_serial_list
|
||||
|
||||
|
||||
def get_block_serials_by_address_range(mba: mbl_array_t, address: int) -> List[int]:
|
||||
blk_serial_list = []
|
||||
for i in range(mba.qty):
|
||||
blk = mba.get_mblock(i)
|
||||
if blk.start <= address <= blk.end:
|
||||
blk_serial_list.append(i)
|
||||
return blk_serial_list
|
||||
|
||||
|
||||
def mba_remove_simple_goto_blocks(mba: mbl_array_t) -> int:
|
||||
last_block_index = mba.qty - 1
|
||||
nb_change = 0
|
||||
for goto_blk_serial in range(last_block_index):
|
||||
goto_blk: mblock_t = mba.get_mblock(goto_blk_serial)
|
||||
if goto_blk.is_simple_goto_block():
|
||||
goto_blk_dst_serial = goto_blk.tail.l.b
|
||||
goto_blk_preset = [x for x in goto_blk.predset]
|
||||
for father_serial in goto_blk_preset:
|
||||
father_blk: mblock_t = mba.get_mblock(father_serial)
|
||||
nb_change += update_blk_successor(father_blk, goto_blk_serial, goto_blk_dst_serial)
|
||||
return nb_change
|
||||
|
||||
|
||||
def mba_deep_cleaning(mba: mbl_array_t) -> int:
|
||||
if mba.maturity < MMAT_CALLS:
|
||||
# Doing this optimization before MMAT_CALLS may create blocks with call instruction (not last instruction)
|
||||
# IDA does like that and will raise a 50864 error
|
||||
return 0
|
||||
mba.remove_empty_blocks()
|
||||
mba.combine_blocks()
|
||||
nb_change = mba_remove_simple_goto_blocks(mba)
|
||||
if nb_change > 0:
|
||||
mba.remove_empty_blocks()
|
||||
mba.combine_blocks()
|
||||
return nb_change
|
||||
|
||||
|
||||
def ensure_child_has_an_unconditional_father(father_block: mblock_t, child_block: mblock_t) -> int:
|
||||
if father_block is None:
|
||||
return 0
|
||||
mba = father_block.mba
|
||||
if father_block.nsucc() == 1:
|
||||
return 0
|
||||
|
||||
if father_block.tail.d.b == child_block.serial:
|
||||
helper_logger.debug("Father {0} is a conditional jump to child {1}, creating a new father"
|
||||
.format(father_block.serial, child_block.serial))
|
||||
new_father_block = insert_nop_blk(mba.get_mblock(mba.qty - 2))
|
||||
change_1way_block_successor(new_father_block, child_block.serial)
|
||||
change_2way_block_conditional_successor(father_block, new_father_block.serial)
|
||||
else:
|
||||
helper_logger.info("Father {0} is a conditional jump to child {1} (default child), creating a new father"
|
||||
.format(father_block.serial, child_block.serial))
|
||||
new_father_block = insert_nop_blk(father_block)
|
||||
change_1way_block_successor(new_father_block, child_block.serial)
|
||||
return 1
|
||||
73
d810/conf/__init__.py
Normal file
73
d810/conf/__init__.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
class D810Configuration(object):
|
||||
def __init__(self):
|
||||
self.config_dir = os.path.join(os.getenv("HOME"), ".idapro", "plugins", "d810", "conf")
|
||||
self.config_file = os.path.join(self.config_dir, "options.json")
|
||||
with open(self.config_file, "r") as fp:
|
||||
self._options = json.load(fp)
|
||||
|
||||
def get(self, name):
|
||||
if (name == "log_dir") and (self._options[name] is None):
|
||||
return os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".."))
|
||||
return self._options[name]
|
||||
|
||||
def set(self, name, value):
|
||||
self._options[name] = value
|
||||
|
||||
def save(self):
|
||||
with open(self.config_file, "w") as fp:
|
||||
json.dump(self._options, fp, indent=2)
|
||||
|
||||
|
||||
class RuleConfiguration(object):
|
||||
def __init__(self, name=None, is_activated=False, config=None):
|
||||
self.name = name
|
||||
self.is_activated = is_activated
|
||||
self.config = config if config is not None else {}
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"name": self.name,
|
||||
"is_activated": self.is_activated,
|
||||
"config": self.config
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_dict(kwargs):
|
||||
return RuleConfiguration(**kwargs)
|
||||
|
||||
|
||||
class ProjectConfiguration(object):
|
||||
def __init__(self, path=None, description=None, ins_rules=None, blk_rules=None, conf_dir=None):
|
||||
self.path = path
|
||||
self.description = description
|
||||
self.conf_dir = conf_dir
|
||||
self.ins_rules = [] if ins_rules is None else ins_rules
|
||||
self.blk_rules = [] if blk_rules is None else blk_rules
|
||||
self.additional_configuration = {}
|
||||
|
||||
def load(self):
|
||||
try:
|
||||
with open(self.path, "r") as fp:
|
||||
project_conf = json.load(fp)
|
||||
except FileNotFoundError as e:
|
||||
if self.conf_dir is not None:
|
||||
self.path = os.path.join(self.conf_dir, self.path)
|
||||
with open(self.path, "r") as fp:
|
||||
project_conf = json.load(fp)
|
||||
|
||||
self.description = project_conf["description"]
|
||||
self.ins_rules = [RuleConfiguration.from_dict(x) for x in project_conf["ins_rules"]]
|
||||
self.blk_rules = [RuleConfiguration.from_dict(x) for x in project_conf["blk_rules"]]
|
||||
|
||||
def save(self):
|
||||
project_conf = {
|
||||
"description": self.description,
|
||||
"ins_rules": [x.to_dict() for x in self.ins_rules],
|
||||
"blk_rules": [x.to_dict() for x in self.blk_rules],
|
||||
}
|
||||
with open(self.path, "w") as fp:
|
||||
json.dump(project_conf, fp, indent=2)
|
||||
921
d810/conf/default_instruction_only.json
Normal file
921
d810/conf/default_instruction_only.json
Normal file
@@ -0,0 +1,921 @@
|
||||
{
|
||||
"description": "Default configuration of D-810 with only instruction level optimization",
|
||||
"ins_rules": [
|
||||
{
|
||||
"name": "AddXor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AddXor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndGetUpperBits_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndOr_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndXor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAdd_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotOr_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_XorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule10",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule11",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule12",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule13",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule14",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule15",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule16",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule17",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule18",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule19",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule20",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule21",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule22",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule7",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule8",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule9",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegAdd_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegAdd_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegOr_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegXor_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegXor_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Neg_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Neg_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_2_variant_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOdd1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOdd2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOr1_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOr2_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetbRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule8",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1Add_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1And1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1And_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1Or_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "XorAlmost_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_NestedStuff",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_SpecialConstantRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_SpecialConstantRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "ArithmeticChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "XorChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3ConstantOptimization",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"min_nb_opcode": 4,
|
||||
"min_nb_constant": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Z3SmodRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3lnotRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3setnzRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3setzRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "ExampleGuessingRule",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"min_nb_var": 1,
|
||||
"max_nb_var": 3,
|
||||
"min_nb_diff_opcodes": 3,
|
||||
"max_nb_diff_opcodes": 6
|
||||
}
|
||||
}
|
||||
],
|
||||
"blk_rules": [
|
||||
{
|
||||
"name": "JumpFixer",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"enabled_rules": [
|
||||
"CompareConstantRule1",
|
||||
"CompareConstantRule2",
|
||||
"CompareConstantRule3",
|
||||
"JaeRule1",
|
||||
"JbRule1",
|
||||
"JnzRule1",
|
||||
"JnzRule2",
|
||||
"JnzRule3",
|
||||
"JnzRule4",
|
||||
"JnzRule5",
|
||||
"JnzRule6",
|
||||
"JnzRule7",
|
||||
"JnzRule8"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
926
d810/conf/default_unflattening_ollvm.json
Normal file
926
d810/conf/default_unflattening_ollvm.json
Normal file
@@ -0,0 +1,926 @@
|
||||
{
|
||||
"description": "Unflattening O-LLVM with control flow flattening",
|
||||
"ins_rules": [
|
||||
{
|
||||
"name": "AddXor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AddXor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndGetUpperBits_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndOr_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndXor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAdd_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotOr_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_XorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule10",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule11",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule12",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule13",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule14",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule15",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule16",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule17",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule18",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule19",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule20",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule21",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule22",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule7",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule8",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule9",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegAdd_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegAdd_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegOr_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegXor_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegXor_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Neg_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Neg_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_2_variant_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOdd1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOdd2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOr1_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOr2_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetbRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule8",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1Add_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1And1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1And_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1Or_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "XorAlmost_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_NestedStuff",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_SpecialConstantRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_SpecialConstantRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "ArithmeticChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "XorChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3ConstantOptimization",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"min_nb_opcode": 4,
|
||||
"min_nb_constant": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Z3SmodRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3lnotRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3setnzRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3setzRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "ExampleGuessingRule",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"min_nb_var": 1,
|
||||
"max_nb_var": 3,
|
||||
"min_nb_diff_opcodes": 3,
|
||||
"max_nb_diff_opcodes": 6
|
||||
}
|
||||
}
|
||||
],
|
||||
"blk_rules": [
|
||||
{
|
||||
"name": "Unflattener",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "JumpFixer",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"enabled_rules": [
|
||||
"CompareConstantRule1",
|
||||
"CompareConstantRule2",
|
||||
"CompareConstantRule3",
|
||||
"JaeRule1",
|
||||
"JbRule1",
|
||||
"JnzRule1",
|
||||
"JnzRule2",
|
||||
"JnzRule3",
|
||||
"JnzRule4",
|
||||
"JnzRule5",
|
||||
"JnzRule6",
|
||||
"JnzRule7",
|
||||
"JnzRule8"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
926
d810/conf/default_unflattening_switch_case.json
Normal file
926
d810/conf/default_unflattening_switch_case.json
Normal file
@@ -0,0 +1,926 @@
|
||||
{
|
||||
"description": "Unflattening Tigress with switch case dispatcher",
|
||||
"ins_rules": [
|
||||
{
|
||||
"name": "AddXor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AddXor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndGetUpperBits_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndOr_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndXor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAdd_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotOr_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_XorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule10",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule11",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule12",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule13",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule14",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule15",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule16",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule17",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule18",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule19",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule20",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule21",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule22",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule7",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule8",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule9",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegAdd_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegAdd_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegOr_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegXor_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegXor_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Neg_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Neg_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_2_variant_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOdd1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOdd2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOr1_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOr2_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetbRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule8",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1Add_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1And1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1And_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1Or_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "XorAlmost_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_NestedStuff",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_SpecialConstantRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_SpecialConstantRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "ArithmeticChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "XorChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3ConstantOptimization",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"min_nb_opcode": 4,
|
||||
"min_nb_constant": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Z3SmodRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3lnotRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3setnzRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3setzRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "ExampleGuessingRule",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"min_nb_var": 1,
|
||||
"max_nb_var": 3,
|
||||
"min_nb_diff_opcodes": 3,
|
||||
"max_nb_diff_opcodes": 6
|
||||
}
|
||||
}
|
||||
],
|
||||
"blk_rules": [
|
||||
{
|
||||
"name": "UnflattenerSwitchCase",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "JumpFixer",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"enabled_rules": [
|
||||
"CompareConstantRule1",
|
||||
"CompareConstantRule2",
|
||||
"CompareConstantRule3",
|
||||
"JaeRule1",
|
||||
"JbRule1",
|
||||
"JnzRule1",
|
||||
"JnzRule2",
|
||||
"JnzRule3",
|
||||
"JnzRule4",
|
||||
"JnzRule5",
|
||||
"JnzRule6",
|
||||
"JnzRule7",
|
||||
"JnzRule8"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
942
d810/conf/example_anel.json
Normal file
942
d810/conf/example_anel.json
Normal file
@@ -0,0 +1,942 @@
|
||||
{
|
||||
"description": "Configuration to deobfuscate ANEL malware",
|
||||
"ins_rules": [
|
||||
{
|
||||
"name": "AddXor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AddXor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_HackersDelightRule_5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_OllvmRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Add_SpecialConstantRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndBnot_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndGetUpperBits_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndOr_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndXor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "And_OllvmRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAdd_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotAnd_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotOr_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "BnotXor_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Bnot_XorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule10",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule11",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule12",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule13",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule14",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule15",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule16",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule17",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule18",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule19",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule20",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule21",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule22",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule7",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule8",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "CstSimplificationRule9",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "GetIdentRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Mul_MbaRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegAdd_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegAdd_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegOr_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegXor_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "NegXor_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Neg_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Neg_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrBnot_FactorRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_HackersDelightRule_2_variant_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_OllvmRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Or_Rule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Pred0Rule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredFFRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOdd1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOdd2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOr1_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredOr2_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetbRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetnzRule8",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "PredSetzRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1Add_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1And1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1And_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1Or_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub1_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Sub_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "WeirdRule6",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor1_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "XorAlmost_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_FactorRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_4",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_HackersDelightRule_5",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_MbaRule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_NestedStuff",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_Rule_3",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_SpecialConstantRule_1",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Xor_SpecialConstantRule_2",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "AndChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "ArithmeticChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "OrChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "XorChain",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3ConstantOptimization",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"min_nb_opcode": 4,
|
||||
"min_nb_constant": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Z3SmodRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3lnotRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3setnzRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "Z3setzRuleGeneric",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "SetGlobalVariablesToZeroIfDetectedReadOnly",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "ExampleGuessingRule",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"min_nb_var": 1,
|
||||
"max_nb_var": 3,
|
||||
"min_nb_diff_opcodes": 3,
|
||||
"max_nb_diff_opcodes": 6
|
||||
}
|
||||
}
|
||||
],
|
||||
"blk_rules": [
|
||||
{
|
||||
"name": "Unflattener",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"maturities": [
|
||||
"MMAT_CALLS",
|
||||
"MMAT_GLBOPT1",
|
||||
"MMAT_GLBOPT2"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "UnflattenerFakeJump",
|
||||
"is_activated": true,
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"name": "JumpFixer",
|
||||
"is_activated": true,
|
||||
"config": {
|
||||
"enabled_rules": [
|
||||
"CompareConstantRule1",
|
||||
"CompareConstantRule2",
|
||||
"CompareConstantRule3",
|
||||
"JaeRule1",
|
||||
"JbRule1",
|
||||
"JnzRule1",
|
||||
"JnzRule2",
|
||||
"JnzRule3",
|
||||
"JnzRule4",
|
||||
"JnzRule5",
|
||||
"JnzRule6",
|
||||
"JnzRule7",
|
||||
"JnzRule8"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
14
d810/conf/options.json
Normal file
14
d810/conf/options.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"erase_logs_on_reload": true,
|
||||
"generate_z3_code": true,
|
||||
"dump_intermediate_microcode": true,
|
||||
"log_dir": null,
|
||||
"configurations": [
|
||||
"default_instruction_only.json",
|
||||
"default_unflattening_ollvm.json",
|
||||
"default_unflattening_switch_case.json",
|
||||
"example_anel.json",
|
||||
"example_unflattening_indirect.json"
|
||||
],
|
||||
"last_project_index": 0
|
||||
}
|
||||
BIN
d810/docs/source/images/gui_plugin_configuration.png
Normal file
BIN
d810/docs/source/images/gui_plugin_configuration.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
505
d810/emulator.py
Normal file
505
d810/emulator.py
Normal file
@@ -0,0 +1,505 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typing import List, Union, Dict
|
||||
from idaapi import getseg, get_qword, SEGPERM_WRITE
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.utils import unsigned_to_signed, signed_to_unsigned, get_add_cf, get_add_of, get_sub_of, ror, get_parity_flag
|
||||
from d810.hexrays_helpers import equal_mops_ignore_size, get_mop_index, AND_TABLE, CONTROL_FLOW_OPCODES, \
|
||||
CONDITIONAL_JUMP_OPCODES
|
||||
from d810.hexrays_formatters import format_minsn_t, format_mop_t, mop_type_to_string, opcode_to_string
|
||||
from d810.cfg_utils import get_block_serials_by_address
|
||||
from d810.errors import EmulationException, EmulationIndirectJumpException, UnresolvedMopException, \
|
||||
WritableMemoryReadException
|
||||
|
||||
emulator_log = logging.getLogger('D810.emulator')
|
||||
|
||||
|
||||
class MicroCodeInterpreter(object):
|
||||
def __init__(self, global_environment=None):
|
||||
self.global_environment = MicroCodeEnvironment() if global_environment is None else global_environment
|
||||
|
||||
def _eval_instruction_and_update_environment(self, blk: mblock_t, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
|
||||
environment.set_cur_flow(blk, ins)
|
||||
res = self._eval_instruction(ins, environment)
|
||||
if res is not None:
|
||||
if (ins.d is not None) and ins.d.t != mop_z:
|
||||
environment.assign(ins.d, res, auto_define=True)
|
||||
return res
|
||||
|
||||
def _eval_instruction(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
|
||||
if ins is None:
|
||||
return None
|
||||
is_flow_instruction = self._eval_control_flow_instruction(ins, environment)
|
||||
if is_flow_instruction:
|
||||
return None
|
||||
call_helper_res = self._eval_call_helper(ins, environment)
|
||||
if call_helper_res is not None:
|
||||
return call_helper_res
|
||||
if ins.opcode == m_call:
|
||||
return self._eval_call(ins, environment)
|
||||
elif ins.opcode == m_icall:
|
||||
return self._eval_call(ins, environment)
|
||||
res_mask = AND_TABLE[ins.d.size]
|
||||
if ins.opcode == m_ldx:
|
||||
return self._eval_load(ins, environment)
|
||||
elif ins.opcode == m_stx:
|
||||
return self._eval_store(ins, environment)
|
||||
elif ins.opcode == m_mov:
|
||||
return (self.eval(ins.l, environment)) & res_mask
|
||||
elif ins.opcode == m_neg:
|
||||
return (- self.eval(ins.l, environment)) & res_mask
|
||||
elif ins.opcode == m_lnot:
|
||||
return self.eval(ins.l, environment) != 0
|
||||
elif ins.opcode == m_bnot:
|
||||
return (self.eval(ins.l, environment) ^ res_mask) & res_mask
|
||||
elif ins.opcode == m_xds:
|
||||
left_value_signed = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
return signed_to_unsigned(left_value_signed, ins.d.size) & res_mask
|
||||
elif ins.opcode == m_xdu:
|
||||
return (self.eval(ins.l, environment)) & res_mask
|
||||
elif ins.opcode == m_low:
|
||||
return (self.eval(ins.l, environment)) & res_mask
|
||||
elif ins.opcode == m_add:
|
||||
return (self.eval(ins.l, environment) + self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_sub:
|
||||
return (self.eval(ins.l, environment) - self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_mul:
|
||||
return (self.eval(ins.l, environment) * self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_udiv:
|
||||
return (self.eval(ins.l, environment) // self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_sdiv:
|
||||
return (self.eval(ins.l, environment) // self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_umod:
|
||||
return (self.eval(ins.l, environment) % self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_smod:
|
||||
return (self.eval(ins.l, environment) % self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_or:
|
||||
return (self.eval(ins.l, environment) | self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_and:
|
||||
return (self.eval(ins.l, environment) & self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_xor:
|
||||
return (self.eval(ins.l, environment) ^ self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_shl:
|
||||
return (self.eval(ins.l, environment) << self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_shr:
|
||||
return (self.eval(ins.l, environment) >> self.eval(ins.r, environment)) & res_mask
|
||||
elif ins.opcode == m_sar:
|
||||
res_signed = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size) >> self.eval(ins.r, environment)
|
||||
return signed_to_unsigned(res_signed, ins.d.size) & res_mask
|
||||
elif ins.opcode == m_cfadd:
|
||||
tmp = get_add_cf(self.eval(ins.l, environment), self.eval(ins.r, environment), ins.l.size)
|
||||
return tmp & res_mask
|
||||
elif ins.opcode == m_ofadd:
|
||||
tmp = get_add_of(self.eval(ins.l, environment), self.eval(ins.r, environment), ins.l.size)
|
||||
return tmp & res_mask
|
||||
elif ins.opcode == m_sets:
|
||||
left_value_signed = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
res = 1 if left_value_signed < 0 else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_seto:
|
||||
left_value_signed = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
right_value_signed = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
|
||||
sub_overflow = get_sub_of(left_value_signed, right_value_signed, ins.l.size)
|
||||
return sub_overflow & res_mask
|
||||
elif ins.opcode == m_setnz:
|
||||
res = 1 if self.eval(ins.l, environment) != self.eval(ins.r, environment) else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_setz:
|
||||
res = 1 if self.eval(ins.l, environment) == self.eval(ins.r, environment) else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_setae:
|
||||
res = 1 if self.eval(ins.l, environment) >= self.eval(ins.r, environment) else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_setb:
|
||||
res = 1 if self.eval(ins.l, environment) < self.eval(ins.r, environment) else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_seta:
|
||||
res = 1 if self.eval(ins.l, environment) > self.eval(ins.r, environment) else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_setbe:
|
||||
res = 1 if self.eval(ins.l, environment) <= self.eval(ins.r, environment) else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_setg:
|
||||
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
|
||||
res = 1 if left_value > right_value else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_setge:
|
||||
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
|
||||
res = 1 if left_value >= right_value else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_setl:
|
||||
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
|
||||
res = 1 if left_value < right_value else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_setle:
|
||||
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
|
||||
res = 1 if left_value <= right_value else 0
|
||||
return res & res_mask
|
||||
elif ins.opcode == m_setp:
|
||||
res = get_parity_flag(self.eval(ins.l, environment), self.eval(ins.r, environment), ins.l.size)
|
||||
return res & res_mask
|
||||
raise EmulationException("Unsupported instruction opcode '{0}': '{1}'"
|
||||
.format(opcode_to_string(ins.opcode), format_minsn_t(ins)))
|
||||
|
||||
@staticmethod
|
||||
def _get_blk_serial(mop: mop_t) -> int:
|
||||
if mop.t == mop_b:
|
||||
return mop.b
|
||||
raise EmulationException("Get block serial with an unsupported mop type '{0}': '{1}'"
|
||||
.format(mop_type_to_string(mop.t), format_mop_t(mop)))
|
||||
|
||||
def _eval_conditional_jump(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
|
||||
if ins.opcode not in CONDITIONAL_JUMP_OPCODES:
|
||||
return None
|
||||
if ins.opcode == m_jtbl:
|
||||
# This is not handled the same way
|
||||
return None
|
||||
cur_blk = environment.cur_blk
|
||||
direct_child_serial = cur_blk.serial + 1
|
||||
if ins.opcode == m_jcnd:
|
||||
jump_taken = self.eval(ins.l, environment) != 0
|
||||
elif ins.opcode == m_jnz:
|
||||
jump_taken = self.eval(ins.l, environment) != self.eval(ins.r, environment)
|
||||
elif ins.opcode == m_jz:
|
||||
jump_taken = self.eval(ins.l, environment) == self.eval(ins.r, environment)
|
||||
elif ins.opcode == m_jae:
|
||||
jump_taken = self.eval(ins.l, environment) >= self.eval(ins.r, environment)
|
||||
elif ins.opcode == m_jb:
|
||||
jump_taken = self.eval(ins.l, environment) < self.eval(ins.r, environment)
|
||||
elif ins.opcode == m_ja:
|
||||
jump_taken = self.eval(ins.l, environment) > self.eval(ins.r, environment)
|
||||
elif ins.opcode == m_jbe:
|
||||
jump_taken = self.eval(ins.l, environment) <= self.eval(ins.r, environment)
|
||||
elif ins.opcode == m_jg:
|
||||
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
|
||||
jump_taken = left_value > right_value
|
||||
elif ins.opcode == m_jge:
|
||||
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
|
||||
jump_taken = left_value >= right_value
|
||||
elif ins.opcode == m_jl:
|
||||
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
|
||||
jump_taken = left_value < right_value
|
||||
elif ins.opcode == m_jle:
|
||||
left_value = unsigned_to_signed(self.eval(ins.l, environment), ins.l.size)
|
||||
right_value = unsigned_to_signed(self.eval(ins.r, environment), ins.r.size)
|
||||
jump_taken = left_value <= right_value
|
||||
else:
|
||||
# This should never happen
|
||||
raise EmulationException("Unhandled conditional jump: '{0}'".format(format_minsn_t(ins)))
|
||||
return self._get_blk_serial(ins.d) if jump_taken else direct_child_serial
|
||||
|
||||
def _eval_control_flow_instruction(self, ins: minsn_t, environment: MicroCodeEnvironment) -> bool:
|
||||
if ins.opcode not in CONTROL_FLOW_OPCODES:
|
||||
return False
|
||||
cur_blk = environment.cur_blk
|
||||
if cur_blk is None:
|
||||
raise EmulationException("Can't evaluate control flow instruction with null block: '{0}'"
|
||||
.format(format_minsn_t(ins)))
|
||||
|
||||
next_blk_serial = self._eval_conditional_jump(ins, environment)
|
||||
if next_blk_serial is not None:
|
||||
next_blk = cur_blk.mba.get_mblock(next_blk_serial)
|
||||
next_ins = next_blk.head
|
||||
environment.set_next_flow(next_blk, next_ins)
|
||||
return True
|
||||
|
||||
if ins.opcode == m_goto:
|
||||
next_blk_serial = self._get_blk_serial(ins.l)
|
||||
elif ins.opcode == m_jtbl:
|
||||
left_value = self.eval(ins.l, environment)
|
||||
cases = ins.r.c
|
||||
# Initialize to default case
|
||||
next_blk_serial = [x for x in cases.targets][-1]
|
||||
for possible_values, target_block_serial in zip(cases.values, cases.targets):
|
||||
for test_value in possible_values:
|
||||
if left_value == test_value:
|
||||
next_blk_serial = target_block_serial
|
||||
break
|
||||
elif ins.opcode == m_ijmp:
|
||||
ijmp_dest_ea = self.eval(ins.d, environment)
|
||||
dest_block_serials = get_block_serials_by_address(environment.cur_blk.mba, ijmp_dest_ea)
|
||||
if len(dest_block_serials) == 0:
|
||||
raise EmulationIndirectJumpException("No blocks found at address {0:x}".format(ijmp_dest_ea),
|
||||
ijmp_dest_ea, dest_block_serials)
|
||||
|
||||
if len(dest_block_serials) > 1:
|
||||
raise EmulationIndirectJumpException("Multiple blocks at address {0:x}: {1}".format(ijmp_dest_ea,
|
||||
dest_block_serials),
|
||||
ijmp_dest_ea, dest_block_serials)
|
||||
next_blk_serial = dest_block_serials[0]
|
||||
|
||||
if next_blk_serial is None:
|
||||
return False
|
||||
next_blk = cur_blk.mba.get_mblock(next_blk_serial)
|
||||
next_ins = next_blk.head
|
||||
environment.set_next_flow(next_blk, next_ins)
|
||||
return True
|
||||
|
||||
def _eval_call_helper(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
|
||||
# Currently, we only support helper calls, (but end goal is to allow to hook calls)
|
||||
if ins.opcode != m_call or ins.l.t != mop_h:
|
||||
return None
|
||||
res_mask = AND_TABLE[ins.d.size]
|
||||
helper_name = ins.l.helper
|
||||
args_list = ins.d
|
||||
|
||||
emulator_log.debug("Call helper for {0}".format(helper_name))
|
||||
# and we support only __ROR4__ (we should add other Hex-Rays created helper calls)
|
||||
if helper_name == "__ROR4__":
|
||||
data_1 = self.eval(args_list.f.args[0], environment)
|
||||
data_2 = self.eval(args_list.f.args[1], environment)
|
||||
return ror(data_1, data_2, 8 * args_list.f.args[0].size) & res_mask
|
||||
elif helper_name == "__readfsqword":
|
||||
return 0
|
||||
return None
|
||||
|
||||
def _eval_load(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
|
||||
res_mask = AND_TABLE[ins.d.size]
|
||||
if ins.opcode == m_ldx:
|
||||
load_address = self.eval(ins.r, environment)
|
||||
formatted_seg_register = format_mop_t(ins.l)
|
||||
if formatted_seg_register == "ss.2":
|
||||
stack_mop = mop_t()
|
||||
stack_mop.erase()
|
||||
stack_mop._make_stkvar(environment.cur_blk.mba, load_address)
|
||||
emulator_log.debug("Searching for stack mop {0}".format(format_mop_t(stack_mop)))
|
||||
stack_mop_value = environment.lookup(stack_mop)
|
||||
emulator_log.debug(" stack mop {0} value : {1}".format(format_mop_t(stack_mop), stack_mop_value))
|
||||
return stack_mop_value & res_mask
|
||||
else:
|
||||
mem_seg = getseg(load_address)
|
||||
seg_perm = mem_seg.perm
|
||||
if (seg_perm & SEGPERM_WRITE) != 0:
|
||||
raise WritableMemoryReadException("ldx {0:x} (writable -> return None)".format(load_address))
|
||||
else:
|
||||
memory_value = get_qword(load_address)
|
||||
emulator_log.debug("ldx {0:x} (non writable -> return {1:x})"
|
||||
.format(load_address, memory_value & res_mask))
|
||||
return memory_value & res_mask
|
||||
|
||||
def _eval_store(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
|
||||
# TODO: implement
|
||||
emulator_log.warning("Evaluation of {0} not implemented: bypassing".format(format_minsn_t(ins)))
|
||||
return None
|
||||
|
||||
def _eval_call(self, ins: minsn_t, environment: MicroCodeEnvironment) -> Union[None, int]:
|
||||
# TODO: implement
|
||||
emulator_log.warning("Evaluation of {0} not implemented: bypassing".format(format_minsn_t(ins)))
|
||||
return None
|
||||
|
||||
def eval(self, mop: mop_t, environment: MicroCodeEnvironment) -> Union[None, int]:
|
||||
if mop.t == mop_n:
|
||||
return mop.nnn.value
|
||||
elif mop.t in [mop_r, mop_S]:
|
||||
return environment.lookup(mop)
|
||||
elif mop.t == mop_d:
|
||||
return self._eval_instruction(mop.d, environment)
|
||||
elif mop.t == mop_a:
|
||||
if mop.a.t == mop_v:
|
||||
emulator_log.debug("Reading a mop_a '{0}' -> {1:x}".format(format_mop_t(mop), mop.a.g))
|
||||
return mop.a.g
|
||||
elif mop.a.t == mop_S:
|
||||
emulator_log.debug("Reading a mop_a '{0}' -> {1:x}".format(format_mop_t(mop), mop.a.s.off))
|
||||
return mop.a.s.off
|
||||
raise UnresolvedMopException("Calling get_cst with unsupported mop type {0} - {1}: '{2}'"
|
||||
.format(mop.t, mop.a.t, format_mop_t(mop)))
|
||||
elif mop.t == mop_v:
|
||||
mem_seg = getseg(mop.g)
|
||||
seg_perm = mem_seg.perm
|
||||
if (seg_perm & SEGPERM_WRITE) != 0:
|
||||
emulator_log.debug("Reading a (writable) mop_v {0}".format(format_mop_t(mop)))
|
||||
return environment.lookup(mop)
|
||||
else:
|
||||
memory_value = get_qword(mop.g)
|
||||
emulator_log.debug("Reading a mop_v {0:x} (non writable -> return {1:x})".format(mop.g, memory_value))
|
||||
return mop.g
|
||||
raise EmulationException("Unsupported mop type '{0}': '{1}'"
|
||||
.format(mop_type_to_string(mop.t), format_mop_t(mop)))
|
||||
|
||||
def eval_instruction(self, blk: mblock_t, ins: minsn_t, environment: Union[None, MicroCodeEnvironment] = None,
|
||||
raise_exception: bool = False) -> bool:
|
||||
try:
|
||||
if environment is None:
|
||||
environment = self.global_environment
|
||||
emulator_log.info("Evaluating microcode instruction : '{0}'".format(format_minsn_t(ins)))
|
||||
if ins is None:
|
||||
return False
|
||||
self._eval_instruction_and_update_environment(blk, ins, environment)
|
||||
return True
|
||||
except EmulationException as e:
|
||||
emulator_log.warning("Can't evaluate instruction: '{0}': {1}".format(format_minsn_t(ins), e))
|
||||
if raise_exception:
|
||||
raise e
|
||||
except Exception as e:
|
||||
emulator_log.warning("Error during evaluation of: '{0}': {1}".format(format_minsn_t(ins), e))
|
||||
if raise_exception:
|
||||
raise e
|
||||
return False
|
||||
|
||||
def eval_mop(self, mop: mop_t, environment: Union[None, MicroCodeEnvironment] = None,
|
||||
raise_exception: bool = False) -> Union[None, int]:
|
||||
try:
|
||||
if environment is None:
|
||||
environment = self.global_environment
|
||||
res = self.eval(mop, environment)
|
||||
return res
|
||||
except EmulationException as e:
|
||||
emulator_log.warning("Can't get constant mop value: '{0}': {1}".format(format_mop_t(mop), e))
|
||||
if raise_exception:
|
||||
raise e
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class MopMapping(object):
|
||||
def __init__(self):
|
||||
self.mops = []
|
||||
self.mops_values = []
|
||||
|
||||
def __setitem__(self, mop: mop_t, mop_value: int):
|
||||
mop_index = get_mop_index(mop, self.mops)
|
||||
mop_value &= AND_TABLE[mop.size]
|
||||
if mop_index != -1:
|
||||
self.mops_values[mop_index] = mop_value
|
||||
return
|
||||
self.mops.append(mop)
|
||||
self.mops_values.append(mop_value)
|
||||
|
||||
def __getitem__(self, mop: mop_t) -> int:
|
||||
mop_index = get_mop_index(mop, self.mops)
|
||||
if mop_index == -1:
|
||||
raise KeyError
|
||||
return self.mops_values[mop_index]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.mops)
|
||||
|
||||
def __delitem__(self, mop: mop_t):
|
||||
mop_index = get_mop_index(mop, self.mops)
|
||||
if mop_index == -1:
|
||||
raise KeyError
|
||||
del self.mops[mop_index]
|
||||
del self.mops_values[mop_index]
|
||||
|
||||
def clear(self):
|
||||
self.mops = []
|
||||
self.mops_values = []
|
||||
|
||||
def copy(self):
|
||||
new_mapping = MopMapping()
|
||||
for mop, mop_value in self.items():
|
||||
new_mapping[mop] = mop_value
|
||||
return new_mapping
|
||||
|
||||
def has_key(self, mop: mop_t):
|
||||
mop_index = get_mop_index(mop, self.mops)
|
||||
return mop_index != -1
|
||||
|
||||
def keys(self) -> List[mop_t]:
|
||||
return self.mops
|
||||
|
||||
def values(self) -> List[int]:
|
||||
return self.mops_values
|
||||
|
||||
def items(self):
|
||||
return [(x, y) for x, y in zip(self.mops, self.mops_values)]
|
||||
|
||||
def __contains__(self, mop: mop_t):
|
||||
return self.has_key(mop)
|
||||
|
||||
|
||||
class MicroCodeEnvironment(object):
|
||||
def __init__(self, parent: Union[None, MicroCodeEnvironment] = None):
|
||||
self.parent = parent
|
||||
self.mop_r_record = MopMapping()
|
||||
self.mop_S_record = MopMapping()
|
||||
|
||||
self.cur_blk = None
|
||||
self.cur_ins = None
|
||||
self.next_blk = None
|
||||
self.next_ins = None
|
||||
|
||||
def items(self):
|
||||
return [x for x in self.mop_r_record.items() + self.mop_S_record.items()]
|
||||
|
||||
def get_copy(self, copy_parent=True) -> MicroCodeEnvironment:
|
||||
parent_copy = self.parent
|
||||
if parent_copy is not None and copy_parent:
|
||||
parent_copy = self.parent.get_copy(copy_parent=True)
|
||||
new_env = MicroCodeEnvironment(parent_copy)
|
||||
for mop, mop_value in self.mop_r_record.items():
|
||||
new_env.define(mop, mop_value)
|
||||
for mop, mop_value in self.mop_S_record.items():
|
||||
new_env.define(mop, mop_value)
|
||||
new_env.cur_blk = self.cur_blk
|
||||
new_env.cur_ins = self.cur_ins
|
||||
new_env.next_blk = self.next_blk
|
||||
new_env.next_ins = self.next_ins
|
||||
return new_env
|
||||
|
||||
def set_cur_flow(self, cur_blk: mblock_t, cur_ins: minsn_t):
|
||||
self.cur_blk = cur_blk
|
||||
self.cur_ins = cur_ins
|
||||
self.next_blk = cur_blk
|
||||
if self.cur_ins is None:
|
||||
self.next_blk = self.cur_blk.mba.get_mblock(self.cur_blk.serial + 1)
|
||||
self.next_ins = self.next_blk.head
|
||||
else:
|
||||
self.next_ins = self.cur_ins.next
|
||||
if self.next_ins is None:
|
||||
self.next_blk = self.cur_blk.mba.get_mblock(self.cur_blk.serial + 1)
|
||||
self.next_ins = self.next_blk.head
|
||||
emulator_log.debug(
|
||||
"Setting next block {0} and next ins {1}".format(self.next_blk.serial, format_minsn_t(self.next_ins)))
|
||||
|
||||
def set_next_flow(self, next_blk: mblock_t, next_ins: minsn_t):
|
||||
self.next_blk = next_blk
|
||||
self.next_ins = next_ins
|
||||
|
||||
def define(self, mop: mblock_t, value: int) -> int:
|
||||
if mop.t == mop_r:
|
||||
self.mop_r_record[mop] = value
|
||||
return value
|
||||
elif mop.t == mop_S:
|
||||
self.mop_S_record[mop] = value
|
||||
return value
|
||||
raise EmulationException("Defining an unsupported mop type '{0}': '{1}'"
|
||||
.format(mop_type_to_string(mop.t), format_mop_t(mop)))
|
||||
|
||||
def _lookup_mop(self, searched_mop: mop_t, mop_value_dict: Dict[mop_t, int], new_mop_value: Union[None, int] = None,
|
||||
auto_define=True, raise_exception=True) -> int:
|
||||
for known_mop, mop_value in mop_value_dict.items():
|
||||
if equal_mops_ignore_size(searched_mop, known_mop):
|
||||
if new_mop_value is not None:
|
||||
mop_value_dict[searched_mop] = new_mop_value
|
||||
return new_mop_value
|
||||
return mop_value
|
||||
if (new_mop_value is not None) and auto_define:
|
||||
self.define(searched_mop, new_mop_value)
|
||||
return new_mop_value
|
||||
if raise_exception:
|
||||
raise EmulationException("Variable '{0}' is not defined".format(format_mop_t(searched_mop)))
|
||||
else:
|
||||
return None
|
||||
|
||||
def lookup(self, mop: mop_t, raise_exception=True) -> int:
|
||||
if mop.t == mop_r:
|
||||
return self._lookup_mop(mop, self.mop_r_record, raise_exception=raise_exception)
|
||||
elif mop.t == mop_S:
|
||||
return self._lookup_mop(mop, self.mop_S_record, raise_exception=raise_exception)
|
||||
|
||||
def assign(self, mop: mop_t, value: int, auto_define=True) -> int:
|
||||
if mop.t == mop_r:
|
||||
return self._lookup_mop(mop, self.mop_r_record, value, auto_define)
|
||||
elif mop.t == mop_S:
|
||||
return self._lookup_mop(mop, self.mop_S_record, value, auto_define)
|
||||
raise EmulationException("Assigning an unsupported mop type '{0}': '{1}'"
|
||||
.format(mop_type_to_string(mop.t), format_mop_t(mop)))
|
||||
41
d810/errors.py
Normal file
41
d810/errors.py
Normal file
@@ -0,0 +1,41 @@
|
||||
class D810Exception(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AstException(D810Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AstEvaluationException(AstException):
|
||||
pass
|
||||
|
||||
|
||||
class D810Z3Exception(D810Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ControlFlowException(D810Exception):
|
||||
pass
|
||||
|
||||
|
||||
class EmulationException(D810Exception):
|
||||
pass
|
||||
|
||||
|
||||
class EmulationIndirectJumpException(EmulationException):
|
||||
def __init__(self, message, dest_ea, dest_serial_list):
|
||||
super().__init__(message)
|
||||
self.dest_ea = dest_ea
|
||||
self.dest_serial_list = dest_serial_list
|
||||
|
||||
|
||||
class UnresolvedMopException(EmulationException):
|
||||
pass
|
||||
|
||||
|
||||
class WritableMemoryReadException(EmulationException):
|
||||
pass
|
||||
|
||||
|
||||
class UnsupportedInstructionException(EmulationException):
|
||||
pass
|
||||
95
d810/hexrays_formatters.py
Normal file
95
d810/hexrays_formatters.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import os
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from d810.hexrays_helpers import OPCODES_INFO, MATURITY_TO_STRING_DICT, STRING_TO_MATURITY_DICT, MOP_TYPE_TO_STRING_DICT
|
||||
from ida_hexrays import minsn_t, mop_t, vd_printer_t, mbl_array_t
|
||||
|
||||
|
||||
logger = logging.getLogger('D810.helper')
|
||||
|
||||
|
||||
def format_minsn_t(ins: minsn_t) -> str:
|
||||
if ins is None:
|
||||
return "minsn_t is None"
|
||||
|
||||
tmp = ins._print()
|
||||
pp_ins = "".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in tmp])
|
||||
return pp_ins
|
||||
|
||||
|
||||
def format_mop_t(mop_in: mop_t) -> str:
|
||||
if mop_in is None:
|
||||
return "mop_t is None"
|
||||
if mop_in.t > 15:
|
||||
# To avoid error 50581
|
||||
return "Unknown mop type {0}".format(mop_in.t)
|
||||
return mop_in.dstr()
|
||||
|
||||
|
||||
def format_mop_list(mop_list: List[mop_t]) -> str:
|
||||
return ", ".join([format_mop_t(x) for x in mop_list])
|
||||
|
||||
|
||||
def maturity_to_string(maturity_level: int) -> str:
|
||||
return MATURITY_TO_STRING_DICT.get(maturity_level, "Unknown maturity: {0}".format(maturity_level))
|
||||
|
||||
|
||||
def string_to_maturity(maturity_string: str) -> int:
|
||||
return STRING_TO_MATURITY_DICT.get(maturity_string)
|
||||
|
||||
|
||||
def mop_type_to_string(mop_type: int) -> str:
|
||||
return MOP_TYPE_TO_STRING_DICT.get(mop_type, "Unknown mop type: {0}".format(mop_type))
|
||||
|
||||
|
||||
def opcode_to_string(opcode) -> str:
|
||||
try:
|
||||
return OPCODES_INFO[opcode]["name"]
|
||||
except KeyError:
|
||||
return "Unknown opcode: {0}".format(opcode)
|
||||
|
||||
|
||||
class mba_printer(vd_printer_t):
|
||||
def __init__(self):
|
||||
vd_printer_t.__init__(self)
|
||||
self.mc = []
|
||||
|
||||
def get_mc(self):
|
||||
return self.mc
|
||||
|
||||
def _print(self, indent, line):
|
||||
self.mc.append("".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in line])+"\n")
|
||||
return 1
|
||||
|
||||
|
||||
class block_printer(vd_printer_t):
|
||||
def __init__(self):
|
||||
vd_printer_t.__init__(self)
|
||||
self.block_ins = []
|
||||
|
||||
def get_block_mc(self):
|
||||
return "\n".join(self.block_ins)
|
||||
|
||||
def _print(self, indent, line):
|
||||
self.block_ins.append("".join([c if 0x20 <= ord(c) <= 0x7e else "" for c in line]))
|
||||
return 1
|
||||
|
||||
|
||||
def write_mc_to_file(mba: mbl_array_t, filename: str, mba_flags: int = 0) -> bool:
|
||||
if not mba:
|
||||
return False
|
||||
|
||||
vp = mba_printer()
|
||||
mba.set_mba_flags(mba_flags)
|
||||
mba._print(vp)
|
||||
|
||||
with open(filename, "w") as f:
|
||||
f.writelines(vp.get_mc())
|
||||
return True
|
||||
|
||||
|
||||
def dump_microcode_for_debug(mba: mbl_array_t, log_dir_path: str, name: str = ""):
|
||||
mc_filename = os.path.join(log_dir_path, "{0:x}_maturity_{1}_{2}.log".format(mba.entry_ea, mba.maturity, name))
|
||||
logger.info("Dumping microcode in file {0}...".format(mc_filename))
|
||||
write_mc_to_file(mba, mc_filename)
|
||||
356
d810/hexrays_helpers.py
Normal file
356
d810/hexrays_helpers.py
Normal file
@@ -0,0 +1,356 @@
|
||||
from ida_hexrays import *
|
||||
from typing import List, Tuple
|
||||
from ida_hexrays import mop_d, mop_n, m_stx, m_ldx, m_xdu, m_xds, mop_z, mop_fn, mop_S, mop_v, EQ_IGNSIZE, mop_b, \
|
||||
mop_r, mop_f, mop_l, mop_a, mop_h, mop_str, mop_c, mop_p, mop_sc
|
||||
|
||||
|
||||
OPCODES_INFO = {
|
||||
m_nop: {"name": "nop", "nb_operands": 0, "is_commutative": True},
|
||||
m_stx: {"name": "stx", "nb_operands": 2, "is_commutative": False},
|
||||
m_ldx: {"name": "ldx", "nb_operands": 2, "is_commutative": False},
|
||||
m_ldc: {"name": "ldc", "nb_operands": 1, "is_commutative": False},
|
||||
m_mov: {"name": "mov", "nb_operands": 1, "is_commutative": False, "symbol": ""},
|
||||
m_neg: {"name": "neg", "nb_operands": 1, "is_commutative": False, "symbol": "-"},
|
||||
m_lnot: {"name": "lnot", "nb_operands": 1, "is_commutative": False, "symbol": "!"},
|
||||
m_bnot: {"name": "bnot", "nb_operands": 1, "is_commutative": False, "symbol": "~"},
|
||||
m_xds: {"name": "xds", "nb_operands": 1, "is_commutative": False, "symbol": "xds"},
|
||||
m_xdu: {"name": "xdu", "nb_operands": 1, "is_commutative": False, "symbol": "xdu"},
|
||||
m_low: {"name": "low", "nb_operands": 1, "is_commutative": False, "symbol": "low"},
|
||||
m_high: {"name": "high", "nb_operands": 1, "is_commutative": False, "symbol": "high"},
|
||||
m_add: {"name": "add", "nb_operands": 2, "is_commutative": True, "symbol": "+"},
|
||||
m_sub: {"name": "sub", "nb_operands": 2, "is_commutative": False, "symbol": "-"},
|
||||
m_mul: {"name": "mul", "nb_operands": 2, "is_commutative": True, "symbol": "*"},
|
||||
m_udiv: {"name": "udiv", "nb_operands": 2, "is_commutative": False, "symbol": "UDiv"},
|
||||
m_sdiv: {"name": "sdiv", "nb_operands": 2, "is_commutative": False, "symbol": "/"},
|
||||
m_umod: {"name": "umod", "nb_operands": 2, "is_commutative": False, "symbol": "URem"},
|
||||
m_smod: {"name": "smod", "nb_operands": 2, "is_commutative": False, "symbol": "%"},
|
||||
m_or: {"name": "or", "nb_operands": 2, "is_commutative": True, "symbol": "|"},
|
||||
m_and: {"name": "and", "nb_operands": 2, "is_commutative": True, "symbol": "&"},
|
||||
m_xor: {"name": "xor", "nb_operands": 2, "is_commutative": True, "symbol": "^"},
|
||||
m_shl: {"name": "shl", "nb_operands": 2, "is_commutative": False, "symbol": "<<"},
|
||||
m_shr: {"name": "shr", "nb_operands": 2, "is_commutative": False, "symbol": "LShR"},
|
||||
m_sar: {"name": "sar", "nb_operands": 2, "is_commutative": False, "symbol": ">>"},
|
||||
m_cfadd: {"name": "cfadd", "nb_operands": 2, "is_commutative": True},
|
||||
m_ofadd: {"name": "ofadd", "nb_operands": 2, "is_commutative": True},
|
||||
m_cfshl: {"name": "cfshl", "nb_operands": 2, "is_commutative": False},
|
||||
m_cfshr: {"name": "cfshr", "nb_operands": 2, "is_commutative": False},
|
||||
m_sets: {"name": "sets", "nb_operands": 2, "is_commutative": False},
|
||||
m_seto: {"name": "seto", "nb_operands": 2, "is_commutative": False},
|
||||
m_setp: {"name": "setp", "nb_operands": 2, "is_commutative": False},
|
||||
m_setnz: {"name": "setnz", "nb_operands": 2, "is_commutative": True, "symbol": "!="},
|
||||
m_setz: {"name": "setz", "nb_operands": 2, "is_commutative": True, "symbol": "=="},
|
||||
m_seta: {"name": "seta", "nb_operands": 2, "is_commutative": False, "symbol": ">"},
|
||||
m_setae: {"name": "setae", "nb_operands": 2, "is_commutative": False, "symbol": ">="},
|
||||
m_setb: {"name": "setb", "nb_operands": 2, "is_commutative": False, "symbol": "<"},
|
||||
m_setbe: {"name": "setbe", "nb_operands": 2, "is_commutative": False, "symbol": "<="},
|
||||
m_setg: {"name": "setg", "nb_operands": 2, "is_commutative": False, "symbol": "UGT"},
|
||||
m_setge: {"name": "setge", "nb_operands": 2, "is_commutative": False, "symbol": "UGE"},
|
||||
m_setl: {"name": "setl", "nb_operands": 2, "is_commutative": False, "symbol": "ULT"},
|
||||
m_setle: {"name": "setle", "nb_operands": 2, "is_commutative": False, "symbol": "ULE"},
|
||||
m_jcnd: {"name": "jcnd", "nb_operands": 1, "is_commutative": False},
|
||||
m_jnz: {"name": "jnz", "nb_operands": 2, "is_commutative": True},
|
||||
m_jz: {"name": "jz", "nb_operands": 2, "is_commutative": True},
|
||||
m_jae: {"name": "jae", "nb_operands": 2, "is_commutative": False},
|
||||
m_jb: {"name": "jb", "nb_operands": 2, "is_commutative": False},
|
||||
m_ja: {"name": "ja", "nb_operands": 2, "is_commutative": False},
|
||||
m_jbe: {"name": "jbe", "nb_operands": 2, "is_commutative": False},
|
||||
m_jg: {"name": "jg", "nb_operands": 2, "is_commutative": False},
|
||||
m_jge: {"name": "jge", "nb_operands": 2, "is_commutative": False},
|
||||
m_jl: {"name": "jl", "nb_operands": 2, "is_commutative": False},
|
||||
m_jle: {"name": "jle", "nb_operands": 2, "is_commutative": False},
|
||||
m_jtbl: {"name": "jtbl", "nb_operands": 2, "is_commutative": False},
|
||||
m_ijmp: {"name": "ijmp", "nb_operands": 2, "is_commutative": False},
|
||||
m_goto: {"name": "goto", "nb_operands": 1, "is_commutative": False},
|
||||
m_call: {"name": "call", "nb_operands": 2, "is_commutative": False},
|
||||
m_icall: {"name": "icall", "nb_operands": 2, "is_commutative": False},
|
||||
m_ret: {"name": "ret", "nb_operands": 0, "is_commutative": False},
|
||||
m_push: {"name": "push", "nb_operands": 0, "is_commutative": False},
|
||||
m_pop: {"name": "pop", "nb_operands": 0, "is_commutative": False},
|
||||
m_und: {"name": "und", "nb_operands": 0, "is_commutative": False},
|
||||
m_ext: {"name": "ext", "nb_operands": 0, "is_commutative": False},
|
||||
m_f2i: {"name": "f2i", "nb_operands": 2, "is_commutative": False},
|
||||
m_f2u: {"name": "f2u", "nb_operands": 2, "is_commutative": False},
|
||||
m_i2f: {"name": "i2f", "nb_operands": 2, "is_commutative": False},
|
||||
m_u2f: {"name": "u2f", "nb_operands": 2, "is_commutative": False},
|
||||
m_f2f: {"name": "f2f", "nb_operands": 2, "is_commutative": False},
|
||||
m_fneg: {"name": "fneg", "nb_operands": 2, "is_commutative": False},
|
||||
m_fadd: {"name": "fadd", "nb_operands": 2, "is_commutative": True},
|
||||
m_fsub: {"name": "fsub", "nb_operands": 2, "is_commutative": False},
|
||||
m_fmul: {"name": "fmul", "nb_operands": 2, "is_commutative": True},
|
||||
m_fdiv: {"name": "fdiv", "nb_operands": 2, "is_commutative": False},
|
||||
}
|
||||
|
||||
|
||||
MATURITY_TO_STRING_DICT = {
|
||||
MMAT_ZERO: "MMAT_ZERO",
|
||||
MMAT_GENERATED: "MMAT_GENERATED",
|
||||
MMAT_PREOPTIMIZED: "MMAT_PREOPTIMIZED",
|
||||
MMAT_LOCOPT: "MMAT_LOCOPT",
|
||||
MMAT_CALLS: "MMAT_CALLS",
|
||||
MMAT_GLBOPT1: "MMAT_GLBOPT1",
|
||||
MMAT_GLBOPT2: "MMAT_GLBOPT2",
|
||||
MMAT_GLBOPT3: "MMAT_GLBOPT3",
|
||||
MMAT_LVARS: "MMAT_LVARS",
|
||||
}
|
||||
STRING_TO_MATURITY_DICT = {v: k for k, v in MATURITY_TO_STRING_DICT.items()}
|
||||
|
||||
MOP_TYPE_TO_STRING_DICT = {
|
||||
mop_z: "mop_z",
|
||||
mop_r: "mop_r",
|
||||
mop_n: "mop_n",
|
||||
mop_str: "mop_str",
|
||||
mop_d: "mop_d",
|
||||
mop_S: "mop_S",
|
||||
mop_v: "mop_v",
|
||||
mop_b: "mop_b",
|
||||
mop_f: "mop_f",
|
||||
mop_l: "mop_l",
|
||||
mop_a: "mop_a",
|
||||
mop_h: "mop_h",
|
||||
mop_c: "mop_c",
|
||||
mop_fn: "mop_fn",
|
||||
mop_p: "mop_p",
|
||||
mop_sc: "mop_sc",
|
||||
}
|
||||
|
||||
Z3_SPECIAL_OPERANDS = ["UDiv", "URem", "LShR", "UGT", "UGE", "ULT", "ULE"]
|
||||
|
||||
BOOLEAN_OPCODES = [m_lnot, m_bnot, m_or, m_and, m_xor]
|
||||
ARITHMETICAL_OPCODES = [m_neg, m_add, m_sub, m_mul, m_udiv, m_sdiv, m_umod, m_smod]
|
||||
BIT_OPERATIONS_OPCODES = [m_shl, m_shr, m_sar, m_mov, m_xds, m_xdu, m_low, m_high]
|
||||
CHECK_OPCODES = [m_sets, m_seto, m_setp, m_setnz, m_setz, m_seta, m_setae, m_setb,
|
||||
m_setbe, m_setg, m_setge, m_setl, m_setle]
|
||||
|
||||
MBA_RELATED_OPCODES = BOOLEAN_OPCODES + ARITHMETICAL_OPCODES + BIT_OPERATIONS_OPCODES + CHECK_OPCODES
|
||||
|
||||
CONDITIONAL_JUMP_OPCODES = [m_jcnd, m_jnz, m_jz, m_jae, m_ja, m_jb, m_jbe, m_jg, m_jge, m_jl, m_jle, m_jtbl]
|
||||
UNCONDITIONAL_JUMP_OPCODES = [m_goto, m_ijmp]
|
||||
CONTROL_FLOW_OPCODES = CONDITIONAL_JUMP_OPCODES + UNCONDITIONAL_JUMP_OPCODES
|
||||
|
||||
MINSN_TO_AST_FORBIDDEN_OPCODES = CONTROL_FLOW_OPCODES + [m_ret, m_nop, m_stx, m_push, m_pop, m_und, m_ext, m_call]
|
||||
|
||||
SUB_TABLE = {1: 0x100, 2: 0x10000, 4: 0x100000000, 8: 0x10000000000000000}
|
||||
AND_TABLE = {1: 0xff, 2: 0xffff, 4: 0xffffffff, 8: 0xffffffffffffffff}
|
||||
MSB_TABLE = {1: 0x80, 2: 0x8000, 4: 0x80000000, 8: 0x8000000000000000}
|
||||
|
||||
|
||||
# Hex-Rays mop equality checking
|
||||
def equal_bnot_cst(lo: mop_t, ro: mop_t) -> bool:
|
||||
if (lo.t != mop_n) or (ro.t != mop_n):
|
||||
return False
|
||||
if lo.size != ro.size:
|
||||
return False
|
||||
return lo.nnn.value ^ ro.nnn.value == AND_TABLE[lo.size]
|
||||
|
||||
|
||||
def equal_bnot_mop(lo: mop_t, ro: mop_t, test_two_sides=True) -> bool:
|
||||
if lo.t == mop_n:
|
||||
return equal_bnot_cst(lo, ro)
|
||||
|
||||
# We first check for a bnot operand
|
||||
if (lo.t == mop_d) and lo.d.opcode == m_bnot:
|
||||
if equal_mops_ignore_size(lo.d.l, ro):
|
||||
return True
|
||||
|
||||
# Otherwise Hexrays may have optimized using ~(-x) = x - 1
|
||||
if (lo.t == mop_d) and lo.d.opcode == m_neg:
|
||||
if (ro.t == mop_d) and ro.d.opcode == m_sub:
|
||||
if ro.d.r.t == mop_n and ro.d.r.nnn.value == 1:
|
||||
if equal_mops_ignore_size(ro.d.l, lo.d.l):
|
||||
return True
|
||||
|
||||
if (lo.t == mop_d) and lo.d.opcode == m_xds:
|
||||
if equal_bnot_mop(lo.d.l, ro):
|
||||
return True
|
||||
|
||||
if test_two_sides:
|
||||
return equal_bnot_mop(ro, lo, test_two_sides=False)
|
||||
return False
|
||||
|
||||
|
||||
def equal_ignore_msb_cst(lo: mop_t, ro: mop_t) -> bool:
|
||||
if (lo.t != mop_n) or (ro.t != mop_n):
|
||||
return False
|
||||
if lo.size != ro.size:
|
||||
return False
|
||||
mask = AND_TABLE[lo.size] ^ MSB_TABLE[lo.size]
|
||||
return lo.nnn.value & mask == ro.nnn.value & mask
|
||||
|
||||
|
||||
def equal_mops_bypass_xdu(lo: mop_t, ro: mop_t) -> bool:
|
||||
if (lo is None) or (ro is None):
|
||||
return False
|
||||
if (lo.t == mop_d) and (lo.d.opcode == m_xdu):
|
||||
return equal_mops_bypass_xdu(lo.d.l, ro)
|
||||
if (ro.t == mop_d) and (ro.d.opcode == m_xdu):
|
||||
return equal_mops_bypass_xdu(lo, ro.d.l)
|
||||
return equal_mops_ignore_size(lo, ro)
|
||||
|
||||
|
||||
def equal_mops_ignore_size(lo: mop_t, ro: mop_t) -> bool:
|
||||
if (lo is None) or (ro is None):
|
||||
return False
|
||||
if lo.t != ro.t:
|
||||
return False
|
||||
if lo.t == mop_z:
|
||||
return True
|
||||
elif lo.t == mop_fn:
|
||||
return lo.fpc == ro.fpc
|
||||
elif lo.t == mop_n:
|
||||
return lo.nnn.value == ro.nnn.value
|
||||
elif lo.t == mop_S:
|
||||
if lo.s == ro.s:
|
||||
return True
|
||||
if lo.s.off == ro.s.off:
|
||||
# Is it right?
|
||||
return True
|
||||
return False
|
||||
elif lo.t == mop_v:
|
||||
return lo.g == ro.g
|
||||
elif lo.t == mop_d:
|
||||
return lo.d.equal_insns(ro.d, EQ_IGNSIZE)
|
||||
# return lo.d.equal_insns(ro.d, EQ_IGNSIZE | EQ_IGNCODE)
|
||||
elif lo.t == mop_b:
|
||||
return lo.b == ro.b
|
||||
elif lo.t == mop_r:
|
||||
return lo.r == ro.r
|
||||
elif lo.t == mop_f:
|
||||
return False
|
||||
elif lo.t == mop_l:
|
||||
return lo.l == ro.l
|
||||
elif lo.t == mop_a:
|
||||
if lo.a.insize != ro.a.insize:
|
||||
return False
|
||||
if lo.a.outsize != ro.a.outsize:
|
||||
return False
|
||||
return equal_mops_ignore_size(lo.a, ro.a)
|
||||
elif lo.t == mop_h:
|
||||
return ro.helper == lo.helper
|
||||
elif lo.t == mop_str:
|
||||
return ro.cstr == lo.cstr
|
||||
elif lo.t == mop_c:
|
||||
return ro.c == lo.c
|
||||
elif lo.t == mop_p:
|
||||
return equal_mops_ignore_size(lo.pair.lop, ro.pair.lop) and equal_mops_ignore_size(lo.pair.hop, ro.pair.hop)
|
||||
elif lo.t == mop_sc:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def is_check_mop(lo: mop_t) -> bool:
|
||||
if lo.t != mop_d:
|
||||
return False
|
||||
if lo.d.opcode in CHECK_OPCODES:
|
||||
return True
|
||||
if lo.d.opcode in [m_xds, m_xdu]:
|
||||
return is_check_mop(lo.d.l)
|
||||
return False
|
||||
|
||||
|
||||
def extract_num_mop(ins: minsn_t) -> Tuple[mop_t, mop_t]:
|
||||
num_mop = None
|
||||
other_mop = None
|
||||
|
||||
if ins.l.t == mop_n:
|
||||
num_mop = ins.l
|
||||
other_mop = ins.r
|
||||
if ins.r.t == mop_n:
|
||||
num_mop = ins.r
|
||||
other_mop = ins.l
|
||||
return [num_mop, other_mop]
|
||||
|
||||
|
||||
def check_ins_mop_size_are_ok(ins: minsn_t) -> bool:
|
||||
"""
|
||||
This function can be used to check if a created instruction has consistent mop size
|
||||
Use it to avoid Hex-Rays decompilation errors when replacing instructions
|
||||
|
||||
:param ins:
|
||||
:return:
|
||||
"""
|
||||
ins_dest_size = ins.d.size
|
||||
if ins.opcode in [m_stx, m_ldx]:
|
||||
if ins.r.t == mop_d:
|
||||
if not check_ins_mop_size_are_ok(ins.r.d):
|
||||
return False
|
||||
return True
|
||||
|
||||
if ins.opcode in [m_xdu, m_xds, m_low, m_high]:
|
||||
if (ins.l.t == mop_d) and (not check_ins_mop_size_are_ok(ins.l.d)):
|
||||
return False
|
||||
return True
|
||||
|
||||
if ins.opcode in [m_sar, m_shr, m_shl]:
|
||||
if ins.l.size != ins_dest_size:
|
||||
return False
|
||||
if (ins.l.t == mop_d) and (not check_ins_mop_size_are_ok(ins.l.d)):
|
||||
return False
|
||||
if (ins.r.t == mop_d) and (not check_ins_mop_size_are_ok(ins.r.d)):
|
||||
return False
|
||||
return True
|
||||
|
||||
if ins.opcode in CHECK_OPCODES:
|
||||
if (ins.l.t == mop_d) and (not check_ins_mop_size_are_ok(ins.l.d)):
|
||||
return False
|
||||
if (ins.r.t == mop_d) and (not check_ins_mop_size_are_ok(ins.r.d)):
|
||||
return False
|
||||
return True
|
||||
|
||||
if ins.l is not None:
|
||||
if ins.l.size != ins_dest_size:
|
||||
return False
|
||||
if ins.l.t == mop_d and (not check_ins_mop_size_are_ok(ins.l.d)):
|
||||
return False
|
||||
|
||||
if ins.r is not None and ins.r.t != mop_z:
|
||||
if ins.r.size != ins_dest_size:
|
||||
return False
|
||||
if ins.r.t == mop_d and (not check_ins_mop_size_are_ok(ins.r.d)):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def check_mop_is_result_of(lo: mop_t, mc) -> bool:
|
||||
if lo.t != mop_d:
|
||||
return False
|
||||
return lo.d.opcode == mc
|
||||
|
||||
|
||||
def extract_by_opcode_type(ins: minsn_t, mc) -> Tuple[mop_t, mop_t]:
|
||||
if check_mop_is_result_of(ins.l, mc):
|
||||
return [ins.l, ins.r]
|
||||
if check_mop_is_result_of(ins.r, mc):
|
||||
return [ins.r, ins.l]
|
||||
return [None, None]
|
||||
|
||||
|
||||
def check_ins_have_same_operands(ins1: minsn_t, ins2: minsn_t, ignore_order=False) -> bool:
|
||||
if equal_mops_ignore_size(ins1.l, ins2.l) and equal_mops_ignore_size(ins1.r, ins2.r):
|
||||
return True
|
||||
if not ignore_order:
|
||||
return False
|
||||
return equal_mops_ignore_size(ins1.l, ins2.r) and equal_mops_ignore_size(ins1.r, ins2.l)
|
||||
|
||||
|
||||
def get_mop_index(searched_mop: mop_t, mop_list) -> int:
|
||||
for i, test_mop in enumerate(mop_list):
|
||||
if equal_mops_ignore_size(searched_mop, test_mop):
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
||||
def append_mop_if_not_in_list(mop: mop_t, mop_list) -> bool:
|
||||
mop_index = get_mop_index(mop, mop_list)
|
||||
if mop_index == -1:
|
||||
mop_list.append(mop)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_blk_index(searched_blk: mblock_t, blk_list: List[mblock_t]) -> int:
|
||||
blk_serial_list = [blk.serial for blk in blk_list]
|
||||
try:
|
||||
return blk_serial_list.index(searched_blk.serial)
|
||||
except ValueError:
|
||||
return -1
|
||||
280
d810/hexrays_hooks.py
Normal file
280
d810/hexrays_hooks.py
Normal file
@@ -0,0 +1,280 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions import PatternOptimizer, ChainOptimizer, Z3Optimizer, EarlyOptimizer, \
|
||||
InstructionAnalyzer
|
||||
from d810.hexrays_helpers import check_ins_mop_size_are_ok, append_mop_if_not_in_list
|
||||
from d810.hexrays_formatters import format_minsn_t, format_mop_t, maturity_to_string, mop_type_to_string, \
|
||||
dump_microcode_for_debug
|
||||
from d810.errors import D810Exception
|
||||
from d810.z3_utils import log_z3_instructions
|
||||
|
||||
from typing import TYPE_CHECKING, List
|
||||
if TYPE_CHECKING:
|
||||
from d810.manager import D810Manager
|
||||
from d810.optimizers.instructions.handler import InstructionOptimizer, InstructionOptimizationRule
|
||||
from d810.optimizers.flow.handler import FlowOptimizationRule
|
||||
|
||||
main_logger = logging.getLogger('D810')
|
||||
optimizer_logger = logging.getLogger('D810.optimizer')
|
||||
helper_logger = logging.getLogger('D810.helper')
|
||||
|
||||
DEFAULT_OPTIMIZATION_PATTERN_MATURITIES = [MMAT_PREOPTIMIZED, MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
|
||||
DEFAULT_OPTIMIZATION_CHAIN_MATURITIES = [MMAT_PREOPTIMIZED, MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
|
||||
DEFAULT_OPTIMIZATION_Z3_MATURITIES = [MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
|
||||
DEFAULT_OPTIMIZATION_EARLY_MATURITIES = [MMAT_GENERATED, MMAT_PREOPTIMIZED]
|
||||
DEFAULT_ANALYZER_MATURITIES = [MMAT_PREOPTIMIZED, MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
|
||||
|
||||
|
||||
class InstructionDefUseCollector(mop_visitor_t):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.unresolved_ins_mops = []
|
||||
self.memory_unresolved_ins_mops = []
|
||||
self.target_mops = []
|
||||
|
||||
def visit_mop(self, op: mop_t, op_type: int, is_target: bool):
|
||||
if is_target:
|
||||
append_mop_if_not_in_list(op, self.target_mops)
|
||||
else:
|
||||
# TODO whatever the case, in the end we will always return 0. May be this code can be better optimized.
|
||||
# TODO handle other special case (e.g. ldx ins, ...)
|
||||
if op.t == mop_S:
|
||||
append_mop_if_not_in_list(op, self.unresolved_ins_mops)
|
||||
elif op.t == mop_r:
|
||||
append_mop_if_not_in_list(op, self.unresolved_ins_mops)
|
||||
elif op.t == mop_v:
|
||||
append_mop_if_not_in_list(op, self.memory_unresolved_ins_mops)
|
||||
elif op.t == mop_a:
|
||||
if op.a.t == mop_v:
|
||||
return 0
|
||||
elif op.a.t == mop_S:
|
||||
return 0
|
||||
helper_logger.warning("Calling visit_mop with unsupported mop type {0} - {1}: '{2}'"
|
||||
.format(mop_type_to_string(op.t), mop_type_to_string(op.a.t), format_mop_t(op)))
|
||||
return 0
|
||||
elif op.t == mop_n:
|
||||
return 0
|
||||
elif op.t == mop_d:
|
||||
return 0
|
||||
elif op.t == mop_h:
|
||||
return 0
|
||||
elif op.t == mop_b:
|
||||
return 0
|
||||
else:
|
||||
helper_logger.warning("Calling visit_mop with unsupported mop type {0}: '{1}'"
|
||||
.format(mop_type_to_string(op.t), format_mop_t(op)))
|
||||
return 0
|
||||
|
||||
|
||||
class InstructionOptimizerManager(optinsn_t):
|
||||
def __init__(self, manager: D810Manager):
|
||||
optimizer_logger.debug("Initializing {0}...".format(self.__class__.__name__))
|
||||
super().__init__()
|
||||
self.manager = manager
|
||||
self.instruction_visitor = InstructionVisitorManager(self)
|
||||
self._last_optimizer_tried = None
|
||||
self.current_maturity = None
|
||||
self.current_blk_serial = None
|
||||
self.generate_z3_code = False
|
||||
self.dump_intermediate_microcode = False
|
||||
|
||||
self.instruction_optimizers = []
|
||||
self.optimizer_usage_info = {}
|
||||
self.add_optimizer(PatternOptimizer(DEFAULT_OPTIMIZATION_PATTERN_MATURITIES, log_dir=self.manager.log_dir))
|
||||
self.add_optimizer(ChainOptimizer(DEFAULT_OPTIMIZATION_CHAIN_MATURITIES, log_dir=self.manager.log_dir))
|
||||
self.add_optimizer(Z3Optimizer(DEFAULT_OPTIMIZATION_Z3_MATURITIES, log_dir=self.manager.log_dir))
|
||||
self.add_optimizer(EarlyOptimizer(DEFAULT_OPTIMIZATION_EARLY_MATURITIES, log_dir=self.manager.log_dir))
|
||||
self.analyzer = InstructionAnalyzer(DEFAULT_ANALYZER_MATURITIES, log_dir=self.manager.log_dir)
|
||||
|
||||
def func(self, blk: mblock_t, ins: minsn_t) -> bool:
|
||||
self.log_info_on_input(blk, ins)
|
||||
try:
|
||||
optimization_performed = self.optimize(blk, ins)
|
||||
|
||||
if not optimization_performed:
|
||||
optimization_performed = ins.for_all_insns(self.instruction_visitor)
|
||||
|
||||
if optimization_performed:
|
||||
ins.optimize_solo()
|
||||
|
||||
if blk is not None:
|
||||
blk.mark_lists_dirty()
|
||||
blk.mba.verify(True)
|
||||
|
||||
return optimization_performed
|
||||
except RuntimeError as e:
|
||||
optimizer_logger.error("RuntimeError while optimizing ins {0} with {1}: {2}"
|
||||
.format(format_minsn_t(ins), self._last_optimizer_tried, e))
|
||||
except D810Exception as e:
|
||||
optimizer_logger.error("D810Exception while optimizing ins {0} with {1}: {2}"
|
||||
.format(format_minsn_t(ins), self._last_optimizer_tried, e))
|
||||
return False
|
||||
|
||||
def reset_rule_usage_statistic(self):
|
||||
self.optimizer_usage_info = {}
|
||||
for ins_optimizer in self.instruction_optimizers:
|
||||
self.optimizer_usage_info[ins_optimizer.name] = 0
|
||||
ins_optimizer.reset_rule_usage_statistic()
|
||||
|
||||
def show_rule_usage_statistic(self):
|
||||
for optimizer_name, optimizer_nb_match in self.optimizer_usage_info.items():
|
||||
if optimizer_nb_match > 0:
|
||||
main_logger.info("Instruction optimizer '{0}' has been used {1} times"
|
||||
.format(optimizer_name, optimizer_nb_match))
|
||||
for ins_optimizer in self.instruction_optimizers:
|
||||
ins_optimizer.show_rule_usage_statistic()
|
||||
|
||||
def log_info_on_input(self, blk: mblock_t, ins: minsn_t):
|
||||
if blk is None:
|
||||
return
|
||||
mba: mbl_array_t = blk.mba
|
||||
|
||||
if (mba is not None) and (mba.maturity != self.current_maturity):
|
||||
self.current_maturity = mba.maturity
|
||||
main_logger.debug("Instruction optimization function called at maturity: {0}"
|
||||
.format(maturity_to_string(self.current_maturity)))
|
||||
self.analyzer.set_maturity(self.current_maturity)
|
||||
self.current_blk_serial = None
|
||||
|
||||
for ins_optimizer in self.instruction_optimizers:
|
||||
ins_optimizer.cur_maturity = self.current_maturity
|
||||
|
||||
if self.dump_intermediate_microcode:
|
||||
dump_microcode_for_debug(mba, self.manager.log_dir, "input_instruction_optimizer")
|
||||
|
||||
if blk.serial != self.current_blk_serial:
|
||||
self.current_blk_serial = blk.serial
|
||||
|
||||
def add_optimizer(self, optimizer: InstructionOptimizer):
|
||||
self.instruction_optimizers.append(optimizer)
|
||||
self.optimizer_usage_info[optimizer.name] = 0
|
||||
|
||||
def add_rule(self, rule: InstructionOptimizationRule):
|
||||
# optimizer_log.info("Trying to add rule {0}".format(rule))
|
||||
for ins_optimizer in self.instruction_optimizers:
|
||||
ins_optimizer.add_rule(rule)
|
||||
self.analyzer.add_rule(rule)
|
||||
|
||||
def configure(self, generate_z3_code=False, dump_intermediate_microcode=False, **kwargs):
|
||||
self.generate_z3_code = generate_z3_code
|
||||
self.dump_intermediate_microcode = dump_intermediate_microcode
|
||||
|
||||
def optimize(self, blk: mblock_t, ins: minsn_t) -> bool:
|
||||
# optimizer_log.info("Trying to optimize {0}".format(format_minsn_t(ins)))
|
||||
for ins_optimizer in self.instruction_optimizers:
|
||||
self._last_optimizer_tried = ins_optimizer
|
||||
new_ins = ins_optimizer.get_optimized_instruction(blk, ins)
|
||||
|
||||
if new_ins is not None:
|
||||
if not check_ins_mop_size_are_ok(new_ins):
|
||||
if check_ins_mop_size_are_ok(ins):
|
||||
main_logger.error("Invalid optimized instruction: {0} (original was {1})".format(
|
||||
format_minsn_t(new_ins), format_minsn_t(ins)))
|
||||
else:
|
||||
main_logger.error("Invalid original instruction : {0} (original was {1})".format(
|
||||
format_minsn_t(new_ins), format_minsn_t(ins)))
|
||||
else:
|
||||
ins.swap(new_ins)
|
||||
self.optimizer_usage_info[ins_optimizer.name] += 1
|
||||
if self.generate_z3_code:
|
||||
try:
|
||||
log_z3_instructions(new_ins, ins)
|
||||
except KeyError:
|
||||
pass
|
||||
return True
|
||||
|
||||
self.analyzer.analyze(blk, ins)
|
||||
return False
|
||||
|
||||
|
||||
class InstructionVisitorManager(minsn_visitor_t):
|
||||
def __init__(self, optimizer: InstructionOptimizerManager):
|
||||
optimizer_logger.debug("Initializing {0}...".format(self.__class__.__name__))
|
||||
super().__init__()
|
||||
self.instruction_optimizer = optimizer
|
||||
|
||||
def visit_minsn(self) -> bool:
|
||||
return self.instruction_optimizer.optimize(self.blk, self.curins)
|
||||
|
||||
|
||||
class BlockOptimizerManager(optblock_t):
|
||||
def __init__(self, manager: D810Manager):
|
||||
optimizer_logger.debug("Initializing {0}...".format(self.__class__.__name__))
|
||||
super().__init__()
|
||||
self.manager = manager
|
||||
self.cfg_rules = set()
|
||||
|
||||
self.current_maturity = None
|
||||
self.cfg_rules_usage_info = {}
|
||||
|
||||
def func(self, blk: mblock_t):
|
||||
self.log_info_on_input(blk)
|
||||
nb_patch = self.optimize(blk)
|
||||
return nb_patch
|
||||
|
||||
def reset_rule_usage_statistic(self):
|
||||
self.cfg_rules_usage_info = {}
|
||||
for rule in self.cfg_rules:
|
||||
self.cfg_rules_usage_info[rule.name] = []
|
||||
|
||||
def show_rule_usage_statistic(self):
|
||||
for rule_name, rule_nb_patch_list in self.cfg_rules_usage_info.items():
|
||||
nb_use = len(rule_nb_patch_list)
|
||||
if nb_use > 0:
|
||||
main_logger.info("BlkRule '{0}' has been used {1} times for a total of {2} patches"
|
||||
.format(rule_name, nb_use, sum(rule_nb_patch_list)))
|
||||
|
||||
def log_info_on_input(self, blk: mblock_t):
|
||||
if blk is None:
|
||||
return
|
||||
mba: mbl_array_t = blk.mba
|
||||
|
||||
if (mba is not None) and (mba.maturity != self.current_maturity):
|
||||
main_logger.debug("BlockOptimizer called at maturity: {0}".format(maturity_to_string(mba.maturity)))
|
||||
self.current_maturity = mba.maturity
|
||||
|
||||
def optimize(self, blk: mblock_t):
|
||||
for cfg_rule in self.cfg_rules:
|
||||
if self.check_if_rule_is_activated_for_address(cfg_rule, blk.mba.entry_ea):
|
||||
nb_patch = cfg_rule.optimize(blk)
|
||||
if nb_patch > 0:
|
||||
optimizer_logger.info("Rule {0} matched: {1} patches".format(cfg_rule.name, nb_patch))
|
||||
self.cfg_rules_usage_info[cfg_rule.name].append(nb_patch)
|
||||
return nb_patch
|
||||
return 0
|
||||
|
||||
def add_rule(self, cfg_rule: FlowOptimizationRule):
|
||||
optimizer_logger.info("Adding cfg rule {0}".format(cfg_rule))
|
||||
self.cfg_rules.add(cfg_rule)
|
||||
self.cfg_rules_usage_info[cfg_rule.name] = []
|
||||
|
||||
def configure(self, **kwargs):
|
||||
pass
|
||||
|
||||
def check_if_rule_is_activated_for_address(self, cfg_rule: FlowOptimizationRule, func_entry_ea: int):
|
||||
if cfg_rule.use_whitelist and (func_entry_ea not in cfg_rule.whitelisted_function_ea_list):
|
||||
return False
|
||||
if cfg_rule.use_blacklist and (func_entry_ea in cfg_rule.blacklisted_function_ea_list):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class HexraysDecompilationHook(Hexrays_Hooks):
|
||||
def __init__(self, manager):
|
||||
super().__init__()
|
||||
self.manager = manager
|
||||
|
||||
def prolog(self, mba: mbl_array_t, fc, reachable_blocks, decomp_flags) -> "int":
|
||||
main_logger.info("Starting decompilation of function at 0x{0:x}".format(mba.entry_ea))
|
||||
self.manager.instruction_optimizer.reset_rule_usage_statistic()
|
||||
self.manager.block_optimizer.reset_rule_usage_statistic()
|
||||
return 0
|
||||
|
||||
def glbopt(self, mba: mbl_array_t) -> "int":
|
||||
main_logger.info("glbopt finished for function at 0x{0:x}".format(mba.entry_ea))
|
||||
self.manager.instruction_optimizer.show_rule_usage_statistic()
|
||||
self.manager.block_optimizer.show_rule_usage_statistic()
|
||||
return 0
|
||||
541
d810/ida_ui.py
Normal file
541
d810/ida_ui.py
Normal file
@@ -0,0 +1,541 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import logging
|
||||
import idaapi
|
||||
import ida_kernwin
|
||||
from PyQt5 import QtCore, QtWidgets, QtGui
|
||||
|
||||
from d810.conf import ProjectConfiguration, RuleConfiguration
|
||||
|
||||
logger = logging.getLogger('D810.ui')
|
||||
|
||||
|
||||
class PluginConfigurationFileForm_t(QtWidgets.QDialog):
|
||||
def __init__(self, parent, state):
|
||||
logger.debug("Initializing PluginConfigurationFileForm_t")
|
||||
super().__init__(parent)
|
||||
self.state = state
|
||||
self.log_dir_changed = False
|
||||
|
||||
self.log_dir = self.state.d810_config.get("log_dir")
|
||||
self.erase_logs_on_reload = self.state.d810_config.get("erase_logs_on_reload")
|
||||
self.generate_z3_code = self.state.d810_config.get("generate_z3_code")
|
||||
self.dump_intermediate_microcode = self.state.d810_config.get("dump_intermediate_microcode")
|
||||
|
||||
self.resize(1000, 500)
|
||||
self.setWindowTitle("Plugin Configuration")
|
||||
|
||||
# Main layout
|
||||
self.config_layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
self.layout_log_dir = QtWidgets.QHBoxLayout()
|
||||
self.lbl_log_dir_info = QtWidgets.QLabel(self)
|
||||
self.lbl_log_dir_info.setText("Current log directory path: ")
|
||||
self.layout_log_dir.addWidget(self.lbl_log_dir_info)
|
||||
self.lbl_log_dir = QtWidgets.QLabel(self)
|
||||
self.lbl_log_dir.setText(self.log_dir)
|
||||
self.layout_log_dir.addWidget(self.lbl_log_dir)
|
||||
self.button_change_log_dir = QtWidgets.QPushButton(self)
|
||||
self.button_change_log_dir.setText("Change log directory")
|
||||
self.button_change_log_dir.clicked.connect(self.choose_log_dir)
|
||||
self.layout_log_dir.addWidget(self.button_change_log_dir)
|
||||
|
||||
self.config_layout.addLayout(self.layout_log_dir)
|
||||
|
||||
self.checkbox_generate_z3_code = QtWidgets.QCheckBox("Generate Z3 code for simplification performed", self)
|
||||
self.checkbox_generate_z3_code.setChecked(self.state.d810_config.get("generate_z3_code"))
|
||||
self.config_layout.addWidget(self.checkbox_generate_z3_code)
|
||||
self.checkbox_dump_intermediate_microcode = QtWidgets.QCheckBox("Dump functions microcode at each maturity", self)
|
||||
self.checkbox_dump_intermediate_microcode.setChecked(self.state.d810_config.get("dump_intermediate_microcode"))
|
||||
self.config_layout.addWidget(self.checkbox_dump_intermediate_microcode)
|
||||
self.checkbox_erase_logs_on_reload = QtWidgets.QCheckBox("Erase log directory content when plugin is reloaded", self)
|
||||
self.checkbox_erase_logs_on_reload.setChecked(self.state.d810_config.get("erase_logs_on_reload"))
|
||||
self.config_layout.addWidget(self.checkbox_erase_logs_on_reload)
|
||||
|
||||
self.layout_button = QtWidgets.QHBoxLayout()
|
||||
self.button_save = QtWidgets.QPushButton(self)
|
||||
self.button_save.setText("Save")
|
||||
self.button_save.clicked.connect(self.save_config)
|
||||
self.layout_button.addWidget(self.button_save)
|
||||
self.button_cancel = QtWidgets.QPushButton(self)
|
||||
self.button_cancel.setText("Cancel")
|
||||
self.button_cancel.clicked.connect(self.reject)
|
||||
self.layout_button.addWidget(self.button_cancel)
|
||||
self.config_layout.addLayout(self.layout_button)
|
||||
|
||||
self.setLayout(self.config_layout)
|
||||
|
||||
def choose_log_dir(self):
|
||||
logger.debug("Calling save_rule_configuration")
|
||||
log_dir = QtWidgets.QFileDialog.getExistingDirectory(self, "Open Directory", "/home",
|
||||
QtWidgets.QFileDialog.ShowDirsOnly |
|
||||
QtWidgets.QFileDialog.DontResolveSymlinks)
|
||||
if log_dir != "":
|
||||
self.log_dir = log_dir
|
||||
self.log_dir_changed = True
|
||||
self.lbl_log_dir.setText(self.log_dir)
|
||||
|
||||
def save_config(self):
|
||||
if self.log_dir_changed:
|
||||
self.state.d810_config.set("log_dir", self.log_dir)
|
||||
self.state.d810_config.set("erase_logs_on_reload", self.checkbox_erase_logs_on_reload.isChecked())
|
||||
self.state.d810_config.set("generate_z3_code", self.checkbox_generate_z3_code.isChecked())
|
||||
self.state.d810_config.set("dump_intermediate_microcode", self.checkbox_dump_intermediate_microcode.isChecked())
|
||||
self.state.d810_config.save()
|
||||
self.accept()
|
||||
|
||||
|
||||
class EditConfigurationFileForm_t(QtWidgets.QDialog):
|
||||
def __init__(self, parent, state):
|
||||
logger.debug("Initializing EditConfigurationFileForm_t")
|
||||
super().__init__(parent)
|
||||
self.state = state
|
||||
self.resize(1000, 500)
|
||||
self.setWindowTitle("Rule Configuration Editor")
|
||||
|
||||
# Main layout
|
||||
self.config_layout = QtWidgets.QVBoxLayout(self)
|
||||
|
||||
# Configuration Name Selection Layout
|
||||
self.layout_cfg_name = QtWidgets.QHBoxLayout()
|
||||
self.lbl_cfg_name = QtWidgets.QLabel(self)
|
||||
self.lbl_cfg_name.setText("Rule Name")
|
||||
self.layout_cfg_name.addWidget(self.lbl_cfg_name)
|
||||
self.in_cfg_name = QtWidgets.QLineEdit(self)
|
||||
self.layout_cfg_name.addWidget(self.in_cfg_name)
|
||||
self.config_layout.addLayout(self.layout_cfg_name)
|
||||
|
||||
# Instructions rule Selection Layout
|
||||
self.table_ins_rule_selection = QtWidgets.QTableWidget(self)
|
||||
# self.table_ins_rule_selection.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
self.table_ins_rule_selection.setRowCount(2)
|
||||
self.table_ins_rule_selection.setColumnCount(4)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText("Is activated")
|
||||
self.table_ins_rule_selection.setHorizontalHeaderItem(0, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText("Rule Name")
|
||||
self.table_ins_rule_selection.setHorizontalHeaderItem(1, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText("Rule Description")
|
||||
self.table_ins_rule_selection.setHorizontalHeaderItem(2, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText("Rule Configuration")
|
||||
self.table_ins_rule_selection.setHorizontalHeaderItem(3, item)
|
||||
self.table_ins_rule_selection.horizontalHeader().setStretchLastSection(True)
|
||||
self.table_ins_rule_selection.verticalHeader().setVisible(False)
|
||||
self.table_ins_rule_selection.setSortingEnabled(True)
|
||||
# self.table_ins_rule_selection.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
||||
self.config_layout.addWidget(self.table_ins_rule_selection)
|
||||
|
||||
# Block rule Selection Layout
|
||||
self.table_blk_rule_selection = QtWidgets.QTableWidget(self)
|
||||
# self.table_blk_rule_selection.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||
self.table_blk_rule_selection.setRowCount(2)
|
||||
self.table_blk_rule_selection.setColumnCount(4)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText("Is activated")
|
||||
self.table_blk_rule_selection.setHorizontalHeaderItem(0, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText("Rule Name")
|
||||
self.table_blk_rule_selection.setHorizontalHeaderItem(1, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText("Rule Description")
|
||||
self.table_blk_rule_selection.setHorizontalHeaderItem(2, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText("Rule Configuration")
|
||||
self.table_blk_rule_selection.setHorizontalHeaderItem(3, item)
|
||||
self.table_blk_rule_selection.horizontalHeader().setStretchLastSection(True)
|
||||
self.table_blk_rule_selection.verticalHeader().setVisible(False)
|
||||
self.table_blk_rule_selection.setSortingEnabled(True)
|
||||
# self.table_blk_rule_selection.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
||||
self.config_layout.addWidget(self.table_blk_rule_selection)
|
||||
|
||||
self.layout_button = QtWidgets.QHBoxLayout()
|
||||
self.button_save = QtWidgets.QPushButton(self)
|
||||
self.button_save.setText("Save")
|
||||
self.button_save.clicked.connect(self.save_rule_configuration)
|
||||
self.layout_button.addWidget(self.button_save)
|
||||
self.button_cancel = QtWidgets.QPushButton(self)
|
||||
self.button_cancel.setText("Cancel")
|
||||
self.button_cancel.clicked.connect(self.reject)
|
||||
self.layout_button.addWidget(self.button_cancel)
|
||||
self.config_layout.addLayout(self.layout_button)
|
||||
|
||||
self.setLayout(self.config_layout)
|
||||
|
||||
self.config_path = None
|
||||
self.config_description = None
|
||||
self.config_rules = []
|
||||
self.update_table_rule_selection()
|
||||
|
||||
def update_form(self, config_description=None, activated_ins_rule_config_list=None, activated_blk_rule_config_list=None, config_path=None):
|
||||
logger.debug("Calling update_form")
|
||||
if config_description is not None:
|
||||
self.in_cfg_name.setText(config_description)
|
||||
if activated_ins_rule_config_list is not None or activated_blk_rule_config_list is not None:
|
||||
self.update_table_rule_selection(activated_ins_rule_config_list, activated_blk_rule_config_list)
|
||||
if config_path is not None:
|
||||
self.config_path = config_path
|
||||
|
||||
def update_table_rule_selection(self, activated_ins_rule_config_list=None, activated_blk_rule_config_list=None):
|
||||
logger.debug("Calling update_table_rule_selection")
|
||||
self.update_table_ins_rule_selection(activated_ins_rule_config_list)
|
||||
self.update_table_blk_rule_selection(activated_blk_rule_config_list)
|
||||
|
||||
def _get_rule_config(self, rule_name, rule_config_list):
|
||||
logger.debug("Calling _get_rule_config")
|
||||
try:
|
||||
rule_name_list = [rule_conf.name for rule_conf in rule_config_list]
|
||||
rule_index = rule_name_list.index(rule_name)
|
||||
return rule_config_list[rule_index]
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def update_table_ins_rule_selection(self, activated_ins_rule_config_list=None):
|
||||
logger.debug("Calling update_table_ins_rule_selection")
|
||||
if activated_ins_rule_config_list is None:
|
||||
activated_ins_rule_config_list = []
|
||||
self.table_ins_rule_selection.setRowCount(len(self.state.known_ins_rules))
|
||||
for i, rule in enumerate(self.state.known_ins_rules):
|
||||
rule_config = self._get_rule_config(rule.name, activated_ins_rule_config_list)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setTextAlignment(QtCore.Qt.AlignCenter)
|
||||
if rule_config is not None and rule_config.is_activated:
|
||||
item.setCheckState(QtCore.Qt.Checked)
|
||||
else:
|
||||
item.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.table_ins_rule_selection.setItem(i, 0, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText(rule.name)
|
||||
item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
self.table_ins_rule_selection.setItem(i, 1, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText(rule.description)
|
||||
item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
self.table_ins_rule_selection.setItem(i, 2, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
if rule_config is not None:
|
||||
item.setText(json.dumps(rule_config.config))
|
||||
else:
|
||||
item.setText("{}")
|
||||
self.table_ins_rule_selection.setItem(i, 3, item)
|
||||
self.table_ins_rule_selection.resizeColumnsToContents()
|
||||
|
||||
def update_table_blk_rule_selection(self, activated_blk_rule_config_list=None):
|
||||
logger.debug("Calling update_table_blk_rule_selection")
|
||||
if activated_blk_rule_config_list is None:
|
||||
activated_blk_rule_config_list = []
|
||||
self.table_blk_rule_selection.setRowCount(len(self.state.known_blk_rules))
|
||||
for i, rule in enumerate(self.state.known_blk_rules):
|
||||
rule_config = self._get_rule_config(rule.name, activated_blk_rule_config_list)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setTextAlignment(QtCore.Qt.AlignCenter)
|
||||
if rule_config is not None and rule_config.is_activated:
|
||||
item.setCheckState(QtCore.Qt.Checked)
|
||||
else:
|
||||
item.setCheckState(QtCore.Qt.Unchecked)
|
||||
self.table_blk_rule_selection.setItem(i, 0, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText(rule.name)
|
||||
item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
self.table_blk_rule_selection.setItem(i, 1, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
item.setText(rule.description)
|
||||
item.setFlags(QtCore.Qt.ItemIsEnabled)
|
||||
self.table_blk_rule_selection.setItem(i, 2, item)
|
||||
item = QtWidgets.QTableWidgetItem()
|
||||
if rule_config is not None:
|
||||
item.setText(json.dumps(rule_config.config))
|
||||
else:
|
||||
item.setText("{}")
|
||||
self.table_blk_rule_selection.setItem(i, 3, item)
|
||||
self.table_blk_rule_selection.resizeColumnsToContents()
|
||||
|
||||
def save_rule_configuration(self):
|
||||
logger.debug("Calling save_rule_configuration")
|
||||
fname, _ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save file', self.config_path, "Project configuration (*.json)")
|
||||
if fname:
|
||||
self.config_path = fname
|
||||
self.config_description = self.in_cfg_name.text()
|
||||
self.config_ins_rules = self.get_ins_rules()
|
||||
self.config_blk_rules = self.get_blk_rules()
|
||||
self.accept()
|
||||
|
||||
def get_ins_rules(self):
|
||||
logger.debug("Calling get_ins_rules")
|
||||
activated_rule_names = []
|
||||
nb_rules = self.table_ins_rule_selection.rowCount()
|
||||
for i in range(nb_rules):
|
||||
if self.table_ins_rule_selection.item(i, 0).checkState():
|
||||
rule_conf = RuleConfiguration(name=self.table_ins_rule_selection.item(i, 1).text(),
|
||||
is_activated=self.table_ins_rule_selection.item(i, 0).checkState() == QtCore.Qt.Checked,
|
||||
config=json.loads(self.table_ins_rule_selection.item(i, 3).text()))
|
||||
activated_rule_names.append(rule_conf)
|
||||
# activated_rule_names.append(self.table_ins_rule_selection.item(i, 1).text())
|
||||
return activated_rule_names
|
||||
|
||||
def get_blk_rules(self):
|
||||
logger.debug("Calling get_blk_rules")
|
||||
activated_rule_names = []
|
||||
nb_rules = self.table_blk_rule_selection.rowCount()
|
||||
for i in range(nb_rules):
|
||||
if self.table_blk_rule_selection.item(i, 0).checkState():
|
||||
rule_conf = RuleConfiguration(name=self.table_blk_rule_selection.item(i, 1).text(),
|
||||
is_activated=self.table_blk_rule_selection.item(i, 0).checkState() == QtCore.Qt.Checked,
|
||||
config=json.loads(self.table_blk_rule_selection.item(i, 3).text()))
|
||||
activated_rule_names.append(rule_conf)
|
||||
# activated_rule_names.append(self.table_blk_rule_selection.item(i, 1).text())
|
||||
return activated_rule_names
|
||||
|
||||
|
||||
class D810ConfigForm_t(ida_kernwin.PluginForm):
|
||||
def __init__(self, state):
|
||||
super().__init__()
|
||||
self.state = state
|
||||
self.shown = False
|
||||
self.created = False
|
||||
self.parent = None
|
||||
|
||||
def OnClose(self, form):
|
||||
logger.debug("Calling OnClose")
|
||||
self.shown = False
|
||||
# self.parent.close()
|
||||
|
||||
def Show(self):
|
||||
logger.debug("Calling Show")
|
||||
if self.shown:
|
||||
return
|
||||
self.shown = True
|
||||
return ida_kernwin.PluginForm.Show(
|
||||
self, "D-810 Configuration",
|
||||
options=(ida_kernwin.PluginForm.WOPN_PERSIST |
|
||||
ida_kernwin.PluginForm.WCLS_SAVE |
|
||||
ida_kernwin.PluginForm.WOPN_MENU |
|
||||
ida_kernwin.PluginForm.WOPN_RESTORE |
|
||||
ida_kernwin.PluginForm.WOPN_TAB))
|
||||
|
||||
def OnCreate(self, form):
|
||||
logger.debug("Calling OnCreate")
|
||||
self.created = True
|
||||
|
||||
# Get parent widget
|
||||
self.parent = self.FormToPyQtWidget(form)
|
||||
layout = QtWidgets.QGridLayout(self.parent)
|
||||
|
||||
# ----------- Config options -----------------------
|
||||
# Horizontal splitter for config boxes
|
||||
cfg_split = QtWidgets.QSplitter(self.parent)
|
||||
layout.addWidget(cfg_split, 0, 0)
|
||||
# Config name label
|
||||
self.curlabel = QtWidgets.QLabel('Current file loaded:')
|
||||
cfg_split.addWidget(self.curlabel)
|
||||
|
||||
self.cfg_select = QtWidgets.QComboBox(self.parent)
|
||||
cfg_split.addWidget(self.cfg_select)
|
||||
|
||||
self.btn_new_cfg = QtWidgets.QPushButton('New')
|
||||
self.btn_new_cfg.clicked.connect(self._create_config)
|
||||
cfg_split.addWidget(self.btn_new_cfg)
|
||||
|
||||
self.btn_duplicate_cfg = QtWidgets.QPushButton('Duplicate')
|
||||
self.btn_duplicate_cfg.clicked.connect(self._duplicate_config)
|
||||
cfg_split.addWidget(self.btn_duplicate_cfg)
|
||||
|
||||
self.btn_edit_cfg = QtWidgets.QPushButton('Edit')
|
||||
self.btn_edit_cfg.clicked.connect(self._edit_config)
|
||||
cfg_split.addWidget(self.btn_edit_cfg)
|
||||
|
||||
self.btn_delele_cfg = QtWidgets.QPushButton('Delete')
|
||||
self.btn_delele_cfg.clicked.connect(self._delete_config)
|
||||
cfg_split.addWidget(self.btn_delele_cfg)
|
||||
|
||||
# leave space for comboboxes in cfg_split, rather than between widgets
|
||||
cfg_split.setStretchFactor(0, 0)
|
||||
cfg_split.setStretchFactor(1, 1)
|
||||
cfg_split.setStretchFactor(2, 0)
|
||||
|
||||
description_split = QtWidgets.QSplitter(self.parent)
|
||||
layout.addWidget(description_split, 1, 0)
|
||||
self.cfg_description_layout = QtWidgets.QHBoxLayout(description_split)
|
||||
self.cfg_description_label = QtWidgets.QLabel("Description")
|
||||
description_split.addWidget(self.cfg_description_label)
|
||||
self.cfg_description = QtWidgets.QLabel("No description")
|
||||
description_split.addWidget(self.cfg_description)
|
||||
description_split.setStretchFactor(0, 0)
|
||||
description_split.setStretchFactor(1, 1)
|
||||
|
||||
self.cfg_ins_preview = QtWidgets.QTableWidget(self.parent)
|
||||
layout.addWidget(self.cfg_ins_preview, 2, 0)
|
||||
|
||||
self.cfg_blk_preview = QtWidgets.QTableWidget(self.parent)
|
||||
layout.addWidget(self.cfg_blk_preview, 3, 0)
|
||||
self.update_cfg_preview()
|
||||
|
||||
# ----------- Analysis buttons -----------------------
|
||||
# Horizontal splitter for buttons
|
||||
btn_split = QtWidgets.QSplitter(self.parent)
|
||||
layout.addWidget(btn_split, 4, 0)
|
||||
|
||||
self.btn_config = QtWidgets.QPushButton('Configuration')
|
||||
self.btn_config.clicked.connect(self._configure_plugin)
|
||||
btn_split.addWidget(self.btn_config)
|
||||
|
||||
|
||||
self.btn_start = QtWidgets.QPushButton('Start')
|
||||
self.btn_start.clicked.connect(self._start_d810)
|
||||
btn_split.addWidget(self.btn_start)
|
||||
|
||||
self.btn_stop = QtWidgets.QPushButton('Stop')
|
||||
self.btn_stop.clicked.connect(self._stop_d810)
|
||||
btn_split.addWidget(self.btn_stop)
|
||||
|
||||
self.plugin_status = QtWidgets.QLabel()
|
||||
self.plugin_status.setText("<span style=\" font-size:8pt; font-weight:600; color:#ff0000;\" >Not Loaded</span>")
|
||||
description_split.addWidget(self.plugin_status)
|
||||
btn_split.addWidget(self.plugin_status)
|
||||
|
||||
|
||||
self.update_cfg_select()
|
||||
self.cfg_select.setCurrentIndex(self.state.current_project_index)
|
||||
self.cfg_select.currentIndexChanged.connect(self._load_config)
|
||||
|
||||
def update_cfg_preview(self):
|
||||
logger.debug("Calling update_cfg_preview")
|
||||
self.update_cfg_ins_preview()
|
||||
self.update_cfg_blk_preview()
|
||||
|
||||
def update_cfg_ins_preview(self):
|
||||
# return
|
||||
logger.debug("Calling update_cfg_ins_preview")
|
||||
self.cfg_ins_preview.setRowCount(len(self.state.current_ins_rules))
|
||||
self.cfg_ins_preview.setColumnCount(3)
|
||||
self.cfg_ins_preview.setHorizontalHeaderLabels(("Name", "Description", "Configuration"))
|
||||
self.cfg_ins_preview.horizontalHeader().setStretchLastSection(True)
|
||||
self.cfg_ins_preview.setSortingEnabled(True)
|
||||
# self.cfg_ins_preview.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
||||
i = 0
|
||||
for rule in self.state.current_ins_rules:
|
||||
cell_file_path = QtWidgets.QTableWidgetItem(rule.name)
|
||||
cell_file_path.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
cell_rule_description = QtWidgets.QTableWidgetItem(rule.description)
|
||||
cell_rule_description.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
cell_rule_config = QtWidgets.QTableWidgetItem(json.dumps(rule.config))
|
||||
cell_rule_config.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.cfg_ins_preview.setItem(i, 0, cell_file_path)
|
||||
self.cfg_ins_preview.setItem(i, 1, cell_rule_description)
|
||||
self.cfg_ins_preview.setItem(i, 2, cell_rule_config)
|
||||
i += 1
|
||||
self.cfg_ins_preview.resizeColumnsToContents()
|
||||
|
||||
def update_cfg_blk_preview(self):
|
||||
logger.debug("Calling update_cfg_blk_preview")
|
||||
self.cfg_blk_preview.setRowCount(len(self.state.current_blk_rules))
|
||||
self.cfg_blk_preview.setColumnCount(3)
|
||||
self.cfg_blk_preview.setHorizontalHeaderLabels(("Name", "Description", "Configuration"))
|
||||
self.cfg_blk_preview.horizontalHeader().setStretchLastSection(True)
|
||||
self.cfg_blk_preview.setSortingEnabled(True)
|
||||
# self.cfg_blk_preview.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
|
||||
i = 0
|
||||
for rule in self.state.current_blk_rules:
|
||||
cell_file_path = QtWidgets.QTableWidgetItem(rule.name)
|
||||
cell_file_path.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
cell_rule_description = QtWidgets.QTableWidgetItem(rule.description)
|
||||
cell_rule_description.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
cell_rule_config = QtWidgets.QTableWidgetItem(json.dumps(rule.config))
|
||||
cell_rule_config.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.cfg_blk_preview.setItem(i, 0, cell_file_path)
|
||||
self.cfg_blk_preview.setItem(i, 1, cell_rule_description)
|
||||
self.cfg_blk_preview.setItem(i, 2, cell_rule_config)
|
||||
i += 1
|
||||
self.cfg_blk_preview.resizeColumnsToContents()
|
||||
|
||||
def update_cfg_select(self):
|
||||
logger.debug("Calling update_cfg_select")
|
||||
tmp = self.state.current_project_index
|
||||
self.cfg_select.clear()
|
||||
self.cfg_select.addItems([proj.path for proj in self.state.projects])
|
||||
self.cfg_select.setCurrentIndex(tmp)
|
||||
|
||||
def _create_config(self):
|
||||
logger.debug("Calling _create_config")
|
||||
self._internal_config_creation(None, None, None, self.state.d810_config.config_dir)
|
||||
|
||||
def _duplicate_config(self):
|
||||
logger.debug("Calling _duplicate_config")
|
||||
cur_cfg = self.state.current_project
|
||||
self._internal_config_creation(None, cur_cfg.ins_rules, cur_cfg.blk_rules, self.state.d810_config.config_dir)
|
||||
|
||||
def _edit_config(self):
|
||||
logger.debug("Calling _edit_config")
|
||||
cur_cfg = self.state.current_project
|
||||
self._internal_config_creation(cur_cfg.description, cur_cfg.ins_rules, cur_cfg.blk_rules, cur_cfg.path, cur_cfg)
|
||||
|
||||
def _internal_config_creation(self, description, start_ins_rules, start_blk_rules, path, old_conf=None):
|
||||
logger.debug("Calling _internal_config_creation")
|
||||
editdlg = EditConfigurationFileForm_t(self.parent, self.state)
|
||||
editdlg.update_form(description, start_ins_rules, start_blk_rules, path)
|
||||
if editdlg.exec_() == QtWidgets.QDialog.Accepted:
|
||||
new_config = ProjectConfiguration(editdlg.config_path, editdlg.config_description, editdlg.config_ins_rules, editdlg.config_blk_rules)
|
||||
new_config.save()
|
||||
if old_conf is None:
|
||||
self.state.add_project(new_config)
|
||||
else:
|
||||
self.state.update_project(old_conf, new_config)
|
||||
self.update_cfg_select()
|
||||
return new_config
|
||||
return None
|
||||
|
||||
# callback when the "Delete" button is clicked
|
||||
def _delete_config(self):
|
||||
logger.debug("Calling _delete_config")
|
||||
self.state.del_project(self.state.current_project)
|
||||
self.update_cfg_select()
|
||||
|
||||
# Called when the edit combo is changed
|
||||
def _load_config(self, index):
|
||||
logger.debug("Calling _load_config")
|
||||
self.state.load_project(index)
|
||||
self.cfg_description.setText(self.state.current_project.description)
|
||||
self.update_cfg_preview()
|
||||
return
|
||||
|
||||
def _configure_plugin(self):
|
||||
editdlg = PluginConfigurationFileForm_t(self.parent, self.state)
|
||||
if editdlg.exec_() == QtWidgets.QDialog.Accepted:
|
||||
return
|
||||
return
|
||||
|
||||
def _start_d810(self):
|
||||
logger.debug("Calling _start_d810")
|
||||
self.state.start_d810()
|
||||
# self.plugin_status.clear()
|
||||
self.plugin_status.setText("<span style=\" font-size:8pt; font-weight:600; color:#00FF00;\" >Loaded</span>")
|
||||
return
|
||||
|
||||
def _stop_d810(self):
|
||||
logger.debug("Calling _stop_d810")
|
||||
self.state.stop_d810()
|
||||
# self.plugin_status.clear()
|
||||
self.plugin_status.setText("<span style=\" font-size:8pt; font-weight:600; color:#FF0000;\" >Not Loaded</span>")
|
||||
return
|
||||
|
||||
|
||||
class D810GUI(object):
|
||||
def __init__(self, state):
|
||||
"""
|
||||
Instanciate D-810 views
|
||||
"""
|
||||
logger.debug("Initializing D810GUI")
|
||||
self.state = state
|
||||
self.d810_config_form = D810ConfigForm_t(self.state)
|
||||
# XXX fix
|
||||
idaapi.set_dock_pos("D-810", "IDA View-A", idaapi.DP_TAB)
|
||||
|
||||
def show_windows(self):
|
||||
logger.debug("Calling show_windows")
|
||||
self.d810_config_form.Show()
|
||||
|
||||
def term(self):
|
||||
logger.debug("Calling term")
|
||||
self.d810_config_form.Close(ida_kernwin.PluginForm.WCLS_SAVE)
|
||||
102
d810/log.ini
Normal file
102
d810/log.ini
Normal file
@@ -0,0 +1,102 @@
|
||||
[loggers]
|
||||
keys=root,D810,D810Ui,D810Optimizer,D810RulesChain,D810PatternSearch,D810BranchFixer,D810Unflat,D810Tracker,D810Emulator,D810Helper,D810Z3Test
|
||||
|
||||
[handlers]
|
||||
keys=consoleHandler,defaultFileHandler,z3FileHandler
|
||||
|
||||
[formatters]
|
||||
keys=defaultFormatter,rawFormatter
|
||||
|
||||
[logger_root]
|
||||
level=DEBUG
|
||||
handlers=consoleHandler
|
||||
|
||||
[logger_D810]
|
||||
level=DEBUG
|
||||
handlers=consoleHandler,defaultFileHandler
|
||||
qualname=D810
|
||||
propagate=0
|
||||
|
||||
[logger_D810Ui]
|
||||
level=ERROR
|
||||
handlers=defaultFileHandler
|
||||
qualname=D810.ui
|
||||
propagate=0
|
||||
|
||||
[logger_D810Optimizer]
|
||||
level=INFO
|
||||
handlers=defaultFileHandler
|
||||
qualname=D810.optimizer
|
||||
propagate=0
|
||||
|
||||
[logger_D810RulesChain]
|
||||
level=INFO
|
||||
handlers=defaultFileHandler
|
||||
qualname=D810.chain
|
||||
propagate=0
|
||||
|
||||
[logger_D810BranchFixer]
|
||||
level=INFO
|
||||
handlers=defaultFileHandler
|
||||
qualname=D810.branch_fixer
|
||||
propagate=0
|
||||
|
||||
[logger_D810Unflat]
|
||||
level=INFO
|
||||
handlers=defaultFileHandler
|
||||
qualname=D810.unflat
|
||||
propagate=0
|
||||
|
||||
[logger_D810Tracker]
|
||||
level=INFO
|
||||
handlers=defaultFileHandler
|
||||
qualname=D810.tracker
|
||||
propagate=0
|
||||
|
||||
[logger_D810Emulator]
|
||||
level=INFO
|
||||
handlers=defaultFileHandler
|
||||
qualname=D810.emulator
|
||||
propagate=0
|
||||
|
||||
[logger_D810Helper]
|
||||
level=INFO
|
||||
handlers=defaultFileHandler
|
||||
qualname=D810.helper
|
||||
propagate=0
|
||||
|
||||
[logger_D810PatternSearch]
|
||||
level=ERROR
|
||||
handlers=defaultFileHandler
|
||||
qualname=D810.pattern_search
|
||||
propagate=0
|
||||
|
||||
[logger_D810Z3Test]
|
||||
level=INFO
|
||||
handlers=z3FileHandler
|
||||
qualname=D810.z3_test
|
||||
propagate=0
|
||||
|
||||
[handler_consoleHandler]
|
||||
class=StreamHandler
|
||||
level=INFO
|
||||
formatter=defaultFormatter
|
||||
args=(sys.stdout,)
|
||||
|
||||
[handler_defaultFileHandler]
|
||||
class=FileHandler
|
||||
level=DEBUG
|
||||
formatter=defaultFormatter
|
||||
args=('%(default_log_filename)s',)
|
||||
|
||||
[handler_z3FileHandler]
|
||||
class=FileHandler
|
||||
level=DEBUG
|
||||
formatter=rawFormatter
|
||||
args=('%(z3_log_filename)s',)
|
||||
|
||||
[formatter_defaultFormatter]
|
||||
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
||||
|
||||
[formatter_rawFormatter]
|
||||
format=%(message)s
|
||||
23
d810/log.py
Normal file
23
d810/log.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import os
|
||||
import shutil
|
||||
import logging
|
||||
import logging.config
|
||||
|
||||
LOG_CONFIG_FILENAME = "log.ini"""
|
||||
LOG_FILENAME = "d810.log"
|
||||
Z3_TEST_FILENAME = "z3_check_instructions_substitution.py"
|
||||
|
||||
|
||||
def clear_logs(log_dir):
|
||||
shutil.rmtree(log_dir, ignore_errors=True)
|
||||
|
||||
|
||||
def configure_loggers(log_dir):
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
log_main_file = os.path.join(log_dir, LOG_FILENAME)
|
||||
z3_test_file = os.path.join(log_dir, Z3_TEST_FILENAME)
|
||||
log_conf_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), LOG_CONFIG_FILENAME)
|
||||
logging.config.fileConfig(log_conf_file, defaults={"default_log_filename": log_main_file,
|
||||
"z3_log_filename": z3_test_file})
|
||||
z3_file_logger = logging.getLogger('D810.z3_test')
|
||||
z3_file_logger.info("from z3 import BitVec, BitVecVal, UDiv, URem, LShR, UGT, UGE, ULT, ULE, prove\n\n")
|
||||
196
d810/manager.py
Normal file
196
d810/manager.py
Normal file
@@ -0,0 +1,196 @@
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import idaapi
|
||||
|
||||
from typing import TYPE_CHECKING, List
|
||||
if TYPE_CHECKING:
|
||||
from d810.conf import D810Configuration, ProjectConfiguration
|
||||
|
||||
|
||||
# Note that imports are performed directly in the functions so that they are reloaded each time the plugin is restarted
|
||||
# This allow to load change code/drop new rules without having to reboot IDA
|
||||
d810_state = None
|
||||
|
||||
D810_LOG_DIR_NAME = "d810_logs"
|
||||
|
||||
MANAGER_INFO_FILENAME = "manager_info.json"
|
||||
logger = logging.getLogger('D810')
|
||||
|
||||
|
||||
def reload_all_modules():
|
||||
manager_info_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), MANAGER_INFO_FILENAME)
|
||||
|
||||
with open(manager_info_path, "r") as f:
|
||||
manager_info = json.load(f)
|
||||
|
||||
for module_name in manager_info["module_list"]:
|
||||
idaapi.require(module_name)
|
||||
|
||||
|
||||
class D810Manager(object):
|
||||
def __init__(self, log_dir):
|
||||
self.instruction_optimizer_rules = []
|
||||
self.instruction_optimizer_config = {}
|
||||
self.block_optimizer_rules = []
|
||||
self.block_optimizer_config = {}
|
||||
self.instruction_optimizer = None
|
||||
self.block_optimizer = None
|
||||
self.hx_decompiler_hook = None
|
||||
self.log_dir = log_dir
|
||||
self.config = {}
|
||||
|
||||
def configure(self, **kwargs):
|
||||
self.config = kwargs
|
||||
|
||||
def reload(self):
|
||||
self.stop()
|
||||
logger.debug("Reloading manager...")
|
||||
|
||||
from d810.hexrays_hooks import InstructionOptimizerManager, BlockOptimizerManager, HexraysDecompilationHook
|
||||
|
||||
self.instruction_optimizer = InstructionOptimizerManager(self)
|
||||
self.instruction_optimizer.configure(**self.instruction_optimizer_config)
|
||||
self.block_optimizer = BlockOptimizerManager(self)
|
||||
self.block_optimizer.configure(**self.block_optimizer_config)
|
||||
|
||||
for rule in self.instruction_optimizer_rules:
|
||||
rule.log_dir = self.log_dir
|
||||
self.instruction_optimizer.add_rule(rule)
|
||||
|
||||
for cfg_rule in self.block_optimizer_rules:
|
||||
cfg_rule.log_dir = self.log_dir
|
||||
self.block_optimizer.add_rule(cfg_rule)
|
||||
|
||||
self.instruction_optimizer.install()
|
||||
self.block_optimizer.install()
|
||||
|
||||
self.hx_decompiler_hook = HexraysDecompilationHook(self)
|
||||
self.hx_decompiler_hook.hook()
|
||||
|
||||
def configure_instruction_optimizer(self, rules, **kwargs):
|
||||
self.instruction_optimizer_rules = [rule for rule in rules]
|
||||
self.instruction_optimizer_config = kwargs
|
||||
|
||||
def configure_block_optimizer(self, rules, **kwargs):
|
||||
self.block_optimizer_rules = [rule for rule in rules]
|
||||
self.block_optimizer_config = kwargs
|
||||
|
||||
def stop(self):
|
||||
if self.instruction_optimizer is not None:
|
||||
logger.debug("Removing InstructionOptimizer...")
|
||||
self.instruction_optimizer.remove()
|
||||
self.instruction_optimizer = None
|
||||
if self.block_optimizer is not None:
|
||||
logger.debug("Removing ControlFlowFixer...")
|
||||
self.block_optimizer.remove()
|
||||
self.block_optimizer = None
|
||||
if self.hx_decompiler_hook is not None:
|
||||
logger.debug("Removing HexraysDecompilationHook...")
|
||||
self.hx_decompiler_hook.unhook()
|
||||
self.hx_decompiler_hook = None
|
||||
|
||||
|
||||
class D810State(object):
|
||||
def __init__(self, d810_config: D810Configuration):
|
||||
# For debugging purposes, to interact with this object from the console
|
||||
# Type in IDA Python shell 'from d810.manager import d810_state' to access it
|
||||
global d810_state
|
||||
d810_state = self
|
||||
reload_all_modules()
|
||||
|
||||
self.d810_config = d810_config
|
||||
self.log_dir = os.path.join(self.d810_config.get("log_dir"), D810_LOG_DIR_NAME)
|
||||
self.manager = D810Manager(self.log_dir)
|
||||
|
||||
from d810.optimizers.instructions import KNOWN_INS_RULES
|
||||
from d810.optimizers.flow import KNOWN_BLK_RULES
|
||||
self.known_ins_rules = [x for x in KNOWN_INS_RULES]
|
||||
self.known_blk_rules = [x for x in KNOWN_BLK_RULES]
|
||||
|
||||
self.gui = None
|
||||
self.current_project = None
|
||||
self.projects: List[ProjectConfiguration] = []
|
||||
self.current_project_index = self.d810_config.get("last_project_index")
|
||||
self.current_ins_rules = []
|
||||
self.current_blk_rules = []
|
||||
|
||||
self.register_default_projects()
|
||||
self.load_project(self.current_project_index)
|
||||
|
||||
def register_default_projects(self):
|
||||
from d810.conf import ProjectConfiguration
|
||||
self.projects = []
|
||||
for project_configuration_path in self.d810_config.get("configurations"):
|
||||
project_configuration = ProjectConfiguration(project_configuration_path,
|
||||
conf_dir=self.d810_config.config_dir)
|
||||
project_configuration.load()
|
||||
self.projects.append(project_configuration)
|
||||
logger.debug("Rule configurations loaded: {0}".format(self.projects))
|
||||
|
||||
def add_project(self, config: ProjectConfiguration):
|
||||
self.projects.append(config)
|
||||
self.d810_config.get("configurations").append(config.path)
|
||||
self.d810_config.save()
|
||||
|
||||
def update_project(self, old_config: ProjectConfiguration, new_config: ProjectConfiguration):
|
||||
old_config_index = self.projects.index(old_config)
|
||||
self.projects[old_config_index] = new_config
|
||||
|
||||
def del_project(self, config: ProjectConfiguration):
|
||||
self.projects.remove(config)
|
||||
self.d810_config.get("configurations").remove(config.path)
|
||||
self.d810_config.save()
|
||||
os.remove(config.path)
|
||||
|
||||
def load_project(self, project_index: int):
|
||||
self.current_project_index = project_index
|
||||
self.current_project = self.projects[project_index]
|
||||
self.current_ins_rules = []
|
||||
self.current_blk_rules = []
|
||||
|
||||
for rule in self.known_ins_rules:
|
||||
for rule_conf in self.current_project.ins_rules:
|
||||
if rule.name == rule_conf.name:
|
||||
rule.configure(rule_conf.config)
|
||||
rule.set_log_dir(self.log_dir)
|
||||
self.current_ins_rules.append(rule)
|
||||
logger.debug("Instruction rules configured")
|
||||
for blk_rule in self.known_blk_rules:
|
||||
for rule_conf in self.current_project.blk_rules:
|
||||
if blk_rule.name == rule_conf.name:
|
||||
blk_rule.configure(rule_conf.config)
|
||||
blk_rule.set_log_dir(self.log_dir)
|
||||
self.current_blk_rules.append(blk_rule)
|
||||
logger.debug("Block rules configured")
|
||||
self.manager.configure(**self.current_project.additional_configuration)
|
||||
logger.debug("Project loaded.")
|
||||
|
||||
def start_d810(self):
|
||||
print("D-810 ready to deobfuscate...")
|
||||
self.manager.configure_instruction_optimizer([rule for rule in self.current_ins_rules],
|
||||
generate_z3_code=self.d810_config.get("generate_z3_code"),
|
||||
dump_intermediate_microcode=self.d810_config.get(
|
||||
"dump_intermediate_microcode"),
|
||||
**self.current_project.additional_configuration)
|
||||
self.manager.configure_block_optimizer([rule for rule in self.current_blk_rules],
|
||||
**self.current_project.additional_configuration)
|
||||
self.manager.reload()
|
||||
self.d810_config.set("last_project_index", self.current_project_index)
|
||||
self.d810_config.save()
|
||||
|
||||
def stop_d810(self):
|
||||
print("Stopping D-810...")
|
||||
self.manager.stop()
|
||||
|
||||
def start_plugin(self):
|
||||
from d810.ida_ui import D810GUI
|
||||
self.gui = D810GUI(self)
|
||||
self.gui.show_windows()
|
||||
|
||||
def stop_plugin(self):
|
||||
self.manager.stop()
|
||||
if self.gui:
|
||||
self.gui.term()
|
||||
self.gui = None
|
||||
60
d810/manager_info.json
Normal file
60
d810/manager_info.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"_comment": "Order of module in module list matters",
|
||||
"module_list": [
|
||||
"d810.cfg_utils",
|
||||
"d810.emulator",
|
||||
"d810.ast",
|
||||
"d810.optimizers.handler",
|
||||
"d810.optimizers.instructions.handler",
|
||||
"d810.optimizers.instructions.pattern_matching.handler",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_add",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_and",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_bnot",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_cst",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_mov",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_mul",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_neg",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_or",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_predicates",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_sub",
|
||||
"d810.optimizers.instructions.pattern_matching.rewrite_xor",
|
||||
"d810.optimizers.instructions.pattern_matching.weird",
|
||||
"d810.optimizers.instructions.pattern_matching",
|
||||
"d810.optimizers.instructions.chain.handler",
|
||||
"d810.optimizers.instructions.chain.chain_rules",
|
||||
"d810.optimizers.instructions.chain",
|
||||
"d810.optimizers.instructions.z3.handler",
|
||||
"d810.optimizers.instructions.z3.cst",
|
||||
"d810.optimizers.instructions.z3.predicates",
|
||||
"d810.optimizers.instructions.z3",
|
||||
"d810.optimizers.instructions.analysis.utils",
|
||||
"d810.optimizers.instructions.analysis.handler",
|
||||
"d810.optimizers.instructions.analysis.pattern_guess",
|
||||
"d810.optimizers.instructions.analysis",
|
||||
"d810.optimizers.instructions.early.handler",
|
||||
"d810.optimizers.instructions.early.mem_read",
|
||||
"d810.optimizers.instructions.early",
|
||||
"d810.optimizers.instructions",
|
||||
"d810.optimizers.flow.handler",
|
||||
"d810.optimizers.flow.jumps.handler",
|
||||
"d810.optimizers.flow.jumps.opaque",
|
||||
"d810.optimizers.flow.jumps.tricks",
|
||||
"d810.optimizers.flow.jumps",
|
||||
"d810.optimizers.flow.flattening.utils",
|
||||
"d810.optimizers.flow.flattening.generic",
|
||||
"d810.optimizers.flow.flattening.unflattener",
|
||||
"d810.optimizers.flow.flattening.unflattener_fake_jump",
|
||||
"d810.optimizers.flow.flattening.unflattener_switch_case",
|
||||
"d810.optimizers.flow.flattening.unflattener_indirect",
|
||||
"d810.optimizers.flow.flattening",
|
||||
"d810.optimizers.flow",
|
||||
"d810.hexrays_helpers",
|
||||
"d810.hexrays_formatters",
|
||||
"d810.hexrays_hooks",
|
||||
"d810.ida_ui",
|
||||
"d810.log",
|
||||
"d810.tracker",
|
||||
"d810.utils",
|
||||
"d810.z3_utils"
|
||||
]
|
||||
}
|
||||
0
d810/optimizers/__init__.py
Normal file
0
d810/optimizers/__init__.py
Normal file
5
d810/optimizers/flow/__init__.py
Normal file
5
d810/optimizers/flow/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from d810.optimizers.flow.flattening import UNFLATTENING_BLK_RULES
|
||||
from d810.optimizers.flow.jumps import JUMP_OPTIMIZATION_BLOCK_RULES, JUMP_OPTIMIZATION_RULES
|
||||
|
||||
KNOWN_BLK_RULES = UNFLATTENING_BLK_RULES + JUMP_OPTIMIZATION_BLOCK_RULES
|
||||
|
||||
6
d810/optimizers/flow/flattening/__init__.py
Normal file
6
d810/optimizers/flow/flattening/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from d810.optimizers.flow.flattening.unflattener import Unflattener
|
||||
from d810.optimizers.flow.flattening.unflattener_switch_case import UnflattenerSwitchCase
|
||||
from d810.optimizers.flow.flattening.unflattener_indirect import UnflattenerTigressIndirect
|
||||
from d810.optimizers.flow.flattening.unflattener_fake_jump import UnflattenerFakeJump
|
||||
|
||||
UNFLATTENING_BLK_RULES = [Unflattener(), UnflattenerSwitchCase(), UnflattenerTigressIndirect(), UnflattenerFakeJump()]
|
||||
469
d810/optimizers/flow/flattening/generic.py
Normal file
469
d810/optimizers/flow/flattening/generic.py
Normal file
@@ -0,0 +1,469 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typing import List, Union, Tuple
|
||||
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.flow.handler import FlowOptimizationRule
|
||||
|
||||
from d810.tracker import MopTracker, MopHistory, remove_segment_registers, duplicate_histories
|
||||
from d810.emulator import MicroCodeEnvironment, MicroCodeInterpreter
|
||||
from d810.hexrays_hooks import InstructionDefUseCollector
|
||||
from d810.hexrays_helpers import extract_num_mop, get_mop_index, append_mop_if_not_in_list, CONTROL_FLOW_OPCODES, \
|
||||
CONDITIONAL_JUMP_OPCODES
|
||||
from d810.hexrays_formatters import format_minsn_t, format_mop_t, dump_microcode_for_debug, format_mop_list
|
||||
from d810.cfg_utils import mba_deep_cleaning, ensure_child_has_an_unconditional_father, ensure_last_block_is_goto, \
|
||||
change_1way_block_successor, create_block
|
||||
from d810.optimizers.flow.flattening.utils import NotResolvableFatherException, NotDuplicableFatherException, \
|
||||
DispatcherUnflatteningException, get_all_possibles_values, check_if_all_values_are_found
|
||||
|
||||
unflat_logger = logging.getLogger('D810.unflat')
|
||||
|
||||
|
||||
class GenericDispatcherBlockInfo(object):
|
||||
|
||||
def __init__(self, blk, father=None):
|
||||
self.blk = blk
|
||||
self.ins = []
|
||||
self.use_list = []
|
||||
self.use_before_def_list = []
|
||||
self.def_list = []
|
||||
self.assume_def_list = []
|
||||
self.comparison_value = None
|
||||
self.compared_mop = None
|
||||
|
||||
self.father = None
|
||||
if father is not None:
|
||||
self.register_father(father)
|
||||
|
||||
@property
|
||||
def serial(self) -> int:
|
||||
return self.blk.serial
|
||||
|
||||
def register_father(self, father: GenericDispatcherBlockInfo):
|
||||
self.father = father
|
||||
self.assume_def_list = [x for x in father.assume_def_list]
|
||||
|
||||
def update_use_def_lists(self, ins_mops_used: List[mop_t], ins_mops_def: List[mop_t]):
|
||||
for mop_used in ins_mops_used:
|
||||
append_mop_if_not_in_list(mop_used, self.use_list)
|
||||
mop_used_index = get_mop_index(mop_used, self.def_list)
|
||||
if mop_used_index == -1:
|
||||
append_mop_if_not_in_list(mop_used, self.use_before_def_list)
|
||||
for mop_def in ins_mops_def:
|
||||
append_mop_if_not_in_list(mop_def, self.def_list)
|
||||
|
||||
def update_with_ins(self, ins: minsn_t):
|
||||
ins_mop_info = InstructionDefUseCollector()
|
||||
ins.for_all_ops(ins_mop_info)
|
||||
cleaned_unresolved_ins_mops = remove_segment_registers(ins_mop_info.unresolved_ins_mops)
|
||||
self.update_use_def_lists(cleaned_unresolved_ins_mops + ins_mop_info.memory_unresolved_ins_mops,
|
||||
ins_mop_info.target_mops)
|
||||
self.ins.append(ins)
|
||||
if ins.opcode in CONDITIONAL_JUMP_OPCODES:
|
||||
num_mop, other_mop = extract_num_mop(ins)
|
||||
if num_mop is not None:
|
||||
self.comparison_value = num_mop.nnn.value
|
||||
self.compared_mop = other_mop
|
||||
|
||||
def parse(self):
|
||||
curins = self.blk.head
|
||||
while curins is not None:
|
||||
self.update_with_ins(curins)
|
||||
curins = curins.next
|
||||
for mop_def in self.def_list:
|
||||
append_mop_if_not_in_list(mop_def, self.assume_def_list)
|
||||
|
||||
def does_only_need(self, prerequisite_mop_list: List[mop_t]) -> bool:
|
||||
for used_before_def_mop in self.use_before_def_list:
|
||||
mop_index = get_mop_index(used_before_def_mop, prerequisite_mop_list)
|
||||
if mop_index == -1:
|
||||
return False
|
||||
return True
|
||||
|
||||
def recursive_get_father(self) -> List[GenericDispatcherBlockInfo]:
|
||||
if self.father is None:
|
||||
return [self]
|
||||
else:
|
||||
return self.father.recursive_get_father() + [self]
|
||||
|
||||
def show_history(self):
|
||||
full_father_list = self.recursive_get_father()
|
||||
unflat_logger.info(" Show history of Block {0}".format(self.blk.serial))
|
||||
for father in full_father_list[:-1]:
|
||||
for ins in father.ins:
|
||||
unflat_logger.info(" {0}.{1}".format(father.blk.serial, format_minsn_t(ins)))
|
||||
|
||||
def print_info(self):
|
||||
unflat_logger.info("Block {0} information:".format(self.blk.serial))
|
||||
unflat_logger.info(" USE list: {0}".format(format_mop_list(self.use_list)))
|
||||
unflat_logger.info(" DEF list: {0}".format(format_mop_list(self.def_list)))
|
||||
unflat_logger.info(" USE BEFORE DEF list: {0}".format(format_mop_list(self.use_before_def_list)))
|
||||
unflat_logger.info(" ASSUME DEF list: {0}".format(format_mop_list(self.assume_def_list)))
|
||||
|
||||
|
||||
class GenericDispatcherInfo(object):
|
||||
def __init__(self, mba: mbl_array_t):
|
||||
self.mba = mba
|
||||
self.mop_compared = None
|
||||
self.entry_block = None
|
||||
self.comparison_values = []
|
||||
self.dispatcher_internal_blocks = []
|
||||
self.dispatcher_exit_blocks = []
|
||||
|
||||
def reset(self):
|
||||
self.mop_compared = None
|
||||
self.entry_block = None
|
||||
self.comparison_values = []
|
||||
self.dispatcher_internal_blocks = []
|
||||
self.dispatcher_exit_blocks = []
|
||||
|
||||
def explore(self, blk: mblock_t) -> bool:
|
||||
return False
|
||||
|
||||
def get_shared_internal_blocks(self, other_dispatcher: GenericDispatcherInfo) -> List[mblock_t]:
|
||||
my_dispatcher_block_serial = [blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks]
|
||||
other_dispatcher_block_serial = [blk_info.blk.serial
|
||||
for blk_info in other_dispatcher.dispatcher_internal_blocks]
|
||||
return [self.mba.get_mblock(blk_serial) for blk_serial in my_dispatcher_block_serial
|
||||
if blk_serial in other_dispatcher_block_serial]
|
||||
|
||||
def is_sub_dispatcher(self, other_dispatcher: GenericDispatcherInfo) -> bool:
|
||||
shared_blocks = self.get_shared_internal_blocks(other_dispatcher)
|
||||
if (len(shared_blocks) > 0) and (self.entry_block.blk.npred() < other_dispatcher.entry_block.blk.npred()):
|
||||
return True
|
||||
return False
|
||||
|
||||
def should_emulation_continue(self, cur_blk: mblock_t) -> bool:
|
||||
exit_block_serial_list = [exit_block.serial for exit_block in self.dispatcher_exit_blocks]
|
||||
if (cur_blk is not None) and (cur_blk.serial not in exit_block_serial_list):
|
||||
return True
|
||||
return False
|
||||
|
||||
def emulate_dispatcher_with_father_history(self, father_history: MopHistory) -> Tuple[mblock_t, List[minsn_t]]:
|
||||
microcode_interpreter = MicroCodeInterpreter()
|
||||
microcode_environment = MicroCodeEnvironment()
|
||||
dispatcher_input_info = []
|
||||
for initialization_mop in self.entry_block.use_before_def_list:
|
||||
initialization_mop_value = father_history.get_mop_constant_value(initialization_mop)
|
||||
if initialization_mop_value is None:
|
||||
raise NotResolvableFatherException("Can't emulate dispatcher {0} with history {1}"
|
||||
.format(self.entry_block.serial, father_history.block_serial_path))
|
||||
microcode_environment.define(initialization_mop, initialization_mop_value)
|
||||
dispatcher_input_info.append("{0} = {1:x}".format(format_mop_t(initialization_mop),
|
||||
initialization_mop_value))
|
||||
|
||||
unflat_logger.info("Executing dispatcher {0} with: {1}"
|
||||
.format(self.entry_block.blk.serial, ", ".join(dispatcher_input_info)))
|
||||
|
||||
instructions_executed = []
|
||||
cur_blk = self.entry_block.blk
|
||||
cur_ins = cur_blk.head
|
||||
while self.should_emulation_continue(cur_blk):
|
||||
unflat_logger.debug(" Executing: {0}.{1}".format(cur_blk.serial, format_minsn_t(cur_ins)))
|
||||
is_ok = microcode_interpreter.eval_instruction(cur_blk, cur_ins, microcode_environment)
|
||||
if not is_ok:
|
||||
return cur_blk, instructions_executed
|
||||
instructions_executed.append(cur_ins)
|
||||
cur_blk = microcode_environment.next_blk
|
||||
cur_ins = microcode_environment.next_ins
|
||||
return cur_blk, instructions_executed
|
||||
|
||||
def print_info(self, verbose=False):
|
||||
unflat_logger.info("Dispatcher information: ")
|
||||
unflat_logger.info(" Entry block: {0}.{1}: ".format(self.entry_block.blk.serial,
|
||||
format_minsn_t(self.entry_block.blk.tail)))
|
||||
unflat_logger.info(" Entry block predecessors: {0}: "
|
||||
.format([blk_serial for blk_serial in self.entry_block.blk.predset]))
|
||||
unflat_logger.info(" Compared mop: {0} ".format(format_mop_t(self.mop_compared)))
|
||||
unflat_logger.info(" Comparison values: {0} ".format(", ".join([hex(x) for x in self.comparison_values])))
|
||||
self.entry_block.print_info()
|
||||
unflat_logger.info(" Number of internal blocks: {0} ({1})"
|
||||
.format(len(self.dispatcher_internal_blocks),
|
||||
[blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks]))
|
||||
if verbose:
|
||||
for disp_blk in self.dispatcher_internal_blocks:
|
||||
unflat_logger.info(" Internal block: {0}.{1} ".format(disp_blk.blk.serial,
|
||||
format_minsn_t(disp_blk.blk.tail)))
|
||||
disp_blk.show_history()
|
||||
unflat_logger.info(" Number of Exit blocks: {0} ({1})"
|
||||
.format(len(self.dispatcher_exit_blocks),
|
||||
[blk_info.blk.serial for blk_info in self.dispatcher_exit_blocks]))
|
||||
if verbose:
|
||||
for exit_blk in self.dispatcher_exit_blocks:
|
||||
unflat_logger.info(" Exit block: {0}.{1} ".format(exit_blk.blk.serial,
|
||||
format_minsn_t(exit_blk.blk.head)))
|
||||
exit_blk.show_history()
|
||||
|
||||
|
||||
class GenericDispatcherCollector(minsn_visitor_t):
|
||||
DISPATCHER_CLASS = GenericDispatcherInfo
|
||||
DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 2
|
||||
DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 2
|
||||
DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 2
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.dispatcher_list = []
|
||||
self.explored_blk_serials = []
|
||||
self.dispatcher_min_internal_block = self.DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK
|
||||
self.dispatcher_min_exit_block = self.DEFAULT_DISPATCHER_MIN_EXIT_BLOCK
|
||||
self.dispatcher_min_comparison_value = self.DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE
|
||||
|
||||
def configure(self, kwargs):
|
||||
if "min_dispatcher_internal_block" in kwargs.keys():
|
||||
self.dispatcher_min_internal_block = kwargs["min_dispatcher_internal_block"]
|
||||
if "min_dispatcher_exit_block" in kwargs.keys():
|
||||
self.dispatcher_min_exit_block = kwargs["min_dispatcher_exit_block"]
|
||||
if "min_dispatcher_comparison_value" in kwargs.keys():
|
||||
self.dispatcher_min_comparison_value = kwargs["min_dispatcher_comparison_value"]
|
||||
|
||||
def specific_checks(self, disp_info: GenericDispatcherInfo) -> bool:
|
||||
unflat_logger.debug("DispatcherInfo {0} : {1} internals, {2} exits, {3} comparison"
|
||||
.format(self.blk.serial, len(disp_info.dispatcher_internal_blocks),
|
||||
len(disp_info.dispatcher_exit_blocks), len(set(disp_info.comparison_values))))
|
||||
if len(disp_info.dispatcher_internal_blocks) < self.dispatcher_min_internal_block:
|
||||
return False
|
||||
if len(disp_info.dispatcher_exit_blocks) < self.dispatcher_min_exit_block:
|
||||
return False
|
||||
if len(set(disp_info.comparison_values)) < self.dispatcher_min_comparison_value:
|
||||
return False
|
||||
self.dispatcher_list.append(disp_info)
|
||||
return True
|
||||
|
||||
def visit_minsn(self):
|
||||
if self.blk.serial in self.explored_blk_serials:
|
||||
return 0
|
||||
self.explored_blk_serials.append(self.blk.serial)
|
||||
disp_info = self.DISPATCHER_CLASS(self.blk.mba)
|
||||
is_good_candidate = disp_info.explore(self.blk)
|
||||
if not is_good_candidate:
|
||||
return 0
|
||||
if not self.specific_checks(disp_info):
|
||||
return 0
|
||||
self.dispatcher_list.append(disp_info)
|
||||
return 0
|
||||
|
||||
def remove_sub_dispatchers(self):
|
||||
main_dispatcher_list = []
|
||||
for dispatcher_1 in self.dispatcher_list:
|
||||
is_dispatcher_1_sub_dispatcher = False
|
||||
for dispatcher_2 in self.dispatcher_list:
|
||||
if dispatcher_1.is_sub_dispatcher(dispatcher_2):
|
||||
is_dispatcher_1_sub_dispatcher = True
|
||||
break
|
||||
if not is_dispatcher_1_sub_dispatcher:
|
||||
main_dispatcher_list.append(dispatcher_1)
|
||||
self.dispatcher_list = [x for x in main_dispatcher_list]
|
||||
|
||||
def reset(self):
|
||||
self.dispatcher_list = []
|
||||
self.explored_blk_serials = []
|
||||
|
||||
def get_dispatcher_list(self) -> List[GenericDispatcherInfo]:
|
||||
self.remove_sub_dispatchers()
|
||||
return self.dispatcher_list
|
||||
|
||||
|
||||
class GenericUnflatteningRule(FlowOptimizationRule):
|
||||
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1, MMAT_GLBOPT2]
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.mba = None
|
||||
self.cur_maturity = MMAT_ZERO
|
||||
self.cur_maturity_pass = 0
|
||||
self.last_pass_nb_patch_done = 0
|
||||
self.maturities = self.DEFAULT_UNFLATTENING_MATURITIES
|
||||
|
||||
def check_if_rule_should_be_used(self, blk: mblock_t) -> bool:
|
||||
if self.cur_maturity == self.mba.maturity:
|
||||
self.cur_maturity_pass += 1
|
||||
else:
|
||||
self.cur_maturity = self.mba.maturity
|
||||
self.cur_maturity_pass = 0
|
||||
if self.cur_maturity not in self.maturities:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class GenericDispatcherUnflatteningRule(GenericUnflatteningRule):
|
||||
DISPATCHER_COLLECTOR_CLASS = GenericDispatcherCollector
|
||||
MOP_TRACKER_MAX_NB_BLOCK = 100
|
||||
MOP_TRACKER_MAX_NB_PATH = 100
|
||||
DEFAULT_MAX_DUPLICATION_PASSES = 20
|
||||
DEFAULT_MAX_PASSES = 5
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.dispatcher_collector = self.DISPATCHER_COLLECTOR_CLASS()
|
||||
self.dispatcher_list = []
|
||||
self.max_duplication_passes = self.DEFAULT_MAX_DUPLICATION_PASSES
|
||||
self.max_passes = self.DEFAULT_MAX_PASSES
|
||||
|
||||
def check_if_rule_should_be_used(self, blk: mblock_t) -> bool:
|
||||
if not super().check_if_rule_should_be_used(blk):
|
||||
return False
|
||||
if (self.cur_maturity_pass >= 1) and (self.last_pass_nb_patch_done == 0):
|
||||
return False
|
||||
if (self.max_passes is not None) and (self.cur_maturity_pass >= self.max_passes):
|
||||
return False
|
||||
return True
|
||||
|
||||
def configure(self, kwargs):
|
||||
super().configure(kwargs)
|
||||
if "max_passes" in self.config.keys():
|
||||
self.max_passes = self.config["max_passes"]
|
||||
if "max_duplication_passes" in self.config.keys():
|
||||
self.max_duplication_passes = self.config["max_duplication_passes"]
|
||||
self.dispatcher_collector.configure(kwargs)
|
||||
|
||||
def retrieve_all_dispatchers(self):
|
||||
self.dispatcher_list = []
|
||||
self.dispatcher_collector.reset()
|
||||
self.mba.for_all_topinsns(self.dispatcher_collector)
|
||||
self.dispatcher_list = [x for x in self.dispatcher_collector.get_dispatcher_list()]
|
||||
|
||||
def ensure_all_dispatcher_fathers_are_direct(self) -> int:
|
||||
nb_change = 0
|
||||
for dispatcher_info in self.dispatcher_list:
|
||||
dispatcher_father_list = [self.mba.get_mblock(x) for x in dispatcher_info.entry_block.blk.predset]
|
||||
for dispatcher_father in dispatcher_father_list:
|
||||
nb_change += ensure_child_has_an_unconditional_father(dispatcher_father,
|
||||
dispatcher_info.entry_block.blk)
|
||||
return nb_change
|
||||
|
||||
def register_initialization_variables(self, mop_tracker):
|
||||
pass
|
||||
|
||||
def get_dispatcher_father_histories(self, dispatcher_father: mblock_t,
|
||||
dispatcher_entry_block: GenericDispatcherBlockInfo) -> List[MopHistory]:
|
||||
father_tracker = MopTracker(dispatcher_entry_block.use_before_def_list,
|
||||
max_nb_block=self.MOP_TRACKER_MAX_NB_BLOCK, max_path=self.MOP_TRACKER_MAX_NB_PATH)
|
||||
father_tracker.reset()
|
||||
self.register_initialization_variables(father_tracker)
|
||||
father_histories = father_tracker.search_backward(dispatcher_father, None)
|
||||
return father_histories
|
||||
|
||||
def check_if_histories_are_resolved(self, mop_histories: List[MopHistory]) -> bool:
|
||||
return all([mop_history.is_resolved() for mop_history in mop_histories])
|
||||
|
||||
def ensure_dispatcher_father_is_resolvable(self, dispatcher_father: mblock_t,
|
||||
dispatcher_entry_block: GenericDispatcherBlockInfo) -> int:
|
||||
father_histories = self.get_dispatcher_father_histories(dispatcher_father, dispatcher_entry_block)
|
||||
father_histories_cst = get_all_possibles_values(father_histories, dispatcher_entry_block.use_before_def_list,
|
||||
verbose=False)
|
||||
father_is_resolvable = self.check_if_histories_are_resolved(father_histories)
|
||||
if not father_is_resolvable:
|
||||
raise NotDuplicableFatherException("Dispatcher {0} predecessor {1} is not duplicable: {2}"
|
||||
.format(dispatcher_entry_block.serial, dispatcher_father.serial,
|
||||
father_histories_cst))
|
||||
|
||||
unflat_logger.info("Dispatcher {0} predecessor {1} is resolvable: {2}"
|
||||
.format(dispatcher_entry_block.serial, dispatcher_father.serial, father_histories_cst))
|
||||
nb_duplication, nb_change = duplicate_histories(father_histories, max_nb_pass=self.max_duplication_passes)
|
||||
unflat_logger.info("Dispatcher {0} predecessor {1} duplication: {2} blocks created, {3} changes made"
|
||||
.format(dispatcher_entry_block.serial, dispatcher_father.serial, nb_duplication, nb_change))
|
||||
return nb_duplication + nb_change
|
||||
|
||||
def resolve_dispatcher_father(self, dispatcher_father: mblock_t, dispatcher_info: GenericDispatcherInfo) -> int:
|
||||
dispatcher_father_histories = self.get_dispatcher_father_histories(dispatcher_father,
|
||||
dispatcher_info.entry_block)
|
||||
father_is_resolvable = self.check_if_histories_are_resolved(dispatcher_father_histories)
|
||||
if not father_is_resolvable:
|
||||
raise NotResolvableFatherException("Can't fix block {0}".format(dispatcher_father.serial))
|
||||
mop_searched_values_list = get_all_possibles_values(dispatcher_father_histories,
|
||||
dispatcher_info.entry_block.use_before_def_list,
|
||||
verbose=False)
|
||||
all_values_found = check_if_all_values_are_found(mop_searched_values_list)
|
||||
if not all_values_found:
|
||||
raise NotResolvableFatherException("Can't fix block {0}".format(dispatcher_father.serial))
|
||||
|
||||
ref_mop_searched_values = mop_searched_values_list[0]
|
||||
for tmp_mop_searched_values in mop_searched_values_list:
|
||||
if tmp_mop_searched_values != ref_mop_searched_values:
|
||||
raise NotResolvableFatherException("Dispatcher {0} predecessor {1} is not resolvable: {2}"
|
||||
.format(dispatcher_info.entry_block.serial, dispatcher_father.serial,
|
||||
mop_searched_values_list))
|
||||
|
||||
target_blk, disp_ins = dispatcher_info.emulate_dispatcher_with_father_history(dispatcher_father_histories[0])
|
||||
if target_blk is not None:
|
||||
unflat_logger.debug("Unflattening graph: Making {0} goto {1}"
|
||||
.format(dispatcher_father.serial, target_blk.serial))
|
||||
ins_to_copy = [ins for ins in disp_ins if ((ins is not None) and (ins.opcode not in CONTROL_FLOW_OPCODES))]
|
||||
if len(ins_to_copy) > 0:
|
||||
unflat_logger.info("Instruction copied: {0}: {1}"
|
||||
.format(len(ins_to_copy),
|
||||
", ".join([format_minsn_t(ins_copied) for ins_copied in ins_to_copy])))
|
||||
dispatcher_side_effect_blk = create_block(self.mba.get_mblock(self.mba.qty - 2), ins_to_copy,
|
||||
is_0_way=(target_blk.type == BLT_0WAY))
|
||||
change_1way_block_successor(dispatcher_father, dispatcher_side_effect_blk.serial)
|
||||
change_1way_block_successor(dispatcher_side_effect_blk, target_blk.serial)
|
||||
else:
|
||||
change_1way_block_successor(dispatcher_father, target_blk.serial)
|
||||
return 2
|
||||
|
||||
raise NotResolvableFatherException("Can't fix block {0}: no block for key: {1}"
|
||||
.format(dispatcher_father.serial, mop_searched_values_list))
|
||||
|
||||
def remove_flattening(self) -> int:
|
||||
total_nb_change = ensure_last_block_is_goto(self.mba)
|
||||
total_nb_change += self.ensure_all_dispatcher_fathers_are_direct()
|
||||
nb_flattened_branches = 0
|
||||
for dispatcher_info in self.dispatcher_list:
|
||||
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_dispatcher_{1}_before_duplication"
|
||||
.format(self.cur_maturity_pass, dispatcher_info.entry_block.serial))
|
||||
unflat_logger.info("Searching dispatcher for entry block {0} {1} -> with variables ({2})..."
|
||||
.format(dispatcher_info.entry_block.serial, format_mop_t(dispatcher_info.mop_compared),
|
||||
format_mop_list(dispatcher_info.entry_block.use_before_def_list)))
|
||||
dispatcher_father_list = [self.mba.get_mblock(x) for x in dispatcher_info.entry_block.blk.predset]
|
||||
for dispatcher_father in dispatcher_father_list:
|
||||
try:
|
||||
total_nb_change += self.ensure_dispatcher_father_is_resolvable(dispatcher_father,
|
||||
dispatcher_info.entry_block)
|
||||
except NotDuplicableFatherException as e:
|
||||
unflat_logger.warning(e)
|
||||
pass
|
||||
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_dispatcher_{1}_after_duplication"
|
||||
.format(self.cur_maturity_pass, dispatcher_info.entry_block.serial))
|
||||
# During the previous step we changed dispatcher entry block fathers, so we need to reload them
|
||||
dispatcher_father_list = [self.mba.get_mblock(x) for x in dispatcher_info.entry_block.blk.predset]
|
||||
nb_flattened_branches = 0
|
||||
for dispatcher_father in dispatcher_father_list:
|
||||
try:
|
||||
nb_flattened_branches += self.resolve_dispatcher_father(dispatcher_father, dispatcher_info)
|
||||
except NotResolvableFatherException as e:
|
||||
unflat_logger.warning(e)
|
||||
pass
|
||||
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_dispatcher_{1}_after_unflattening"
|
||||
.format(self.cur_maturity_pass, dispatcher_info.entry_block.serial))
|
||||
|
||||
unflat_logger.info("Unflattening removed {0} branch".format(nb_flattened_branches))
|
||||
total_nb_change += nb_flattened_branches
|
||||
return total_nb_change
|
||||
|
||||
def optimize(self, blk: mblock_t) -> int:
|
||||
self.mba = blk.mba
|
||||
if not self.check_if_rule_should_be_used(blk):
|
||||
return 0
|
||||
self.last_pass_nb_patch_done = 0
|
||||
unflat_logger.info("Unflattening at maturity {0} path {1}".format(self.cur_maturity, self.cur_maturity_pass))
|
||||
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_start".format(self.cur_maturity_pass))
|
||||
self.retrieve_all_dispatchers()
|
||||
if len(self.dispatcher_list) == 0:
|
||||
unflat_logger.info("No dispatcher found at maturity {0}".format(self.mba.maturity))
|
||||
return 0
|
||||
else:
|
||||
unflat_logger.info("Unflattening: {0} dispatcher(s) found".format(len(self.dispatcher_list)))
|
||||
for dispatcher_info in self.dispatcher_list:
|
||||
dispatcher_info.print_info()
|
||||
self.last_pass_nb_patch_done = self.remove_flattening()
|
||||
unflat_logger.info("Unflattening at maturity {0} path {1}: {2} changes"
|
||||
.format(self.cur_maturity, self.cur_maturity_pass, self.last_pass_nb_patch_done))
|
||||
mba_deep_cleaning(self.mba)
|
||||
dump_microcode_for_debug(self.mba, self.log_dir, "unflat_{0}_after_cleaning".format(self.cur_maturity_pass))
|
||||
if self.last_pass_nb_patch_done > 0:
|
||||
self.mba.mark_chains_dirty()
|
||||
self.mba.optimize_local(0)
|
||||
self.mba.verify(True)
|
||||
return self.last_pass_nb_patch_done
|
||||
115
d810/optimizers/flow/flattening/unflattener.py
Normal file
115
d810/optimizers/flow/flattening/unflattener.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import logging
|
||||
from typing import Tuple, List
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.hexrays_helpers import extract_num_mop, append_mop_if_not_in_list
|
||||
from d810.optimizers.flow.flattening.generic import GenericDispatcherCollector, GenericDispatcherInfo, \
|
||||
GenericDispatcherBlockInfo, GenericDispatcherUnflatteningRule
|
||||
|
||||
|
||||
unflat_logger = logging.getLogger('D810.unflat')
|
||||
FLATTENING_JUMP_OPCODES = [m_jnz, m_jz, m_jae, m_jb, m_ja, m_jbe, m_jg, m_jge, m_jl, m_jle]
|
||||
|
||||
|
||||
class OllvmDispatcherBlockInfo(GenericDispatcherBlockInfo):
|
||||
pass
|
||||
|
||||
|
||||
class OllvmDispatcherInfo(GenericDispatcherInfo):
|
||||
def explore(self, blk: mblock_t) -> bool:
|
||||
self.reset()
|
||||
if not self._is_candidate_for_dispatcher_entry_block(blk):
|
||||
return False
|
||||
self.entry_block = OllvmDispatcherBlockInfo(blk)
|
||||
self.entry_block.parse()
|
||||
for used_mop in self.entry_block.use_list:
|
||||
append_mop_if_not_in_list(used_mop, self.entry_block.assume_def_list)
|
||||
self.dispatcher_internal_blocks.append(self.entry_block)
|
||||
num_mop, self.mop_compared = self._get_comparison_info(self.entry_block.blk)
|
||||
self.comparison_values.append(num_mop.nnn.value)
|
||||
self._explore_children(self.entry_block)
|
||||
dispatcher_blk_with_external_father = self._get_dispatcher_blocks_with_external_father()
|
||||
if len(dispatcher_blk_with_external_father) != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _is_candidate_for_dispatcher_entry_block(self, blk: mblock_t) -> bool:
|
||||
# blk must be a condition branch with one numerical operand
|
||||
num_mop, mop_compared = self._get_comparison_info(blk)
|
||||
if (num_mop is None) or (mop_compared is None):
|
||||
return False
|
||||
# Its fathers are not conditional branch with this mop
|
||||
for father_serial in blk.predset:
|
||||
father_blk = self.mba.get_mblock(father_serial)
|
||||
father_num_mop, father_mop_compared = self._get_comparison_info(father_blk)
|
||||
if (father_num_mop is not None) and (father_mop_compared is not None):
|
||||
if mop_compared.equal_mops(father_mop_compared, EQ_IGNSIZE):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_comparison_info(self, blk: mblock_t) -> Tuple[mop_t, mop_t]:
|
||||
# We check if blk is a good candidate for dispatcher entry block: blk.tail must be a conditional branch
|
||||
if (blk.tail is None) or (blk.tail.opcode not in FLATTENING_JUMP_OPCODES):
|
||||
return None, None
|
||||
# One operand must be numerical
|
||||
num_mop, mop_compared = extract_num_mop(blk.tail)
|
||||
if num_mop is None or mop_compared is None:
|
||||
return None, None
|
||||
return num_mop, mop_compared
|
||||
|
||||
def is_part_of_dispatcher(self, block_info: OllvmDispatcherBlockInfo) -> bool:
|
||||
is_ok = block_info.does_only_need(block_info.father.assume_def_list)
|
||||
if not is_ok:
|
||||
return False
|
||||
if (block_info.blk.tail is not None) and (block_info.blk.tail.opcode not in FLATTENING_JUMP_OPCODES):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _explore_children(self, father_info: OllvmDispatcherBlockInfo):
|
||||
for child_serial in father_info.blk.succset:
|
||||
if child_serial in [blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks]:
|
||||
return
|
||||
if child_serial in [blk_info.blk.serial for blk_info in self.dispatcher_exit_blocks]:
|
||||
return
|
||||
child_blk = self.mba.get_mblock(child_serial)
|
||||
child_info = OllvmDispatcherBlockInfo(child_blk, father_info)
|
||||
child_info.parse()
|
||||
if not self.is_part_of_dispatcher(child_info):
|
||||
self.dispatcher_exit_blocks.append(child_info)
|
||||
else:
|
||||
self.dispatcher_internal_blocks.append(child_info)
|
||||
if child_info.comparison_value is not None:
|
||||
self.comparison_values.append(child_info.comparison_value)
|
||||
self._explore_children(child_info)
|
||||
|
||||
def _get_external_fathers(self, block_info: OllvmDispatcherBlockInfo) -> List[mblock_t]:
|
||||
internal_serials = [blk_info.blk.serial for blk_info in self.dispatcher_internal_blocks]
|
||||
external_fathers = []
|
||||
for blk_father in block_info.blk.predset:
|
||||
if blk_father not in internal_serials:
|
||||
external_fathers.append(blk_father)
|
||||
return external_fathers
|
||||
|
||||
def _get_dispatcher_blocks_with_external_father(self) -> List[mblock_t]:
|
||||
dispatcher_blocks_with_external_father = []
|
||||
for blk_info in self.dispatcher_internal_blocks:
|
||||
if blk_info.blk.serial != self.entry_block.blk.serial:
|
||||
external_fathers = self._get_external_fathers(blk_info)
|
||||
if len(external_fathers) > 0:
|
||||
dispatcher_blocks_with_external_father.append(blk_info)
|
||||
return dispatcher_blocks_with_external_father
|
||||
|
||||
|
||||
class OllvmDispatcherCollector(GenericDispatcherCollector):
|
||||
DISPATCHER_CLASS = OllvmDispatcherInfo
|
||||
DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 2
|
||||
DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 2
|
||||
DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 2
|
||||
|
||||
|
||||
class Unflattener(GenericDispatcherUnflatteningRule):
|
||||
DESCRIPTION = "Remove control flow flattening generated by OLLVM"
|
||||
DISPATCHER_COLLECTOR_CLASS = OllvmDispatcherCollector
|
||||
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1, MMAT_GLBOPT2]
|
||||
DEFAULT_MAX_DUPLICATION_PASSES = 20
|
||||
DEFAULT_MAX_PASSES = 5
|
||||
95
d810/optimizers/flow/flattening/unflattener_fake_jump.py
Normal file
95
d810/optimizers/flow/flattening/unflattener_fake_jump.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import logging
|
||||
from typing import List
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.tracker import MopTracker
|
||||
from d810.cfg_utils import change_1way_block_successor
|
||||
from d810.hexrays_formatters import format_minsn_t, dump_microcode_for_debug
|
||||
from d810.optimizers.flow.flattening.utils import get_all_possibles_values
|
||||
from d810.optimizers.flow.flattening.generic import GenericUnflatteningRule
|
||||
|
||||
unflat_logger = logging.getLogger('D810.unflat')
|
||||
|
||||
FAKE_LOOP_OPCODES = [m_jz, m_jnz]
|
||||
|
||||
|
||||
class UnflattenerFakeJump(GenericUnflatteningRule):
|
||||
DESCRIPTION = "Check if a jump is always taken for each father blocks and remove them"
|
||||
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1]
|
||||
DEFAULT_MAX_PASSES = None
|
||||
|
||||
def analyze_blk(self, blk: mblock_t) -> int:
|
||||
if (blk.tail is None) or blk.tail.opcode not in FAKE_LOOP_OPCODES:
|
||||
return 0
|
||||
if blk.get_reginsn_qty() != 1:
|
||||
return 0
|
||||
if blk.tail.r.t != mop_n:
|
||||
return 0
|
||||
unflat_logger.info("Checking if block {0} is fake loop: {1}".format(blk.serial, format_minsn_t(blk.tail)))
|
||||
op_compared = mop_t(blk.tail.l)
|
||||
blk_preset_list = [x for x in blk.predset]
|
||||
nb_change = 0
|
||||
for pred_serial in blk_preset_list:
|
||||
cmp_variable_tracker = MopTracker([op_compared], max_nb_block=100, max_path=1000)
|
||||
cmp_variable_tracker.reset()
|
||||
pred_blk = blk.mba.get_mblock(pred_serial)
|
||||
pred_histories = cmp_variable_tracker.search_backward(pred_blk, pred_blk.tail)
|
||||
|
||||
father_is_resolvable = all([father_history.is_resolved() for father_history in pred_histories])
|
||||
if not father_is_resolvable:
|
||||
return 0
|
||||
pred_values = get_all_possibles_values(pred_histories, [op_compared])
|
||||
pred_values = [x[0] for x in pred_values]
|
||||
if None in pred_values:
|
||||
unflat_logger.info("Some path are not resolved, can't fix jump")
|
||||
return 0
|
||||
unflat_logger.info("Pred {0} has {1} possible path ({2} different cst): {3}"
|
||||
.format(pred_blk.serial, len(pred_values), len(set(pred_values)), pred_values))
|
||||
if self.fix_successor(blk, pred_blk, pred_values):
|
||||
nb_change += 1
|
||||
return nb_change
|
||||
|
||||
def fix_successor(self, fake_loop_block: mblock_t, pred: mblock_t, pred_comparison_values: List[int]) -> bool:
|
||||
if len(pred_comparison_values) == 0:
|
||||
return False
|
||||
jmp_ins = fake_loop_block.tail
|
||||
compared_value = jmp_ins.r.nnn.value
|
||||
jmp_taken = False
|
||||
jmp_not_taken = False
|
||||
dst_serial = None
|
||||
if jmp_ins.opcode == m_jz:
|
||||
jmp_taken = all([possible_value == compared_value for possible_value in pred_comparison_values])
|
||||
|
||||
jmp_not_taken = all([possible_value != compared_value for possible_value in pred_comparison_values])
|
||||
elif jmp_ins.opcode == m_jnz:
|
||||
jmp_taken = all([possible_value != compared_value for possible_value in pred_comparison_values])
|
||||
jmp_not_taken = all([possible_value == compared_value for possible_value in pred_comparison_values])
|
||||
# TODO: handles other jumps cases
|
||||
if jmp_taken:
|
||||
unflat_logger.info("It seems that '{0}' is always taken when coming from {1}: {2}"
|
||||
.format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values))
|
||||
dst_serial = jmp_ins.d.b
|
||||
if jmp_not_taken:
|
||||
unflat_logger.info("It seems that '{0}' is never taken when coming from {1}: {2}"
|
||||
.format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values))
|
||||
dst_serial = fake_loop_block.serial + 1
|
||||
if dst_serial is None:
|
||||
unflat_logger.debug("Jump seems legit '{0}' from {1}: {2}"
|
||||
.format(format_minsn_t(jmp_ins), pred.serial, pred_comparison_values))
|
||||
return False
|
||||
dump_microcode_for_debug(self.mba, self.log_dir, "{0}_before_fake_jump".format(self.cur_maturity_pass))
|
||||
unflat_logger.info("Making pred {0} with value {1} goto {2} ({3})"
|
||||
.format(pred.serial, pred_comparison_values, dst_serial, format_minsn_t(jmp_ins)))
|
||||
dump_microcode_for_debug(self.mba, self.log_dir, "{0}_after_fake_jump".format(self.cur_maturity_pass))
|
||||
return change_1way_block_successor(pred, dst_serial)
|
||||
|
||||
def optimize(self, blk: mblock_t) -> int:
|
||||
self.mba = blk.mba
|
||||
if not self.check_if_rule_should_be_used(blk):
|
||||
return 0
|
||||
self.last_pass_nb_patch_done = self.analyze_blk(blk)
|
||||
if self.last_pass_nb_patch_done > 0:
|
||||
self.mba.mark_chains_dirty()
|
||||
self.mba.optimize_local(0)
|
||||
self.mba.verify(True)
|
||||
return self.last_pass_nb_patch_done
|
||||
116
d810/optimizers/flow/flattening/unflattener_indirect.py
Normal file
116
d810/optimizers/flow/flattening/unflattener_indirect.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import logging
|
||||
import idaapi
|
||||
from typing import List
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.hexrays_helpers import append_mop_if_not_in_list, AND_TABLE, CONTROL_FLOW_OPCODES
|
||||
from d810.tracker import MopTracker, MopHistory
|
||||
from d810.optimizers.flow.flattening.generic import GenericDispatcherBlockInfo, GenericDispatcherInfo, \
|
||||
GenericDispatcherCollector, GenericDispatcherUnflatteningRule, NotDuplicableFatherException, DispatcherUnflatteningException, NotResolvableFatherException
|
||||
from d810.optimizers.flow.flattening.utils import configure_mop_tracker_log_verbosity, restore_mop_tracker_log_verbosity
|
||||
from d810.tracker import duplicate_histories
|
||||
from d810.cfg_utils import create_block, change_1way_block_successor
|
||||
from d810.hexrays_formatters import format_minsn_t, format_mop_t
|
||||
from d810.emulator import MicroCodeEnvironment, MicroCodeInterpreter
|
||||
|
||||
unflat_logger = logging.getLogger('D810.unflat')
|
||||
FLATTENING_JUMP_OPCODES = [m_jtbl]
|
||||
|
||||
|
||||
class TigressIndirectDispatcherBlockInfo(GenericDispatcherBlockInfo):
|
||||
pass
|
||||
|
||||
|
||||
class TigressIndirectDispatcherInfo(GenericDispatcherInfo):
|
||||
def explore(self, blk: mblock_t):
|
||||
self.reset()
|
||||
if not self._is_candidate_for_dispatcher_entry_block(blk):
|
||||
return
|
||||
self.mop_compared = self._get_comparison_info(blk)
|
||||
self.entry_block = TigressIndirectDispatcherBlockInfo(blk)
|
||||
self.entry_block.parse()
|
||||
for used_mop in self.entry_block.use_list:
|
||||
append_mop_if_not_in_list(used_mop, self.entry_block.assume_def_list)
|
||||
self.dispatcher_internal_blocks.append(self.entry_block)
|
||||
|
||||
self.dispatcher_exit_blocks = []
|
||||
self.comparison_values = []
|
||||
return True
|
||||
|
||||
def _get_comparison_info(self, blk: mblock_t):
|
||||
# blk.tail must be a jtbl
|
||||
if (blk.tail is None) or (blk.tail.opcode != m_ijmp):
|
||||
return None, None
|
||||
return blk.tail.l
|
||||
|
||||
def _is_candidate_for_dispatcher_entry_block(self, blk: mblock_t):
|
||||
if (blk.tail is None) or (blk.tail.opcode != m_ijmp):
|
||||
return False
|
||||
return True
|
||||
|
||||
def should_emulation_continue(self, cur_blk: mblock_t):
|
||||
if (cur_blk is not None) and (cur_blk.serial == self.entry_block.serial):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class TigressIndirectDispatcherCollector(GenericDispatcherCollector):
|
||||
DISPATCHER_CLASS = TigressIndirectDispatcherInfo
|
||||
DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 0
|
||||
DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 0
|
||||
DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 0
|
||||
|
||||
|
||||
class LabelTableInfo(object):
|
||||
def __init__(self, sp_offset, mem_offset, nb_elt):
|
||||
self.sp_offset = sp_offset
|
||||
self.mem_offset = mem_offset
|
||||
self.nb_elt = nb_elt
|
||||
|
||||
def update_mop_tracker(self, mba: mbl_array_t, mop_tracker: MopTracker):
|
||||
stack_array_base_address = mba.stkoff_ida2vd(self.sp_offset)
|
||||
# print("stack_array_base_address: {0:x}".format(stack_array_base_address))
|
||||
for i in range(self.nb_elt):
|
||||
tmp_mop = mop_t()
|
||||
tmp_mop.erase()
|
||||
tmp_mop._make_stkvar(mba, stack_array_base_address + 8 * i)
|
||||
tmp_mop.size = 8
|
||||
mem_val = idaapi.get_qword(self.mem_offset + 8 * i) & AND_TABLE[8]
|
||||
mop_tracker.add_mop_definition(tmp_mop, mem_val)
|
||||
|
||||
|
||||
class UnflattenerTigressIndirect(GenericDispatcherUnflatteningRule):
|
||||
DESCRIPTION = ""
|
||||
DISPATCHER_COLLECTOR_CLASS = TigressIndirectDispatcherCollector
|
||||
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_LOCOPT]
|
||||
DEFAULT_MAX_DUPLICATION_PASSES = 20
|
||||
DEFAULT_MAX_PASSES = 1
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.label_info = None
|
||||
self.goto_table_info = {}
|
||||
|
||||
def configure(self, kwargs):
|
||||
super().configure(kwargs)
|
||||
if "goto_table_info" in self.config.keys():
|
||||
for ea_str, table_info in self.config["goto_table_info"].items():
|
||||
self.goto_table_info[int(ea_str, 16)] = LabelTableInfo(sp_offset=int(table_info["stack_table_offset"], 16),
|
||||
mem_offset=int(table_info["table_address"], 16),
|
||||
nb_elt=table_info["table_nb_elt"])
|
||||
|
||||
def check_if_rule_should_be_used(self, blk: mblock_t):
|
||||
if not super().check_if_rule_should_be_used(blk):
|
||||
return False
|
||||
if self.mba.entry_ea not in self.goto_table_info:
|
||||
return False
|
||||
if (self.cur_maturity_pass >= 1) and (self.last_pass_nb_patch_done == 0):
|
||||
return False
|
||||
self.label_info = self.goto_table_info[self.mba.entry_ea]
|
||||
return True
|
||||
|
||||
def register_initialization_variables(self, mop_tracker: MopTracker):
|
||||
self.label_info.update_mop_tracker(self.mba, mop_tracker)
|
||||
|
||||
def check_if_histories_are_resolved(self, mop_histories: List[MopHistory]):
|
||||
return True
|
||||
60
d810/optimizers/flow/flattening/unflattener_switch_case.py
Normal file
60
d810/optimizers/flow/flattening/unflattener_switch_case.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import logging
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.hexrays_helpers import append_mop_if_not_in_list
|
||||
from d810.optimizers.flow.flattening.generic import GenericDispatcherBlockInfo, GenericDispatcherInfo, \
|
||||
GenericDispatcherCollector, GenericDispatcherUnflatteningRule
|
||||
|
||||
|
||||
unflat_logger = logging.getLogger('D810.unflat')
|
||||
FLATTENING_JUMP_OPCODES = [m_jtbl]
|
||||
|
||||
|
||||
class TigressSwitchDispatcherBlockInfo(GenericDispatcherBlockInfo):
|
||||
pass
|
||||
|
||||
|
||||
class TigressSwitchDispatcherInfo(GenericDispatcherInfo):
|
||||
def explore(self, blk: mblock_t):
|
||||
self.reset()
|
||||
if not self._is_candidate_for_dispatcher_entry_block(blk):
|
||||
return
|
||||
self.mop_compared, mcases = self._get_comparison_info(blk)
|
||||
self.entry_block = TigressSwitchDispatcherBlockInfo(blk)
|
||||
self.entry_block.parse()
|
||||
for used_mop in self.entry_block.use_list:
|
||||
append_mop_if_not_in_list(used_mop, self.entry_block.assume_def_list)
|
||||
self.dispatcher_internal_blocks.append(self.entry_block)
|
||||
for possible_values, target_block_serial in zip(mcases.c.values, mcases.c.targets):
|
||||
if target_block_serial == self.entry_block.blk.serial:
|
||||
continue
|
||||
exit_block = TigressSwitchDispatcherBlockInfo(blk.mba.get_mblock(target_block_serial), self.entry_block)
|
||||
self.dispatcher_exit_blocks.append(exit_block)
|
||||
self.comparison_values.append(possible_values[0])
|
||||
return True
|
||||
|
||||
def _get_comparison_info(self, blk: mblock_t):
|
||||
# blk.tail must be a jtbl
|
||||
if (blk.tail is None) or (blk.tail.opcode != m_jtbl):
|
||||
return None, None
|
||||
return blk.tail.l, blk.tail.r
|
||||
|
||||
def _is_candidate_for_dispatcher_entry_block(self, blk: mblock_t):
|
||||
if (blk.tail is None) or (blk.tail.opcode != m_jtbl):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class TigressSwitchDispatcherCollector(GenericDispatcherCollector):
|
||||
DISPATCHER_CLASS = TigressSwitchDispatcherInfo
|
||||
DEFAULT_DISPATCHER_MIN_INTERNAL_BLOCK = 0
|
||||
DEFAULT_DISPATCHER_MIN_EXIT_BLOCK = 4
|
||||
DEFAULT_DISPATCHER_MIN_COMPARISON_VALUE = 4
|
||||
|
||||
|
||||
class UnflattenerSwitchCase(GenericDispatcherUnflatteningRule):
|
||||
DESCRIPTION = "Remove control flow flattening generated by Tigress with Switch case dispatcher"
|
||||
DISPATCHER_COLLECTOR_CLASS = TigressSwitchDispatcherCollector
|
||||
DEFAULT_UNFLATTENING_MATURITIES = [MMAT_GLBOPT1]
|
||||
DEFAULT_MAX_DUPLICATION_PASSES = 20
|
||||
DEFAULT_MAX_PASSES = 5
|
||||
56
d810/optimizers/flow/flattening/utils.py
Normal file
56
d810/optimizers/flow/flattening/utils.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import logging
|
||||
|
||||
|
||||
tracker_logger = logging.getLogger('D810.tracker')
|
||||
emulator_logger = logging.getLogger('D810.emulator')
|
||||
|
||||
|
||||
class UnflatteningException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class DispatcherUnflatteningException(UnflatteningException):
|
||||
pass
|
||||
|
||||
|
||||
class NotDuplicableFatherException(UnflatteningException):
|
||||
pass
|
||||
|
||||
|
||||
class NotResolvableFatherException(UnflatteningException):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
def configure_mop_tracker_log_verbosity(verbose=False):
|
||||
tracker_log_level = tracker_logger.getEffectiveLevel()
|
||||
emulator_log_level = emulator_logger.getEffectiveLevel()
|
||||
if not verbose:
|
||||
tracker_logger.setLevel(logging.ERROR)
|
||||
emulator_logger.setLevel(logging.ERROR)
|
||||
return [tracker_log_level, emulator_log_level]
|
||||
|
||||
|
||||
def restore_mop_tracker_log_verbosity(tracker_log_level, emulator_log_level):
|
||||
tracker_logger.setLevel(tracker_log_level)
|
||||
emulator_logger.setLevel(emulator_log_level)
|
||||
|
||||
|
||||
def get_all_possibles_values(mop_histories, searched_mop_list, verbose=False):
|
||||
log_levels = configure_mop_tracker_log_verbosity(verbose)
|
||||
mop_cst_values_list = []
|
||||
for mop_history in mop_histories:
|
||||
mop_cst_values_list.append([mop_history.get_mop_constant_value(searched_mop)
|
||||
for searched_mop in searched_mop_list])
|
||||
restore_mop_tracker_log_verbosity(*log_levels)
|
||||
return mop_cst_values_list
|
||||
|
||||
|
||||
def check_if_all_values_are_found(mop_cst_values_list):
|
||||
all_values_are_found = True
|
||||
for cst_list in mop_cst_values_list:
|
||||
if None in cst_list:
|
||||
all_values_are_found = False
|
||||
break
|
||||
return all_values_are_found
|
||||
39
d810/optimizers/flow/handler.py
Normal file
39
d810/optimizers/flow/handler.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import logging
|
||||
import idc
|
||||
|
||||
from d810.optimizers.handler import OptimizationRule, DEFAULT_FLOW_MATURITIES
|
||||
|
||||
logger = logging.getLogger('D810.optimizer')
|
||||
|
||||
|
||||
class FlowOptimizationRule(OptimizationRule):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.maturities = DEFAULT_FLOW_MATURITIES
|
||||
self.use_whitelist = False
|
||||
self.whitelisted_function_ea_list = []
|
||||
self.use_blacklist = False
|
||||
self.blacklisted_function_ea_list = []
|
||||
|
||||
def configure(self, kwargs):
|
||||
super().configure(kwargs)
|
||||
self.use_whitelist = False
|
||||
self.whitelisted_function_ea_list = []
|
||||
self.use_blacklist = False
|
||||
self.blacklisted_function_ea_list = []
|
||||
if "whitelisted_functions" in self.config.keys():
|
||||
self.use_whitelist = True
|
||||
for func_ea in self.config["whitelisted_functions"]:
|
||||
self.whitelisted_function_ea_list.append(int(func_ea, 16))
|
||||
func_name_list = [idc.get_func_name(ea) for ea in self.whitelisted_function_ea_list]
|
||||
logger.info("Whitelisted functions for {0}: {1} -> {2}".format(self.__class__.__name__,
|
||||
self.whitelisted_function_ea_list,
|
||||
func_name_list))
|
||||
if "blacklisted_functions" in self.config.keys():
|
||||
self.use_blacklist = True
|
||||
for func_ea in self.config["whitelisted_functions"]:
|
||||
self.blacklisted_function_ea_list.append(int(func_ea, 16))
|
||||
func_name_list = [idc.get_func_name(ea) for ea in self.blacklisted_function_ea_list]
|
||||
logger.info("Blacklisted functions for {0}: {1} -> {2}".format(self.__class__.__name__,
|
||||
self.blacklisted_function_ea_list,
|
||||
func_name_list))
|
||||
11
d810/optimizers/flow/jumps/__init__.py
Normal file
11
d810/optimizers/flow/jumps/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from d810.utils import get_all_subclasses
|
||||
from d810.optimizers.flow.jumps.handler import JumpOptimizationRule, JumpFixer
|
||||
from d810.optimizers.flow.jumps.opaque import *
|
||||
from d810.optimizers.flow.jumps.tricks import *
|
||||
|
||||
|
||||
JUMP_OPTIMIZATION_RULES = [x() for x in get_all_subclasses(JumpOptimizationRule)]
|
||||
jump_fixer = JumpFixer()
|
||||
for jump_optimization_rule in JUMP_OPTIMIZATION_RULES:
|
||||
jump_fixer.register_rule(jump_optimization_rule)
|
||||
JUMP_OPTIMIZATION_BLOCK_RULES = [jump_fixer]
|
||||
183
d810/optimizers/flow/jumps/handler.py
Normal file
183
d810/optimizers/flow/jumps/handler.py
Normal file
@@ -0,0 +1,183 @@
|
||||
import logging
|
||||
from ida_hexrays import *
|
||||
from typing import Union
|
||||
|
||||
from d810.optimizers.instructions.handler import InstructionOptimizationRule
|
||||
from d810.optimizers.instructions.pattern_matching.handler import ast_generator
|
||||
from d810.ast import mop_to_ast, AstNode
|
||||
from d810.hexrays_formatters import format_minsn_t, opcode_to_string
|
||||
from d810.optimizers.flow.handler import FlowOptimizationRule
|
||||
from d810.cfg_utils import make_2way_block_goto, is_conditional_jump, change_2way_block_conditional_successor
|
||||
|
||||
|
||||
logger = logging.getLogger("D810.branch_fixer")
|
||||
optimizer_logger = logging.getLogger('D810.optimizer')
|
||||
|
||||
|
||||
class JumpOptimizationRule(InstructionOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = []
|
||||
LEFT_PATTERN = None
|
||||
RIGHT_PATTERN = None
|
||||
|
||||
REPLACEMENT_OPCODE = None
|
||||
REPLACEMENT_LEFT_PATTERN = None
|
||||
REPLACEMENT_RIGHT_PATTERN = None
|
||||
|
||||
FUZZ_PATTERNS = True
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.fuzz_patterns = self.FUZZ_PATTERNS
|
||||
self.left_pattern_candidates = []
|
||||
self.right_pattern_candidates = []
|
||||
self.jump_original_block_serial = None
|
||||
self.direct_block_serial = None
|
||||
self.jump_replacement_block_serial = None
|
||||
|
||||
def configure(self, fuzz_pattern=None, **kwargs):
|
||||
super().configure(kwargs)
|
||||
if fuzz_pattern is not None:
|
||||
self.fuzz_patterns = fuzz_pattern
|
||||
self._generate_pattern_candidates()
|
||||
|
||||
def _generate_pattern_candidates(self):
|
||||
self.fuzz_patterns = self.FUZZ_PATTERNS
|
||||
if self.LEFT_PATTERN is not None:
|
||||
self.LEFT_PATTERN.reset_mops()
|
||||
if not self.fuzz_patterns:
|
||||
self.left_pattern_candidates = [self.LEFT_PATTERN]
|
||||
else:
|
||||
self.left_pattern_candidates = ast_generator(self.LEFT_PATTERN)
|
||||
if self.RIGHT_PATTERN is not None:
|
||||
self.RIGHT_PATTERN.reset_mops()
|
||||
if not self.fuzz_patterns:
|
||||
self.right_pattern_candidates = [self.RIGHT_PATTERN]
|
||||
else:
|
||||
self.right_pattern_candidates = ast_generator(self.RIGHT_PATTERN)
|
||||
|
||||
def check_candidate(self, opcode, left_candidate: AstNode, right_candidate: AstNode):
|
||||
return False
|
||||
|
||||
def get_valid_candidates(self, instruction, left_ast: AstNode, right_ast: AstNode, stop_early=True):
|
||||
valid_candidates = []
|
||||
if left_ast is None or right_ast is None:
|
||||
return []
|
||||
|
||||
for left_candidate_pattern in self.left_pattern_candidates:
|
||||
if not left_candidate_pattern.check_pattern_and_copy_mops(left_ast):
|
||||
continue
|
||||
for right_candidate_pattern in self.right_pattern_candidates:
|
||||
if not right_candidate_pattern.check_pattern_and_copy_mops(right_ast):
|
||||
continue
|
||||
if not self.check_candidate(instruction.opcode, left_candidate_pattern, right_candidate_pattern):
|
||||
continue
|
||||
valid_candidates.append([left_candidate_pattern, right_candidate_pattern])
|
||||
if stop_early:
|
||||
return valid_candidates
|
||||
return []
|
||||
|
||||
def check_pattern_and_replace(self, blk: mblock_t, instruction: minsn_t, left_ast: AstNode, right_ast: AstNode):
|
||||
if instruction.opcode not in self.ORIGINAL_JUMP_OPCODES:
|
||||
return None
|
||||
self.jump_original_block_serial = instruction.d.b
|
||||
self.direct_block_serial = blk.serial + 1
|
||||
self.jump_replacement_block_serial = None
|
||||
valid_candidates = self.get_valid_candidates(instruction, left_ast, right_ast, stop_early=True)
|
||||
if len(valid_candidates) == 0:
|
||||
return None
|
||||
if self.jump_original_block_serial is None:
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
left_candidate, right_candidate = valid_candidates[0]
|
||||
new_ins = self.get_replacement(instruction, left_candidate, right_candidate)
|
||||
return new_ins
|
||||
|
||||
def get_replacement(self, original_ins: minsn_t, left_candidate: AstNode, right_candidate: AstNode):
|
||||
new_left_mop = None
|
||||
new_right_mop = None
|
||||
new_dst_mop = None
|
||||
|
||||
if self.jump_original_block_serial is not None:
|
||||
new_dst_mop = mop_t()
|
||||
new_dst_mop.make_blkref(self.jump_replacement_block_serial)
|
||||
|
||||
if self.REPLACEMENT_LEFT_PATTERN is not None:
|
||||
is_ok = self.REPLACEMENT_LEFT_PATTERN.update_leafs_mop(left_candidate, right_candidate)
|
||||
if not is_ok:
|
||||
return None
|
||||
new_left_mop = self.REPLACEMENT_LEFT_PATTERN.create_mop(original_ins.ea)
|
||||
if self.REPLACEMENT_RIGHT_PATTERN is not None:
|
||||
is_ok = self.REPLACEMENT_RIGHT_PATTERN.update_leafs_mop(left_candidate, right_candidate)
|
||||
if not is_ok:
|
||||
return None
|
||||
new_right_mop = self.REPLACEMENT_RIGHT_PATTERN.create_mop(original_ins.ea)
|
||||
|
||||
new_ins = self.create_new_ins(original_ins, new_left_mop, new_right_mop, new_dst_mop)
|
||||
return new_ins
|
||||
|
||||
def create_new_ins(self, original_ins: minsn_t, new_left_mop: mop_t,
|
||||
new_right_mop: Union[None, mop_t] = None, new_dst_mop: Union[None, mop_t] = None) -> minsn_t:
|
||||
new_ins = minsn_t(original_ins)
|
||||
new_ins.opcode = self.REPLACEMENT_OPCODE
|
||||
if self.REPLACEMENT_OPCODE == m_goto:
|
||||
new_ins.l = new_dst_mop
|
||||
new_ins.r.erase()
|
||||
new_ins.d.erase()
|
||||
return new_ins
|
||||
new_ins.l = new_left_mop
|
||||
if new_right_mop is not None:
|
||||
new_ins.r = new_right_mop
|
||||
if new_dst_mop is not None:
|
||||
new_ins.d = new_dst_mop
|
||||
return new_ins
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
self.LEFT_PATTERN.reset_mops()
|
||||
self.RIGHT_PATTERN.reset_mops()
|
||||
return "{0}: {1}, {2}".format(",".join([opcode_to_string(x) for x in self.JMP_OPCODES]),
|
||||
self.LEFT_PATTERN, self.RIGHT_PATTERN)
|
||||
|
||||
|
||||
class JumpFixer(FlowOptimizationRule):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.known_rules = []
|
||||
self.rules = []
|
||||
|
||||
def register_rule(self, rule: JumpOptimizationRule):
|
||||
self.known_rules.append(rule)
|
||||
|
||||
def configure(self, kwargs):
|
||||
super().configure(kwargs)
|
||||
if "enabled_rules" in self.config.keys():
|
||||
for rule in self.known_rules:
|
||||
if rule.name in self.config["enabled_rules"]:
|
||||
rule.configure()
|
||||
self.rules.append(rule)
|
||||
optimizer_logger.debug("JumpFixer enables rule {0}".format(rule.name))
|
||||
else:
|
||||
optimizer_logger.debug("JumpFixer disables rule {0}".format(rule.name))
|
||||
|
||||
def optimize(self, blk: mblock_t) -> bool:
|
||||
if not is_conditional_jump(blk):
|
||||
return False
|
||||
left_ast = mop_to_ast(blk.tail.l)
|
||||
right_ast = mop_to_ast(blk.tail.r)
|
||||
for rule in self.rules:
|
||||
try:
|
||||
new_ins = rule.check_pattern_and_replace(blk, blk.tail, left_ast, right_ast)
|
||||
if new_ins:
|
||||
optimizer_logger.info("Rule {0} matched:".format(rule.name))
|
||||
optimizer_logger.info(" orig: {0}".format(format_minsn_t(blk.tail)))
|
||||
optimizer_logger.info(" new : {0}".format(format_minsn_t(new_ins)))
|
||||
if new_ins.opcode == m_goto:
|
||||
make_2way_block_goto(blk, new_ins.d.b)
|
||||
else:
|
||||
change_2way_block_conditional_successor(blk, new_ins.d.b)
|
||||
blk.make_nop(blk.tail)
|
||||
blk.insert_into_block(new_ins, blk.tail)
|
||||
return True
|
||||
except RuntimeError as e:
|
||||
optimizer_logger.error("Error during rule {0} for instruction {1}: {2}"
|
||||
.format(rule, format_minsn_t(blk.tail), e))
|
||||
return False
|
||||
184
d810/optimizers/flow/jumps/opaque.py
Normal file
184
d810/optimizers/flow/jumps/opaque.py
Normal file
@@ -0,0 +1,184 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.optimizers.flow.jumps.handler import JumpOptimizationRule
|
||||
|
||||
|
||||
class JnzRule1(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
|
||||
LEFT_PATTERN = AstNode(m_neg,
|
||||
AstNode(m_and,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("1", 1)))
|
||||
RIGHT_PATTERN = AstLeaf("x_0")
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
if opcode == m_jnz:
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
else:
|
||||
self.jump_replacement_block_serial = self.direct_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class JnzRule2(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
|
||||
LEFT_PATTERN = AstNode(m_or,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("1", 1))
|
||||
RIGHT_PATTERN = AstConstant("0", 0)
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
if opcode == m_jnz:
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
else:
|
||||
self.jump_replacement_block_serial = self.direct_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class JnzRule3(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
|
||||
LEFT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2")))
|
||||
RIGHT_PATTERN = AstConstant("0", 0)
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
tmp = left_candidate["c_1"].value & left_candidate["c_2"].value
|
||||
if tmp == 0:
|
||||
return False
|
||||
if opcode == m_jnz:
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
else:
|
||||
self.jump_replacement_block_serial = self.direct_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class JnzRule4(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
|
||||
LEFT_PATTERN = AstNode(m_sub,
|
||||
AstConstant("3", 3),
|
||||
AstLeaf("x_0"))
|
||||
RIGHT_PATTERN = AstLeaf("x_0")
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
if opcode == m_jnz:
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
else:
|
||||
self.jump_replacement_block_serial = self.direct_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class JnzRule5(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
|
||||
LEFT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_sub,
|
||||
AstConstant("3", 3),
|
||||
AstLeaf("x_0")),
|
||||
AstLeaf("x_0"))
|
||||
RIGHT_PATTERN = AstConstant("0", 0)
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
if opcode == m_jnz:
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
else:
|
||||
self.jump_replacement_block_serial = self.direct_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class JnzRule6(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
|
||||
LEFT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_sub,
|
||||
AstConstant("3", 3),
|
||||
AstLeaf("x_0"))),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")))
|
||||
RIGHT_PATTERN = AstConstant("0", 0)
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
if opcode == m_jnz:
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
else:
|
||||
self.jump_replacement_block_serial = self.direct_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class JnzRule7(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
|
||||
LEFT_PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1"))
|
||||
RIGHT_PATTERN = AstConstant("c_2")
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
tmp = left_candidate["c_1"].value & right_candidate["c_2"].value
|
||||
if tmp == right_candidate["c_2"].value:
|
||||
return False
|
||||
if opcode == m_jnz:
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
else:
|
||||
self.jump_replacement_block_serial = self.direct_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class JnzRule8(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jnz, m_jz]
|
||||
PATTERN = AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1"))
|
||||
RIGHT_PATTERN = AstConstant("c_2")
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
tmp = left_candidate["c_1"].value & right_candidate["c_2"].value
|
||||
if tmp == left_candidate["c_1"].value:
|
||||
return False
|
||||
|
||||
if opcode == m_jnz:
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
else:
|
||||
self.jump_replacement_block_serial = self.direct_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class JbRule1(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jb]
|
||||
PATTERN = AstNode(m_xdu,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("1", 1)))
|
||||
RIGHT_PATTERN = AstConstant("2", 2)
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class JaeRule1(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jae]
|
||||
PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1"))
|
||||
RIGHT_PATTERN = AstConstant("c_2")
|
||||
REPLACEMENT_OPCODE = m_goto
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
if left_candidate["c_1"].value >= right_candidate["c_2"].value:
|
||||
return False
|
||||
self.jump_replacement_block_serial = self.direct_block_serial
|
||||
return True
|
||||
98
d810/optimizers/flow/jumps/tricks.py
Normal file
98
d810/optimizers/flow/jumps/tricks.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_mops_bypass_xdu, equal_bnot_mop
|
||||
from d810.optimizers.flow.jumps.handler import JumpOptimizationRule
|
||||
|
||||
|
||||
class CompareConstantRule1(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jge]
|
||||
LEFT_PATTERN = AstNode(m_and,
|
||||
AstNode(m_or, AstLeaf("xdu_x_0"), AstConstant("c_2")),
|
||||
AstNode(m_or,
|
||||
AstNode(m_xor, AstLeaf("x_0"), AstConstant("c_1")),
|
||||
AstNode(m_bnot, AstNode(m_sub, AstLeaf("x_0"), AstConstant("c_1")))))
|
||||
RIGHT_PATTERN = AstConstant("0", 0)
|
||||
|
||||
REPLACEMENT_OPCODE = m_jl
|
||||
REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0")
|
||||
REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1")
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
if not equal_mops_bypass_xdu(left_candidate["xdu_x_0"].mop, left_candidate["x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(left_candidate["c_2"].mop, left_candidate["c_1"].mop):
|
||||
return False
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class CompareConstantRule2(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jge]
|
||||
LEFT_PATTERN = AstNode(m_or,
|
||||
AstNode(m_xdu,
|
||||
AstNode(m_and,
|
||||
AstNode(m_bnot, AstLeaf("x_0")), AstConstant("c_1"))),
|
||||
AstNode(m_and,
|
||||
AstNode(m_sub, AstLeaf('xdu_x_0'), AstConstant('xdu_c_1')),
|
||||
AstNode(m_bnot, AstNode(m_xdu, AstNode(m_xor, AstLeaf('xdu1_x_0'), AstConstant('xdu_c_1'))))))
|
||||
RIGHT_PATTERN = AstConstant("0", 0)
|
||||
|
||||
REPLACEMENT_OPCODE = m_jge
|
||||
REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0")
|
||||
REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1")
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
if not equal_mops_bypass_xdu(left_candidate["xdu_x_0"].mop, left_candidate["x_0"].mop):
|
||||
return False
|
||||
if not equal_mops_bypass_xdu(left_candidate["xdu1_x_0"].mop, left_candidate["x_0"].mop):
|
||||
return False
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class CompareConstantRule3(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jge]
|
||||
LEFT_PATTERN = AstNode(m_and,
|
||||
AstNode(m_sub, AstLeaf('x_0'), AstConstant('c_1')),
|
||||
AstNode(m_bnot, AstLeaf("x_0")))
|
||||
RIGHT_PATTERN = AstConstant("0", 0)
|
||||
|
||||
REPLACEMENT_OPCODE = m_jg
|
||||
REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0")
|
||||
REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1")
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
return True
|
||||
|
||||
|
||||
class CompareConstantRule4(JumpOptimizationRule):
|
||||
ORIGINAL_JUMP_OPCODES = [m_jl, m_jge]
|
||||
LEFT_PATTERN = AstNode(m_and,
|
||||
AstNode(m_or,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_sub,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('c_1'))),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('c_1'))),
|
||||
AstNode(m_or,
|
||||
AstLeaf("xdu_x_0"),
|
||||
AstConstant('bnot_c_1')))
|
||||
|
||||
RIGHT_PATTERN = AstConstant("0", 0)
|
||||
|
||||
REPLACEMENT_OPCODE = m_jge
|
||||
REPLACEMENT_LEFT_PATTERN = AstLeaf("x_0")
|
||||
REPLACEMENT_RIGHT_PATTERN = AstLeaf("c_1")
|
||||
|
||||
def check_candidate(self, opcode, left_candidate, right_candidate):
|
||||
print("dflighdrth")
|
||||
if not equal_mops_bypass_xdu(left_candidate["xdu_x_0"].mop, left_candidate["x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(left_candidate["c_1"].mop, left_candidate["bnot_c_1"].mop):
|
||||
return False
|
||||
self.jump_replacement_block_serial = self.jump_original_block_serial
|
||||
return True
|
||||
36
d810/optimizers/handler.py
Normal file
36
d810/optimizers/handler.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.hexrays_formatters import string_to_maturity
|
||||
|
||||
DEFAULT_INSTRUCTION_MATURITIES = [MMAT_LOCOPT, MMAT_CALLS, MMAT_GLBOPT1]
|
||||
DEFAULT_FLOW_MATURITIES = [MMAT_CALLS, MMAT_GLBOPT1]
|
||||
|
||||
|
||||
class OptimizationRule(object):
|
||||
NAME = None
|
||||
DESCRIPTION = None
|
||||
|
||||
def __init__(self):
|
||||
self.maturities = []
|
||||
self.config = {}
|
||||
self.log_dir = None
|
||||
|
||||
def set_log_dir(self, log_dir):
|
||||
self.log_dir = log_dir
|
||||
|
||||
def configure(self, kwargs):
|
||||
self.config = kwargs if kwargs is not None else {}
|
||||
if "maturities" in self.config.keys():
|
||||
self.maturities = [string_to_maturity(x) for x in self.config["maturities"]]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self.NAME is not None:
|
||||
return self.NAME
|
||||
return self.__class__.__name__
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
if self.DESCRIPTION is not None:
|
||||
return self.DESCRIPTION
|
||||
return "No description available"
|
||||
7
d810/optimizers/instructions/__init__.py
Normal file
7
d810/optimizers/instructions/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from d810.optimizers.instructions.chain import CHAIN_RULES, ChainOptimizer
|
||||
from d810.optimizers.instructions.pattern_matching import PATTERN_MATCHING_RULES, PatternOptimizer
|
||||
from d810.optimizers.instructions.z3 import Z3_RULES, Z3Optimizer
|
||||
from d810.optimizers.instructions.analysis import INSTRUCTION_ANALYSIS_RULES, InstructionAnalyzer
|
||||
from d810.optimizers.instructions.early import EARLY_RULES, EarlyOptimizer
|
||||
|
||||
KNOWN_INS_RULES = PATTERN_MATCHING_RULES + CHAIN_RULES + Z3_RULES + EARLY_RULES + INSTRUCTION_ANALYSIS_RULES
|
||||
5
d810/optimizers/instructions/analysis/__init__.py
Normal file
5
d810/optimizers/instructions/analysis/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from d810.utils import get_all_subclasses
|
||||
from d810.optimizers.instructions.analysis.handler import InstructionAnalyzer, InstructionAnalysisRule
|
||||
from d810.optimizers.instructions.analysis.pattern_guess import *
|
||||
|
||||
INSTRUCTION_ANALYSIS_RULES = CHAIN_RULES = [x() for x in get_all_subclasses(InstructionAnalysisRule)]
|
||||
42
d810/optimizers/instructions/analysis/handler.py
Normal file
42
d810/optimizers/instructions/analysis/handler.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import logging
|
||||
from ida_hexrays import *
|
||||
from d810.hexrays_formatters import format_minsn_t
|
||||
from d810.optimizers.instructions.handler import InstructionOptimizer, InstructionOptimizationRule
|
||||
|
||||
|
||||
optimizer_logger = logging.getLogger('D810.optimizer')
|
||||
|
||||
|
||||
class InstructionAnalysisRule(InstructionOptimizationRule):
|
||||
def analyze_instruction(self, blk, ins):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class InstructionAnalyzer(InstructionOptimizer):
|
||||
RULE_CLASSES = [InstructionAnalysisRule]
|
||||
|
||||
def set_maturity(self, maturity: int):
|
||||
self.cur_maturity = maturity
|
||||
for rule in self.rules:
|
||||
rule.set_maturity(self.cur_maturity)
|
||||
|
||||
def analyze(self, blk: mblock_t, ins: minsn_t):
|
||||
if blk is not None:
|
||||
self.cur_maturity = blk.mba.maturity
|
||||
|
||||
if self.cur_maturity not in self.maturities:
|
||||
return None
|
||||
|
||||
for rule in self.rules:
|
||||
try:
|
||||
rule.analyze_instruction(blk, ins)
|
||||
except RuntimeError:
|
||||
optimizer_logger.error("error during rule {0} for instruction {1}".format(rule, format_minsn_t(ins)))
|
||||
return None
|
||||
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self.NAME is not None:
|
||||
return self.NAME
|
||||
return self.__class__.__name__
|
||||
89
d810/optimizers/instructions/analysis/pattern_guess.py
Normal file
89
d810/optimizers/instructions/analysis/pattern_guess.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import os
|
||||
|
||||
from d810.ast import minsn_to_ast
|
||||
from d810.hexrays_formatters import format_minsn_t, format_mop_t, maturity_to_string
|
||||
|
||||
from d810.optimizers.instructions.analysis.handler import InstructionAnalysisRule
|
||||
from d810.optimizers.instructions.analysis.utils import get_possible_patterns
|
||||
|
||||
|
||||
class ExampleGuessingRule(InstructionAnalysisRule):
|
||||
DESCRIPTION = "Detect pattern with variable used multiple times and with multiple different opcodes"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.cur_maturity = None
|
||||
self.min_nb_var = 1
|
||||
self.max_nb_var = 3
|
||||
self.min_nb_diff_opcodes = 3
|
||||
self.max_nb_diff_opcodes = -1
|
||||
|
||||
self.cur_index = 0
|
||||
self.max_index = 1000
|
||||
self.cur_ins_guessed = [""] * self.max_index
|
||||
self.pattern_filename_path = None
|
||||
|
||||
def log_info(self, message):
|
||||
with open(self.pattern_filename_path, "a") as f:
|
||||
f.write('{0}\n'.format(message))
|
||||
|
||||
def set_maturity(self, maturity):
|
||||
self.log_info("Patterns guessed at maturity {0}".format(maturity_to_string(maturity)))
|
||||
self.cur_maturity = maturity
|
||||
|
||||
def set_log_dir(self, log_dir):
|
||||
super().set_log_dir(log_dir)
|
||||
self.pattern_filename_path = os.path.join(self.log_dir, "pattern_guess.log")
|
||||
f = open(self.pattern_filename_path, "w")
|
||||
f.close()
|
||||
|
||||
def configure(self, kwargs):
|
||||
super().configure(kwargs)
|
||||
if "min_nb_var" in kwargs.keys():
|
||||
self.min_nb_var = kwargs["min_nb_var"]
|
||||
if "max_nb_var" in kwargs.keys():
|
||||
self.max_nb_var = kwargs["max_nb_var"]
|
||||
if "min_nb_diff_opcodes" in kwargs.keys():
|
||||
self.min_nb_diff_opcodes = kwargs["min_nb_diff_opcodes"]
|
||||
if "max_nb_diff_opcodes" in kwargs.keys():
|
||||
self.max_nb_diff_opcodes = kwargs["max_nb_diff_opcodes"]
|
||||
|
||||
if self.max_nb_var == -1:
|
||||
self.max_nb_var = 0xff
|
||||
if self.max_nb_diff_opcodes == -1:
|
||||
self.max_nb_diff_opcodes = 0xff
|
||||
|
||||
def analyze_instruction(self, blk, ins):
|
||||
if self.cur_maturity not in self.maturities:
|
||||
return None
|
||||
formatted_ins = str(format_minsn_t(ins))
|
||||
if formatted_ins in self.cur_ins_guessed:
|
||||
return False
|
||||
tmp = minsn_to_ast(ins)
|
||||
if tmp is None:
|
||||
return False
|
||||
is_good_candidate = self.check_if_possible_pattern(tmp)
|
||||
if is_good_candidate:
|
||||
self.cur_ins_guessed[self.cur_index] = formatted_ins
|
||||
self.cur_index = (self.cur_index + 1) % self.max_index
|
||||
return is_good_candidate
|
||||
|
||||
def check_if_possible_pattern(self, test_ast):
|
||||
patterns = get_possible_patterns(test_ast, min_nb_use=2, ref_ast_info_by_index=None, max_nb_pattern=64)
|
||||
for pattern in patterns:
|
||||
leaf_info_list, cst_leaf_values, opcodes = pattern.get_information()
|
||||
leaf_nb_use = [leaf_info.number_of_use for leaf_info in leaf_info_list]
|
||||
if not(self.min_nb_var <= len(leaf_info_list) <= self.max_nb_var):
|
||||
continue
|
||||
if not(self.min_nb_diff_opcodes <= len(set(opcodes)) <= self.max_nb_diff_opcodes):
|
||||
continue
|
||||
if not(min(leaf_nb_use) >= 2):
|
||||
continue
|
||||
ins = pattern.mop.d
|
||||
self.log_info("IR: 0x{0:x} - {1}".format(ins.ea, format_minsn_t(ins)))
|
||||
for leaf_info in leaf_info_list:
|
||||
self.log_info(" {0} -> {1}".format(leaf_info.ast, format_mop_t(leaf_info.ast.mop)))
|
||||
self.log_info("Pattern: {0}".format(pattern))
|
||||
self.log_info("AstNode: {0}\n".format(pattern.get_pattern()))
|
||||
return True
|
||||
return False
|
||||
39
d810/optimizers/instructions/analysis/utils.py
Normal file
39
d810/optimizers/instructions/analysis/utils.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from d810.ast import AstNode, AstLeaf
|
||||
|
||||
|
||||
def get_possible_patterns(ast, min_nb_use=2, ref_ast_info_by_index=None, max_nb_pattern=64):
|
||||
# max_nb_pattern is used to prevent memory explosion when very large patterns are parsed
|
||||
if ast.is_leaf():
|
||||
return [ast]
|
||||
if ref_ast_info_by_index is None:
|
||||
if ast.ast_index not in ast.sub_ast_info_by_index.keys():
|
||||
ast.compute_sub_ast()
|
||||
ref_ast_info_by_index = ast.sub_ast_info_by_index
|
||||
possible_patterns = []
|
||||
if ref_ast_info_by_index[ast.ast_index].number_of_use >= min_nb_use:
|
||||
node_as_leaf = AstLeaf("x_{0}".format(ast.ast_index))
|
||||
node_as_leaf.mop = ast.mop
|
||||
node_as_leaf.ast_index = ast.ast_index
|
||||
possible_patterns.append(node_as_leaf)
|
||||
left_patterns = []
|
||||
right_patterns = []
|
||||
if ast.left is not None:
|
||||
left_patterns = get_possible_patterns(ast.left, min_nb_use, ref_ast_info_by_index, max_nb_pattern)
|
||||
if ast.right is not None:
|
||||
right_patterns = get_possible_patterns(ast.right, min_nb_use, ref_ast_info_by_index, max_nb_pattern)
|
||||
|
||||
for left_pattern in left_patterns:
|
||||
if ast.right is not None:
|
||||
for right_pattern in right_patterns:
|
||||
node = AstNode(ast.opcode, left_pattern, right_pattern)
|
||||
node.mop = ast.mop
|
||||
node.ast_index = ast.ast_index
|
||||
if len(possible_patterns) < max_nb_pattern:
|
||||
possible_patterns.append(node)
|
||||
else:
|
||||
node = AstNode(ast.opcode, left_pattern)
|
||||
node.mop = ast.mop
|
||||
node.ast_index = ast.ast_index
|
||||
if len(possible_patterns) < max_nb_pattern:
|
||||
possible_patterns.append(node)
|
||||
return possible_patterns
|
||||
5
d810/optimizers/instructions/chain/__init__.py
Normal file
5
d810/optimizers/instructions/chain/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from d810.utils import get_all_subclasses
|
||||
from d810.optimizers.instructions.chain.handler import ChainSimplificationRule, ChainOptimizer
|
||||
from d810.optimizers.instructions.chain.chain_rules import *
|
||||
|
||||
CHAIN_RULES = [x() for x in get_all_subclasses(ChainSimplificationRule)]
|
||||
375
d810/optimizers/instructions/chain/chain_rules.py
Normal file
375
d810/optimizers/instructions/chain/chain_rules.py
Normal file
@@ -0,0 +1,375 @@
|
||||
import logging
|
||||
from functools import reduce
|
||||
from ida_hexrays import *
|
||||
|
||||
|
||||
from d810.optimizers.instructions.chain.handler import ChainSimplificationRule
|
||||
from d810.hexrays_helpers import equal_bnot_mop, equal_mops_ignore_size, \
|
||||
SUB_TABLE, AND_TABLE
|
||||
from d810.hexrays_formatters import format_minsn_t
|
||||
|
||||
rules_chain_logger = logging.getLogger('D810.rules.chain')
|
||||
|
||||
|
||||
class ChainSimplification(object):
|
||||
def __init__(self, opcode):
|
||||
self.opcode = opcode
|
||||
self.formatted_ins = ""
|
||||
self.non_cst_mop_list = []
|
||||
self.cst_mop_list = []
|
||||
self._is_instruction_simplified = False
|
||||
|
||||
def add_mop(self, mop):
|
||||
if (mop.t == mop_d) and (mop.d.opcode == self.opcode):
|
||||
self.add_mop(mop.d.l)
|
||||
self.add_mop(mop.d.r)
|
||||
else:
|
||||
if mop.t == mop_n:
|
||||
self.cst_mop_list.append(mop)
|
||||
else:
|
||||
self.non_cst_mop_list.append(mop)
|
||||
|
||||
def do_simplification(self):
|
||||
final_mop_list = self.get_simplified_non_constant()
|
||||
final_mop_list += self.get_simplified_constant()
|
||||
return final_mop_list
|
||||
|
||||
def get_simplified_constant(self):
|
||||
if len(self.cst_mop_list) == 0:
|
||||
return []
|
||||
elif len(self.cst_mop_list) == 1:
|
||||
return self.cst_mop_list
|
||||
else:
|
||||
cst_size_list = [c.size for c in self.cst_mop_list]
|
||||
cst_value_list = [c.nnn.value for c in self.cst_mop_list]
|
||||
final_cst_size = max(cst_size_list)
|
||||
rules_chain_logger.debug("Doing cst simplification: {0}".format(cst_value_list))
|
||||
self._is_instruction_simplified = True
|
||||
if self.opcode == m_xor:
|
||||
final_cst = reduce(lambda x, y: x ^ y, cst_value_list)
|
||||
elif self.opcode == m_and:
|
||||
final_cst = reduce(lambda x, y: x & y, cst_value_list)
|
||||
elif self.opcode == m_or:
|
||||
final_cst = reduce(lambda x, y: x | y, cst_value_list)
|
||||
elif self.opcode == m_add:
|
||||
final_cst = reduce(lambda x, y: x + y, cst_value_list)
|
||||
else:
|
||||
raise NotImplementedError("Euh")
|
||||
final_cst = final_cst & AND_TABLE[final_cst_size]
|
||||
rules_chain_logger.debug("Final cst: {0}".format(final_cst))
|
||||
final_cst_mop = mop_t()
|
||||
final_cst_mop.make_number(final_cst, max(cst_size_list))
|
||||
return [final_cst_mop]
|
||||
|
||||
def get_simplified_non_constant(self):
|
||||
if len(self.non_cst_mop_list) == 0:
|
||||
return []
|
||||
elif len(self.non_cst_mop_list) == 1:
|
||||
return self.non_cst_mop_list
|
||||
else:
|
||||
is_always_0 = False
|
||||
index_removed = []
|
||||
for i in range(len(self.non_cst_mop_list)):
|
||||
for j in range(i + 1, len(self.non_cst_mop_list)):
|
||||
if (i not in index_removed) and (j not in index_removed):
|
||||
if equal_mops_ignore_size(self.non_cst_mop_list[i], self.non_cst_mop_list[j]):
|
||||
if self.opcode == m_xor:
|
||||
# x ^ x == 0
|
||||
rules_chain_logger.debug("Doing non cst simplification (xor): {0}, {1} in {2}"
|
||||
.format(i, j, self.formatted_ins))
|
||||
index_removed += [i, j]
|
||||
elif self.opcode == m_and:
|
||||
# x & x == x
|
||||
rules_chain_logger.debug("Doing non cst simplification (and): {0}, {1} in {2}"
|
||||
.format(i, j, self.formatted_ins))
|
||||
index_removed += [j]
|
||||
elif self.opcode == m_or:
|
||||
# x | x == x
|
||||
rules_chain_logger.debug("Doing non cst simplification (or): {0}, {1} in {2}"
|
||||
.format(i, j, self.formatted_ins))
|
||||
index_removed += [j]
|
||||
elif equal_bnot_mop(self.non_cst_mop_list[i], self.non_cst_mop_list[j]):
|
||||
if self.opcode == m_and:
|
||||
is_always_0 = True
|
||||
|
||||
if len(index_removed) == 0 and not is_always_0:
|
||||
return self.non_cst_mop_list
|
||||
final_mop_list = []
|
||||
self._is_instruction_simplified = True
|
||||
if is_always_0:
|
||||
final_mop_list.append(self.create_cst_mop(0, self.res_mop_size))
|
||||
return final_mop_list
|
||||
for i in range(len(self.non_cst_mop_list)):
|
||||
if i not in index_removed:
|
||||
final_mop_list.append(self.non_cst_mop_list[i])
|
||||
return final_mop_list
|
||||
|
||||
def simplify(self, ins):
|
||||
self.res_mop_size = ins.d.size
|
||||
if ins.opcode != self.opcode:
|
||||
return None
|
||||
|
||||
self.formatted_ins = format_minsn_t(ins)
|
||||
self.non_cst_mop_list = []
|
||||
self.cst_mop_list = []
|
||||
self.add_mop(ins.l)
|
||||
self.add_mop(ins.r)
|
||||
|
||||
self._is_instruction_simplified = False
|
||||
final_mop_list = self.do_simplification()
|
||||
if not self._is_instruction_simplified:
|
||||
return None
|
||||
|
||||
return self.create_new_chain(ins, final_mop_list)
|
||||
|
||||
def create_new_chain(self, original_ins, mop_list):
|
||||
new_ins = minsn_t(original_ins.ea)
|
||||
new_ins.opcode = self.opcode
|
||||
if len(mop_list) == 0:
|
||||
mop_list.append(self.create_cst_mop(0, original_ins.d.size))
|
||||
if len(mop_list) == 1:
|
||||
mop_list.append(self.create_cst_mop(0, original_ins.d.size))
|
||||
new_ins.l = self._create_mop_chain(original_ins, mop_list[:-1], original_ins.d.size)
|
||||
new_ins.r = mop_list[-1]
|
||||
if new_ins.r.t == mop_n:
|
||||
new_ins.r.size = original_ins.d.size
|
||||
new_ins.d = original_ins.d
|
||||
return new_ins
|
||||
|
||||
def create_cst_mop(self, value, size):
|
||||
cst_mop = mop_t()
|
||||
cst_mop.make_number(value, size)
|
||||
return cst_mop
|
||||
|
||||
def _create_mop_chain(self, ea, mop_list, size):
|
||||
if len(mop_list) == 1:
|
||||
return mop_list[0]
|
||||
new_ins = minsn_t(ea)
|
||||
new_ins.opcode = self.opcode
|
||||
new_ins.l = self._create_mop_chain(ea, mop_list[:-1], size)
|
||||
new_ins.r = mop_list[-1]
|
||||
new_ins.d = mop_t()
|
||||
new_ins.d.size = size
|
||||
mop = mop_t()
|
||||
mop.create_from_insn(new_ins)
|
||||
return mop
|
||||
|
||||
|
||||
class ArithmeticChainSimplification(object):
|
||||
def __init__(self):
|
||||
self.formatted_ins = ""
|
||||
self.add_non_cst_mop_list = []
|
||||
self.add_cst_mop_list = []
|
||||
self.sub_non_cst_mop_list = []
|
||||
self.sub_cst_mop_list = []
|
||||
self.add_other_cst_list = []
|
||||
self.sub_other_cst_list = []
|
||||
self._is_instruction_simplified = False
|
||||
|
||||
def add_mop(self, sign, mop):
|
||||
# sign is 0 if +, 1 is minus => minus minus = 1 ^ 1 = 0 so add
|
||||
if (mop.t == mop_d) and (mop.d.opcode in [m_add, m_sub]):
|
||||
|
||||
self.add_mop(sign, mop.d.l)
|
||||
if mop.d.opcode == m_add:
|
||||
self.add_mop(sign, mop.d.r)
|
||||
else:
|
||||
self.add_mop(sign ^ 1, mop.d.r)
|
||||
elif (mop.t == mop_d) and (mop.d.opcode == m_neg):
|
||||
self.add_mop(sign ^ 1, mop.d.l)
|
||||
else:
|
||||
if mop.t == mop_n:
|
||||
if sign == 0:
|
||||
self.add_cst_mop_list.append(mop)
|
||||
else:
|
||||
self.sub_cst_mop_list.append(mop)
|
||||
else:
|
||||
if sign == 0:
|
||||
self.add_non_cst_mop_list.append(mop)
|
||||
else:
|
||||
self.sub_non_cst_mop_list.append(mop)
|
||||
|
||||
def do_simplification(self):
|
||||
final_add_cst_list, final_sub_cst_list = self.get_simplified_constant()
|
||||
final_add_list, final_sub_list, final_add_cst_mop = self.get_simplified_non_constant()
|
||||
if final_add_cst_mop.nnn.value != 0:
|
||||
final_add_cst_list.append(final_add_cst_mop)
|
||||
return final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list
|
||||
|
||||
def get_simplified_constant(self):
|
||||
if len(self.add_cst_mop_list) == 0 and len(self.sub_cst_mop_list) == 0:
|
||||
return [[], []]
|
||||
if len(self.add_cst_mop_list) == 1 and len(self.sub_cst_mop_list) == 0:
|
||||
return self.add_cst_mop_list, []
|
||||
if len(self.add_cst_mop_list) == 0 and len(self.sub_cst_mop_list) == 1:
|
||||
return [], self.sub_cst_mop_list
|
||||
add_cst_size_list = [c.size for c in self.add_cst_mop_list]
|
||||
add_cst_value_list = [c.nnn.value for c in self.add_cst_mop_list]
|
||||
sub_cst_size_list = [c.size for c in self.sub_cst_mop_list]
|
||||
sub_cst_value_list = [SUB_TABLE[c.size] - c.nnn.value for c in self.sub_cst_mop_list]
|
||||
self._is_instruction_simplified = True
|
||||
|
||||
final_cst_size = max(add_cst_size_list + sub_cst_size_list)
|
||||
rules_chain_logger.debug("Doing arithmetic cst simplification: {0} {1}"
|
||||
.format(add_cst_value_list, sub_cst_value_list))
|
||||
final_cst = reduce(lambda x, y: x + y, add_cst_value_list + sub_cst_value_list)
|
||||
final_cst = final_cst & AND_TABLE[final_cst_size]
|
||||
rules_chain_logger.debug("Final cst: {0}".format(final_cst))
|
||||
final_cst_mop = mop_t()
|
||||
final_cst_mop.make_number(final_cst, final_cst_size)
|
||||
return [final_cst_mop], []
|
||||
|
||||
def get_simplified_non_constant(self):
|
||||
if len(self.add_non_cst_mop_list) == 0 and len(self.sub_non_cst_mop_list) == 0:
|
||||
return [[], []]
|
||||
final_add_list = self.add_non_cst_mop_list
|
||||
final_sub_list = self.sub_non_cst_mop_list
|
||||
index_add_removed = []
|
||||
index_sub_removed = []
|
||||
for (i, add_mop) in enumerate(self.add_non_cst_mop_list):
|
||||
for (j, sub_mop) in enumerate(self.sub_non_cst_mop_list):
|
||||
if (i not in index_add_removed) and (j not in index_sub_removed):
|
||||
if equal_mops_ignore_size(add_mop, sub_mop):
|
||||
index_add_removed.append(i)
|
||||
index_sub_removed.append(j)
|
||||
|
||||
if len(index_add_removed) > 0:
|
||||
self._is_instruction_simplified = True
|
||||
final_add_list = []
|
||||
for i in range(len(self.add_non_cst_mop_list)):
|
||||
if i not in index_add_removed:
|
||||
final_add_list.append(self.add_non_cst_mop_list[i])
|
||||
final_sub_list = []
|
||||
for i in range(len(self.sub_non_cst_mop_list)):
|
||||
if i not in index_sub_removed:
|
||||
final_sub_list.append(self.sub_non_cst_mop_list[i])
|
||||
|
||||
final_add_list, final_sub_list, final_add_cst_mop = self.check_bnot_mop(final_add_list, final_sub_list)
|
||||
return final_add_list, final_sub_list, final_add_cst_mop
|
||||
|
||||
def check_bnot_mop(self, add_non_cst_mop_list, sub_non_cst_mop_list):
|
||||
add_index_removed = []
|
||||
sub_index_removed = []
|
||||
cst_value = 0
|
||||
final_add_non_cst_mop_list = add_non_cst_mop_list
|
||||
final_sub_non_cst_mop_list = sub_non_cst_mop_list
|
||||
add_size_list = [c.size for c in add_non_cst_mop_list]
|
||||
sub_size_list = [c.size for c in sub_non_cst_mop_list]
|
||||
final_cst_size = max(add_size_list + sub_size_list)
|
||||
|
||||
for i in range(len(add_non_cst_mop_list)):
|
||||
for j in range(i + 1, len(add_non_cst_mop_list)):
|
||||
if (i not in add_index_removed) and (j not in add_index_removed):
|
||||
if equal_bnot_mop(add_non_cst_mop_list[i], add_non_cst_mop_list[j]):
|
||||
cst_value += AND_TABLE[add_non_cst_mop_list[i].size]
|
||||
add_index_removed += [i, j]
|
||||
|
||||
for i in range(len(sub_non_cst_mop_list)):
|
||||
for j in range(i + 1, len(sub_non_cst_mop_list)):
|
||||
if (i not in sub_index_removed) and (j not in sub_index_removed):
|
||||
if equal_bnot_mop(sub_non_cst_mop_list[i], sub_non_cst_mop_list[j]):
|
||||
cst_value += 1
|
||||
sub_index_removed += [i, j]
|
||||
|
||||
final_add_cst_mop = mop_t()
|
||||
final_add_cst_mop.make_number(cst_value & AND_TABLE[final_cst_size], final_cst_size)
|
||||
|
||||
if len(add_index_removed) > 0:
|
||||
final_add_non_cst_mop_list = []
|
||||
self._is_instruction_simplified = True
|
||||
for i in range(len(add_non_cst_mop_list)):
|
||||
if i not in add_index_removed:
|
||||
final_add_non_cst_mop_list.append(add_non_cst_mop_list[i])
|
||||
if len(sub_index_removed) > 0:
|
||||
final_sub_non_cst_mop_list = []
|
||||
self._is_instruction_simplified = True
|
||||
for i in range(len(sub_non_cst_mop_list)):
|
||||
if i not in sub_index_removed:
|
||||
final_sub_non_cst_mop_list.append(sub_non_cst_mop_list[i])
|
||||
return final_add_non_cst_mop_list, final_sub_non_cst_mop_list, final_add_cst_mop
|
||||
|
||||
def simplify(self, ins):
|
||||
if ins.opcode not in [m_add, m_sub]:
|
||||
return None
|
||||
self.formatted_ins = format_minsn_t(ins)
|
||||
self.add_non_cst_mop_list = []
|
||||
self.add_cst_mop_list = []
|
||||
self.sub_non_cst_mop_list = []
|
||||
self.sub_cst_mop_list = []
|
||||
self.add_mop(0, ins.l)
|
||||
if ins.opcode == m_add:
|
||||
self.add_mop(0, ins.r)
|
||||
else:
|
||||
self.add_mop(1, ins.r)
|
||||
|
||||
self._is_instruction_simplified = False
|
||||
final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list = self.do_simplification()
|
||||
if not self._is_instruction_simplified:
|
||||
return None
|
||||
|
||||
simplified_ins = self.create_new_chain(ins, final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list)
|
||||
|
||||
return simplified_ins
|
||||
|
||||
def create_new_chain(self, original_ins, final_add_list, final_sub_list, final_add_cst_list, final_sub_cst_list):
|
||||
mod_add = self._create_mop_add_chain(original_ins.ea, final_add_list + final_add_cst_list, original_ins.d.size)
|
||||
mod_sub = self._create_mop_add_chain(original_ins.ea, final_sub_list + final_sub_cst_list, original_ins.d.size)
|
||||
new_ins = minsn_t(original_ins.ea)
|
||||
new_ins.opcode = m_sub
|
||||
new_ins.l = mod_add
|
||||
new_ins.r = mod_sub
|
||||
new_ins.d = original_ins.d
|
||||
return new_ins
|
||||
|
||||
def _create_mop_add_chain(self, ea, mop_list, size):
|
||||
if len(mop_list) == 0:
|
||||
res = mop_t()
|
||||
res.make_number(0, size)
|
||||
return res
|
||||
elif len(mop_list) == 1:
|
||||
return mop_list[0]
|
||||
new_ins = minsn_t(ea)
|
||||
new_ins.opcode = m_add
|
||||
new_ins.l = self._create_mop_add_chain(ea, mop_list[:-1], size)
|
||||
new_ins.r = mop_list[-1]
|
||||
new_ins.d = mop_t()
|
||||
new_ins.d.size = size
|
||||
mop = mop_t()
|
||||
mop.create_from_insn(new_ins)
|
||||
return mop
|
||||
|
||||
|
||||
class XorChain(ChainSimplificationRule):
|
||||
DESCRIPTION = "Remove XOR chains with common terms. E.g. x ^ 4 ^ y ^ 6 ^ 5 ^ x ==> y ^ 7"
|
||||
|
||||
def check_and_replace(self, blk, ins):
|
||||
xor_simplifier = ChainSimplification(m_xor)
|
||||
new_ins = xor_simplifier.simplify(ins)
|
||||
return new_ins
|
||||
|
||||
|
||||
class AndChain(ChainSimplificationRule):
|
||||
DESCRIPTION = "Remove AND chains with common terms. E.g. x & 4 & y & 6 & 5 & x ==> x & y & 4"
|
||||
|
||||
def check_and_replace(self, blk, ins):
|
||||
and_simplifier = ChainSimplification(m_and)
|
||||
new_ins = and_simplifier.simplify(ins)
|
||||
return new_ins
|
||||
|
||||
|
||||
class OrChain(ChainSimplificationRule):
|
||||
DESCRIPTION = "Remove OR chains with common terms. E.g. x | 4 | y | 6 | 5 | x ==> x | y | 7"
|
||||
|
||||
def check_and_replace(self, blk, ins):
|
||||
or_simplifier = ChainSimplification(m_or)
|
||||
new_ins = or_simplifier.simplify(ins)
|
||||
return new_ins
|
||||
|
||||
|
||||
class ArithmeticChain(ChainSimplificationRule):
|
||||
DESCRIPTION = "Remove arithmetic chains with common terms. E.g. x + 4 + y - (6 + x - 5) ==> y + 3"
|
||||
|
||||
def check_and_replace(self, blk, ins):
|
||||
arithmetic_simplifier = ArithmeticChainSimplification()
|
||||
new_ins = arithmetic_simplifier.simplify(ins)
|
||||
return new_ins
|
||||
9
d810/optimizers/instructions/chain/handler.py
Normal file
9
d810/optimizers/instructions/chain/handler.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from d810.optimizers.instructions.handler import InstructionOptimizationRule, InstructionOptimizer
|
||||
|
||||
|
||||
class ChainSimplificationRule(InstructionOptimizationRule):
|
||||
pass
|
||||
|
||||
|
||||
class ChainOptimizer(InstructionOptimizer):
|
||||
RULE_CLASSES = [ChainSimplificationRule]
|
||||
5
d810/optimizers/instructions/early/__init__.py
Normal file
5
d810/optimizers/instructions/early/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from d810.utils import get_all_subclasses
|
||||
from d810.optimizers.instructions.early.handler import EarlyRule, EarlyOptimizer
|
||||
from d810.optimizers.instructions.early.mem_read import *
|
||||
|
||||
EARLY_RULES = [x() for x in get_all_subclasses(EarlyRule)]
|
||||
9
d810/optimizers/instructions/early/handler.py
Normal file
9
d810/optimizers/instructions/early/handler.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer
|
||||
|
||||
|
||||
class EarlyRule(GenericPatternRule):
|
||||
pass
|
||||
|
||||
|
||||
class EarlyOptimizer(InstructionOptimizer):
|
||||
RULE_CLASSES = [EarlyRule]
|
||||
73
d810/optimizers/instructions/early/mem_read.py
Normal file
73
d810/optimizers/instructions/early/mem_read.py
Normal file
@@ -0,0 +1,73 @@
|
||||
from ida_hexrays import *
|
||||
from idaapi import SEGPERM_READ, SEGPERM_WRITE, xrefblk_t, getseg, segment_t, XREF_DATA, dr_W, is_loaded
|
||||
|
||||
from d810.optimizers.instructions.early.handler import EarlyRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
|
||||
|
||||
class SetGlobalVariablesToZero(EarlyRule):
|
||||
DESCRIPTION = "This rule can be used to patch memory read"
|
||||
|
||||
PATTERN = AstNode(m_mov, AstLeaf("ro_dword"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.ro_dword_min_ea = None
|
||||
self.ro_dword_max_ea = None
|
||||
|
||||
def configure(self, kwargs):
|
||||
super().configure(kwargs)
|
||||
self.ro_dword_min_ea = None
|
||||
self.ro_dword_max_ea = None
|
||||
if "ro_dword_min_ea" in kwargs.keys():
|
||||
self.ro_dword_min_ea = int(kwargs["ro_dword_min_ea"], 16)
|
||||
if "ro_dword_max_ea" in kwargs.keys():
|
||||
self.ro_dword_max_ea = int(kwargs["ro_dword_max_ea"], 16)
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (self.ro_dword_min_ea is None) or (self.ro_dword_max_ea is None):
|
||||
return False
|
||||
if candidate["ro_dword"].mop.t != mop_v:
|
||||
return False
|
||||
mem_read_address = candidate["ro_dword"].mop.g
|
||||
if not(self.ro_dword_min_ea <= mem_read_address <= self.ro_dword_max_ea):
|
||||
return False
|
||||
|
||||
candidate.add_constant_leaf("val_res", 0, candidate["ro_dword"].mop.size)
|
||||
return True
|
||||
|
||||
|
||||
# This rule is from
|
||||
# https://www.carbonblack.com/blog/defeating-compiler-level-obfuscations-used-in-apt10-malware/
|
||||
class SetGlobalVariablesToZeroIfDetectedReadOnly(EarlyRule):
|
||||
DESCRIPTION = "WARNING: Use it only if you know what you are doing as it may patch data not related to obfuscation"
|
||||
|
||||
PATTERN = AstNode(m_mov, AstLeaf("ro_dword"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
|
||||
|
||||
def is_read_only_inited_var(self, address):
|
||||
s: segment_t = getseg(address)
|
||||
if s is None:
|
||||
return False
|
||||
if s.perm != (SEGPERM_READ | SEGPERM_WRITE):
|
||||
return False
|
||||
if is_loaded(address):
|
||||
return False
|
||||
ref_finder = xrefblk_t()
|
||||
is_ok = ref_finder.first_to(address, XREF_DATA)
|
||||
while is_ok:
|
||||
if ref_finder.type == dr_W:
|
||||
return False
|
||||
is_ok = ref_finder.next_to()
|
||||
return True
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if candidate["ro_dword"].mop.t != mop_v:
|
||||
return False
|
||||
mem_read_address = candidate["ro_dword"].mop.g
|
||||
if not self.is_read_only_inited_var(mem_read_address):
|
||||
return False
|
||||
|
||||
candidate.add_constant_leaf("val_res", 0, candidate["ro_dword"].mop.size)
|
||||
return True
|
||||
138
d810/optimizers/instructions/handler.py
Normal file
138
d810/optimizers/instructions/handler.py
Normal file
@@ -0,0 +1,138 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typing import List
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.handler import OptimizationRule, DEFAULT_INSTRUCTION_MATURITIES
|
||||
from d810.hexrays_formatters import format_minsn_t
|
||||
from d810.ast import minsn_to_ast, AstNode
|
||||
from d810.errors import D810Exception
|
||||
|
||||
|
||||
d810_logger = logging.getLogger('D810')
|
||||
optimizer_logger = logging.getLogger('D810.optimizer')
|
||||
|
||||
|
||||
class InstructionOptimizationRule(OptimizationRule):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.maturities = DEFAULT_INSTRUCTION_MATURITIES
|
||||
|
||||
def check_and_replace(self, blk, ins):
|
||||
return None
|
||||
|
||||
|
||||
class GenericPatternRule(InstructionOptimizationRule):
|
||||
PATTERN = None
|
||||
PATTERNS = None
|
||||
REPLACEMENT_PATTERN = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.pattern_candidates = [self.PATTERN]
|
||||
if self.PATTERNS is not None:
|
||||
self.pattern_candidates += self.PATTERNS
|
||||
|
||||
def check_candidate(self, candidate: AstNode):
|
||||
# Perform rule specific checks
|
||||
return False
|
||||
|
||||
def get_valid_candidates(self, instruction: minsn_t, stop_early=True):
|
||||
valid_candidates = []
|
||||
tmp = minsn_to_ast(instruction)
|
||||
if tmp is None:
|
||||
return []
|
||||
for candidate_pattern in self.pattern_candidates:
|
||||
if not candidate_pattern.check_pattern_and_copy_mops(tmp):
|
||||
continue
|
||||
if not self.check_candidate(candidate_pattern):
|
||||
continue
|
||||
valid_candidates.append(candidate_pattern)
|
||||
if stop_early:
|
||||
return valid_candidates
|
||||
return []
|
||||
|
||||
def get_replacement(self, candidate: AstNode):
|
||||
is_ok = self.REPLACEMENT_PATTERN.update_leafs_mop(candidate)
|
||||
if not is_ok:
|
||||
return None
|
||||
new_ins = self.REPLACEMENT_PATTERN.create_minsn(candidate.ea, candidate.dst_mop)
|
||||
return new_ins
|
||||
|
||||
def check_and_replace(self, blk: mblock_t, instruction: minsn_t):
|
||||
valid_candidates = self.get_valid_candidates(instruction, stop_early=True)
|
||||
if len(valid_candidates) == 0:
|
||||
return None
|
||||
new_instruction = self.get_replacement(valid_candidates[0])
|
||||
return new_instruction
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
if self.DESCRIPTION is not None:
|
||||
return self.DESCRIPTION
|
||||
if (self.PATTERN is None) or (self.REPLACEMENT_PATTERN is None):
|
||||
return ""
|
||||
self.PATTERN.reset_mops()
|
||||
self.REPLACEMENT_PATTERN.reset_mops()
|
||||
return "{0} => {1}".format(self.PATTERN, self.REPLACEMENT_PATTERN)
|
||||
|
||||
|
||||
class InstructionOptimizer(object):
|
||||
RULE_CLASSES = []
|
||||
NAME = None
|
||||
|
||||
def __init__(self, maturities: List[int], log_dir=None):
|
||||
self.rules = set()
|
||||
self.rules_usage_info = {}
|
||||
self.maturities = maturities
|
||||
self.log_dir = log_dir
|
||||
self.cur_maturity = MMAT_PREOPTIMIZED
|
||||
|
||||
def add_rule(self, rule: InstructionOptimizationRule):
|
||||
is_valid_rule_class = False
|
||||
for rule_class in self.RULE_CLASSES:
|
||||
if isinstance(rule, rule_class):
|
||||
is_valid_rule_class = True
|
||||
break
|
||||
if not is_valid_rule_class:
|
||||
return False
|
||||
optimizer_logger.debug("Adding rule {0}".format(rule))
|
||||
self.rules.add(rule)
|
||||
self.rules_usage_info[rule.name] = 0
|
||||
return True
|
||||
|
||||
def reset_rule_usage_statistic(self):
|
||||
self.rules_usage_info = {}
|
||||
for rule in self.rules:
|
||||
self.rules_usage_info[rule.name] = 0
|
||||
|
||||
def show_rule_usage_statistic(self):
|
||||
for rule_name, rule_nb_match in self.rules_usage_info.items():
|
||||
if rule_nb_match > 0:
|
||||
d810_logger.info("Instruction Rule '{0}' has been used {1} times".format(rule_name, rule_nb_match))
|
||||
|
||||
def get_optimized_instruction(self, blk: mblock_t, ins: minsn_t):
|
||||
if blk is not None:
|
||||
self.cur_maturity = blk.mba.maturity
|
||||
if self.cur_maturity not in self.maturities:
|
||||
return None
|
||||
for rule in self.rules:
|
||||
try:
|
||||
new_ins = rule.check_and_replace(blk, ins)
|
||||
if new_ins is not None:
|
||||
self.rules_usage_info[rule.name] += 1
|
||||
optimizer_logger.info("Rule {0} matched:".format(rule.name))
|
||||
optimizer_logger.info(" orig: {0}".format(format_minsn_t(ins)))
|
||||
optimizer_logger.info(" new : {0}".format(format_minsn_t(new_ins)))
|
||||
return new_ins
|
||||
except RuntimeError as e:
|
||||
optimizer_logger.error("Runtime error during rule {0} for instruction {1}: {2}".format(rule, format_minsn_t(ins), e))
|
||||
except D810Exception as e:
|
||||
optimizer_logger.error("D810Exception during rule {0} for instruction {1}: {2}".format(rule, format_minsn_t(ins), e))
|
||||
return None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
if self.NAME is not None:
|
||||
return self.NAME
|
||||
return self.__class__.__name__
|
||||
18
d810/optimizers/instructions/pattern_matching/__init__.py
Normal file
18
d810/optimizers/instructions/pattern_matching/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from d810.utils import get_all_subclasses
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule, PatternOptimizer
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_add import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_and import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_bnot import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_cst import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_mov import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_mul import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_neg import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_predicates import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_or import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_sub import *
|
||||
from d810.optimizers.instructions.pattern_matching.rewrite_xor import *
|
||||
from d810.optimizers.instructions.pattern_matching.weird import *
|
||||
|
||||
PATTERN_MATCHING_RULES = [x() for x in get_all_subclasses(PatternMatchingRule)]
|
||||
|
||||
|
||||
340
d810/optimizers/instructions/pattern_matching/handler.py
Normal file
340
d810/optimizers/instructions/pattern_matching/handler.py
Normal file
@@ -0,0 +1,340 @@
|
||||
import logging
|
||||
import itertools
|
||||
from ida_hexrays import *
|
||||
from typing import List, Union
|
||||
from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer, InstructionOptimizationRule
|
||||
from d810.ast import minsn_to_ast, AstNode, AstLeaf
|
||||
from d810.hexrays_formatters import format_minsn_t, format_mop_t
|
||||
|
||||
optimizer_logger = logging.getLogger('D810.optimizer')
|
||||
pattern_search_logger = logging.getLogger('D810.pattern_search')
|
||||
|
||||
|
||||
class PatternMatchingRule(GenericPatternRule):
|
||||
PATTERN = None
|
||||
PATTERNS = None
|
||||
FUZZ_PATTERN = True
|
||||
REPLACEMENT_PATTERN = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.fuzz_pattern = self.FUZZ_PATTERN
|
||||
|
||||
def configure(self, fuzz_pattern=None, **kwargs):
|
||||
super().configure(kwargs)
|
||||
if fuzz_pattern is not None:
|
||||
self.fuzz_pattern = fuzz_pattern
|
||||
self._generate_pattern_candidates()
|
||||
pattern_search_logger.debug("Rule {0} configured with {1} patterns"
|
||||
.format(self.__class__.__name__, len(self.pattern_candidates)))
|
||||
|
||||
def _generate_pattern_candidates(self):
|
||||
self.fuzz_pattern = self.FUZZ_PATTERN
|
||||
if self.PATTERN is not None:
|
||||
self.PATTERN.reset_mops()
|
||||
if not self.fuzz_pattern:
|
||||
if self.PATTERN is not None:
|
||||
self.pattern_candidates = [self.PATTERN]
|
||||
if self.PATTERNS is not None:
|
||||
self.pattern_candidates += [x for x in self.PATTERNS]
|
||||
else:
|
||||
self.pattern_candidates = [x for x in self.PATTERNS]
|
||||
else:
|
||||
self.pattern_candidates = ast_generator(self.PATTERN)
|
||||
|
||||
def check_candidate(self, candidate: AstNode):
|
||||
return True
|
||||
|
||||
def check_pattern_and_replace(self, candidate_pattern: AstNode, test_ast: AstNode):
|
||||
if not candidate_pattern.check_pattern_and_copy_mops(test_ast):
|
||||
return None
|
||||
if not self.check_candidate(candidate_pattern):
|
||||
return None
|
||||
new_instruction = self.get_replacement(candidate_pattern)
|
||||
return new_instruction
|
||||
|
||||
|
||||
class RulePatternInfo(object):
|
||||
def __init__(self, rule, pattern):
|
||||
self.rule = rule
|
||||
self.pattern = pattern
|
||||
|
||||
|
||||
def signature_generator(ref_sig):
|
||||
for i, x in enumerate(ref_sig):
|
||||
if x not in ["N", "L"]:
|
||||
for sig_suffix in signature_generator(ref_sig[i + 1:]):
|
||||
yield ref_sig[:i] + ["L"] + sig_suffix
|
||||
yield ref_sig
|
||||
|
||||
|
||||
class PatternStorage(object):
|
||||
# The PatternStorage object is used to store patterns associated to rules
|
||||
# A PatternStorage contains a dictionary (next_layer_patterns) where:
|
||||
# - keys are the signature of a pattern at a specific depth (i.e. the opcodes, the variable and constant)
|
||||
# - values are PatternStorage object for the next depth
|
||||
# Additionally, it stores the rule objects which are resolved for the PatternStorage depth
|
||||
def __init__(self, depth=1):
|
||||
self.depth = depth
|
||||
self.next_layer_patterns = {}
|
||||
self.rule_resolved = []
|
||||
|
||||
def add_pattern_for_rule(self, pattern: AstNode, rule: InstructionOptimizationRule):
|
||||
layer_signature = self.layer_signature_to_key(pattern.get_depth_signature(self.depth))
|
||||
if len(layer_signature.replace(",", "")) == (layer_signature.count("N")):
|
||||
self.rule_resolved.append(RulePatternInfo(rule, pattern))
|
||||
else:
|
||||
if layer_signature not in self.next_layer_patterns.keys():
|
||||
self.next_layer_patterns[layer_signature] = PatternStorage(self.depth + 1)
|
||||
self.next_layer_patterns[layer_signature].add_pattern_for_rule(pattern, rule)
|
||||
|
||||
@staticmethod
|
||||
def layer_signature_to_key(sig: List[str]) -> str:
|
||||
return ",".join(sig)
|
||||
|
||||
@staticmethod
|
||||
def is_layer_signature_compatible(instruction_signature: str, pattern_signature: str) -> bool:
|
||||
if instruction_signature == pattern_signature:
|
||||
return True
|
||||
instruction_node_list = instruction_signature.split(",")
|
||||
pattern_node_list = pattern_signature.split(",")
|
||||
for ins_node_sig, pattern_node_sig in zip(instruction_node_list, pattern_node_list):
|
||||
if pattern_node_sig not in ["L", "C", "N"] and ins_node_sig != pattern_node_sig:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_matching_rule_pattern_info(self, pattern: AstNode):
|
||||
pattern_search_logger.info("Searching : {0}".format(pattern))
|
||||
return self.explore_one_level(pattern, 1)
|
||||
|
||||
def explore_one_level(self, searched_pattern: AstNode, cur_level: int):
|
||||
# We need to check if searched_pattern is in self.next_layer_patterns
|
||||
# Easy solution: try/except self.next_layer_patterns[searched_pattern]
|
||||
# Problem is that known patterns may not exactly match the microcode instruction, e.g.
|
||||
# -> Pattern layer 3 signature is ["L", "N", "15", "L"]
|
||||
# -> Multiple instruction can match that: ["L", "N", "15", "L"], ["C", "N", "15", "L"], ["C", "N", "15", "13"]
|
||||
# This piece of code tries to handles that in a (semi) efficient way
|
||||
if len(self.next_layer_patterns) == 0:
|
||||
return []
|
||||
searched_layer_signature = searched_pattern.get_depth_signature(cur_level)
|
||||
nb_possible_signature = 2 ** (len(searched_layer_signature) - searched_layer_signature.count("N") - \
|
||||
searched_layer_signature.count("L"))
|
||||
pattern_search_logger.debug(" Layer {0}: {1} -> {2} variations (storage has {3} signature)"
|
||||
.format(cur_level, searched_layer_signature, nb_possible_signature,
|
||||
len(self.next_layer_patterns)))
|
||||
matched_rule_pattern_info = []
|
||||
if nb_possible_signature < len(self.next_layer_patterns):
|
||||
pattern_search_logger.debug(" => Using method 1")
|
||||
for possible_sig in signature_generator(searched_layer_signature):
|
||||
try:
|
||||
test_sig = self.layer_signature_to_key(possible_sig)
|
||||
pattern_storage = self.next_layer_patterns[test_sig]
|
||||
pattern_search_logger.info(" Compatible signature: {0} -> resolved: {1}"
|
||||
.format(test_sig, pattern_storage.rule_resolved))
|
||||
matched_rule_pattern_info += pattern_storage.rule_resolved
|
||||
matched_rule_pattern_info += pattern_storage.explore_one_level(searched_pattern, cur_level + 1)
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
pattern_search_logger.debug(" => Using method 2")
|
||||
searched_layer_signature_key = self.layer_signature_to_key(searched_layer_signature)
|
||||
for test_sig, pattern_storage in self.next_layer_patterns.items():
|
||||
if self.is_layer_signature_compatible(searched_layer_signature_key, test_sig):
|
||||
pattern_search_logger.info(" Compatible signature: {0} -> resolved: {1}"
|
||||
.format(test_sig, pattern_storage.rule_resolved))
|
||||
matched_rule_pattern_info += pattern_storage.rule_resolved
|
||||
matched_rule_pattern_info += pattern_storage.explore_one_level(searched_pattern, cur_level + 1)
|
||||
return matched_rule_pattern_info
|
||||
|
||||
|
||||
class PatternOptimizer(InstructionOptimizer):
|
||||
# The main idea of PatternOptimizer is to generate/store all possible patterns associated to all known rules in a $
|
||||
# dictionary-like object (PatternStorage) when the plugin is loaded.
|
||||
# => it means that we generate a very large number of patterns
|
||||
#
|
||||
# At runtime, we transform the microcode instruction in a list of keys that we search in the PatternStorage object
|
||||
# to speed up the checks
|
||||
# => we don't want to test all patterns, so we use the PatternStorage object to (quickly) get the patterns
|
||||
# which have the same shape as the microcode instruction
|
||||
|
||||
RULE_CLASSES = [PatternMatchingRule]
|
||||
|
||||
def __init__(self, maturities, log_dir=None):
|
||||
super().__init__(maturities, log_dir=log_dir)
|
||||
self.pattern_storage = PatternStorage(depth=1)
|
||||
|
||||
def add_rule(self, rule: InstructionOptimizationRule):
|
||||
is_ok = super().add_rule(rule)
|
||||
if not is_ok:
|
||||
return False
|
||||
for pattern in rule.pattern_candidates:
|
||||
self.pattern_storage.add_pattern_for_rule(pattern, rule)
|
||||
return True
|
||||
|
||||
def get_optimized_instruction(self, blk: mblock_t, ins: minsn_t) -> Union[None, minsn_t]:
|
||||
if blk is not None:
|
||||
self.cur_maturity = blk.mba.maturity
|
||||
if self.cur_maturity not in self.maturities:
|
||||
return None
|
||||
tmp = minsn_to_ast(ins)
|
||||
if tmp is None:
|
||||
return None
|
||||
|
||||
all_matchs = self.pattern_storage.get_matching_rule_pattern_info(tmp)
|
||||
for rule_pattern_info in all_matchs:
|
||||
try:
|
||||
new_ins = rule_pattern_info.rule.check_pattern_and_replace(rule_pattern_info.pattern, tmp)
|
||||
if new_ins is not None:
|
||||
self.rules_usage_info[rule_pattern_info.rule.name] += 1
|
||||
optimizer_logger.info("Rule {0} matched:".format(rule_pattern_info.rule.name))
|
||||
optimizer_logger.info(" orig: {0}".format(format_minsn_t(ins)))
|
||||
optimizer_logger.info(" new : {0}".format(format_minsn_t(new_ins)))
|
||||
return new_ins
|
||||
except RuntimeError as e:
|
||||
optimizer_logger.error("Error during rule {0} for instruction {1}: {2}"
|
||||
.format(rule_pattern_info.rule, format_minsn_t(ins), e))
|
||||
return None
|
||||
|
||||
# AST equivalent pattern generation stuff
|
||||
# TODO: refactor/clean this
|
||||
|
||||
|
||||
def rec_get_all_binary_subtree_representation(elt_list):
|
||||
if len(elt_list) == 1:
|
||||
return elt_list
|
||||
if len(elt_list) == 2:
|
||||
return [elt_list]
|
||||
tmp_res = []
|
||||
for i in range(1, len(elt_list)):
|
||||
left_list = rec_get_all_binary_subtree_representation(elt_list[:i])
|
||||
right_list = rec_get_all_binary_subtree_representation(elt_list[i:])
|
||||
for l in left_list:
|
||||
for r in right_list:
|
||||
tmp_res.append([l, r])
|
||||
return tmp_res
|
||||
|
||||
|
||||
def rec_get_all_binary_tree_representation(elt_list):
|
||||
if len(elt_list) <= 1:
|
||||
return elt_list
|
||||
tmp = list(itertools.permutations(elt_list))
|
||||
tmp2 = []
|
||||
for perm_tmp in tmp:
|
||||
tmp2 += rec_get_all_binary_subtree_representation(perm_tmp)
|
||||
return tmp2
|
||||
|
||||
|
||||
def get_all_binary_tree_representation(all_elt):
|
||||
tmp = rec_get_all_binary_tree_representation(all_elt)
|
||||
return tmp
|
||||
|
||||
|
||||
def generate_ast(opcode, leafs):
|
||||
if isinstance(leafs, AstLeaf):
|
||||
return leafs
|
||||
if isinstance(leafs, AstNode):
|
||||
return leafs
|
||||
if len(leafs) == 1:
|
||||
return leafs[0]
|
||||
if len(leafs) == 2:
|
||||
return AstNode(opcode, generate_ast(opcode, leafs[0]), generate_ast(opcode, leafs[1]))
|
||||
|
||||
|
||||
def get_addition_operands(ast_node):
|
||||
if not isinstance(ast_node, AstNode):
|
||||
return [ast_node]
|
||||
if ast_node.opcode == m_add:
|
||||
return get_addition_operands(ast_node.left) + get_addition_operands(ast_node.right)
|
||||
elif ast_node.opcode == m_sub:
|
||||
tmp = get_addition_operands(ast_node.left)
|
||||
for aaa in get_addition_operands(ast_node.right):
|
||||
tmp.append(AstNode(m_neg, aaa))
|
||||
return tmp
|
||||
else:
|
||||
return [ast_node]
|
||||
|
||||
|
||||
def get_opcode_operands(ref_opcode, ast_node):
|
||||
if not isinstance(ast_node, AstNode):
|
||||
return [ast_node]
|
||||
if ast_node.opcode == ref_opcode:
|
||||
return get_opcode_operands(ref_opcode, ast_node.left) + get_opcode_operands(ref_opcode, ast_node.right)
|
||||
else:
|
||||
return [ast_node]
|
||||
|
||||
|
||||
def get_similar_opcode_operands(ast_node):
|
||||
if ast_node.opcode in [m_add, m_sub]:
|
||||
add_elts = get_addition_operands(ast_node)
|
||||
all_add_ordering = get_all_binary_tree_representation(add_elts)
|
||||
ast_res = []
|
||||
for leaf_ordering in all_add_ordering:
|
||||
ast_res.append(generate_ast(m_add, leaf_ordering))
|
||||
return ast_res
|
||||
elif ast_node.opcode in [m_xor, m_or, m_and, m_mul]:
|
||||
same_elts = get_opcode_operands(ast_node.opcode, ast_node)
|
||||
all_same_ordering = get_all_binary_tree_representation(same_elts)
|
||||
ast_res = []
|
||||
for leaf_ordering in all_same_ordering:
|
||||
ast_res.append(generate_ast(ast_node.opcode, leaf_ordering))
|
||||
return ast_res
|
||||
|
||||
else:
|
||||
return [ast_node]
|
||||
|
||||
|
||||
def get_ast_variations_with_add_sub(opcode, left, right):
|
||||
possible_ast = [AstNode(opcode, left, right)]
|
||||
if opcode == m_add:
|
||||
if isinstance(left, AstNode) and isinstance(right, AstNode):
|
||||
if (left.opcode == m_neg) and (right.opcode == m_neg):
|
||||
possible_ast.append(AstNode(m_neg, AstNode(m_add, left.left, right.left)))
|
||||
if isinstance(right, AstNode) and (right.opcode == m_neg):
|
||||
possible_ast.append(AstNode(m_sub, left, right.left))
|
||||
return possible_ast
|
||||
|
||||
|
||||
def ast_generator(ast_node, excluded_opcodes=None):
|
||||
if not isinstance(ast_node, AstNode):
|
||||
return [ast_node]
|
||||
res_ast = []
|
||||
excluded_opcodes = excluded_opcodes if excluded_opcodes is not None else []
|
||||
if ast_node.opcode not in excluded_opcodes:
|
||||
if ast_node.opcode in [m_add, m_sub]:
|
||||
similar_ast_list = get_similar_opcode_operands(ast_node)
|
||||
for similar_ast in similar_ast_list:
|
||||
sub_ast_left_list = ast_generator(similar_ast.left, excluded_opcodes=[m_add, m_sub])
|
||||
sub_ast_right_list = ast_generator(similar_ast.right, excluded_opcodes=[m_add, m_sub])
|
||||
for sub_ast_left in sub_ast_left_list:
|
||||
for sub_ast_right in sub_ast_right_list:
|
||||
res_ast += get_ast_variations_with_add_sub(m_add, sub_ast_left, sub_ast_right)
|
||||
return res_ast
|
||||
if ast_node.opcode in [m_xor, m_or, m_and, m_mul]:
|
||||
similar_ast_list = get_similar_opcode_operands(ast_node)
|
||||
for similar_ast in similar_ast_list:
|
||||
sub_ast_left_list = ast_generator(similar_ast.left, excluded_opcodes=[ast_node.opcode])
|
||||
sub_ast_right_list = ast_generator(similar_ast.right, excluded_opcodes=[ast_node.opcode])
|
||||
for sub_ast_left in sub_ast_left_list:
|
||||
for sub_ast_right in sub_ast_right_list:
|
||||
res_ast += get_ast_variations_with_add_sub(ast_node.opcode, sub_ast_left, sub_ast_right)
|
||||
return res_ast
|
||||
if ast_node.opcode not in [m_add, m_sub, m_or, m_and, m_mul]:
|
||||
excluded_opcodes = []
|
||||
nb_operands = 0
|
||||
if ast_node.left is not None:
|
||||
nb_operands += 1
|
||||
if ast_node.right is not None:
|
||||
nb_operands += 1
|
||||
if nb_operands == 1:
|
||||
sub_ast_list = ast_generator(ast_node.left, excluded_opcodes=excluded_opcodes)
|
||||
for sub_ast in sub_ast_list:
|
||||
res_ast.append(AstNode(ast_node.opcode, sub_ast))
|
||||
return res_ast
|
||||
if nb_operands == 2:
|
||||
sub_ast_left_list = ast_generator(ast_node.left, excluded_opcodes=excluded_opcodes)
|
||||
sub_ast_right_list = ast_generator(ast_node.right, excluded_opcodes=excluded_opcodes)
|
||||
for sub_ast_left in sub_ast_left_list:
|
||||
for sub_ast_right in sub_ast_right_list:
|
||||
res_ast += get_ast_variations_with_add_sub(ast_node.opcode, sub_ast_left, sub_ast_right)
|
||||
return res_ast
|
||||
return []
|
||||
243
d810/optimizers/instructions/pattern_matching/rewrite_add.py
Normal file
243
d810/optimizers/instructions/pattern_matching/rewrite_add.py
Normal file
@@ -0,0 +1,243 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_ignore_msb_cst, equal_bnot_mop, AND_TABLE
|
||||
|
||||
|
||||
class Add_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_sub,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_1")),
|
||||
AstConstant("1", 1)))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Add_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Add_HackersDelightRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Add_HackersDelightRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Add_HackersDelightRule_5(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_2"))),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_1"),
|
||||
AstLeaf("x_2"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstNode(m_or, AstLeaf("x_1"), AstLeaf("x_2")))
|
||||
|
||||
|
||||
class Add_SpecialConstantRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstConstant("c_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
return equal_ignore_msb_cst(candidate["c_1"].mop, candidate["c_2"].mop)
|
||||
|
||||
|
||||
class Add_SpecialConstantRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("val_ff", 0xff)),
|
||||
AstConstant("c_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstConstant("c_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
return (candidate["c_1"].value & 0xff) == candidate["c_2"].value
|
||||
|
||||
|
||||
class Add_SpecialConstantRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf("x_0"), AstConstant("val_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["c_1"].mop, candidate["c_2"].mop):
|
||||
return False
|
||||
candidate.add_constant_leaf("val_res", candidate["c_2"].value - 1, candidate["x_0"].size)
|
||||
return True
|
||||
|
||||
|
||||
class Add_OllvmRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_1'),
|
||||
AstLeaf('x_0'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class Add_OllvmRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))),
|
||||
AstNode(m_mul,
|
||||
AstConstant("val_fe"),
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (candidate["val_fe"].value + 2) & AND_TABLE[candidate["val_fe"].size] != 0:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class Add_OllvmRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf('x_0'), AstLeaf('x_1'))
|
||||
|
||||
|
||||
class Add_OllvmRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_mul,
|
||||
AstConstant("val_fe"),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add, AstLeaf('x_0'), AstLeaf('x_1'))
|
||||
|
||||
|
||||
class AddXor_Rule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstConstant("val_2"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
candidate.add_constant_leaf("val_2", 2, candidate["x_0"].size)
|
||||
return True
|
||||
|
||||
|
||||
class AddXor_Rule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_and,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("x_1")))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_add,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("val_2"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
candidate.add_constant_leaf("val_2", 2, candidate["x_0"].size)
|
||||
return True
|
||||
|
||||
253
d810/optimizers/instructions/pattern_matching/rewrite_and.py
Normal file
253
d810/optimizers/instructions/pattern_matching/rewrite_and.py
Normal file
@@ -0,0 +1,253 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE
|
||||
|
||||
|
||||
class And_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_or,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_bnot, AstLeaf("x_0")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class And_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("1", 1)))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
return equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop)
|
||||
|
||||
|
||||
class And_HackersDelightRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class And_HackersDelightRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
|
||||
|
||||
|
||||
class And_OllvmRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
|
||||
|
||||
|
||||
class And_OllvmRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
return equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop)
|
||||
|
||||
|
||||
class And_OllvmRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
|
||||
|
||||
|
||||
|
||||
class And_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")),
|
||||
AstLeaf("x_1"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
return equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop)
|
||||
|
||||
|
||||
class And_FactorRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstLeaf('x_1'))
|
||||
|
||||
|
||||
class AndBnot_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_1"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot, AstLeaf("x_1")))
|
||||
|
||||
|
||||
class AndBnot_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot, AstLeaf("x_1")))
|
||||
|
||||
|
||||
class AndBnot_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot, AstLeaf("x_1")))
|
||||
|
||||
|
||||
class AndBnot_FactorRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot, AstLeaf("x_1")))
|
||||
|
||||
|
||||
class AndBnot_FactorRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_1"))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot, AstLeaf("x_1")))
|
||||
|
||||
|
||||
class AndBnot_FactorRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_1'),
|
||||
AstLeaf('x_0')),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x_1'))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_1"),
|
||||
AstNode(m_bnot, AstLeaf("x_0")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
return equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop)
|
||||
|
||||
|
||||
class AndOr_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_2")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_1"),
|
||||
AstLeaf("x_2")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_2"))
|
||||
|
||||
|
||||
class AndXor_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_2")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_1"),
|
||||
AstLeaf("x_2")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_2"))
|
||||
|
||||
|
||||
class And1_MbaRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_mul, AstLeaf("x_0"), AstLeaf("x_0")),
|
||||
AstConstant("3", 3))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class AndGetUpperBits_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_mul,
|
||||
AstConstant("c_1"),
|
||||
AstNode(m_and,
|
||||
AstNode(m_shr,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant("c_2")),
|
||||
AstConstant("c_3")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstLeaf('x_0'), AstConstant("c_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (2 ** candidate["c_2"].value) != candidate["c_1"].value:
|
||||
return False
|
||||
c_res = (SUB_TABLE[candidate["c_1"].size] - candidate["c_1"].value) & candidate["c_3"].value
|
||||
candidate.add_constant_leaf("c_res", c_res, candidate["x_0"].size)
|
||||
return True
|
||||
272
d810/optimizers/instructions/pattern_matching/rewrite_bnot.py
Normal file
272
d810/optimizers/instructions/pattern_matching/rewrite_bnot.py
Normal file
@@ -0,0 +1,272 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE
|
||||
|
||||
|
||||
class Bnot_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_neg,
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("1", 1))
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_0"))
|
||||
|
||||
|
||||
class Bnot_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Bnot_MbaRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("1", 1)),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstLeaf("x_0")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_0"))
|
||||
|
||||
|
||||
class Bnot_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))),
|
||||
AstLeaf("x_1"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_0"))
|
||||
|
||||
|
||||
class Bnot_FactorRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstConstant("minus_1"),
|
||||
AstLeaf("x_0"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if candidate["minus_1"].value != SUB_TABLE[candidate["minus_1"].size] - 1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Bnot_FactorRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot, AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Bnot_FactorRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_bnot, AstLeaf('x_0')),
|
||||
AstNode(m_bnot, AstLeaf('x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf('x_0'), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class BnotXor_Rule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("bnot_x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class BnotXor_Rule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_or,
|
||||
AstLeaf('bnot_x_0'),
|
||||
AstLeaf('bnot_x_1')))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
class BnotXor_Rule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x_1')),
|
||||
AstNode(m_or,
|
||||
AstLeaf('bnot_x_0'),
|
||||
AstLeaf('x_1')))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class BnotXor_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
|
||||
class BnotAnd_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
|
||||
|
||||
class BnotAnd_FactorRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_or,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("bnot_x_1")),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class BnotAnd_FactorRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
|
||||
class BnotAnd_FactorRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_and, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class BnotOr_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
|
||||
class BnotAdd_MbaRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_add, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Bnot_Rule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("bnot_x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Bnot_XorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_bnot, AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1")))
|
||||
448
d810/optimizers/instructions/pattern_matching/rewrite_cst.py
Normal file
448
d810/optimizers/instructions/pattern_matching/rewrite_cst.py
Normal file
@@ -0,0 +1,448 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.hexrays_helpers import equal_bnot_cst, SUB_TABLE, AND_TABLE, equal_bnot_mop
|
||||
|
||||
|
||||
class CstSimplificationRule1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstNode(m_xor,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("c_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot,
|
||||
AstConstant("c_1"))),
|
||||
AstNode(m_bnot, AstConstant("c_1")))
|
||||
|
||||
|
||||
class CstSimplificationRule2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1_1")),
|
||||
AstConstant("c_2_1")),
|
||||
AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1_2")),
|
||||
AstConstant("c_2_2")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstConstant("c_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_cst(candidate["c_2_1"].mop, candidate["c_2_2"].mop):
|
||||
return False
|
||||
c_res = ((candidate["c_1_1"].value ^ candidate["c_1_2"].value) & candidate["c_2_1"].value)
|
||||
c_res ^= candidate["c_1_2"].value
|
||||
candidate.add_constant_leaf("c_res", c_res, candidate["c_1_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_0")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("c_1"),
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_mul,
|
||||
AstConstant("c_coeff"),
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("c_sub"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
c_coeff = candidate["c_1"].value + 1
|
||||
c_sub = (candidate["c_1"].value * candidate["c_2"].value) + candidate["c_0"].value
|
||||
candidate.add_constant_leaf("c_coeff", c_coeff, candidate["c_1"].size)
|
||||
candidate.add_constant_leaf("c_sub", c_sub, candidate["c_2"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_sub,
|
||||
AstConstant("c_1"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_1"),
|
||||
AstConstant("c_res")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
c_res = SUB_TABLE[candidate["c_1"].size] - candidate["c_1"].value
|
||||
candidate.add_constant_leaf("c_res", c_res, candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule5(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_1"),
|
||||
AstConstant("c_2")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstConstant("c_1")),
|
||||
AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
return equal_bnot_cst(candidate["c_1"].mop, candidate["c_2"].mop)
|
||||
|
||||
|
||||
class CstSimplificationRule6(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2")),
|
||||
AstConstant("c_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
c_res = candidate["c_1"].value & candidate["c_2"].value
|
||||
candidate.add_constant_leaf("c_res", c_res, candidate["c_2"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule7(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_shr,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstNode(m_shr,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2")),
|
||||
AstConstant("c_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
c_res = candidate["c_1"].value >> candidate["c_2"].value
|
||||
candidate.add_constant_leaf("c_res", c_res, candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule8(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_res")),
|
||||
AstConstant("c_2"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
c_res = candidate["c_1"].value & ~candidate["c_2"].value
|
||||
if c_res == candidate["c_1"].value:
|
||||
return False
|
||||
candidate.add_constant_leaf("c_res", c_res, candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule9(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_and")),
|
||||
AstConstant("c_xor"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
c_and = (AND_TABLE[candidate["c_1"].size] ^ candidate["c_1"].value) & candidate["c_2"].value
|
||||
c_xor = candidate["c_1"].value & candidate["c_2"].value
|
||||
candidate.add_constant_leaf("c_and", c_and, candidate["x_0"].size)
|
||||
candidate.add_constant_leaf("c_xor", c_xor, candidate["x_0"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule10(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_and")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (candidate["c_1"].value & candidate["c_2"].value) != candidate["c_1"].value:
|
||||
return False
|
||||
c_and = (AND_TABLE[candidate["c_1"].size] ^ candidate["c_1"].value) & candidate["c_2"].value
|
||||
candidate.add_constant_leaf("c_and", c_and, candidate["x_0"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule11(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_xor,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("c_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1_bnot")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_and")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
c_1_bnot = (AND_TABLE[candidate["c_1"].size] ^ candidate["c_1"].value)
|
||||
c_and = c_1_bnot & candidate["c_2"].value
|
||||
candidate.add_constant_leaf("c_1_bnot", c_1_bnot, candidate["c_1"].size)
|
||||
candidate.add_constant_leaf("c_and", c_and, candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule12(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_sub,
|
||||
AstConstant("c_1"),
|
||||
AstLeaf("x_0")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("c_2"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_xor,
|
||||
AstNode(m_bnot, AstLeaf("x_0")),
|
||||
AstConstant("c_2")),
|
||||
AstConstant("c_diff"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
c_diff = candidate["c_2"].value - candidate["c_1"].value
|
||||
candidate.add_constant_leaf("c_diff", c_diff, candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule13(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstConstant("cst_1"),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))),
|
||||
AstLeaf("x_1"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("cst_1")),
|
||||
AstNode(m_and, AstLeaf("x_1"),
|
||||
AstConstant("not_cst_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("not_cst_1", ~candidate["cst_1"].value, candidate["cst_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule14(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("lnot_c_1")),
|
||||
AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
lnot_c_1_value = candidate["c_1"].value ^ AND_TABLE[candidate["c_1"].size]
|
||||
tmp = lnot_c_1_value ^ candidate["c_2"].value
|
||||
if tmp != 1:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_1", 1, candidate["c_2"].size)
|
||||
candidate.add_constant_leaf("lnot_c_1", lnot_c_1_value, candidate["c_1"].size)
|
||||
|
||||
|
||||
class CstSimplificationRule15(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_shr,
|
||||
AstNode(m_shr,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_shr, AstLeaf("x_0"), AstConstant("c_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("c_res", candidate["c_1"].value + candidate["c_2"].value, candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule16(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_c_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("bnot_c_1", candidate["c_1"].value ^ AND_TABLE[candidate["c_1"].size],
|
||||
candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule17(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and,
|
||||
AstNode(m_bnot, AstLeaf("x_0")),
|
||||
AstLeaf("bnot_c_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("bnot_c_1", candidate["c_1"].value ^ AND_TABLE[candidate["c_1"].size],
|
||||
candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule18(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or,
|
||||
AstNode(m_bnot, AstLeaf("x_0")),
|
||||
AstLeaf("bnot_c_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("bnot_c_1", candidate["c_1"].value ^ AND_TABLE[candidate["c_1"].size],
|
||||
candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class CstSimplificationRule19(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sar,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_and, AstNode(m_shr, AstLeaf("x_0"), AstConstant("c_2")), AstConstant("c_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("c_res", candidate["c_1"].value >> candidate["c_2"].value,
|
||||
candidate["c_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
# Found sometimes with OLLVM
|
||||
class CstSimplificationRule20(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf('bnot_x_0'),
|
||||
AstConstant('c_and_1')),
|
||||
AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('c_and_2')),
|
||||
AstConstant('c_xor')))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_and_res")),
|
||||
AstConstant("c_xor_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if candidate["c_and_1"].value & candidate["c_and_2"].value != 0:
|
||||
return False
|
||||
candidate.add_constant_leaf("c_and_res", candidate["c_and_1"].value ^ candidate["c_and_2"].value,
|
||||
candidate["c_and_1"].size)
|
||||
candidate.add_constant_leaf("c_xor_res", candidate["c_and_1"].value ^ candidate["c_xor"].value,
|
||||
candidate["c_and_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
# Found sometimes with OLLVM
|
||||
class CstSimplificationRule21(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('c_and')),
|
||||
AstConstant('c_xor_1')),
|
||||
AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('bnot_c_and')),
|
||||
AstConstant('c_xor_2')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstConstant("c_xor_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_cst(candidate["c_and"].mop, candidate["bnot_c_and"].mop):
|
||||
return False
|
||||
if candidate["c_xor_1"].mop.nnn.value & candidate["c_xor_2"].mop.nnn.value != 0:
|
||||
return False
|
||||
candidate.add_constant_leaf("c_xor_res", candidate["c_xor_1"].value ^ candidate["c_xor_2"].value,
|
||||
candidate["c_xor_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
# Found sometimes with OLLVM
|
||||
class CstSimplificationRule22(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('c_and')),
|
||||
AstConstant('c_xor_1')),
|
||||
AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf('bnot_x_0'),
|
||||
AstConstant('bnot_c_and')),
|
||||
AstConstant('c_xor_2')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstConstant("c_xor_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_cst(candidate["c_and"].mop, candidate["bnot_c_and"].mop):
|
||||
return False
|
||||
if candidate["c_xor_1"].mop.nnn.value & candidate["c_xor_2"].mop.nnn.value != 0:
|
||||
return False
|
||||
if candidate["c_xor_1"].mop.nnn.value & candidate["bnot_c_and"].mop.nnn.value != 0:
|
||||
return False
|
||||
candidate.add_constant_leaf("c_xor_res", candidate["c_xor_1"].value ^ candidate["c_xor_2"].value ^ candidate["bnot_c_and"].value,
|
||||
candidate["c_xor_1"].size)
|
||||
return True
|
||||
50
d810/optimizers/instructions/pattern_matching/rewrite_mov.py
Normal file
50
d810/optimizers/instructions/pattern_matching/rewrite_mov.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_bnot_mop, AND_TABLE
|
||||
|
||||
|
||||
# GetIdentRule1: ((x_0 & x_1) + (x_0 & ~x_1)) == x_0
|
||||
class GetIdentRule1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("x_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# GetIdentRule2: ((x_0 & x_1) ^ (x_0 & ~x_1)) == x_0 i
|
||||
class GetIdentRule2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("x_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class GetIdentRule3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("x_0"))
|
||||
153
d810/optimizers/instructions/pattern_matching/rewrite_mul.py
Normal file
153
d810/optimizers/instructions/pattern_matching/rewrite_mul.py
Normal file
@@ -0,0 +1,153 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_bnot_mop, is_check_mop, SUB_TABLE
|
||||
|
||||
|
||||
class Mul_MbaRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_mul,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))),
|
||||
AstNode(m_mul,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x_1')),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_1'),
|
||||
AstLeaf('bnot_x_0'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mul, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Mul_MbaRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_mul,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('c_1')),
|
||||
AstLeaf('x_0')),
|
||||
AstNode(m_mul,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('bnot_c_1')),
|
||||
AstNode(m_and,
|
||||
AstConstant('c_1'),
|
||||
AstLeaf('bnot_x_0'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mul, AstLeaf("x_0"), AstConstant('c_1'))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not is_check_mop(candidate["x_0"].mop):
|
||||
return False
|
||||
if candidate["c_1"].value & 0x1 != 1:
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["c_1"].mop, candidate["bnot_c_1"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Mul_MbaRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_mul,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('c_1')),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('c_1'))),
|
||||
AstNode(m_mul,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_and,
|
||||
AstConstant('c_1'),
|
||||
AstLeaf('bnot_x_0'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mul, AstLeaf("x_0"), AstConstant('c_1'))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not is_check_mop(candidate["x_0"].mop):
|
||||
return False
|
||||
if candidate["c_1"].value & 0x1 == 1:
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Mul_MbaRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_mul,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))),
|
||||
AstNode(m_mul,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1"))),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mul, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Mul_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_1"),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Mul_FactorRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_neg,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mul,
|
||||
AstConstant("val_fe"),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_fe", SUB_TABLE[candidate.size] - 2, candidate.size)
|
||||
return True
|
||||
126
d810/optimizers/instructions/pattern_matching/rewrite_neg.py
Normal file
126
d810/optimizers/instructions/pattern_matching/rewrite_neg.py
Normal file
@@ -0,0 +1,126 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.hexrays_helpers import AND_TABLE
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
|
||||
|
||||
class Neg_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("1", 1))
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg, AstLeaf("x_0"))
|
||||
|
||||
|
||||
class Neg_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_bnot,
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("1", 1)))
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg, AstLeaf("x_0"))
|
||||
|
||||
|
||||
class NegAdd_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
|
||||
class NegAdd_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_1"),
|
||||
AstLeaf("x_2"))),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_2"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_1"),
|
||||
AstLeaf("x_2"))))
|
||||
|
||||
|
||||
class NegAdd_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_mul,
|
||||
AstConstant('val_fe'),
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (candidate["val_fe"].value + 2) & AND_TABLE[candidate["val_fe"].size] != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
class NegOr_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
|
||||
class NegXor_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
|
||||
class NegXor_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_mul,
|
||||
AstConstant('2', 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
259
d810/optimizers/instructions/pattern_matching/rewrite_or.py
Normal file
259
d810/optimizers/instructions/pattern_matching/rewrite_or.py
Normal file
@@ -0,0 +1,259 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_bnot_mop
|
||||
|
||||
|
||||
class Or_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")),
|
||||
AstLeaf("x_1"))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Or_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Or_HackersDelightRule_2_variant_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_neg, AstLeaf("x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_neg, AstLeaf("x_1")))
|
||||
|
||||
|
||||
class Or_MbaRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Or_MbaRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_add,
|
||||
AstNode(m_add,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstConstant('1', 1)),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_1'),
|
||||
AstLeaf('x_0'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Or_MbaRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Or_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Or_FactorRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_1"),
|
||||
AstLeaf("x_2"))),
|
||||
AstNode(m_xor,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_2")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstNode(m_xor, AstLeaf("x_1"), AstLeaf("x_2")))
|
||||
|
||||
|
||||
class Or_FactorRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("bnot_x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Or_OllvmRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Or_Rule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_0"))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Or_Rule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_1"))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Or_Rule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf('bnot_x_0'),
|
||||
AstLeaf('bnot_x_1'))),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Or_Rule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class OrBnot_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstLeaf("x_1"))
|
||||
|
||||
|
||||
class OrBnot_FactorRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_and,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class OrBnot_FactorRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_or,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstNode(m_bnot, AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class OrBnot_FactorRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_or,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or, AstLeaf("x_0"), AstNode(m_bnot, AstLeaf("x_1")))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
return True
|
||||
@@ -0,0 +1,403 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE, AND_TABLE
|
||||
|
||||
|
||||
# PredSetnzRule1: (x_0 | c_1) != c_2 ==> 1 if c_1 | c_2 != c_2
|
||||
class PredSetnzRule1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setnz,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (candidate["c_1"].value | candidate["c_2"].value) == candidate["c_2"].value:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetnzRule2: (x_0 & c_1) != c_2 ==> 1 if c_1 & c_2 != c_2
|
||||
class PredSetnzRule2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setnz,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (candidate["c_1"].value & candidate["c_2"].value) == candidate["c_2"].value:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetnzRule3: (x_0 | 2) + (x_0 ^ 2) != 0 ==> 1 (because math)
|
||||
class PredSetnzRule3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setnz,
|
||||
AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("2", 2)),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("2", 2))),
|
||||
AstConstant("0", 0))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetnzRule4: (cst_1 - x_0) ^ x_0 != 0 ==> 1 if cst_1 % 2 == 1 (because math)
|
||||
class PredSetnzRule4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setnz,
|
||||
AstNode(m_xor,
|
||||
AstNode(m_sub,
|
||||
AstConstant("cst_1"),
|
||||
AstLeaf("x_0")),
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("0", 0))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (candidate["cst_1"].value % 2) == 0:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetnzRule5: (-(~x_0 & 1)) != x_0 ==> 1 (because math)
|
||||
class PredSetnzRule5(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setnz,
|
||||
AstNode(m_neg,
|
||||
AstNode(m_and,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstConstant("1", 1))),
|
||||
AstLeaf("x_0"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetnzRule6: ((x_0 + c_1) + ((x_0 + c_2) & 1)) != 0 ==> 1 (if (c_2 - c_1) & 1 == 1)
|
||||
class PredSetnzRule6(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setnz,
|
||||
AstNode(m_add,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstNode(m_and,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_2")),
|
||||
AstConstant("1", 1))),
|
||||
AstConstant("0", 0))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (candidate["c_2"].value - candidate["c_1"].value) & 0x1 != 1:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetnzRule8: bnot((3 - x_0)) ^ bnot(x_0) != 0 ==> 1
|
||||
class PredSetnzRule8(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setnz,
|
||||
AstNode(m_xor,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_sub,
|
||||
AstConstant("3", 3),
|
||||
AstLeaf("x_0"))),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0"))),
|
||||
AstConstant("0", 0))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetzRule1: (x_0 | c_1) == c_2 ==> 0 if c_1 | c_2 != c_2
|
||||
class PredSetzRule1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setz,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (candidate["c_1"].value | candidate["c_2"].value) == candidate["c_2"].value:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetzRule2: (x_0 & c_1) == c_2 ==> 0 if c_1 & c_2 != c_2
|
||||
class PredSetzRule2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setz,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if (candidate["c_1"].value & candidate["c_2"].value) == candidate["c_2"].value:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetzRule3: (x_0 | 2) + (x_0 ^ 2) == 0 ==> 0 (because math)
|
||||
class PredSetzRule3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setz,
|
||||
AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("2", 2)),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("2", 2))),
|
||||
AstConstant("0", 0))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# PredSetbRule1: (x_0 & c_1) <u c_2 ==> 0 if c_1 <u c_2
|
||||
class PredSetbRule1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setb,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1")),
|
||||
AstConstant("c_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if candidate["c_1"].value >= candidate["c_2"].value:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class PredOdd1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_mul,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_sub,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('1', 1))),
|
||||
AstConstant('1', 1))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant('val_0'))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class PredOdd2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_mul,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_add,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('1', 1))),
|
||||
AstConstant('1', 1))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant('val_0'))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# Pred0Rule1: (x_0 & ~x_0) ==> 0
|
||||
class Pred0Rule1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# Pred0Rule2: (xdu(x_0 & 1) == 2) ==> 0
|
||||
class Pred0Rule2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_setz,
|
||||
AstNode(m_xdu,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("c_1", 1))),
|
||||
AstConstant("c_2", 2))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class Pred0Rule3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("val_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class Pred0Rule4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("val_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class Pred0Rule5(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("val_0"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_0", 0, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class PredFFRule1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_ff"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_ff", AND_TABLE[candidate.size], candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
# Pred1Rule2: (x_0 ^ x_1) | (~x_0 | x_1) ==> 0xff
|
||||
class PredFFRule2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_or,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_ff"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
candidate.add_constant_leaf("val_ff", AND_TABLE[candidate.size], candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class PredFFRule3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstLeaf("val_ff"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_ff", AND_TABLE[candidate.size], candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class PredFFRule4(PatternMatchingRule):
|
||||
DESCRIPTION = "(x_0 | x_1) | (~(x_0 & x_1)) ==> 0xff"
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_ff"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_ff", AND_TABLE[candidate.size], candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class PredOr2_Rule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_and,
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_mul,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_0'))),
|
||||
AstConstant('3', 3))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstNode(m_bnot, AstLeaf('x_0')),
|
||||
AstConstant('val_1')),
|
||||
AstConstant('val_2'))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate["x_0"].mop.size)
|
||||
candidate.add_constant_leaf("val_2", 2, candidate["x_0"].mop.size)
|
||||
return True
|
||||
|
||||
|
||||
class PredOr1_Rule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_add,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('1', 1)),
|
||||
AstConstant('1', 1)))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_mul,
|
||||
AstConstant('val_2'),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant('val_1')))),
|
||||
AstConstant('val_1'))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate["x_0"].mop.size)
|
||||
candidate.add_constant_leaf("val_2", 2, candidate["x_0"].mop.size)
|
||||
return True
|
||||
177
d810/optimizers/instructions/pattern_matching/rewrite_sub.py
Normal file
177
d810/optimizers/instructions/pattern_matching/rewrite_sub.py
Normal file
@@ -0,0 +1,177 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE
|
||||
|
||||
|
||||
class Sub_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_add,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_1")),
|
||||
AstConstant("1", 1)))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Sub_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstLeaf("x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Sub_HackersDelightRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Sub_HackersDelightRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1"))),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Sub1_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_sub,
|
||||
AstNode(m_neg,
|
||||
AstLeaf('x_0')),
|
||||
AstConstant('1', 1)),
|
||||
AstNode(m_mul,
|
||||
AstConstant('c_minus_2'),
|
||||
AstLeaf('x_0')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if candidate["c_minus_2"].value != SUB_TABLE[candidate["c_minus_2"].size] - 2:
|
||||
return False
|
||||
candidate.add_constant_leaf("val_1", 1, candidate["x_0"].size)
|
||||
return True
|
||||
|
||||
|
||||
class Sub1_FactorRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstLeaf("x_0")),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub, AstLeaf("x_0"), AstConstant("1", 1))
|
||||
|
||||
|
||||
class Sub1Add_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))),
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
candidate.add_constant_leaf("val_1", 1, candidate["x_1"].size)
|
||||
return True
|
||||
|
||||
|
||||
class Sub1And_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")),
|
||||
AstLeaf("x_1"))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
candidate.add_constant_leaf("val_1", 1, candidate["x_0"].size)
|
||||
return True
|
||||
|
||||
|
||||
class Sub1Or_MbaRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstConstant("val_1"))
|
||||
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class Sub1And1_MbaRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf('x_0')),
|
||||
AstConstant("1", 1)),
|
||||
AstLeaf('x_0'))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstConstant("val_1_1")),
|
||||
AstConstant("val_1_2"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1_1", 1, candidate["x_0"].size)
|
||||
candidate.add_constant_leaf("val_1_2", 1, candidate["x_0"].size)
|
||||
return True
|
||||
325
d810/optimizers/instructions/pattern_matching/rewrite_xor.py
Normal file
325
d810/optimizers/instructions/pattern_matching/rewrite_xor.py
Normal file
@@ -0,0 +1,325 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_bnot_mop, SUB_TABLE
|
||||
|
||||
|
||||
class Xor_HackersDelightRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_HackersDelightRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))),
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_HackersDelightRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_HackersDelightRule_4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_sub,
|
||||
AstNode(m_sub,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_mul,
|
||||
AstConstant('2', 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_bnot, AstLeaf('x_1'))))),
|
||||
AstConstant('2', 2))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_HackersDelightRule_5(PatternMatchingRule):
|
||||
FUZZ_PATTERN = False
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_sub,
|
||||
AstNode(m_mul,
|
||||
AstConstant('2', 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))),
|
||||
AstLeaf("x_1")))
|
||||
PATTERNS = [
|
||||
AstNode(m_sub, AstLeaf("x_0"), AstNode(m_sub, AstNode(m_mul, AstConstant('2', 2), AstNode(m_and, AstLeaf("x_1"), AstLeaf("x_0"))), AstLeaf("x_1")))
|
||||
]
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_MbaRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_sub,
|
||||
AstNode(m_mul,
|
||||
AstConstant('2', 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_1'),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))),
|
||||
AstLeaf('x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_MbaRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_sub,
|
||||
AstNode(m_mul,
|
||||
AstConstant('2', 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))),
|
||||
AstLeaf('x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_MbaRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_mul,
|
||||
AstConstant('2', 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_FactorRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Xor_FactorRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf('bnot_x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Xor_FactorRule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_xor,
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_SpecialConstantRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_and,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_0")),
|
||||
AstLeaf("x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
|
||||
class Xor_SpecialConstantRule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_add,
|
||||
AstNode(m_mul,
|
||||
AstConstant('0xfe'),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1'))),
|
||||
AstLeaf('x_1')))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf("x_0"), AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
return candidate["0xfe"].value == SUB_TABLE[candidate["0xfe"].size] - 2
|
||||
|
||||
|
||||
class Xor1_MbaRule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_bnot,
|
||||
AstLeaf('x_0')),
|
||||
AstNode(m_or,
|
||||
AstNode(m_mul,
|
||||
AstConstant('2', 2),
|
||||
AstLeaf('x_0')),
|
||||
AstConstant('2', 2)))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf('x_0'), AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class Xor_Rule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_bnot,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_bnot, AstLeaf("x_1")))
|
||||
|
||||
|
||||
# Found sometimes with OLLVM
|
||||
class Xor_Rule_2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_2')),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_1'),
|
||||
AstLeaf('bnot_x2'))),
|
||||
AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x2')),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_1'),
|
||||
AstLeaf('x_2'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstLeaf('x_0'), AstLeaf('x_1'))
|
||||
|
||||
|
||||
# Found sometimes with OLLVM
|
||||
class Xor_Rule_3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_or,
|
||||
AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_2')),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_1'),
|
||||
AstLeaf('x_2'))),
|
||||
AstNode(m_and,
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('bnot_x2')),
|
||||
AstNode(m_xor,
|
||||
AstLeaf('x_1'),
|
||||
AstLeaf('bnot_x2'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor, AstNode(m_bnot, AstLeaf('x_0')), AstLeaf('x_1'))
|
||||
|
||||
|
||||
class XorAlmost_Rule_1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_sub,
|
||||
AstLeaf("x_1"),
|
||||
AstConstant("1", 1)))))
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_add,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_neg, AstLeaf("x_1"))),
|
||||
AstLeaf("val_2"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_2", 2, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class Xor_NestedStuff(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstNode(m_add,
|
||||
AstLeaf('x_9'),
|
||||
AstLeaf('x_10')),
|
||||
AstLeaf("x_11")),
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_14"),
|
||||
AstNode(m_mul,
|
||||
AstConstant('2', 2),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_10'),
|
||||
AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf('x_9'),
|
||||
AstLeaf("x_11")),
|
||||
AstLeaf("x_14"))))))
|
||||
|
||||
|
||||
REPLACEMENT_PATTERN = AstNode(m_xor,
|
||||
AstLeaf("x_10"),
|
||||
AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstLeaf('x_9'),
|
||||
AstLeaf("x_11")),
|
||||
AstLeaf("x_14")))
|
||||
FUZZ_PATTERN = False
|
||||
|
||||
117
d810/optimizers/instructions/pattern_matching/weird.py
Normal file
117
d810/optimizers/instructions/pattern_matching/weird.py
Normal file
@@ -0,0 +1,117 @@
|
||||
from ida_hexrays import *
|
||||
from d810.optimizers.instructions.pattern_matching.handler import PatternMatchingRule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.hexrays_helpers import equal_bnot_mop
|
||||
|
||||
|
||||
class WeirdRule1(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot, AstLeaf("x_1"))),
|
||||
AstConstant("val_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
candidate.add_constant_leaf("val_1", 1, candidate.size)
|
||||
return True
|
||||
|
||||
|
||||
class WeirdRule2(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstLeaf("x_0")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot, AstLeaf("x_1"))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
|
||||
|
||||
class WeirdRule3(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_bnot, AstLeaf("x_1"))),
|
||||
AstNode(m_mul,
|
||||
AstConstant("2", 2),
|
||||
AstLeaf("x_0")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_neg,
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))))
|
||||
|
||||
|
||||
class WeirdRule4(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("bnot_x_1")),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")))
|
||||
REPLACEMENT_PATTERN = AstNode(m_sub,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf("x_1"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class WeirdRule5(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_sub,
|
||||
AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstLeaf("bnot_x_0"),
|
||||
AstNode(m_and,
|
||||
AstLeaf("bnot_x_1"),
|
||||
AstLeaf("x_2"))),
|
||||
AstNode(m_add,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_and,
|
||||
AstLeaf("x_1"),
|
||||
AstLeaf("x_2")))),
|
||||
AstLeaf("x_2"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_or,
|
||||
AstLeaf("x_0"),
|
||||
AstNode(m_or,
|
||||
AstLeaf("x_1"),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf("x_2"))))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if not equal_bnot_mop(candidate["x_0"].mop, candidate["bnot_x_0"].mop):
|
||||
return False
|
||||
if not equal_bnot_mop(candidate["x_1"].mop, candidate["bnot_x_1"].mop):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class WeirdRule6(PatternMatchingRule):
|
||||
PATTERN = AstNode(m_add,
|
||||
AstNode(m_or,
|
||||
AstLeaf('x_0'),
|
||||
AstLeaf('x_1')),
|
||||
AstNode(m_and,
|
||||
AstLeaf('x_0'),
|
||||
AstNode(m_bnot,
|
||||
AstLeaf('x_1'))))
|
||||
REPLACEMENT_PATTERN = AstNode(m_add,
|
||||
AstNode(m_xor,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1")),
|
||||
AstLeaf('x_0'))
|
||||
7
d810/optimizers/instructions/z3/__init__.py
Normal file
7
d810/optimizers/instructions/z3/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from d810.utils import get_all_subclasses
|
||||
from d810.optimizers.instructions.z3.handler import Z3Rule, Z3Optimizer
|
||||
from d810.optimizers.instructions.z3.cst import *
|
||||
from d810.optimizers.instructions.z3.predicates import *
|
||||
|
||||
|
||||
Z3_RULES = [x() for x in get_all_subclasses(Z3Rule)]
|
||||
51
d810/optimizers/instructions/z3/cst.py
Normal file
51
d810/optimizers/instructions/z3/cst.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from ida_hexrays import *
|
||||
from d810.optimizers.instructions.z3.handler import Z3Rule
|
||||
from d810.ast import AstConstant, AstNode
|
||||
from d810.ast import minsn_to_ast
|
||||
from d810.errors import AstEvaluationException
|
||||
from d810.z3_utils import z3_check_mop_equality
|
||||
|
||||
|
||||
class Z3ConstantOptimization(Z3Rule):
|
||||
DESCRIPTION = "Detect and replace obfuscated constants"
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("c_res"))
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.min_nb_opcode = 3
|
||||
self.min_nb_constant = 3
|
||||
|
||||
def configure(self, kwargs):
|
||||
super().configure(kwargs)
|
||||
if "min_nb_opcode" in kwargs.keys():
|
||||
self.min_nb_opcode = kwargs["min_nb_opcode"]
|
||||
if "min_nb_constant" in kwargs.keys():
|
||||
self.min_nb_constant = kwargs["min_nb_constant"]
|
||||
|
||||
def check_and_replace(self, blk, instruction):
|
||||
tmp = minsn_to_ast(instruction)
|
||||
if tmp is None:
|
||||
return None
|
||||
leaf_info_list, cst_leaf_values, opcodes = tmp.get_information()
|
||||
if len(leaf_info_list) == 1 and \
|
||||
len(opcodes) >= self.min_nb_opcode and \
|
||||
(len(cst_leaf_values) >= self.min_nb_constant):
|
||||
try:
|
||||
val_0 = tmp.evaluate_with_leaf_info(leaf_info_list, [0])
|
||||
val_1 = tmp.evaluate_with_leaf_info(leaf_info_list, [0xffffffff])
|
||||
|
||||
if val_0 == val_1:
|
||||
c_res_mop = mop_t()
|
||||
c_res_mop.make_number(val_0, tmp.mop.size)
|
||||
is_ok = z3_check_mop_equality(tmp.mop, c_res_mop)
|
||||
if is_ok:
|
||||
tmp.add_leaf("c_res", c_res_mop)
|
||||
new_instruction = self.get_replacement(tmp)
|
||||
return new_instruction
|
||||
return None
|
||||
except ZeroDivisionError:
|
||||
pass
|
||||
except AstEvaluationException as e:
|
||||
print("Error while evaluating {0}: {1}".format(tmp, e))
|
||||
pass
|
||||
return None
|
||||
9
d810/optimizers/instructions/z3/handler.py
Normal file
9
d810/optimizers/instructions/z3/handler.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer
|
||||
|
||||
|
||||
class Z3Rule(GenericPatternRule):
|
||||
pass
|
||||
|
||||
|
||||
class Z3Optimizer(InstructionOptimizer):
|
||||
RULE_CLASSES = [Z3Rule]
|
||||
78
d810/optimizers/instructions/z3/predicates.py
Normal file
78
d810/optimizers/instructions/z3/predicates.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.optimizers.instructions.z3.handler import Z3Rule
|
||||
from d810.ast import AstLeaf, AstConstant, AstNode
|
||||
from d810.z3_utils import z3_check_mop_equality, z3_check_mop_inequality
|
||||
|
||||
|
||||
class Z3setzRuleGeneric(Z3Rule):
|
||||
DESCRIPTION = "Check with Z3 if a m_setz check is always True or False"
|
||||
PATTERN = AstNode(m_setz,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if z3_check_mop_equality(candidate["x_0"].mop, candidate["x_1"].mop):
|
||||
candidate.add_constant_leaf("val_res", 1, candidate.size)
|
||||
return True
|
||||
if z3_check_mop_inequality(candidate["x_0"].mop, candidate["x_1"].mop):
|
||||
candidate.add_constant_leaf("val_res", 0, candidate.size)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Z3setnzRuleGeneric(Z3Rule):
|
||||
DESCRIPTION = "Check with Z3 if a m_setnz check is always True or False"
|
||||
PATTERN = AstNode(m_setnz,
|
||||
AstLeaf("x_0"),
|
||||
AstLeaf("x_1"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
if z3_check_mop_equality(candidate["x_0"].mop, candidate["x_1"].mop):
|
||||
candidate.add_constant_leaf("val_res", 0, candidate.size)
|
||||
return True
|
||||
if z3_check_mop_inequality(candidate["x_0"].mop, candidate["x_1"].mop):
|
||||
candidate.add_constant_leaf("val_res", 1, candidate.size)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Z3lnotRuleGeneric(Z3Rule):
|
||||
DESCRIPTION = "Check with Z3 if a m_lnot check is always True or False"
|
||||
PATTERN = AstNode(m_lnot,
|
||||
AstLeaf("x_0"))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
val_0_mop = mop_t()
|
||||
val_0_mop.make_number(0, candidate["x_0"].size)
|
||||
if z3_check_mop_equality(candidate["x_0"].mop, val_0_mop):
|
||||
candidate.add_constant_leaf("val_res", 1, candidate.size)
|
||||
return True
|
||||
if z3_check_mop_inequality(candidate["x_0"].mop, val_0_mop):
|
||||
candidate.add_constant_leaf("val_res", 0, candidate.size)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Z3SmodRuleGeneric(Z3Rule):
|
||||
DESCRIPTION = "Check with Z3 if a m_setz check is always True or False"
|
||||
PATTERN = AstNode(m_smod,
|
||||
AstLeaf("x_0"),
|
||||
AstConstant("2", 2))
|
||||
REPLACEMENT_PATTERN = AstNode(m_mov, AstConstant("val_res"))
|
||||
|
||||
def check_candidate(self, candidate):
|
||||
cst_0_mop = mop_t()
|
||||
cst_0_mop.make_number(0, candidate.size)
|
||||
if z3_check_mop_equality(candidate.mop, cst_0_mop):
|
||||
candidate.add_leaf("val_res", cst_0_mop)
|
||||
return True
|
||||
cst_1_mop = mop_t()
|
||||
cst_1_mop.make_number(1, candidate.size)
|
||||
if z3_check_mop_equality(candidate.mop, cst_1_mop):
|
||||
candidate.add_leaf("val_res", cst_1_mop)
|
||||
return True
|
||||
return False
|
||||
477
d810/tracker.py
Normal file
477
d810/tracker.py
Normal file
@@ -0,0 +1,477 @@
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from typing import List, Union, Tuple, Dict
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.emulator import MicroCodeEnvironment, MicroCodeInterpreter
|
||||
from d810.cfg_utils import change_1way_block_successor, change_2way_block_conditional_successor, duplicate_block
|
||||
from d810.hexrays_hooks import InstructionDefUseCollector
|
||||
from d810.hexrays_helpers import equal_mops_ignore_size, get_mop_index, get_blk_index
|
||||
from d810.hexrays_formatters import format_minsn_t, format_mop_t
|
||||
|
||||
# This module can be use to find the instruction that define the value of a mop. Basically, you:
|
||||
# 1 - Create a MopTracker object with the list of mops to search
|
||||
# 2 - Call search_backward while specifying the instruction where the search should start
|
||||
# It will return a list if MopHistory, each MopHistory object of this list:
|
||||
# * Represents one possible path to compute the searched mops
|
||||
# * Stores all instructions used to compute the searched mops
|
||||
#
|
||||
# You can get the value of one of the searched mop by calling the get_mop_constant_value API of a MopHistory object.
|
||||
# Behind the scene, it will emulate all microcode instructions on the MopHistory path.
|
||||
#
|
||||
# Finally the duplicate_histories API can be used to duplicate microcode blocks so that for each microcode block,
|
||||
# the searched mops have only one possible values. For instance, this is a preliminary step used in code unflattening.
|
||||
|
||||
|
||||
logger = logging.getLogger('D810.tracker')
|
||||
|
||||
|
||||
class BlockInfo(object):
|
||||
def __init__(self, blk: mblock_t, ins=None):
|
||||
self.blk = blk
|
||||
self.ins_list = []
|
||||
if ins is not None:
|
||||
self.ins_list.append(ins)
|
||||
|
||||
def get_copy(self) -> BlockInfo:
|
||||
new_block_info = BlockInfo(self.blk)
|
||||
new_block_info.ins_list = [x for x in self.ins_list]
|
||||
return new_block_info
|
||||
|
||||
|
||||
class MopHistory(object):
|
||||
def __init__(self, searched_mop_list: List[mop_t]):
|
||||
self.searched_mop_list = [mop_t(x) for x in searched_mop_list]
|
||||
self.history = []
|
||||
self.unresolved_mop_list = []
|
||||
|
||||
self._mc_interpreter = MicroCodeInterpreter()
|
||||
self._mc_initial_environment = MicroCodeEnvironment()
|
||||
self._mc_current_environment = self._mc_initial_environment.get_copy()
|
||||
self._is_dirty = True
|
||||
|
||||
def add_mop_initial_value(self, mop: mop_t, value: int):
|
||||
self._is_dirty = True
|
||||
self._mc_initial_environment.define(mop, value)
|
||||
|
||||
def get_copy(self) -> MopHistory:
|
||||
new_mop_history = MopHistory(self.searched_mop_list)
|
||||
new_mop_history.history = [x.get_copy() for x in self.history]
|
||||
new_mop_history.unresolved_mop_list = [x for x in self.unresolved_mop_list]
|
||||
new_mop_history._mc_initial_environment = self._mc_initial_environment.get_copy()
|
||||
new_mop_history._mc_current_environment = new_mop_history._mc_initial_environment.get_copy()
|
||||
return new_mop_history
|
||||
|
||||
def is_resolved(self) -> bool:
|
||||
if len(self.unresolved_mop_list) == 0:
|
||||
return True
|
||||
for x in self.unresolved_mop_list:
|
||||
x_value = self._mc_initial_environment.lookup(x, raise_exception=False)
|
||||
if x_value is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def block_path(self) -> List[mblock_t]:
|
||||
return [blk_info.blk for blk_info in self.history]
|
||||
|
||||
@property
|
||||
def block_serial_path(self) -> List[int]:
|
||||
return [blk.serial for blk in self.block_path]
|
||||
|
||||
def replace_block_in_path(self, old_blk: mblock_t, new_blk: mblock_t) -> bool:
|
||||
blk_index = get_blk_index(old_blk, self.block_path)
|
||||
if blk_index > 0:
|
||||
self.history[blk_index].blk = new_blk
|
||||
self._is_dirty = True
|
||||
return True
|
||||
else:
|
||||
logger.error("replace_block_in_path: should not happen")
|
||||
return False
|
||||
|
||||
def insert_block_in_path(self, blk: mblock_t, where_index: int):
|
||||
self.history = self.history[:where_index] + [BlockInfo(blk)] + self.history[where_index:]
|
||||
self._is_dirty = True
|
||||
|
||||
def insert_ins_in_block(self, blk: mblock_t, ins: minsn_t, before=True):
|
||||
blk_index = get_blk_index(blk, self.block_path)
|
||||
if blk_index < 0:
|
||||
return False
|
||||
blk_info = self.history[blk_index]
|
||||
if before:
|
||||
blk_info.ins_list = [ins] + blk_info.ins_list
|
||||
else:
|
||||
blk_info.ins_list = blk_info.ins_list + [ins]
|
||||
self._is_dirty = True
|
||||
|
||||
def _execute_microcode(self) -> bool:
|
||||
if not self._is_dirty:
|
||||
return True
|
||||
formatted_mop_searched_list = "['" + "', '".join([format_mop_t(x) for x in self.searched_mop_list]) + "']"
|
||||
logger.debug("Computing: {0} for path {1}".format(formatted_mop_searched_list, self.block_serial_path))
|
||||
self._mc_current_environment = self._mc_initial_environment.get_copy()
|
||||
for blk_info in self.history:
|
||||
for blk_ins in blk_info.ins_list:
|
||||
logger.debug("Executing: {0}.{1}".format(blk_info.blk.serial, format_minsn_t(blk_ins)))
|
||||
if not self._mc_interpreter.eval_instruction(blk_info.blk, blk_ins, self._mc_current_environment):
|
||||
self._is_dirty = False
|
||||
return False
|
||||
self._is_dirty = False
|
||||
return True
|
||||
|
||||
def get_mop_constant_value(self, searched_mop: mop_t) -> Union[None, int]:
|
||||
if not self._execute_microcode():
|
||||
return None
|
||||
return self._mc_interpreter.eval_mop(searched_mop, self._mc_current_environment)
|
||||
|
||||
def print_info(self, detailed_info=False):
|
||||
formatted_mop_searched_list = [format_mop_t(x) for x in self.searched_mop_list]
|
||||
tmp = ", ".join(["{0}={1}".format(formatted_mop, self.get_mop_constant_value(mop))
|
||||
for formatted_mop, mop in zip(formatted_mop_searched_list, self.searched_mop_list)])
|
||||
logger.info("MopHistory: resolved={0}, path={1}, mops={2}"
|
||||
.format(self.is_resolved(), self.block_serial_path, tmp))
|
||||
if detailed_info:
|
||||
str_mop_list = "['" + "', '".join(formatted_mop_searched_list) + "']"
|
||||
if len(self.block_path) == 0:
|
||||
logger.info("MopHistory for {0} => nothing".format(str_mop_list))
|
||||
return
|
||||
|
||||
end_blk = self.block_path[-1]
|
||||
end_ins = end_blk.tail
|
||||
if self.history[-1].ins_list:
|
||||
end_ins = self.history[-1].ins_list[-1]
|
||||
|
||||
if end_ins:
|
||||
logger.info("MopHistory for {0} {1}.{2}".format(str_mop_list, end_blk.serial, format_minsn_t(end_ins)))
|
||||
else:
|
||||
logger.info("MopHistory for '{0}' {1}.tail".format(str_mop_list, end_blk.serial))
|
||||
logger.info(" path {0}".format(self.block_serial_path))
|
||||
for blk_info in self.history:
|
||||
for blk_ins in blk_info.ins_list:
|
||||
logger.info(" {0}.{1}".format(blk_info.blk.serial, format_minsn_t(blk_ins)))
|
||||
|
||||
|
||||
def get_standard_and_memory_mop_lists(mop_in: mop_t) -> Tuple[List[mop_t], List[mop_t]]:
|
||||
if mop_in.t in [mop_r, mop_S]:
|
||||
return [mop_in], []
|
||||
elif mop_in.t == mop_v:
|
||||
return [], [mop_in]
|
||||
elif mop_in.t == mop_d:
|
||||
ins_mop_info = InstructionDefUseCollector()
|
||||
mop_in.d.for_all_ops(ins_mop_info)
|
||||
return remove_segment_registers(ins_mop_info.unresolved_ins_mops), ins_mop_info.memory_unresolved_ins_mops
|
||||
else:
|
||||
logger.warning("Calling get_standard_and_memory_mop_lists with unsupported mop type {0}: '{1}'"
|
||||
.format(mop_in.t, format_mop_t(mop_in)))
|
||||
return [], []
|
||||
|
||||
|
||||
# A MopTracker will create new MopTracker to recursively track variable when multiple paths are possible,
|
||||
# The cur_mop_tracker_nb_path global variable is used to limit the number of MopTracker created
|
||||
cur_mop_tracker_nb_path = 0
|
||||
|
||||
|
||||
class MopTracker(object):
|
||||
def __init__(self, searched_mop_list: List[mop_t], max_nb_block=-1, max_path=-1):
|
||||
self.mba = None
|
||||
self._unresolved_mops = []
|
||||
self._memory_unresolved_mops = []
|
||||
for searched_mop in searched_mop_list:
|
||||
a, b = get_standard_and_memory_mop_lists(searched_mop)
|
||||
self._unresolved_mops += a
|
||||
self._memory_unresolved_mops += b
|
||||
self.history = MopHistory(searched_mop_list)
|
||||
self.max_nb_block = max_nb_block
|
||||
self.max_path = max_path
|
||||
self.avoid_list = []
|
||||
self.call_detected = False
|
||||
self.constant_mops = []
|
||||
|
||||
@staticmethod
|
||||
def reset():
|
||||
global cur_mop_tracker_nb_path
|
||||
cur_mop_tracker_nb_path = 0
|
||||
|
||||
def add_mop_definition(self, mop: mop_t, cst_value: int):
|
||||
self.constant_mops.append([mop, cst_value])
|
||||
self.history.add_mop_initial_value(mop, cst_value)
|
||||
|
||||
def get_copy(self) -> MopTracker:
|
||||
global cur_mop_tracker_nb_path
|
||||
new_mop_tracker = MopTracker(self._unresolved_mops, self.max_nb_block, self.max_path)
|
||||
new_mop_tracker._memory_unresolved_mops = [x for x in self._memory_unresolved_mops]
|
||||
new_mop_tracker.constant_mops = [[x[0], x[1]] for x in self.constant_mops]
|
||||
new_mop_tracker.history = self.history.get_copy()
|
||||
cur_mop_tracker_nb_path += 1
|
||||
return new_mop_tracker
|
||||
|
||||
def search_backward(self, blk: mblock_t, ins: minsn_t, avoid_list=None, must_use_pred=None,
|
||||
stop_at_first_duplication=False) -> List[MopHistory]:
|
||||
logger.debug("Searching backward (reg): {0}".format([format_mop_t(x) for x in self._unresolved_mops]))
|
||||
logger.debug("Searching backward (mem): {0}".format([format_mop_t(x) for x in self._memory_unresolved_mops]))
|
||||
logger.debug("Searching backward (cst): {0}"
|
||||
.format(["{0}: {1:x}".format(format_mop_t(x[0]), x[1]) for x in self.constant_mops]))
|
||||
self.mba = blk.mba
|
||||
self.avoid_list = avoid_list if avoid_list else []
|
||||
blk_with_multiple_pred = self.search_until_multiple_predecessor(blk, ins)
|
||||
if self.is_resolved():
|
||||
logger.debug("MopTracker is resolved: {0}".format(self.history.block_serial_path))
|
||||
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
|
||||
return [self.history]
|
||||
elif blk_with_multiple_pred is None:
|
||||
logger.debug("MopTracker unresolved: (blk_with_multiple_pred): {0}".format(self.history.block_serial_path))
|
||||
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
|
||||
return [self.history]
|
||||
elif self.max_nb_block != -1 and len(self.history.block_serial_path) > self.max_nb_block:
|
||||
logger.debug("MopTracker unresolved: (max_nb_block): {0}".format(self.history.block_serial_path))
|
||||
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
|
||||
return [self.history]
|
||||
elif self.max_path != -1 and cur_mop_tracker_nb_path > self.max_path:
|
||||
logger.debug("MopTracker unresolved: (max_path: {0}".format(cur_mop_tracker_nb_path))
|
||||
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
|
||||
return [self.history]
|
||||
elif self.call_detected:
|
||||
logger.debug("MopTracker unresolved: (call): {0}".format(self.history.block_serial_path))
|
||||
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
|
||||
return [self.history]
|
||||
|
||||
if stop_at_first_duplication:
|
||||
self.history.unresolved_mop_list = [x for x in self._unresolved_mops]
|
||||
return [self.history]
|
||||
logger.debug("MopTracker creating child because multiple pred: {0}".format(self.history.block_serial_path))
|
||||
possible_histories = []
|
||||
if must_use_pred is not None and must_use_pred.serial in blk_with_multiple_pred.predset:
|
||||
new_tracker = self.get_copy()
|
||||
possible_histories += new_tracker.search_backward(must_use_pred, None, self.avoid_list, must_use_pred)
|
||||
else:
|
||||
for blk_pred_serial in blk_with_multiple_pred.predset:
|
||||
new_tracker = self.get_copy()
|
||||
possible_histories += new_tracker.search_backward(self.mba.get_mblock(blk_pred_serial), None,
|
||||
self.avoid_list, must_use_pred)
|
||||
return possible_histories
|
||||
|
||||
def search_until_multiple_predecessor(self, blk: mblock_t, ins: Union[None, minsn_t] = None) -> Union[None, mblock_t]:
|
||||
# By default, we start searching from block tail
|
||||
cur_ins = ins if ins else blk.tail
|
||||
cur_blk = blk
|
||||
|
||||
while not self.is_resolved():
|
||||
# Explore one block
|
||||
if cur_blk.serial in self.history.block_serial_path:
|
||||
self.history.insert_block_in_path(cur_blk, 0)
|
||||
return None
|
||||
if cur_blk.serial in self.avoid_list:
|
||||
self.history.insert_block_in_path(cur_blk, 0)
|
||||
return None
|
||||
self.history.insert_block_in_path(cur_blk, 0)
|
||||
cur_ins = self.blk_find_def_backward(cur_blk, cur_ins)
|
||||
while cur_ins:
|
||||
cur_ins = self.blk_find_def_backward(cur_blk, cur_ins)
|
||||
if cur_blk.npred() > 1:
|
||||
return cur_blk
|
||||
elif cur_blk.npred() == 0:
|
||||
return None
|
||||
else:
|
||||
cur_blk = self.mba.get_mblock(cur_blk.predset[0])
|
||||
cur_ins = cur_blk.tail
|
||||
|
||||
# We want to handle cases where the self.is_resolved() is True without doing anything
|
||||
if len(self.history.block_serial_path) == 0:
|
||||
self.history.insert_block_in_path(cur_blk, 0)
|
||||
return None
|
||||
|
||||
def is_resolved(self) -> bool:
|
||||
if (len(self._unresolved_mops) == 0) and (len(self._memory_unresolved_mops) == 0):
|
||||
return True
|
||||
|
||||
for x in self._unresolved_mops:
|
||||
x_index = get_mop_index(x, [y[0] for y in self.constant_mops])
|
||||
if x_index == -1:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _build_ml_list(self, blk: mblock_t) -> Union[None, mlist_t]:
|
||||
ml = mlist_t()
|
||||
for unresolved_mop in self._unresolved_mops:
|
||||
if unresolved_mop.t not in [mop_r, mop_S]:
|
||||
logger.warning("_build_ml_list: Not supported mop type '{0}'".format(unresolved_mop.t))
|
||||
return None
|
||||
blk.append_use_list(ml, unresolved_mop, MUST_ACCESS)
|
||||
return ml
|
||||
|
||||
def blk_find_def_backward(self, blk: mblock_t, ins_start: minsn_t) -> Union[None, minsn_t]:
|
||||
if self.is_resolved():
|
||||
return None
|
||||
ml = self._build_ml_list(blk)
|
||||
if not ml:
|
||||
logger.warning("blk_find_def_backward: _build_ml_list failed")
|
||||
return None
|
||||
ins_def = self._blk_find_ins_def_backward(blk, ins_start, ml)
|
||||
if ins_def:
|
||||
is_ok = self.update_history(blk, ins_def)
|
||||
if not is_ok:
|
||||
return None
|
||||
ins_def = ins_def.prev
|
||||
return ins_def
|
||||
|
||||
def update_history(self, blk: mblock_t, ins_def: minsn_t) -> bool:
|
||||
logger.debug("Updating history with {0}.{1}".format(blk.serial, format_minsn_t(ins_def)))
|
||||
self.history.insert_ins_in_block(blk, ins_def, before=True)
|
||||
if ins_def.opcode == m_call:
|
||||
self.call_detected = True
|
||||
return False
|
||||
ins_mop_info = InstructionDefUseCollector()
|
||||
ins_def.for_all_ops(ins_mop_info)
|
||||
|
||||
for target_mop in ins_mop_info.target_mops:
|
||||
resolved_mop_index = get_mop_index(target_mop, self._unresolved_mops)
|
||||
if resolved_mop_index != -1:
|
||||
logger.debug("Removing {0} from unresolved mop".format(format_mop_t(target_mop)))
|
||||
self._unresolved_mops.pop(resolved_mop_index)
|
||||
cleaned_unresolved_ins_mops = remove_segment_registers(ins_mop_info.unresolved_ins_mops)
|
||||
for ins_def_mop in cleaned_unresolved_ins_mops:
|
||||
ins_def_mop_index = get_mop_index(ins_def_mop, self._unresolved_mops)
|
||||
if ins_def_mop_index == -1:
|
||||
logger.debug("Adding {0} in unresolved mop".format(format_mop_t(ins_def_mop)))
|
||||
self._unresolved_mops.append(ins_def_mop)
|
||||
|
||||
for target_mop in ins_mop_info.target_mops:
|
||||
resolved_mop_index = get_mop_index(target_mop, self._memory_unresolved_mops)
|
||||
if resolved_mop_index != -1:
|
||||
logger.debug("Removing {0} from memory unresolved mop".format(format_mop_t(target_mop)))
|
||||
self._memory_unresolved_mops.pop(resolved_mop_index)
|
||||
for ins_def_mem_mop in ins_mop_info.memory_unresolved_ins_mops:
|
||||
ins_def_mop_index = get_mop_index(ins_def_mem_mop, self._memory_unresolved_mops)
|
||||
if ins_def_mop_index == -1:
|
||||
logger.debug("Adding {0} in memory unresolved mop".format(format_mop_t(ins_def_mem_mop)))
|
||||
self._memory_unresolved_mops.append(ins_def_mem_mop)
|
||||
return True
|
||||
|
||||
def _blk_find_ins_def_backward(self, blk: mblock_t, ins_start: minsn_t, ml: mlist_t) -> Union[None, minsn_t]:
|
||||
cur_ins = ins_start
|
||||
while cur_ins is not None:
|
||||
def_list = blk.build_def_list(cur_ins, MAY_ACCESS | FULL_XDSU)
|
||||
if ml.has_common(def_list):
|
||||
return cur_ins
|
||||
for mem_mop in self._memory_unresolved_mops:
|
||||
if equal_mops_ignore_size(cur_ins.d, mem_mop):
|
||||
return cur_ins
|
||||
cur_ins = cur_ins.prev
|
||||
return None
|
||||
|
||||
|
||||
def get_block_with_multiple_predecessors(var_histories: List[MopHistory]) -> Tuple[Union[None, mblock_t],
|
||||
Union[None, Dict[int, List[MopHistory]]]]:
|
||||
for i, var_history in enumerate(var_histories):
|
||||
pred_blk = var_history.block_path[0]
|
||||
for block in var_history.block_path[1:]:
|
||||
tmp_dict = {pred_blk.serial: [var_history]}
|
||||
for j in range(i + 1, len(var_histories)):
|
||||
blk_index = get_blk_index(block, var_histories[j].block_path)
|
||||
if (blk_index - 1) >= 0:
|
||||
other_pred = var_histories[j].block_path[blk_index - 1]
|
||||
if other_pred.serial not in tmp_dict.keys():
|
||||
tmp_dict[other_pred.serial] = []
|
||||
tmp_dict[other_pred.serial].append(var_histories[j])
|
||||
if len(tmp_dict) > 1:
|
||||
return block, tmp_dict
|
||||
pred_blk = block
|
||||
return None, None
|
||||
|
||||
|
||||
def try_to_duplicate_one_block(var_histories: List[MopHistory]) -> Tuple[int, int]:
|
||||
nb_duplication = 0
|
||||
nb_change = 0
|
||||
if (len(var_histories) == 0) or (len(var_histories[0].block_path) == 0):
|
||||
return nb_duplication, nb_change
|
||||
mba = var_histories[0].block_path[0].mba
|
||||
block_to_duplicate, pred_dict = get_block_with_multiple_predecessors(var_histories)
|
||||
if block_to_duplicate is None:
|
||||
return nb_duplication, nb_change
|
||||
logger.debug("Block to duplicate found: {0} with {1} successors"
|
||||
.format(block_to_duplicate.serial, block_to_duplicate.nsucc()))
|
||||
i = 0
|
||||
for pred_serial, pred_history_group in pred_dict.items():
|
||||
# We do not duplicate first group
|
||||
if i >= 1:
|
||||
logger.debug(" Before {0}: {1}"
|
||||
.format(pred_serial, [var_history.block_serial_path for var_history in pred_history_group]))
|
||||
pred_block = mba.get_mblock(pred_serial)
|
||||
duplicated_blk_jmp, duplicated_blk_default = duplicate_block(block_to_duplicate)
|
||||
nb_duplication += 1 if duplicated_blk_jmp is not None else 0
|
||||
nb_duplication += 1 if duplicated_blk_default is not None else 0
|
||||
logger.debug(" Making {0} goto {1}".format(pred_block.serial, duplicated_blk_jmp.serial))
|
||||
if (pred_block.tail is None) or (not is_mcode_jcond(pred_block.tail.opcode)):
|
||||
change_1way_block_successor(pred_block, duplicated_blk_jmp.serial)
|
||||
nb_change += 1
|
||||
else:
|
||||
if block_to_duplicate.serial == pred_block.tail.d.b:
|
||||
change_2way_block_conditional_successor(pred_block, duplicated_blk_jmp.serial)
|
||||
nb_change += 1
|
||||
else:
|
||||
logger.warning(" not sure this is suppose to happen")
|
||||
change_1way_block_successor(pred_block.mba.get_mblock(pred_block.serial + 1),
|
||||
duplicated_blk_jmp.serial)
|
||||
nb_change += 1
|
||||
|
||||
block_to_duplicate_default_successor = mba.get_mblock(block_to_duplicate.serial + 1)
|
||||
logger.debug(" Now, we fix var histories...")
|
||||
for var_history in pred_history_group:
|
||||
var_history.replace_block_in_path(block_to_duplicate, duplicated_blk_jmp)
|
||||
if block_to_duplicate.tail is not None and is_mcode_jcond(block_to_duplicate.tail.opcode):
|
||||
index_jump_block = get_blk_index(duplicated_blk_jmp, var_history.block_path)
|
||||
if index_jump_block + 1 < len(var_history.block_path):
|
||||
original_jump_block_successor = var_history.block_path[index_jump_block + 1]
|
||||
if original_jump_block_successor.serial == block_to_duplicate_default_successor.serial:
|
||||
var_history.insert_block_in_path(duplicated_blk_default, index_jump_block + 1)
|
||||
i += 1
|
||||
logger.debug(" After {0}: {1}"
|
||||
.format(pred_serial, [var_history.block_serial_path for var_history in pred_history_group]))
|
||||
for i, var_history in enumerate(var_histories):
|
||||
logger.debug(" internal_pass_end.{0}: {1}".format(i, var_history.block_serial_path))
|
||||
return nb_duplication, nb_change
|
||||
|
||||
|
||||
def duplicate_histories(var_histories: List[MopHistory], max_nb_pass: int = 10) -> Tuple[int, int]:
|
||||
cur_pass = 0
|
||||
total_nb_duplication = 0
|
||||
total_nb_change = 0
|
||||
logger.info("Trying to fix new var_history...")
|
||||
for i, var_history in enumerate(var_histories):
|
||||
logger.info(" start.{0}: {1}".format(i, var_history.block_serial_path))
|
||||
while cur_pass < max_nb_pass:
|
||||
logger.debug("Current path {0}".format(cur_pass))
|
||||
nb_duplication, nb_change = try_to_duplicate_one_block(var_histories)
|
||||
if nb_change == 0 and nb_duplication == 0:
|
||||
break
|
||||
total_nb_duplication += nb_duplication
|
||||
total_nb_change += nb_change
|
||||
cur_pass += 1
|
||||
for i, var_history in enumerate(var_histories):
|
||||
logger.info(" end.{0}: {1}".format(i, var_history.block_serial_path))
|
||||
return total_nb_duplication, total_nb_change
|
||||
|
||||
|
||||
def get_segment_register_indexes(mop_list: List[mop_t]) -> List[int]:
|
||||
# This is a very dirty and probably buggy
|
||||
segment_register_indexes = []
|
||||
for i, mop in enumerate(mop_list):
|
||||
if mop.t == mop_r:
|
||||
formatted_mop = format_mop_t(mop)
|
||||
if formatted_mop in ["ds.2", "cs.2", "es.2", "ss.2"]:
|
||||
segment_register_indexes.append(i)
|
||||
return segment_register_indexes
|
||||
|
||||
|
||||
def remove_segment_registers(mop_list: List[mop_t]) -> List[mop_t]:
|
||||
# TODO: instead of doing that, we should add the segment registers to the (global?) emulation environment
|
||||
segment_register_indexes = get_segment_register_indexes(mop_list)
|
||||
if len(segment_register_indexes) == 0:
|
||||
return mop_list
|
||||
new_mop_list = []
|
||||
for i, mop in enumerate(mop_list):
|
||||
if i in segment_register_indexes:
|
||||
pass
|
||||
else:
|
||||
new_mop_list.append(mop)
|
||||
return new_mop_list
|
||||
69
d810/utils.py
Normal file
69
d810/utils.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import ctypes
|
||||
|
||||
from d810.hexrays_helpers import MSB_TABLE
|
||||
|
||||
CTYPE_SIGNED_TABLE = {1: ctypes.c_int8, 2: ctypes.c_int16, 4: ctypes.c_int32, 8: ctypes.c_int64}
|
||||
CTYPE_UNSIGNED_TABLE = {1: ctypes.c_uint8, 2: ctypes.c_uint16, 4: ctypes.c_uint32, 8: ctypes.c_uint64}
|
||||
|
||||
|
||||
def get_all_subclasses(python_class):
|
||||
python_class.__subclasses__()
|
||||
|
||||
subclasses = set()
|
||||
check_these = [python_class]
|
||||
|
||||
while check_these:
|
||||
parent = check_these.pop()
|
||||
for child in parent.__subclasses__():
|
||||
if child not in subclasses:
|
||||
subclasses.add(child)
|
||||
check_these.append(child)
|
||||
|
||||
return sorted(subclasses, key=lambda x: x.__name__)
|
||||
|
||||
|
||||
def unsigned_to_signed(unsigned_value, nb_bytes):
|
||||
return CTYPE_SIGNED_TABLE[nb_bytes](unsigned_value).value
|
||||
|
||||
|
||||
def signed_to_unsigned(signed_value, nb_bytes):
|
||||
return CTYPE_UNSIGNED_TABLE[nb_bytes](signed_value).value
|
||||
|
||||
|
||||
def get_msb(value, nb_bytes):
|
||||
return (value & MSB_TABLE[nb_bytes]) >> (nb_bytes * 8 - 1)
|
||||
|
||||
|
||||
def get_add_cf(op1, op2, nb_bytes):
|
||||
res = op1 + op2
|
||||
return get_msb((((op1 ^ op2) ^ res) ^ ((op1 ^ res) & (~(op1 ^ op2)))), nb_bytes)
|
||||
|
||||
|
||||
def get_add_of(op1, op2, nb_bytes):
|
||||
res = op1 + op2
|
||||
return get_msb(((op1 ^ res) & (~(op1 ^ op2))), nb_bytes)
|
||||
|
||||
|
||||
def get_sub_cf(op1, op2, nb_bytes):
|
||||
res = op1 - op2
|
||||
return get_msb((((op1 ^ op2) ^ res) ^ ((op1 ^ res) & (op1 ^ op2))), nb_bytes)
|
||||
|
||||
|
||||
def get_sub_of(op1, op2, nb_bytes):
|
||||
res = op1 - op2
|
||||
return get_msb(((op1 ^ res) & (op1 ^ op2)), nb_bytes)
|
||||
|
||||
|
||||
def get_parity_flag(op1, op2, nb_bytes):
|
||||
tmp = CTYPE_UNSIGNED_TABLE[nb_bytes](op1 - op2).value
|
||||
return (bin(tmp).count("1") + 1) % 2
|
||||
|
||||
|
||||
def ror(x, n, nb_bits=32):
|
||||
mask = (2 ** n) - 1
|
||||
mask_bits = x & mask
|
||||
return (x >> n) | (mask_bits << (nb_bits - n))
|
||||
|
||||
|
||||
def rol(x, n, nb_bits=32):
|
||||
return ror(x, nb_bits - n, nb_bits)
|
||||
158
d810/z3_utils.py
Normal file
158
d810/z3_utils.py
Normal file
@@ -0,0 +1,158 @@
|
||||
import logging
|
||||
from typing import List, Union
|
||||
from ida_hexrays import *
|
||||
|
||||
from d810.hexrays_helpers import get_mop_index
|
||||
from d810.hexrays_formatters import format_minsn_t, opcode_to_string
|
||||
from d810.ast import mop_to_ast, minsn_to_ast, AstLeaf, AstNode
|
||||
from d810.errors import D810Z3Exception
|
||||
|
||||
logger = logging.getLogger('D810.plugin')
|
||||
z3_file_logger = logging.getLogger('D810.z3_test')
|
||||
|
||||
try:
|
||||
import z3
|
||||
Z3_INSTALLED = True
|
||||
except ImportError:
|
||||
logger.info("Z3 features disabled. Install Z3 to enable them")
|
||||
Z3_INSTALLED = False
|
||||
|
||||
|
||||
def create_z3_vars(leaf_list: List[AstLeaf]):
|
||||
if not Z3_INSTALLED:
|
||||
raise D810Z3Exception("Z3 is not installed")
|
||||
known_leaf_list = []
|
||||
known_leaf_z3_var_list = []
|
||||
for leaf in leaf_list:
|
||||
if not leaf.is_constant():
|
||||
leaf_index = get_mop_index(leaf.mop, known_leaf_list)
|
||||
if leaf_index == -1:
|
||||
known_leaf_list.append(leaf.mop)
|
||||
leaf_index = len(known_leaf_list) - 1
|
||||
if leaf.mop.size in [1, 2, 4, 8]:
|
||||
# Normally, we should create variable based on their size
|
||||
# but for now it can cause issue when instructions like XDU are used, hence this ugly fix
|
||||
# known_leaf_z3_var_list.append(z3.BitVec("x_{0}".format(leaf_index), 8 * leaf.mop.size))
|
||||
known_leaf_z3_var_list.append(z3.BitVec("x_{0}".format(leaf_index), 32))
|
||||
pass
|
||||
else:
|
||||
known_leaf_z3_var_list.append(z3.BitVec("x_{0}".format(leaf_index), 32))
|
||||
leaf.z3_var = known_leaf_z3_var_list[leaf_index]
|
||||
leaf.z3_var_name = "x_{0}".format(leaf_index)
|
||||
return known_leaf_z3_var_list
|
||||
|
||||
|
||||
def ast_to_z3_expression(ast: Union[AstNode, AstLeaf], use_bitvecval=False):
|
||||
if not Z3_INSTALLED:
|
||||
raise D810Z3Exception("Z3 is not installed")
|
||||
if isinstance(ast, AstLeaf):
|
||||
if ast.is_constant():
|
||||
return z3.BitVecVal(ast.value, 32)
|
||||
return ast.z3_var
|
||||
if ast.opcode == m_neg:
|
||||
return -(ast_to_z3_expression(ast.left, use_bitvecval))
|
||||
elif ast.opcode == m_lnot:
|
||||
return not (ast_to_z3_expression(ast.left, use_bitvecval))
|
||||
elif ast.opcode == m_bnot:
|
||||
return ~(ast_to_z3_expression(ast.left, use_bitvecval))
|
||||
elif ast.opcode == m_add:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) + (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_sub:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) - (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_mul:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) * (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_udiv:
|
||||
return z3.UDiv(ast_to_z3_expression(ast.left, use_bitvecval=True),
|
||||
ast_to_z3_expression(ast.right, use_bitvecval=True))
|
||||
elif ast.opcode == m_sdiv:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) / (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_umod:
|
||||
return z3.URem(ast_to_z3_expression(ast.left, use_bitvecval), ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_smod:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) % (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_or:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) | (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_and:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) & (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_xor:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) ^ (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_shl:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) << (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_shr:
|
||||
return z3.LShR(ast_to_z3_expression(ast.left, use_bitvecval), ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode == m_sar:
|
||||
return (ast_to_z3_expression(ast.left, use_bitvecval)) >> (ast_to_z3_expression(ast.right, use_bitvecval))
|
||||
elif ast.opcode in [m_xdu, m_xds, m_low, m_high]:
|
||||
return ast_to_z3_expression(ast.left, use_bitvecval)
|
||||
raise D810Z3Exception("Z3 evaluation: Unknown opcode {0} for {1}".format(opcode_to_string(ast.opcode), ast))
|
||||
|
||||
|
||||
def mop_list_to_z3_expression_list(mop_list: List[mop_t]):
|
||||
if not Z3_INSTALLED:
|
||||
raise D810Z3Exception("Z3 is not installed")
|
||||
ast_list = [mop_to_ast(mop) for mop in mop_list]
|
||||
ast_leaf_list = []
|
||||
for ast in ast_list:
|
||||
ast_leaf_list += ast.get_leaf_list()
|
||||
_ = create_z3_vars(ast_leaf_list)
|
||||
return [ast_to_z3_expression(ast) for ast in ast_list]
|
||||
|
||||
|
||||
def z3_check_mop_equality(mop1: mop_t, mop2: mop_t) -> bool:
|
||||
if not Z3_INSTALLED:
|
||||
raise D810Z3Exception("Z3 is not installed")
|
||||
z3_mop1, z3_mop2 = mop_list_to_z3_expression_list([mop1, mop2])
|
||||
s = z3.Solver()
|
||||
s.add(z3.Not(z3_mop1 == z3_mop2))
|
||||
if s.check().r == -1:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def z3_check_mop_inequality(mop1: mop_t, mop2: mop_t) -> bool:
|
||||
if not Z3_INSTALLED:
|
||||
raise D810Z3Exception("Z3 is not installed")
|
||||
z3_mop1, z3_mop2 = mop_list_to_z3_expression_list([mop1, mop2])
|
||||
s = z3.Solver()
|
||||
s.add(z3_mop1 == z3_mop2)
|
||||
if s.check().r == -1:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def rename_leafs(leaf_list: List[AstLeaf]) -> List[str]:
|
||||
if not Z3_INSTALLED:
|
||||
raise D810Z3Exception("Z3 is not installed")
|
||||
known_leaf_list = []
|
||||
for leaf in leaf_list:
|
||||
if not leaf.is_constant() and leaf.mop.t != mop_z:
|
||||
leaf_index = get_mop_index(leaf.mop, known_leaf_list)
|
||||
if leaf_index == -1:
|
||||
known_leaf_list.append(leaf.mop)
|
||||
leaf_index = len(known_leaf_list) - 1
|
||||
leaf.z3_var_name = "x_{0}".format(leaf_index)
|
||||
|
||||
return ["x_{0} = BitVec('x_{0}', {1})".format(i, 8 * leaf.size) for i, leaf in enumerate(known_leaf_list)]
|
||||
|
||||
|
||||
def log_z3_instructions(original_ins: minsn_t, new_ins: minsn_t):
|
||||
if not Z3_INSTALLED:
|
||||
raise D810Z3Exception("Z3 is not installed")
|
||||
orig_mba_tree = minsn_to_ast(original_ins)
|
||||
new_mba_tree = minsn_to_ast(new_ins)
|
||||
if orig_mba_tree is None or new_mba_tree is None:
|
||||
return None
|
||||
orig_leaf_list = orig_mba_tree.get_leaf_list()
|
||||
new_leaf_list = new_mba_tree.get_leaf_list()
|
||||
|
||||
var_def_list = rename_leafs(orig_leaf_list + new_leaf_list)
|
||||
|
||||
z3_file_logger.info("print('Testing: {0} == {1}')".format(format_minsn_t(original_ins), format_minsn_t(new_ins)))
|
||||
for var_def in var_def_list:
|
||||
z3_file_logger.info("{0}".format(var_def))
|
||||
|
||||
removed_xdu = "{0}".format(orig_mba_tree).replace("xdu","")
|
||||
z3_file_logger.info("original_expr = {0}".format(removed_xdu))
|
||||
removed_xdu = "{0}".format(new_mba_tree).replace("xdu","")
|
||||
z3_file_logger.info("new_expr = {0}".format(removed_xdu))
|
||||
z3_file_logger.info("prove(original_expr == new_expr)\n")
|
||||
Reference in New Issue
Block a user