Initial commit

This commit is contained in:
Boris Batteux
2020-10-29 11:09:07 +01:00
commit 33f8d22f28
73 changed files with 14194 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
**/__pycache__/
**/.idea/
**/.vscode/
**/venv/
**~
**.pyc
**.log

3
AUTHORS.md Normal file
View File

@@ -0,0 +1,3 @@
# Authors list
- Boris Batteux

75
D810.py Normal file
View 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
View 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
View 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
![](d810/docs/source/images/gui_plugin_configuration.png)
* 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
View File

605
d810/ast.py Normal file
View 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
View 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
View 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)

View 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"
]
}
}
]
}

View 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"
]
}
}
]
}

View 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
View 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
View 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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

505
d810/emulator.py Normal file
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"
]
}

View File

View 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

View 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()]

View 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

View 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

View 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

View 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

View 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

View 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

View 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))

View 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]

View 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

View 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

View 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

View 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"

View 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

View 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)]

View 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__

View 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

View 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

View 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)]

View 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

View File

@@ -0,0 +1,9 @@
from d810.optimizers.instructions.handler import InstructionOptimizationRule, InstructionOptimizer
class ChainSimplificationRule(InstructionOptimizationRule):
pass
class ChainOptimizer(InstructionOptimizer):
RULE_CLASSES = [ChainSimplificationRule]

View 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)]

View File

@@ -0,0 +1,9 @@
from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer
class EarlyRule(GenericPatternRule):
pass
class EarlyOptimizer(InstructionOptimizer):
RULE_CLASSES = [EarlyRule]

View 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

View 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__

View 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)]

View 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 []

View 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

View 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

View 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")))

View 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

View 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"))

View 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

View 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")))

View 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

View File

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

View 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

View 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

View 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'))

View 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)]

View 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

View File

@@ -0,0 +1,9 @@
from d810.optimizers.instructions.handler import GenericPatternRule, InstructionOptimizer
class Z3Rule(GenericPatternRule):
pass
class Z3Optimizer(InstructionOptimizer):
RULE_CLASSES = [Z3Rule]

View 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
View 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
View 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
View 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")