Initial commit
This commit is contained in:
73
InterfaceDiscovery.sh
Normal file
73
InterfaceDiscovery.sh
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function check_tool()
|
||||||
|
{
|
||||||
|
tool=$1
|
||||||
|
cmd=$2
|
||||||
|
|
||||||
|
a=`${cmd} 2>/dev/null`
|
||||||
|
if [ ! $? -eq 0 ] ; then
|
||||||
|
echo "${tool} is missing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_tools()
|
||||||
|
{
|
||||||
|
check_tool "readelf" "readelf --version"
|
||||||
|
check_tool "objdump" "objdump --version"
|
||||||
|
check_tool "c++filt" "c++filt --version"
|
||||||
|
|
||||||
|
if [ ! -e ./vtable-dumper ] ; then
|
||||||
|
check_tool "git" "git --version"
|
||||||
|
check_tool "make" "make --version"
|
||||||
|
check_tool "g++" "g++ --version"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
TARGET=$1
|
||||||
|
readelf_target="${TARGET}.readelf"
|
||||||
|
sections_target="${TARGET}.sections"
|
||||||
|
vtable_target="${TARGET}.vtable"
|
||||||
|
|
||||||
|
if [ ! -f "${TARGET}" ] ; then
|
||||||
|
echo "$0 <target>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_tools
|
||||||
|
|
||||||
|
if [ ${TARGET} -nt ${readelf_target} ] ; then
|
||||||
|
echo "Regenerating ${readelf_target}"
|
||||||
|
$((readelf -sW ${TARGET}|c++filt > ${readelf_target}))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${TARGET} -nt ${sections_target} ] ; then
|
||||||
|
echo "Regenerating ${sections_target}"
|
||||||
|
$((readelf --sections ${TARGET}|c++filt > ${sections_target}))
|
||||||
|
fi
|
||||||
|
|
||||||
|
VTABLE_ARG="-V ${vtable_target}"
|
||||||
|
if [ ${TARGET} -nt ${vtable_target} ] ; then
|
||||||
|
`objdump -d ${TARGET} 2>/dev/null`
|
||||||
|
if [ ! $? -eq 0 ]; then
|
||||||
|
echo "Unable to do objdump, maybe ${TARGET} is for another platform"
|
||||||
|
VTABLE_ARG=""
|
||||||
|
else
|
||||||
|
if [ ! -e ./vtable-dumper/vtable-dumper ] ; then
|
||||||
|
git clone XXX || exit 1
|
||||||
|
cd ./vtable-dumper
|
||||||
|
make
|
||||||
|
cd -
|
||||||
|
fi
|
||||||
|
if [ -x ./vtable-dumper/vtable-dumper ] ; then
|
||||||
|
echo "Regenerating ${vtable_target}"
|
||||||
|
$((./vtable-dumper/vtable-dumper --demangle ${TARGET}|c++filt > ${vtable_target}))
|
||||||
|
else
|
||||||
|
echo "./vtable-dumper not available"
|
||||||
|
VTABLE_ARG=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
./InterfaceDiscovery.py -f ${TARGET} -S ${readelf_target} -s ${sections_target} ${VTABLE_ARG} -c
|
||||||
674
LICENCE
Normal file
674
LICENCE
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is 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. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
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.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
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 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. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
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 Affero 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 special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU 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 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 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 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 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
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 GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
67
README.md
Normal file
67
README.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
SOAdvancedDissector
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
SOAdvancedDissector is a Python(3) script that rely on _GNU readelf_, _c++filt_ and _vtable-dumper_ to extract symbols from Linux shared libraries (.so file).
|
||||||
|
|
||||||
|
Thanks to these symbols, the full class hierarchy is built allowing to link your code with the target library.
|
||||||
|
|
||||||
|
Nevertheless, extracted information is only the start of work. It needs to be reworked to find function type returns, attributes types, filter public/private functions/attributes/methods, add some class attributes and clean some unneeded symbols.
|
||||||
|
|
||||||
|
**Important** vtable-dumper has been forked, the original tool must not be used because it doesn't (for now) implements all needed features. Please use the one from https://github.com/soutade/vtable-dumper
|
||||||
|
|
||||||
|
|
||||||
|
Details
|
||||||
|
=======
|
||||||
|
|
||||||
|
A first pass is done thanks to _readelf_ + binary analysis to extract static information, it's mandatory. It scans _typeinfo_ and _vtable_ entries.
|
||||||
|
|
||||||
|
A second optional pass use _vtable-dumper_ which load the shared library allowing to read runtime vtable (which can be cleared in static file compiled with -fPIC) and find class hierarchy. This can be done apart, especially if shared library has been compiled for another platform (ARM).
|
||||||
|
|
||||||
|
|
||||||
|
Improvments
|
||||||
|
===========
|
||||||
|
|
||||||
|
This tool has been designed to do reverse engineering of a specific library (_librmsdk.so_ from Adobe) and even if I tried to do my best, it may doesn't cover all your cases. I won't do a long term support on it but feel free to send patches.
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
SOAdvancedDissector.py [-h] -f TARGET -s SECTION_FILE -S SYMBOL_FILE [-V VTABLE_FILE] [-o OUTPUT_DIR] [-c] [-r]
|
||||||
|
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
-f TARGET, --file TARGET
|
||||||
|
Target file
|
||||||
|
-s SECTION_FILE, --section-file SECTION_FILE
|
||||||
|
Section file (result from 'readelf --sections|c++filt')
|
||||||
|
-S SYMBOL_FILE, --symbol-file SYMBOL_FILE
|
||||||
|
Symbol file (result from 'readelf -sW|c++filt')
|
||||||
|
-V VTABLE_FILE, --vtable-file VTABLE_FILE
|
||||||
|
Dynamic vtable file (result from 'vtable-dumper --demangle|c++filt')
|
||||||
|
-o OUTPUT_DIR, --output-dir OUTPUT_DIR
|
||||||
|
output directory (default ./output)
|
||||||
|
-c, --clean-output-dir
|
||||||
|
Clean output directory before computing (instead update it)
|
||||||
|
-r, --print-raw-virtual-table
|
||||||
|
Print raw virtual table (debug purpose)
|
||||||
|
|
||||||
|
|
||||||
|
It's recommended to use _SOAdvancedDissector.sh_ script that do all tools extraction stuff.
|
||||||
|
|
||||||
|
|
||||||
|
Sources
|
||||||
|
-------
|
||||||
|
|
||||||
|
Sources can be found @ http://indefero.soutade.fr/p/soadvanceddissector
|
||||||
|
|
||||||
|
|
||||||
|
Copyright
|
||||||
|
---------
|
||||||
|
|
||||||
|
Grégory Soutadé
|
||||||
|
|
||||||
|
|
||||||
|
Licence
|
||||||
|
-------
|
||||||
|
|
||||||
|
GNU GPLv3
|
||||||
728
SOAdvancedDissector.py
Executable file
728
SOAdvancedDissector.py
Executable file
@@ -0,0 +1,728 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright Grégory Soutadé
|
||||||
|
|
||||||
|
# This file is part of SOAdvancedDissector
|
||||||
|
|
||||||
|
# SOAdvancedDissector is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# SOAdvancedDissector is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with SOAdvancedDissector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import argparse
|
||||||
|
from objects import *
|
||||||
|
from cppprototypeparser import CPPPrototypeParser
|
||||||
|
from display import ProgressionDisplay
|
||||||
|
|
||||||
|
#
|
||||||
|
# Regexp for readelf -sW|c++filt
|
||||||
|
#
|
||||||
|
# 6: 006f25d0 20 OBJECT WEAK DEFAULT 20 vtable for dpdoc::Annot
|
||||||
|
num_re = '(?P<num>[0-9]+\:)'
|
||||||
|
address_re = '(?P<address>[0-9a-f]+)'
|
||||||
|
size_re = '(?P<size>[0-9]+)'
|
||||||
|
ustr_re = '(?P<{}>[A-Z]+)'
|
||||||
|
type_re = ustr_re.format('type')
|
||||||
|
link_re = ustr_re.format('link')
|
||||||
|
visibility_re = ustr_re.format('visibility')
|
||||||
|
ndx_re = '(?P<ndx>[0-9]+)'
|
||||||
|
name_re = '(?P<name>.*)'
|
||||||
|
|
||||||
|
readelf = r'[ ]+{}[ ]+{}[ ]+{}[ ]+{}[ ]+{}[ ]+{}[ ]+{}[ ]+{}'
|
||||||
|
readelf = readelf.format(num_re, address_re, size_re, type_re, link_re, visibility_re,ndx_re,name_re)
|
||||||
|
readelf_re = re.compile(readelf)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Regexp for readelf --sections|c++filt
|
||||||
|
#
|
||||||
|
# [ 0] NULL 00000000 000000 000000 00 0 0 0
|
||||||
|
# [ 1] .interp PROGBITS 00000154 000154 000019 00 A 0 0 1
|
||||||
|
num_re = '(?P<num>\[[ ]*[0-9]+\])'
|
||||||
|
name_re = '(?P<name>.+)'
|
||||||
|
type_re = ustr_re.format('type')
|
||||||
|
address_re = '(?P<address>[0-9a-f]+)'
|
||||||
|
offset_re = '(?P<offset>[0-9a-f]+)'
|
||||||
|
size_re = '(?P<size>[0-9a-f]+)'
|
||||||
|
sections = r'[ ]+{}[ ]+{}[ ]+{}[ ]+{}[ ]+{}[ ]+{}[ ]+.*'
|
||||||
|
sections = sections.format(num_re, name_re, type_re, address_re, offset_re, size_re)
|
||||||
|
sections_re = re.compile(sections)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Regexp for vtable-dumper --demangle|c++filt
|
||||||
|
#
|
||||||
|
# 0 0000000000000000
|
||||||
|
# 4 006e9fd0 (& typeinfo for zip::EditableStream)
|
||||||
|
# Inherit from dputils::GuardedStream
|
||||||
|
# Inherit from dpio::StreamClient
|
||||||
|
# 8 002a793d zip::EditableStream::~EditableStream()
|
||||||
|
dynvtable_idx_re = re.compile(r'(?P<index>[0-9]+)[ ]+(?P<vtable_index>[-]?0[x]?[0-9a-f]+)')
|
||||||
|
dynvtable_entry_re = re.compile(r'(?P<index>[0-9]+)[ ]+(?P<address>[0-9a-f]+) (?P<name>.*)')
|
||||||
|
dynvtable_inherit_re = re.compile(r'Inherit from (?P<inherit>.*)')
|
||||||
|
|
||||||
|
#
|
||||||
|
# Global variables
|
||||||
|
#
|
||||||
|
global_namespace = Namespace('global')
|
||||||
|
namespaces = {'global':global_namespace} # Root namespace with one (without namespace) 'global'
|
||||||
|
matched_lines = [] # Matched lines from readelf symbols output
|
||||||
|
sections_lines = [] # Matched lines from readelf sections output
|
||||||
|
sections_addr = [] # Contains tuple (section_addr_start, section_addr_end, section_offset)
|
||||||
|
address_size = 4 # Target address size (32bits by default)
|
||||||
|
classes_with_inheritance = []
|
||||||
|
namespace_dependencies = {}
|
||||||
|
|
||||||
|
display = ProgressionDisplay()
|
||||||
|
|
||||||
|
def line_count(filename):
|
||||||
|
"""Do 'wc -l <filename>'
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
|
Line count of filename
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return int(subprocess.check_output(['wc', '-l', filename]).split()[0])
|
||||||
|
except:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def findBinOffset(addr):
|
||||||
|
"""Find offset into binary file from target address
|
||||||
|
Sections must have been extracted
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
addr : int
|
||||||
|
Target address
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
|
Offset or None if not found
|
||||||
|
"""
|
||||||
|
for (start, end, offset) in sections_addr:
|
||||||
|
if addr >= start and addr <= end:
|
||||||
|
return (addr - start) + offset
|
||||||
|
return None
|
||||||
|
|
||||||
|
def funcnameFromProtype(fullname):
|
||||||
|
"""Return function name (name + parameters)
|
||||||
|
from prototype (includes namespaces + base class)
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
fullname : str
|
||||||
|
Full function name (package::NCXStreamReceiver::totalLengthReady(unsigned int))
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
Function name + parameters
|
||||||
|
"""
|
||||||
|
if fullname.startswith('typeinfo'):
|
||||||
|
return ('typeinfo()', '')
|
||||||
|
parser = CPPPrototypeParser(fullname)
|
||||||
|
|
||||||
|
return (parser.funcname + parser.fullparameters, '::'.join(parser.namespaces))
|
||||||
|
|
||||||
|
def findObjectInCache(name):
|
||||||
|
"""Find class object in namespaces
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
Full class name (package::NCXStreamReceiver)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
obj
|
||||||
|
Found class or None
|
||||||
|
"""
|
||||||
|
|
||||||
|
parser = CPPPrototypeParser(name)
|
||||||
|
|
||||||
|
if parser.is_function:
|
||||||
|
return global_namespace.child(parser.funcname)
|
||||||
|
|
||||||
|
if not parser.namespaces:
|
||||||
|
return global_namespace.child(parser.classname)
|
||||||
|
|
||||||
|
if not parser.namespaces[0] in namespaces.keys():
|
||||||
|
return None
|
||||||
|
|
||||||
|
namespace = namespaces[parser.namespaces[0]]
|
||||||
|
|
||||||
|
# Don't directly use find on root to avoid duplicate name in sub namespaces
|
||||||
|
# eg : ns0::ns1::ns2::ns0::ns3::func
|
||||||
|
|
||||||
|
for targetNamespace in parser.namespaces[1:]:
|
||||||
|
namespace = namespace.find(targetNamespace)
|
||||||
|
if not namespace: return None
|
||||||
|
|
||||||
|
# print('findObjectInCache({}) --> {}'.format(name, namespace.name))
|
||||||
|
return namespace.find(parser.classname)
|
||||||
|
|
||||||
|
def findObjectFromAddr(addr):
|
||||||
|
"""Find object from address (in readelf output)
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
addr : int
|
||||||
|
Object address
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
obj
|
||||||
|
Matched object or None
|
||||||
|
"""
|
||||||
|
for match in matched_lines:
|
||||||
|
if int(match.group('address'),16) == addr:
|
||||||
|
#print(match.groups())
|
||||||
|
return match
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def createClass(fullname):
|
||||||
|
"""Find class object in namespaces or create
|
||||||
|
it if it doesn't exists
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
Full class name (package::NCXStreamReceiver)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
obj
|
||||||
|
Found class or created one
|
||||||
|
"""
|
||||||
|
parser = CPPPrototypeParser(fullname)
|
||||||
|
class_ = Class(parser.classname, '::'.join(parser.namespaces))
|
||||||
|
|
||||||
|
if not parser.namespaces:
|
||||||
|
global_namespace.addChild(class_)
|
||||||
|
else:
|
||||||
|
if not parser.namespaces[0] in namespaces.keys():
|
||||||
|
lastNamespace = Namespace(parser.namespaces[0])
|
||||||
|
namespaces[parser.namespaces[0]] = lastNamespace
|
||||||
|
else:
|
||||||
|
lastNamespace = namespaces[parser.namespaces[0]]
|
||||||
|
for name in parser.namespaces[1:]:
|
||||||
|
newNamespace = lastNamespace.child(name)
|
||||||
|
if not newNamespace:
|
||||||
|
newNamespace = Namespace(name)
|
||||||
|
lastNamespace.addChild(newNamespace)
|
||||||
|
lastNamespace = newNamespace
|
||||||
|
lastNamespace.addChild(class_)
|
||||||
|
return class_
|
||||||
|
|
||||||
|
|
||||||
|
def parseDynamicVtable(filename):
|
||||||
|
"""Parse dynamic vtable (vtable-dumper) file
|
||||||
|
and fix static vtable
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
filename : str
|
||||||
|
Filename to parse
|
||||||
|
"""
|
||||||
|
|
||||||
|
display.setTarget(' * Parse dynamic vtable', line_count(filename), 10)
|
||||||
|
curObj = None
|
||||||
|
with open(filename, 'r') as fd:
|
||||||
|
for line in fd.readlines():
|
||||||
|
display.progress(1)
|
||||||
|
# Empty line -> end of current class
|
||||||
|
line = line.strip()
|
||||||
|
if len(line) == 0:
|
||||||
|
curObj = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
if curObj is None:
|
||||||
|
# New vtable
|
||||||
|
if not line.startswith('Vtable for '): continue
|
||||||
|
objname = line[len('Vtable for '):]
|
||||||
|
curObj = findObjectInCache(objname)
|
||||||
|
else:
|
||||||
|
# First, try object vtable entry
|
||||||
|
match = dynvtable_entry_re.match(line)
|
||||||
|
if match:
|
||||||
|
idx = int(match.group('index'))
|
||||||
|
func = None
|
||||||
|
if match.group('name') == '__cxa_pure_virtual':
|
||||||
|
func = Function('virtfunc{}()'.format(idx), 0, True, True)
|
||||||
|
else:
|
||||||
|
funcaddr = int(match.group('address'),16)
|
||||||
|
match = findObjectFromAddr(funcaddr)
|
||||||
|
funcname = 'unknown_virtfunc{}()'.format(idx)
|
||||||
|
funcnamespaces = ''
|
||||||
|
if match:
|
||||||
|
(funcname, funcnamespaces) = funcnameFromProtype(match.group('name'))
|
||||||
|
else:
|
||||||
|
sys.stderr.write('Error dynvtable0, no match for {}\n'.format(hex(funcaddr)))
|
||||||
|
func = Function(funcname, funcaddr, virtual=True, namespace=funcnamespaces)
|
||||||
|
curObj.updateVirtualFunction(int(idx/address_size), func)
|
||||||
|
continue
|
||||||
|
# Index vtable entry
|
||||||
|
match = dynvtable_idx_re.match(line)
|
||||||
|
if match:
|
||||||
|
funcaddr = int(match.group('vtable_index'),16)
|
||||||
|
funcname = 'vtable_index{}'.format(-funcaddr)
|
||||||
|
func = Function(funcname, funcaddr, True)
|
||||||
|
curObj.updateVirtualFunction(int(int(match.group('index'))/address_size), func)
|
||||||
|
continue
|
||||||
|
# Inherit entry
|
||||||
|
match = dynvtable_inherit_re.match(line)
|
||||||
|
if match:
|
||||||
|
basename = match.group('inherit')
|
||||||
|
base = findObjectInCache(basename)
|
||||||
|
if not base:
|
||||||
|
base = createClass(basename)
|
||||||
|
curObj.addBaseClass(base)
|
||||||
|
classes_with_inheritance.append(curObj)
|
||||||
|
continue
|
||||||
|
|
||||||
|
sys.stderr.write('Error dynvtable, no match for {}\n'.format(line))
|
||||||
|
sys.stderr.flush()
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
def fixupInheritance():
|
||||||
|
"""Fix inheritance : each class reports implemented pure virtual function
|
||||||
|
into its base(s)
|
||||||
|
"""
|
||||||
|
global classes_with_inheritance
|
||||||
|
display.setTarget(' * Fixup inheritance', len(classes_with_inheritance))
|
||||||
|
for class_ in classes_with_inheritance:
|
||||||
|
display.progress(1)
|
||||||
|
class_.fixupInheritance()
|
||||||
|
display.finish()
|
||||||
|
classes_with_inheritance = None # Release memory
|
||||||
|
|
||||||
|
def parseStaticVtable(binfile):
|
||||||
|
"""Parse vtable vtable object into binary file
|
||||||
|
and create static vtable entries
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
binfile : str
|
||||||
|
Filename of binary file to inspect objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
display.setTarget(' * Parse static vtable', len(matched_lines), 10)
|
||||||
|
with open(binfile, 'rb') as fd:
|
||||||
|
for match in matched_lines:
|
||||||
|
display.progress(1)
|
||||||
|
name = match.group('name')
|
||||||
|
if not name.startswith('vtable for'): continue
|
||||||
|
address = int(match.group('address'), 16)
|
||||||
|
# vtable for mtext::cts::ListOfGlyphRunsCTS
|
||||||
|
classname = name[len('vtable for '):]
|
||||||
|
class_ = findObjectInCache(classname)
|
||||||
|
if class_ is None:
|
||||||
|
class_ = createClass(classname)
|
||||||
|
binaddr = findBinOffset(address)
|
||||||
|
fd.seek(binaddr)
|
||||||
|
nb_funcs = int(int(match.group('size'))/address_size)
|
||||||
|
#print('vtable {} {} funcs'.format(classname, nb_funcs))
|
||||||
|
fd.seek(binaddr)
|
||||||
|
for i in range(0, nb_funcs):
|
||||||
|
funcaddr = 0
|
||||||
|
if address_size == 4:
|
||||||
|
funcaddr, = struct.unpack('<I', fd.read(address_size))
|
||||||
|
elif address_size == 8:
|
||||||
|
funcaddr, = struct.unpack('<Q', fd.read(address_size))
|
||||||
|
func = None
|
||||||
|
if funcaddr == 0:
|
||||||
|
# Address == 0 --> pure virtual
|
||||||
|
func = Function('virtfunc{}()'.format(i), 0, True, True)
|
||||||
|
else:
|
||||||
|
funcname = ''
|
||||||
|
funcnamespaces = ''
|
||||||
|
if funcaddr == 0 or hex(funcaddr).startswith('0xf'): # Negative address
|
||||||
|
funcname = 'vtable_index{}'.format(-funcaddr)
|
||||||
|
else:
|
||||||
|
match = findObjectFromAddr(funcaddr)
|
||||||
|
try:
|
||||||
|
if not match:
|
||||||
|
sys.stderr.write('No func found at : {}\n'.format(hex(funcaddr)))
|
||||||
|
(funcname, funcnamespaces) = funcnameFromProtype(match.group('name'))
|
||||||
|
except:
|
||||||
|
if match:
|
||||||
|
sys.stderr.write('FFP except : {}'.format(match.group('name')))
|
||||||
|
funcname = 'unknown_virtfunc{}()'.format(i)
|
||||||
|
func = Function(funcname, funcaddr, virtual=True, namespace=funcnamespaces)
|
||||||
|
try:
|
||||||
|
# print('Add virt {}'.format(func))
|
||||||
|
class_.addVirtualFunction(func)
|
||||||
|
except:
|
||||||
|
print(match.group('name'))
|
||||||
|
print(class_)
|
||||||
|
raise
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
def parseTypeinfo():
|
||||||
|
"""Parse typeinfo objects in matched_lines
|
||||||
|
and create classes
|
||||||
|
"""
|
||||||
|
typeinfos = []
|
||||||
|
for match in matched_lines:
|
||||||
|
name = match.group('name')
|
||||||
|
# "typeinfo for css::MediaParser"
|
||||||
|
if not name.startswith('typeinfo for'): continue
|
||||||
|
typeinfos.append(name[len('typeinfo for '):])
|
||||||
|
# print(name)
|
||||||
|
|
||||||
|
# Sort array before creating class in order to have the right
|
||||||
|
# class hierarchy
|
||||||
|
for classname in sorted(typeinfos):
|
||||||
|
#print(classname)
|
||||||
|
createClass(classname)
|
||||||
|
|
||||||
|
def parseSymbolFile(filename):
|
||||||
|
"""Parse readelf symbols output file
|
||||||
|
and fill matched_lines
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
filename : str
|
||||||
|
Filename to parse
|
||||||
|
"""
|
||||||
|
|
||||||
|
display.setTarget(' * Parse symbols file', line_count(filename), 10)
|
||||||
|
with open(filename, 'r') as fd:
|
||||||
|
for line in fd.readlines():
|
||||||
|
display.progress(1)
|
||||||
|
line = line.rstrip()
|
||||||
|
if not line: continue
|
||||||
|
match = readelf_re.match(line)
|
||||||
|
if not match: continue
|
||||||
|
if match.group('type') not in ('FUNC', 'OBJECT'):
|
||||||
|
continue
|
||||||
|
if match.group('link') not in ('GLOBAL', 'WEAK'):
|
||||||
|
continue
|
||||||
|
if match.group('visibility') not in ('DEFAULT'):
|
||||||
|
continue
|
||||||
|
matched_lines.append(match)
|
||||||
|
if matched_lines:
|
||||||
|
address_size = int(len(matched_lines[0].group('address'))/2)
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
def parseSectionFile(filename):
|
||||||
|
"""Parse readelf sections output file
|
||||||
|
and fill sections_lines and sections_addr
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
filename : str
|
||||||
|
Filename to parse
|
||||||
|
"""
|
||||||
|
|
||||||
|
# We assume there is about 20 sections
|
||||||
|
display.setTarget(' * Parse sections file (1/2)', line_count(filename))
|
||||||
|
with open(filename, 'r') as fd:
|
||||||
|
for line in fd.readlines():
|
||||||
|
display.progress(1)
|
||||||
|
line = line.rstrip()
|
||||||
|
if not line: continue
|
||||||
|
match = sections_re.match(line)
|
||||||
|
if not match: continue
|
||||||
|
sections_lines.append(match)
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
display.setTarget(' * Parse sections file (2/2)', line_count(filename))
|
||||||
|
for match in sections_lines:
|
||||||
|
display.progress(1)
|
||||||
|
start = int(match.group('address'), 16)
|
||||||
|
size = int(match.group('size'), 16)
|
||||||
|
offset = int(match.group('offset'), 16)
|
||||||
|
sections_addr.append((start, start+size, offset))
|
||||||
|
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
def addAllMembers():
|
||||||
|
"""Add all other members that have not been computed
|
||||||
|
"""
|
||||||
|
|
||||||
|
display.setTarget(' * Add all members', len(matched_lines), 10)
|
||||||
|
for match in matched_lines:
|
||||||
|
display.progress(1)
|
||||||
|
virtual = False
|
||||||
|
funcaddr = int(match.group('address'),16)
|
||||||
|
name = match.group('name')
|
||||||
|
if name.startswith('typeinfo') or\
|
||||||
|
name.startswith('vtable'):
|
||||||
|
continue
|
||||||
|
if name.startswith('non-virtual thunk to '):
|
||||||
|
name = name[len('non-virtual thunk to '):]
|
||||||
|
virtual = True
|
||||||
|
|
||||||
|
parser = CPPPrototypeParser(name)
|
||||||
|
class_ = None
|
||||||
|
obj = None
|
||||||
|
funcname = ''
|
||||||
|
classname = ''
|
||||||
|
if match.group('type') == 'FUNC':
|
||||||
|
classname = parser.fullclassname
|
||||||
|
# C functions doesn't have () in their name
|
||||||
|
if not '(' in name:
|
||||||
|
obj = Function(name + '()', funcaddr)
|
||||||
|
global_namespace.addChild(obj)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
(funcname, funcnamespaces) = funcnameFromProtype(name)
|
||||||
|
obj = Function(funcname, funcaddr, virtual=virtual, namespace=funcnamespaces)
|
||||||
|
# No classname : add into global namespace
|
||||||
|
if not classname:
|
||||||
|
global_namespace.addChild(obj)
|
||||||
|
continue
|
||||||
|
class_ = findObjectInCache(classname)
|
||||||
|
else: # Object
|
||||||
|
if parser.funcname:
|
||||||
|
obj = Attribute(parser.funcname, funcaddr)
|
||||||
|
classname = parser.classname
|
||||||
|
elif parser.classname:
|
||||||
|
obj = Attribute(parser.classname, funcaddr)
|
||||||
|
# No namespaces : add into global namespace
|
||||||
|
if not parser.namespaces:
|
||||||
|
global_namespace.addChild(obj)
|
||||||
|
continue
|
||||||
|
classname = '::'.join(parser.namespaces)
|
||||||
|
class_ = findObjectInCache(parser.fullclassname)
|
||||||
|
|
||||||
|
# Try to search in namespaces from C++ method
|
||||||
|
if not class_ and parser.namespaces:
|
||||||
|
class_ = findObjectInCache('::'.join(parser.namespaces))
|
||||||
|
|
||||||
|
# Try to search in namespaces from C function
|
||||||
|
if not class_ and classname in namespaces.keys():
|
||||||
|
class_ = namespaces[classname]
|
||||||
|
|
||||||
|
# If still not, it's a new class/function
|
||||||
|
if not class_:
|
||||||
|
if not classname:
|
||||||
|
sys.stderr.write('AAM Err3 "{}" "{}"\n'.format(name, funcname))
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
class_ = createClass(classname)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Could be class or namespace
|
||||||
|
class_.addChild(obj)
|
||||||
|
except:
|
||||||
|
sys.stderr.write('Not class {} {}\n'.format(name, class_.name))
|
||||||
|
sys.stderr.flush()
|
||||||
|
# raise
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
def fixBadClassAssertion():
|
||||||
|
"""We could have consider obj from a class and created it,
|
||||||
|
but in fact, it just namespace
|
||||||
|
"""
|
||||||
|
toDelete = []
|
||||||
|
toAdd = []
|
||||||
|
display.setTarget(' * Fix bad class assertion (1/2)', len(global_namespace.childs))
|
||||||
|
for obj in global_namespace.childs:
|
||||||
|
display.progress(1)
|
||||||
|
if type(obj) == Class and obj.looksLikeNamespace():
|
||||||
|
if obj.name in namespaces:
|
||||||
|
newNamespace = namespaces[obj.name]
|
||||||
|
else:
|
||||||
|
newNamespace = Namespace(obj.name)
|
||||||
|
newNamespace.fillFrom(obj)
|
||||||
|
toAdd.append(newNamespace)
|
||||||
|
toDelete.append(obj)
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
display.setTarget(' * Fix bad class assertion (2/2)', len(toDelete)+len(toAdd))
|
||||||
|
for obj in toDelete:
|
||||||
|
display.progress(1)
|
||||||
|
global_namespace.removeChild(obj)
|
||||||
|
|
||||||
|
for obj in toAdd:
|
||||||
|
display.progress(1)
|
||||||
|
global_namespace.addChild(obj)
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
def analyseDependencies():
|
||||||
|
"""Find classes present in method parameters but
|
||||||
|
not previously found
|
||||||
|
"""
|
||||||
|
display.setTarget(' * Analyse dependencies (1/2)', len(namespaces.keys()))
|
||||||
|
allParams = []
|
||||||
|
for namespace in namespaces.values():
|
||||||
|
display.progress(1)
|
||||||
|
params = namespace.getDependencies()
|
||||||
|
for param in params:
|
||||||
|
allParams.append(param)
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
if allParams:
|
||||||
|
allParams = list(set(allParams))
|
||||||
|
display.setTarget(' * Analyse dependencies (2/2)', len(allParams))
|
||||||
|
for param in allParams:
|
||||||
|
display.progress(1)
|
||||||
|
class_ = findObjectInCache(param)
|
||||||
|
if not class_:
|
||||||
|
createClass(param)
|
||||||
|
display.finish()
|
||||||
|
|
||||||
|
header_re = re.compile('[A-Za-z0-9_-]+')
|
||||||
|
def headerNameToFilename(name):
|
||||||
|
"""Transform namespace name into filename
|
||||||
|
Keep only characters from header_re and change name
|
||||||
|
in lower case
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
Name to transform
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
Computed name
|
||||||
|
"""
|
||||||
|
if '::' in name:
|
||||||
|
parser = CPPPrototypeParser(name)
|
||||||
|
if not parser.namespaces:
|
||||||
|
return None
|
||||||
|
res = parser.namespaces[0].lower()
|
||||||
|
else:
|
||||||
|
res = name.lower()
|
||||||
|
if '.so' in res:
|
||||||
|
res = os.path.basename(res).split('.so')[0] # Remove .so* extension
|
||||||
|
res = ''.join([c for c in res if header_re.match(c)])
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def outputHeader(namespace, filename):
|
||||||
|
"""Create header file from namespace description
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
namespace : str
|
||||||
|
Namespace name
|
||||||
|
|
||||||
|
filename : str
|
||||||
|
Output filename
|
||||||
|
"""
|
||||||
|
|
||||||
|
dependecies = namespace.getDependencies()
|
||||||
|
# Remove standard dependencies
|
||||||
|
if 'std' in dependecies:
|
||||||
|
dependecies.remove('std')
|
||||||
|
elif '__gnu_cxx' in dependecies:
|
||||||
|
dependecies.remove('__gnu_cxx')
|
||||||
|
headers = []
|
||||||
|
|
||||||
|
define = '_{}'.format(os.path.basename(filename).upper().replace('.', '_'))
|
||||||
|
with open(filename, 'w') as fd:
|
||||||
|
fd.write('/*\n')
|
||||||
|
fd.write(' File automatically generated by SOAdvancedDissector.py\n')
|
||||||
|
fd.write(' More information at http://indefero.soutade.fr/p/soadvanceddissector\n')
|
||||||
|
fd.write('*/\n\n')
|
||||||
|
|
||||||
|
fd.write('#ifndef {}\n'.format(define))
|
||||||
|
fd.write('#define {}\n\n'.format(define))
|
||||||
|
|
||||||
|
for dep in dependecies:
|
||||||
|
headername = headerNameToFilename(dep)
|
||||||
|
if headername and not headername in headers:
|
||||||
|
fd.write('#include <{}.h>\n'.format(headername))
|
||||||
|
# Filter multiple headers
|
||||||
|
headers.append(headername)
|
||||||
|
|
||||||
|
if dependecies:
|
||||||
|
fd.write('\n\n')
|
||||||
|
|
||||||
|
fd.write('{}'.format(namespace))
|
||||||
|
|
||||||
|
fd.write('#endif // {}'.format(define))
|
||||||
|
|
||||||
|
def writeResult(target, outputDir, cleanOutputDir):
|
||||||
|
"""Write result into header files (one per namespace)
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
cleanOutputDir : bool
|
||||||
|
Clean output directory before processing
|
||||||
|
"""
|
||||||
|
|
||||||
|
if cleanOutputDir:
|
||||||
|
print('Clean {}'.format(outputDir))
|
||||||
|
shutil.rmtree(outputDir, ignore_errors=True)
|
||||||
|
|
||||||
|
if not os.path.exists(outputDir):
|
||||||
|
os.mkdir(outputDir)
|
||||||
|
|
||||||
|
setPrintIndent(True)
|
||||||
|
keys = namespaces.keys()
|
||||||
|
display.setTarget(' * Write output files', len(keys))
|
||||||
|
for namespace in keys:
|
||||||
|
if namespace == 'std': continue # Don't try to write std classes
|
||||||
|
filename = '{}.h'.format(headerNameToFilename(namespace))
|
||||||
|
if namespace == 'global':
|
||||||
|
filename = '{}.h'.format(headerNameToFilename(target))
|
||||||
|
outputHeader(namespaces[namespace], '{}/{}'.format(outputDir, filename))
|
||||||
|
display.progress(1)
|
||||||
|
display.finish()
|
||||||
|
setPrintIndent(False)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description='Extract interfaces (classes, functions, variables) from a GNU/Linux shared library')
|
||||||
|
parser.add_argument('-f', '--file', help="Target file",
|
||||||
|
dest="target", required=True)
|
||||||
|
parser.add_argument('-s', '--section-file', help="Section file (result from 'readelf --sections|c++filt')",
|
||||||
|
dest="section_file", required=True)
|
||||||
|
parser.add_argument('-S', '--symbol-file', help="Symbol file (result from 'readelf -sW|c++filt')",
|
||||||
|
dest="symbol_file", required=True)
|
||||||
|
parser.add_argument('-V', '--vtable-file', help="Dynamic vtable file (result from 'vtable-dumper --demangle|c++filt')",
|
||||||
|
dest="vtable_file")
|
||||||
|
parser.add_argument('-o', '--output-dir', help="output directory (default ./output)",
|
||||||
|
default="./output", dest="output_dir")
|
||||||
|
parser.add_argument('-c', '--clean-output-dir', help="Clean output directory before computing (instead update it)",
|
||||||
|
default=False, action="store_true", dest="clean_output")
|
||||||
|
parser.add_argument('-r', '--print-raw-virtual-table', help="Print raw virtual table (debug purpose)",
|
||||||
|
default=False, action="store_true", dest="raw_virtual_table")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
setPrintRawVirtualTable(args.raw_virtual_table)
|
||||||
|
|
||||||
|
print('Analyse {}'.format(args.target))
|
||||||
|
|
||||||
|
parseSectionFile(args.section_file)
|
||||||
|
parseSymbolFile(args.symbol_file)
|
||||||
|
parseTypeinfo()
|
||||||
|
parseStaticVtable(args.target)
|
||||||
|
|
||||||
|
if args.vtable_file:
|
||||||
|
parseDynamicVtable(args.vtable_file)
|
||||||
|
fixupInheritance()
|
||||||
|
|
||||||
|
addAllMembers()
|
||||||
|
|
||||||
|
if args.vtable_file:
|
||||||
|
fixBadClassAssertion()
|
||||||
|
|
||||||
|
analyseDependencies()
|
||||||
|
writeResult(args.target, args.output_dir, args.clean_output)
|
||||||
|
|
||||||
|
print('Result wrote in {}'.format(args.output_dir))
|
||||||
75
SOAdvancedDissector.sh
Executable file
75
SOAdvancedDissector.sh
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function check_tool()
|
||||||
|
{
|
||||||
|
tool=$1
|
||||||
|
cmd=$2
|
||||||
|
|
||||||
|
a=`${cmd} 2>/dev/null`
|
||||||
|
if [ ! $? -eq 0 ] ; then
|
||||||
|
echo "${tool} is missing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_tools()
|
||||||
|
{
|
||||||
|
check_tool "readelf" "readelf --version"
|
||||||
|
check_tool "objdump" "objdump --version"
|
||||||
|
check_tool "c++filt" "c++filt --version"
|
||||||
|
|
||||||
|
if [ ! -e ./vtable-dumper ] ; then
|
||||||
|
check_tool "git" "git --version"
|
||||||
|
check_tool "make" "make --version"
|
||||||
|
check_tool "g++" "g++ --version"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
TARGET=$1
|
||||||
|
target=`basename ${TARGET}`
|
||||||
|
readelf_target="${target}.readelf"
|
||||||
|
sections_target="${target}.sections"
|
||||||
|
vtable_target="${target}.vtable"
|
||||||
|
|
||||||
|
if [ ! -f "${TARGET}" ] ; then
|
||||||
|
echo "$0 <target>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_tools
|
||||||
|
|
||||||
|
if [ ${TARGET} -nt ${readelf_target} ] ; then
|
||||||
|
echo "Regenerating ${readelf_target}"
|
||||||
|
readelf -sW ${TARGET}|c++filt > ${readelf_target}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${TARGET} -nt ${sections_target} ] ; then
|
||||||
|
echo "Regenerating ${sections_target}"
|
||||||
|
readelf --sections ${TARGET}|c++filt > ${sections_target}
|
||||||
|
fi
|
||||||
|
|
||||||
|
VTABLE_ARG="-V ${vtable_target}"
|
||||||
|
if [ ${TARGET} -nt ${vtable_target} ] ; then
|
||||||
|
echo objdump -d ${TARGET}
|
||||||
|
objdump -d ${TARGET} >/dev/null 2>&1
|
||||||
|
if [ ! $? -eq 0 ]; then
|
||||||
|
echo "Unable to do objdump, maybe ${TARGET} is for another platform"
|
||||||
|
VTABLE_ARG=""
|
||||||
|
else
|
||||||
|
if [ ! -e ./vtable-dumper/vtable-dumper ] ; then
|
||||||
|
git clone https://github.com/soutade/vtable-dumper.git || exit 1
|
||||||
|
cd ./vtable-dumper
|
||||||
|
make
|
||||||
|
cd -
|
||||||
|
fi
|
||||||
|
if [ -x ./vtable-dumper/vtable-dumper ] ; then
|
||||||
|
echo "Regenerating ${vtable_target}"
|
||||||
|
./vtable-dumper/vtable-dumper -demangled ${TARGET}|c++filt > ${vtable_target}
|
||||||
|
else
|
||||||
|
echo "./vtable-dumper not available"
|
||||||
|
VTABLE_ARG=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
./SOAdvancedDissector.py -f ${TARGET} -S ${readelf_target} -s ${sections_target} ${VTABLE_ARG} -c
|
||||||
220
cppprototypeparser.py
Executable file
220
cppprototypeparser.py
Executable file
@@ -0,0 +1,220 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright Grégory Soutadé
|
||||||
|
|
||||||
|
# This file is part of SOAdvancedDissector
|
||||||
|
|
||||||
|
# SOAdvancedDissector is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# SOAdvancedDissector is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with SOAdvancedDissector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
class CPPPrototypeParser:
|
||||||
|
"""Class that extract all parts of a cpp method prototype"""
|
||||||
|
|
||||||
|
def __init__(self, fullname=''):
|
||||||
|
if fullname:
|
||||||
|
self.parse(fullname)
|
||||||
|
else:
|
||||||
|
self._reset()
|
||||||
|
|
||||||
|
def _reset(self):
|
||||||
|
self.fullclassname = '' # Class name with its namespaces
|
||||||
|
self.names = [] # All decoded names : namespaces, class, function
|
||||||
|
self.namespaces = [] # Namespaces part
|
||||||
|
self.parameters = [] # Parameters part
|
||||||
|
self.fullparameters = '' # Full parameters string
|
||||||
|
self.funcname = '' # Function name
|
||||||
|
self.classname = '' # Class name
|
||||||
|
self.fullname = '' # namespaces + class + function + parameters
|
||||||
|
self.has_parameters = False
|
||||||
|
self.is_function = False # Is single function or class method
|
||||||
|
|
||||||
|
# Should parse something like :
|
||||||
|
# uft::ClassDescriptor<layout::MasterConditionalReference>::queryFunc(uft::StructDescriptor const*, void*, uft::Value const&, void*)
|
||||||
|
def parse(self, fullname):
|
||||||
|
"""Parse CPP method prototype and fill class members
|
||||||
|
with right values
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
fullname : str
|
||||||
|
Prototype to parse
|
||||||
|
"""
|
||||||
|
self._reset()
|
||||||
|
self.fullname = fullname
|
||||||
|
if '(' in fullname:
|
||||||
|
self.has_parameters = True
|
||||||
|
splitname = fullname.split('(')
|
||||||
|
if 'operator()' in fullname:
|
||||||
|
namepart = splitname[0] + '()'
|
||||||
|
self.fullparameters = '(' + ''.join(splitname[2:])
|
||||||
|
else:
|
||||||
|
namepart = splitname[0]
|
||||||
|
self.fullparameters = '(' + ''.join(splitname[1:])
|
||||||
|
self._parseParameters()
|
||||||
|
else:
|
||||||
|
namepart = fullname
|
||||||
|
|
||||||
|
self.names = self._parseName(namepart)
|
||||||
|
|
||||||
|
# Function or class method
|
||||||
|
if self.has_parameters:
|
||||||
|
self.funcname = self.names[-1]
|
||||||
|
# Class method with at least class name + method name
|
||||||
|
if len(self.names) >= 2:
|
||||||
|
self.classname = self.names[-2]
|
||||||
|
self.fullclassname = '::'.join(self.names[:-1])
|
||||||
|
# Contains namespaces
|
||||||
|
if len(self.names) > 2:
|
||||||
|
self.namespaces = self.names[:-2]
|
||||||
|
# Simple function
|
||||||
|
else:
|
||||||
|
self.is_function = True
|
||||||
|
self.funcname = self.names[0]
|
||||||
|
else:
|
||||||
|
# Class name with or without namespaces
|
||||||
|
self.classname = self.names[-1]
|
||||||
|
if len(self.names) > 1:
|
||||||
|
self.namespaces = self.names[:-1]
|
||||||
|
|
||||||
|
if self.funcname.endswith(' const'):
|
||||||
|
self.funcname = self.funcname[:-len(' const')]
|
||||||
|
|
||||||
|
if self.classname.endswith(' const'):
|
||||||
|
self.classname = self.classname[:-len(' const')]
|
||||||
|
|
||||||
|
def _parseName(self, name):
|
||||||
|
"""Parse CPP method name and split in different parts
|
||||||
|
using '::' as delimiter.
|
||||||
|
It supports templates names and function prototypes
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
Name to parse
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
list
|
||||||
|
Splitted name parts
|
||||||
|
"""
|
||||||
|
nb_parenthesis = 0
|
||||||
|
nb_chevrons = 0
|
||||||
|
nb_parenthesis = 0
|
||||||
|
cur_buf = ''
|
||||||
|
names = []
|
||||||
|
nb_db_points = 0
|
||||||
|
in_template = False
|
||||||
|
in_parenthesis = False
|
||||||
|
operator = False
|
||||||
|
|
||||||
|
for c in name.strip():
|
||||||
|
if operator:
|
||||||
|
cur_buf += c
|
||||||
|
continue
|
||||||
|
|
||||||
|
if c == '(':
|
||||||
|
cur_buf += c
|
||||||
|
nb_parenthesis += 1
|
||||||
|
in_parenthesis = True
|
||||||
|
elif c == ')':
|
||||||
|
cur_buf += c
|
||||||
|
nb_parenthesis -= 1
|
||||||
|
if nb_parenthesis == 0:
|
||||||
|
in_parenthesis = False
|
||||||
|
elif c == ':':
|
||||||
|
if in_template or in_parenthesis:
|
||||||
|
cur_buf += c
|
||||||
|
continue
|
||||||
|
nb_db_points += 1
|
||||||
|
if nb_db_points == 2:
|
||||||
|
names.append(cur_buf)
|
||||||
|
cur_buf = ''
|
||||||
|
nb_db_points = 0
|
||||||
|
elif c == '<':
|
||||||
|
cur_buf += c
|
||||||
|
in_template = True
|
||||||
|
nb_chevrons += 1
|
||||||
|
elif c == '>':
|
||||||
|
cur_buf += c
|
||||||
|
nb_chevrons -= 1
|
||||||
|
if nb_chevrons == 0:
|
||||||
|
in_template = False
|
||||||
|
else:
|
||||||
|
cur_buf += c
|
||||||
|
|
||||||
|
# Next characters are part of operator name
|
||||||
|
if cur_buf == 'operator':
|
||||||
|
operator = True
|
||||||
|
|
||||||
|
if cur_buf:
|
||||||
|
names.append(cur_buf)
|
||||||
|
|
||||||
|
return names
|
||||||
|
|
||||||
|
def _parseParameters(self):
|
||||||
|
"""Parse each parameters and do split on them
|
||||||
|
using '::' as delimiter.
|
||||||
|
Update self.parameters member
|
||||||
|
"""
|
||||||
|
nb_chevrons = 0
|
||||||
|
in_template = False
|
||||||
|
cur_buf = ''
|
||||||
|
|
||||||
|
# Parse fullparameters except parenthesis
|
||||||
|
for c in self.fullparameters[1:-1]:
|
||||||
|
if c == ',':
|
||||||
|
if in_template: continue
|
||||||
|
self.parameters.append(self._parseName(cur_buf))
|
||||||
|
cur_buf = ''
|
||||||
|
elif c == '<':
|
||||||
|
cur_buf += c
|
||||||
|
if in_template: continue
|
||||||
|
in_template = True
|
||||||
|
nb_chevrons += 1
|
||||||
|
elif c == '>':
|
||||||
|
cur_buf += c
|
||||||
|
nb_chevrons -= 1
|
||||||
|
if nb_chevrons == 0:
|
||||||
|
in_template = False
|
||||||
|
else:
|
||||||
|
cur_buf += c
|
||||||
|
|
||||||
|
if cur_buf:
|
||||||
|
self.parameters.append(self._parseName(cur_buf))
|
||||||
|
|
||||||
|
# Some tests
|
||||||
|
if __name__ == "__main__":
|
||||||
|
strings = ['uft::ClassDescriptor<layout::MasterConditionalReference>::queryFunc(uft::StructDescriptor const*, void*, uft::Value const&, void*)',
|
||||||
|
'adept::LicenseImpl::getVoucherID()',
|
||||||
|
'adept::DRMProcessorImpl::addSignIn()',
|
||||||
|
'layout::InlineLayoutEngine::AnnotationGlyphRunCounter::operator()(uft::sref<layout::RunListItem>&)',
|
||||||
|
'dp::PointerVector<void>::~PointerVector()',
|
||||||
|
'adept::IoCallbackWrapper<adept::DRMProcessorImpl>::IoCallbackWrapper(adept::DRMProcessorImpl*, void (adept::DRMProcessorImpl::*)(dp::Unknown*, bool), void (adept::DRMProcessorImpl::*)(double), void (adept::DRMProcessorImpl::*)(dp::String const&))',
|
||||||
|
'tetraphilia::ThreadImpl<T3AppTraits, tetraphilia::PFiber<T3AppTraits>, tetraphilia::NoClientYieldHook<T3AppTraits> >',
|
||||||
|
'debugOutputOpen']
|
||||||
|
for s in strings:
|
||||||
|
p = CPPPrototypeParser(s)
|
||||||
|
print('Original {}'.format(s))
|
||||||
|
print('Full parameters {}'.format(p.fullparameters))
|
||||||
|
print('Names {}'.format(p.names))
|
||||||
|
print('Namespaces {}'.format(p.namespaces))
|
||||||
|
print('Parameters {}'.format(p.parameters))
|
||||||
|
print('Class name {}'.format(p.classname))
|
||||||
|
print('Func name {}'.format(p.funcname))
|
||||||
|
print('Full class name {}'.format(p.fullclassname))
|
||||||
|
print('Full parameters {}'.format(p.fullparameters))
|
||||||
|
print('Is function ? {}'.format(p.is_function))
|
||||||
|
print('Has parameters ? {}'.format(p.has_parameters))
|
||||||
|
print("")
|
||||||
89
display.py
Normal file
89
display.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright Grégory Soutadé
|
||||||
|
|
||||||
|
# This file is part of SOAdvancedDissector
|
||||||
|
|
||||||
|
# SOAdvancedDissector is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# SOAdvancedDissector is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with SOAdvancedDissector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class ProgressionDisplay():
|
||||||
|
"""Class that manage nice display
|
||||||
|
with progression continuous update in percent"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.reset(False)
|
||||||
|
|
||||||
|
def reset(self, printEmptyLine=False):
|
||||||
|
"""Reset display and optionaly print an empty line"""
|
||||||
|
self.message = ''
|
||||||
|
self.target_progression = 0
|
||||||
|
self.cur_progression = 0
|
||||||
|
self.watermark = 0
|
||||||
|
self.cur_watermark = 0
|
||||||
|
if printEmptyLine:
|
||||||
|
print('')
|
||||||
|
|
||||||
|
def setTarget(self, message, target=0, watermark=0):
|
||||||
|
"""New progression to display
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
message : str
|
||||||
|
Message to display
|
||||||
|
|
||||||
|
target (Optional) : int
|
||||||
|
Max progression value, if not set, no percent will be displayed
|
||||||
|
|
||||||
|
watermark (Optional) : int
|
||||||
|
Only display message when progression >= watermark
|
||||||
|
"""
|
||||||
|
self.reset()
|
||||||
|
self.message = message
|
||||||
|
self.target_progression = target
|
||||||
|
self.watermark = watermark
|
||||||
|
|
||||||
|
def progress(self, increment):
|
||||||
|
"""Increment progression and display new one"""
|
||||||
|
self.cur_progression += increment
|
||||||
|
self.cur_watermark += increment
|
||||||
|
|
||||||
|
if self.cur_progression > self.target_progression:
|
||||||
|
self.cur_progression = self.target_progression
|
||||||
|
|
||||||
|
if self.cur_watermark < self.watermark and\
|
||||||
|
self.cur_progression < self.target_progression:
|
||||||
|
return
|
||||||
|
|
||||||
|
display = ''
|
||||||
|
if self.target_progression:
|
||||||
|
cur_percent = int((self.cur_progression*100)/self.target_progression)
|
||||||
|
display = '{} [{}%]'.format(self.message, cur_percent)
|
||||||
|
else:
|
||||||
|
display = '{}'.format(self.message)
|
||||||
|
|
||||||
|
sys.stdout.write('\r{}\r'.format(' ' * (len(display)+5))) # First, clear line
|
||||||
|
sys.stdout.write(display)
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
"""End of progression, ensure we display "100%" value"""
|
||||||
|
if self.target_progression:
|
||||||
|
self.progress(self.target_progression) # Print 100%
|
||||||
|
print('')
|
||||||
|
else:
|
||||||
|
pass # Do nothing
|
||||||
|
|
||||||
536
objects.py
Normal file
536
objects.py
Normal file
@@ -0,0 +1,536 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright Grégory Soutadé
|
||||||
|
|
||||||
|
# This file is part of SOAdvancedDissector
|
||||||
|
|
||||||
|
# SOAdvancedDissector is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# SOAdvancedDissector is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with SOAdvancedDissector. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from cppprototypeparser import CPPPrototypeParser
|
||||||
|
|
||||||
|
print_raw_virtual_table = False
|
||||||
|
print_indent = False
|
||||||
|
cur_indent = 0
|
||||||
|
|
||||||
|
parser = CPPPrototypeParser()
|
||||||
|
|
||||||
|
def setPrintRawVirtualTable(value):
|
||||||
|
global print_raw_virtual_table
|
||||||
|
print_raw_virtual_table = value
|
||||||
|
|
||||||
|
def setPrintIndent(value):
|
||||||
|
global print_indent
|
||||||
|
print_indent = value
|
||||||
|
|
||||||
|
def _getIndent():
|
||||||
|
global print_indent
|
||||||
|
indent = ''
|
||||||
|
if print_indent:
|
||||||
|
indent = ' '*cur_indent
|
||||||
|
return indent
|
||||||
|
|
||||||
|
class Object:
|
||||||
|
"""Abstract object representation (simply a name)"""
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def find(self, obj):
|
||||||
|
if self == obj:
|
||||||
|
return self
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getParametersDependencies(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getDependencies(self):
|
||||||
|
"""Get dependencies from other namespaces"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if type(other) == str:
|
||||||
|
return self.name == other
|
||||||
|
else:
|
||||||
|
return self.name == other.name
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.name < other.name
|
||||||
|
|
||||||
|
class Attribute(Object):
|
||||||
|
"""Class attribute (member)"""
|
||||||
|
def __init__(self, name, address=0, namespace=''):
|
||||||
|
Object.__init__(self, name)
|
||||||
|
self.address = address
|
||||||
|
self.namespace = namespace
|
||||||
|
|
||||||
|
def fullname(self):
|
||||||
|
"""Return namespace::name"""
|
||||||
|
if self.namespace:
|
||||||
|
return '{}::{}'.format(self.namespace, self.name)
|
||||||
|
else:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if type(other) == str:
|
||||||
|
return self.name == other
|
||||||
|
else:
|
||||||
|
if hasattr(other, 'address'):
|
||||||
|
return self.address == other.address
|
||||||
|
else:
|
||||||
|
return self.name == other.name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{}void* {};\n'.format(_getIndent(), self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class Function(Attribute):
|
||||||
|
"""Function description"""
|
||||||
|
def __init__(self, name, address=0, virtual=False, pure_virtual=False, namespace=''):
|
||||||
|
# Be sure we have () in name
|
||||||
|
if '(' in name and not name.endswith(')') and not name.endswith('const')\
|
||||||
|
and not name.endswith('const&'):
|
||||||
|
name += ')'
|
||||||
|
Attribute.__init__(self, name, address, namespace)
|
||||||
|
self.virtual = virtual
|
||||||
|
self.pure_virtual = pure_virtual
|
||||||
|
self.constructor = False
|
||||||
|
|
||||||
|
def isPure(self):
|
||||||
|
"""Is function pure virtual"""
|
||||||
|
return self.pure_virtual
|
||||||
|
|
||||||
|
def setConstructor(self, value):
|
||||||
|
"""Set/clear constructor property"""
|
||||||
|
self.constructor = value
|
||||||
|
|
||||||
|
def getDependencies(self):
|
||||||
|
"""Get dependencies from other namespaces"""
|
||||||
|
dependencies = []
|
||||||
|
parser.parse(self.name)
|
||||||
|
if not parser.has_parameters:
|
||||||
|
return None
|
||||||
|
for p in parser.parameters:
|
||||||
|
# If parameter has namespace, add into dependencies
|
||||||
|
if type(p) == list and len(p) > 1 and not self.namespace.startswith(p[0]):
|
||||||
|
dep = '::'.join(p)
|
||||||
|
if not '::' in dep: continue
|
||||||
|
dep = dep.replace('*', '')
|
||||||
|
dep = dep.replace('&', '')
|
||||||
|
if dep.endswith(' const'):
|
||||||
|
dep = dep[:-len(' const')]
|
||||||
|
dependencies.append(dep)
|
||||||
|
if dependencies:
|
||||||
|
return list(set(dependencies))
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _getReturnType(self):
|
||||||
|
"""Get method return type"""
|
||||||
|
type_ = 'void '
|
||||||
|
if self.name.startswith('vtable_index'): type_='void* '
|
||||||
|
elif self.name.startswith('operator new'): type_='void* '
|
||||||
|
elif self.constructor: type_ = ''
|
||||||
|
return type_
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
res = ''
|
||||||
|
type_ = self._getReturnType()
|
||||||
|
if self.pure_virtual:
|
||||||
|
res = 'virtual {}{} = 0;\n'.format(type_, self.name)
|
||||||
|
elif self.virtual:
|
||||||
|
res = 'virtual {}{};\n'.format(type_, self.name)
|
||||||
|
else:
|
||||||
|
res = '{}{};\n'.format(type_, self.name)
|
||||||
|
res = '{}{}'.format(_getIndent(), res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
class Namespace(Object):
|
||||||
|
"""Namespace description"""
|
||||||
|
def __init__(self, name):
|
||||||
|
Object.__init__(self, name)
|
||||||
|
self.childs = []
|
||||||
|
self.dependencies = [] # Dependencies from objects in other namespace
|
||||||
|
|
||||||
|
def addChild(self, child):
|
||||||
|
"""Add child (function, class, attribute) to namespace"""
|
||||||
|
if not child in self.childs:
|
||||||
|
self.childs.append(child)
|
||||||
|
|
||||||
|
def removeChild(self, child):
|
||||||
|
"""Remove child from namespace"""
|
||||||
|
self.childs.remove(child)
|
||||||
|
|
||||||
|
def child(self, name):
|
||||||
|
"""Try to find name in childs without recursion"""
|
||||||
|
for child in self.childs:
|
||||||
|
if child.name == name:
|
||||||
|
return child
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find(self, obj):
|
||||||
|
"""Try to find obj in childs and their own child (with recursion)"""
|
||||||
|
if self == obj:
|
||||||
|
return self
|
||||||
|
|
||||||
|
for child in self.childs:
|
||||||
|
res = child.find(obj)
|
||||||
|
if res:
|
||||||
|
return res
|
||||||
|
return None
|
||||||
|
|
||||||
|
def fillFrom(self, other):
|
||||||
|
"""Copy all childs from other object"""
|
||||||
|
for child in other.childs:
|
||||||
|
self.childs.append(child)
|
||||||
|
|
||||||
|
def getDependencies(self):
|
||||||
|
"""Get dependencies from other namespaces"""
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
for child in self.childs:
|
||||||
|
depend = child.getDependencies()
|
||||||
|
if depend:
|
||||||
|
for d in depend:
|
||||||
|
if not d.startswith('{}::'.format(self.name)):
|
||||||
|
dependencies.append(d)
|
||||||
|
|
||||||
|
if dependencies:
|
||||||
|
return list(set(dependencies))
|
||||||
|
return []
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
global cur_indent
|
||||||
|
if self.name != 'global':
|
||||||
|
res = '{}namespace {} {{\n\n'.format(_getIndent(), self.name)
|
||||||
|
cur_indent += 1
|
||||||
|
else:
|
||||||
|
res = ''
|
||||||
|
|
||||||
|
namespaces = []
|
||||||
|
classes = []
|
||||||
|
functions = []
|
||||||
|
other = []
|
||||||
|
for child in self.childs:
|
||||||
|
if type(child) == Namespace: namespaces.append(child)
|
||||||
|
elif type(child) == Class: classes.append(child)
|
||||||
|
elif type(child) == Function: functions.append(child)
|
||||||
|
else: other.append(child)
|
||||||
|
|
||||||
|
# Compute classes inheritance dependency
|
||||||
|
classes_dep = []
|
||||||
|
for class_ in sorted(classes):
|
||||||
|
isDep = False
|
||||||
|
for pos, class2 in enumerate(classes_dep):
|
||||||
|
if class_ in class2.inherit_from:
|
||||||
|
isDep = True
|
||||||
|
classes_dep.insert(pos, class_)
|
||||||
|
break
|
||||||
|
if not isDep:
|
||||||
|
classes_dep.append(class_)
|
||||||
|
|
||||||
|
|
||||||
|
# Add class declaration
|
||||||
|
if len(classes_dep) > 1:
|
||||||
|
for c in classes_dep:
|
||||||
|
res += '{}class {};\n'.format(_getIndent(), c.name)
|
||||||
|
if classes_dep: res += '\n\n'
|
||||||
|
|
||||||
|
for namespace in sorted(namespaces):
|
||||||
|
res += namespace.__str__()
|
||||||
|
if namespaces: res += '\n'
|
||||||
|
|
||||||
|
for c in classes_dep:
|
||||||
|
res += c.__str__()
|
||||||
|
if classes_dep: res += '\n'
|
||||||
|
|
||||||
|
for func in sorted(functions):
|
||||||
|
res += func.__str__()
|
||||||
|
if functions: res += '\n'
|
||||||
|
|
||||||
|
for child in sorted(other):
|
||||||
|
res += child.__str__()
|
||||||
|
if other: res += '\n'
|
||||||
|
|
||||||
|
if self.name != 'global':
|
||||||
|
cur_indent -= 1
|
||||||
|
res += '{}}}\n'.format(_getIndent())
|
||||||
|
res += '\n'
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
class Class(Namespace):
|
||||||
|
"""Class description"""
|
||||||
|
def __init__(self, name, namespace=''):
|
||||||
|
Namespace.__init__(self, name)
|
||||||
|
|
||||||
|
self.constructors = []
|
||||||
|
self.destructors = []
|
||||||
|
self.inherit_from = []
|
||||||
|
self.virtual_functions = []
|
||||||
|
self.namespace = namespace
|
||||||
|
|
||||||
|
def fullname(self):
|
||||||
|
"""Return namespace::name"""
|
||||||
|
if self.namespace:
|
||||||
|
return '{}::{}'.format(self.namespace, self.name)
|
||||||
|
else:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def _checkConstructor(self, obj):
|
||||||
|
"""Check if obj is a constructor/destructor and
|
||||||
|
set its property.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
list
|
||||||
|
Adequat list (constructor or destructor) or None
|
||||||
|
"""
|
||||||
|
if type(obj) != Function: return None
|
||||||
|
if obj.name.startswith('~'):
|
||||||
|
# sys.stderr.write('Check C {} -> D\n'.format(obj.name))
|
||||||
|
obj.setConstructor(True)
|
||||||
|
return self.destructors
|
||||||
|
if obj.name.startswith('{}('.format(self.name)):
|
||||||
|
# sys.stderr.write('Check C {} -> C\n'.format(obj.name))
|
||||||
|
obj.setConstructor(True)
|
||||||
|
return self.constructors
|
||||||
|
# sys.stderr.write('Check C {} -> N\n'.format(obj.name))
|
||||||
|
return None
|
||||||
|
|
||||||
|
def addVirtualFunction(self, obj):
|
||||||
|
"""Add a new virtual function"""
|
||||||
|
if obj.address == 0 or not obj in self.virtual_functions:
|
||||||
|
self._checkConstructor(obj)
|
||||||
|
self.virtual_functions.append(obj)
|
||||||
|
|
||||||
|
def updateVirtualFunction(self, idx, obj):
|
||||||
|
"""Update virtual function at index idx"""
|
||||||
|
try:
|
||||||
|
self._checkConstructor(obj)
|
||||||
|
self.virtual_functions[idx] = obj
|
||||||
|
except:
|
||||||
|
sys.stderr.write('updateVirtualFunction Error, cur vtable size {}; idx {}, class {}, obj {}\n'.format(len(self.virtual_functions), idx, self.name, obj))
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
def addMember(self, obj):
|
||||||
|
"""Add a new member"""
|
||||||
|
if obj in self.virtual_functions or\
|
||||||
|
obj in self.constructors or\
|
||||||
|
obj in self.destructors or\
|
||||||
|
obj in self.childs:
|
||||||
|
return
|
||||||
|
|
||||||
|
targetList = self._checkConstructor(obj)
|
||||||
|
if targetList is None:
|
||||||
|
self.childs.append(obj)
|
||||||
|
else:
|
||||||
|
targetList.append(obj)
|
||||||
|
|
||||||
|
def addChild(self, child):
|
||||||
|
return self.addMember(child)
|
||||||
|
|
||||||
|
def addBaseClass(self, obj):
|
||||||
|
self.inherit_from.append(obj)
|
||||||
|
|
||||||
|
def fixVirtualFunction(self, index, newName):
|
||||||
|
"""Set real name for virtfunc and unknown virtfunc
|
||||||
|
generic names
|
||||||
|
"""
|
||||||
|
if index >= len(self.virtual_functions):
|
||||||
|
sys.stderr.write('FVF Error {} > {} for {}/{}\n'.format(index, len(self.virtual_functions), newName, self.fullname()))
|
||||||
|
return False
|
||||||
|
|
||||||
|
virtfunc = self.virtual_functions[index]
|
||||||
|
|
||||||
|
if not virtfunc.isPure():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if virtfunc.name.startswith('virtfunc') or\
|
||||||
|
virtfunc.name.startswith('unknown_virtfunc'):
|
||||||
|
virtfunc.name = newName
|
||||||
|
self._checkConstructor(virtfunc)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def hasMultipleVirtualSections(self):
|
||||||
|
"""Check if we have a vtable_indexX (X>0) entry in our
|
||||||
|
virtual functions table
|
||||||
|
"""
|
||||||
|
if not self.virtual_functions:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for vfunc in self.virtual_functions:
|
||||||
|
if vfunc.name.startswith('vtable_index') and\
|
||||||
|
vfunc.name != 'vtable_index0':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def fixupInheritance(self):
|
||||||
|
"""Report virtual function name in base class
|
||||||
|
if there are pure virtual.
|
||||||
|
"""
|
||||||
|
if not self.inherit_from:
|
||||||
|
return
|
||||||
|
|
||||||
|
# First, handle all of our bases
|
||||||
|
for base in self.inherit_from:
|
||||||
|
base.fixupInheritance()
|
||||||
|
|
||||||
|
updated = False
|
||||||
|
curIdx = 0
|
||||||
|
if self.hasMultipleVirtualSections():
|
||||||
|
for base in self.inherit_from:
|
||||||
|
# First is vtable_index, skip it
|
||||||
|
curIdx += 1
|
||||||
|
targetIdx = 1
|
||||||
|
updated = False
|
||||||
|
while curIdx < len(base.virtual_functions):
|
||||||
|
vfunc = self.virtual_functions[curIdx]
|
||||||
|
if vfunc.name.startswith('virtual_index'):
|
||||||
|
break
|
||||||
|
if base.fixVirtualFunction(targetIdx, vfunc.name):
|
||||||
|
updated = True
|
||||||
|
curIdx += 1
|
||||||
|
targetIdx += 1
|
||||||
|
if updated:
|
||||||
|
base.fixupInheritance()
|
||||||
|
# Go to next vtable index if we are not
|
||||||
|
while curIdx < len(self.virtual_functions):
|
||||||
|
vfunc = self.virtual_functions[curIdx]
|
||||||
|
if vfunc.name.startswith('virtual_index'):
|
||||||
|
break
|
||||||
|
curIdx += 1
|
||||||
|
else:
|
||||||
|
for base in self.inherit_from:
|
||||||
|
targetIdx = 0
|
||||||
|
while targetIdx < len(base.virtual_functions):
|
||||||
|
vfunc = self.virtual_functions[curIdx]
|
||||||
|
if base.fixVirtualFunction(targetIdx, vfunc.name):
|
||||||
|
updated = True
|
||||||
|
curIdx += 1
|
||||||
|
targetIdx += 1
|
||||||
|
if updated:
|
||||||
|
base.fixupInheritance()
|
||||||
|
|
||||||
|
def looksLikeNamespace(self):
|
||||||
|
"""Empty specific class attributes looks like a namespace
|
||||||
|
"""
|
||||||
|
return not self.constructors and\
|
||||||
|
not self.destructors and\
|
||||||
|
not self.inherit_from and\
|
||||||
|
not self.virtual_functions
|
||||||
|
|
||||||
|
def _getDependencies(self, targetList, dependencies):
|
||||||
|
for obj in targetList:
|
||||||
|
res = obj.getDependencies()
|
||||||
|
if res:
|
||||||
|
for d in res:
|
||||||
|
if self.namespace and not d.startswith(self.namespace):
|
||||||
|
dependencies.append(d)
|
||||||
|
|
||||||
|
def getDependencies(self):
|
||||||
|
"""Get dependencies from other namespaces"""
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
for base in self.inherit_from:
|
||||||
|
if base.namespace:
|
||||||
|
dependencies.append(base.fullname())
|
||||||
|
|
||||||
|
self._getDependencies(self.constructors, dependencies)
|
||||||
|
self._getDependencies(self.destructors, dependencies)
|
||||||
|
self._getDependencies(self.virtual_functions, dependencies)
|
||||||
|
self._getDependencies(self.childs, dependencies)
|
||||||
|
|
||||||
|
if dependencies:
|
||||||
|
return list(set(dependencies))
|
||||||
|
return None
|
||||||
|
|
||||||
|
def printOverloadedVirtualTable(self):
|
||||||
|
"""Only select overloaded methods from
|
||||||
|
virtual table
|
||||||
|
"""
|
||||||
|
res = ''
|
||||||
|
vfunc_res = ''
|
||||||
|
for vfunc in self.virtual_functions:
|
||||||
|
if vfunc.name.startswith('vtable_index') or\
|
||||||
|
vfunc.name.startswith('typeinfo()'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Not overloaded by us
|
||||||
|
if vfunc.namespace and vfunc.namespace != self.namespace:
|
||||||
|
continue
|
||||||
|
|
||||||
|
vfunc_res = vfunc.__str__()
|
||||||
|
|
||||||
|
# Method already in result,
|
||||||
|
# It's the case for virtual descriptor
|
||||||
|
if vfunc_res in res:
|
||||||
|
continue
|
||||||
|
|
||||||
|
res += vfunc_res
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
global print_raw_virtual_table
|
||||||
|
global cur_indent
|
||||||
|
res = '{}class {}'.format(_getIndent(), self.name)
|
||||||
|
if self.inherit_from:
|
||||||
|
res += ': '
|
||||||
|
bases = []
|
||||||
|
for base in self.inherit_from:
|
||||||
|
bases.append('public {}'.format(base.fullname()))
|
||||||
|
res += ', '.join(bases)
|
||||||
|
res += '\n{}{{\n'.format(_getIndent())
|
||||||
|
res += '{}public:\n'.format(_getIndent())
|
||||||
|
cur_indent += 1
|
||||||
|
|
||||||
|
for constructor in sorted(self.constructors):
|
||||||
|
res += constructor.__str__()
|
||||||
|
if len(self.constructors): res += '\n'
|
||||||
|
|
||||||
|
for destructor in sorted(self.destructors):
|
||||||
|
res += destructor.__str__()
|
||||||
|
if len(self.destructors): res += '\n'
|
||||||
|
|
||||||
|
# Do not sort virtual tables !
|
||||||
|
virtfuncs = ''
|
||||||
|
if print_raw_virtual_table:
|
||||||
|
for virtfunc in self.virtual_functions:
|
||||||
|
virtfuncs += virtfunc.__str__()
|
||||||
|
else:
|
||||||
|
virtfuncs = self.printOverloadedVirtualTable()
|
||||||
|
|
||||||
|
if len(virtfuncs): res += '{}\n'.format(virtfuncs)
|
||||||
|
|
||||||
|
methods = []
|
||||||
|
other = []
|
||||||
|
|
||||||
|
for child in self.childs:
|
||||||
|
if type(child) == Function: methods.append(child)
|
||||||
|
else: other.append(child)
|
||||||
|
|
||||||
|
for method in sorted(methods):
|
||||||
|
res += method.__str__()
|
||||||
|
if methods: res += '\n'
|
||||||
|
|
||||||
|
for child in sorted(other):
|
||||||
|
res += child.__str__()
|
||||||
|
if other: res += '\n'
|
||||||
|
|
||||||
|
cur_indent -= 1
|
||||||
|
res += '{}}};\n\n'.format(_getIndent())
|
||||||
|
|
||||||
|
return res
|
||||||
Reference in New Issue
Block a user