pax_global_header00006660000000000000000000000064131702327140014512gustar00rootroot0000000000000052 comment=4590cbb17ea3ebe5a8af807a7666082b6719a9cd scanmem-0.17/000077500000000000000000000000001317023271400130645ustar00rootroot00000000000000scanmem-0.17/.gitignore000066400000000000000000000013301317023271400150510ustar00rootroot00000000000000# Compiled program /scanmem # Auto-generated files *.o *.lo *.la .deps .libs m4 autom4te.cache config.h* configure Makefile Makefile.in aclocal.m4 compile config.guess config.log config.status config.sub depcomp install-sh libtool ltmain.sh missing py-compile stamp-h1 tags test-driver # GameConqueror gui/GameConqueror.appdata.xml gui/GameConqueror.desktop gui/consts.py gui/*.pyc gui/*.pyo gui/__pycache__ gui/gameconqueror gui/org.freedesktop.gameconqueror.policy gui/org.freedesktop.gameconqueror.policy.in # Translations po/.intltool-merge-cache po/GameConqueror.pot po/Makefile.in.in po/POTFILES po/*.gmo po/stamp-it # Android builds # libreadline and ncurses downloads and links readline-* ncurses-* readline ncurses scanmem-0.17/.travis.yml000066400000000000000000000025511317023271400152000ustar00rootroot00000000000000language: c sudo: required compiler: gcc-6 env: global: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - secure: "C3Gd8W9U0yQIxnUkcKlzicB2iOeZ42NPpVXTOnigjoc2UoI4eVYlDjEZjccZAIn0wHUguircvBt0eyLD7cuNf2mTozJ21BG0NpMdYbG1n/aVchnJBr6rldb2X3kVmQCj50LQKm+aINbK1qSs56VIUwNepPiul+HPMV6sL5sQ5M0=" - PATH=/usr/lib/binutils-2.26/bin:${PATH} - ASAN_OPTIONS='halt_on_error=1' - UBSAN_OPTIONS='halt_on_error=1' before_install: - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- addons: apt: sources: - ubuntu-toolchain-r-test packages: - gcc-6 binutils-2.26 intltool # Coverity scan add-on, fires only when pushing to the `coverity_scan` branch coverity_scan: project: name: "scanmem/scanmem" description: "Build submitted via Travis CI" notification_email: andreastacchiotti@gmail.com build_command_prepend: "./autogen.sh && ./configure CC=gcc" build_command: "make" branch_pattern: coverity_scan script: - if [ "${COVERITY_SCAN_BRANCH}" == 1 ]; then exit ; fi - ./autogen.sh && ./configure --enable-gui - make CFLAGS='-O2 -fsanitize=address,undefined' # Testing requires `sudo` for `ptrace()` - sudo make check VERBOSE=1 scanmem-0.17/AUTHORS000066400000000000000000000013051317023271400141330ustar00rootroot00000000000000## Release 0.17 # Author list from `git shortlog -sen --no-merges` # Duplicates removed and Tavis put manually on top. Tavis Ormandy Lu Wang Andrea Stacchiotti Sebastian Parschauer Igor Gnatenko Mattias Münster Jonathan Pelletier Bijan Kazemi-Shirkadeh Eli Dupree # Current maintainers: Andrea Stacchiotti Sebastian Parschauer scanmem-0.17/COPYING000066400000000000000000001045131317023271400141230ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . scanmem-0.17/ChangeLog000066400000000000000000000172751317023271400146520ustar00rootroot000000000000002016-04-28 Sebastian Parschauer * See https://github.com/scanmem/scanmem/releases for new releases 2012-03-26 WANG Lu * Fix for '!=' as 'not equal to' * Typo * Option 'dump_with_ascii' added (Thanks to sec) 2011-12-16 WANG Lu * Support wildcards when writing bytearrays [Thanks to Incarus6] * Delete an item upon DELETE key is pressed * Copy address & cheatlist reorderable 2011-08-16 WANG Lu * Fix freeze after switching the target process 2011-08-15 WANG Lu * Improve the process selection dialog * Make tooltip for the 'Value' label more obvious 2011-08-02 WANG Lu * Fix memory browser 2011-07-08 WANG Lu * Fix manpage * Fix process list displaying (Issue 26) 2011-03-14 WANG Lu * Ignore empty input at cheatlist * Process list dialog is resizable 2011-01-13 WANG Lu * Run as root * Skip when readlink on /proc/pid/exe fails * Added a script for building packages for PPA 2011-01-12 WANG Lu * Using pager when showing help messages 2011-01-11 WANG Lu * Check version of scanmem right after GameConqueror starts 2010-09-19 WANG Lu * Fixed memory leak * Set monospace font to the lists * Fixed value parsing for strings 2010-05-22 Wang Lu * Fixed `set' command 2010-05-16 Wang Lu * Fixed build on FreeBSD * Bug fixed: freezing when scanning multiple variables 2010-04-28 Wang Lu * Bug fixed: dump_to_file [Thanks to Bryan Cain] * Manually add a cheat entry * Update values in the lists 2010-01-19 WANG Lu * Bug fixed: read /proc//mem on 32bit machines * Bug fixed: scan for double variables on 32bit machines * a little improvement on peekbuf * Better communication, replace readline with printf when running as backend * `scan for address` supported 2010-01-16 WANG Lu * Memory Editor * Fix copyright strings (again) * new 'dump' command 2010-01-12 WANG Lu * Bugs fixed * Better DEFAULT_LONGDOC * Better GUI * Better communication between GUI & backend * Progress bar in GUI * Several other UI improvement 2010-01-10 WANG Lu * STRING supported * Fixed a bug relacted to match_flag, now match_flags seems to be `dangerous` since I made it a union now. * Fixed a memory bug in target_memory_info_array.c 2010-01-09 WANG Lu * BYTEARRAY supported, oh yeah 2010-01-07 WANG Lu * Added uservalue_t and clean up some value_t, this make it more flexible, and make it possible to support more data types and even user-specific scan routine * in scan routines old_value and user_value are both provided * clean up MATCHES_AND_VALUES stuff, since currently we don't use MATCHES or VALUES * removed snapshot and use MATCHANY instead, such that this can be used to filter out data types * now user can provide float numbers * INCREASEDBY and DECREASEDBY support 2010-01-06 WANG Lu * Fixed detection of reverse change, but it's now working partially, see comments in `help option` * MATCHANY can be used to filter out types now * GREATERTHAN & LESSTHAN supported 2010-01-03 WANG Lu * better GUI * support scanning for a speicific type of data * autotools-ized gui * added an option to determine which regions to be searched 2009-12-20 WANG Lu * basic float/double support * fixed a peek buffer bug, that the buffer is not shifted correctly * change format of some commands for the front-end * added a new 'write' command * fixed a data overwritten issue (Issue 1) xxxx-xx-xx Tavis Ormandy * fixed infinite loop if a command was just leading whitespace. * add message to configure script about `libreadline-dev` package. * correct some typos reported by debian. 2007-06-07 Tavis Ormandy * make license clearer, added gpl notice to all source files, and added a show command. - eg, show copying, show warranty, etc. * handle unspecified value in set properly, eg set /4 * autotooled the build process. * fixed bug where unaligned variables might be missed at end of region. * begin testsuite using dejagnu, to help prevent any regressions in future. * use /proc/pid/mem if available. 2007-04-08 Tavis Ormandy * corrected lots of lint errors. * include copy of GPL in COPYING file. * use more EXPECT(). * fixed two memory leaks (very small). * much more scalable commandline parsing, simple switch/case was getting too unwieldy. * deprecated cont command, and re-wrote set to make it much more powerful. - use set x/y instead of cont, cont will print a warning if you try to use it. * preliminary support for float type (WARNING: experimental) * implemented simple custom completer for readline, so tab completion now works for commands. - arguments will work with completion in next version. * massive improvements to online help, each command can register its own documentation. - example : `help set` will now print detailed usage information. * added quick shell escape support (shell command). * added interrupt support to commands that continue until stopped. * changed the format of list output, which now has a set of flags that represent the possible formats that this variable could be, eg 'CSi' means could be a char or a short, but cant be an int. * new command `watch` that monitors how a variable changes. * updated manual 2007-03-04 Tavis Ormandy * buffered overlapping peeks to improve scan performance. * automatically support chars, shorts and ints. - removed width command * pid command can change target. * = command, to accompany < and > to indicate variable has not changed. * snapshot command to enhance <, > and =. WARNING: experimental, very inefficient. - snapshot will use a more efficient format next version, right now it should not be used on large programs, or will eat all your free memory. 2007-01-11 Tavis Ormandy * cleaned up some lintian errors * used __builtin_expect() to try to improve performance in some areas * minor UI tweaks, give region counts during scan. * start using readline(), should implement completion at some point. * fixed memory leak. 2006-11-16 Tavis Ormandy * Made buffer management more intelligent. * Add commands to list and delete regions (lregions, dregion). * Add commands to indicate variable has increased or decreased (<, >). * Improved usability of set, cont, list, etc. * Add progress meter, large programs can take a long time for initial scan. * Testing support for variable width targets, signedness not currenlty handled. * delete command to eliminate matches. * list command now shows where a variable is located. * General code cleanup. * Eliminate useless command line options, use interactive versions instead. * Improved documentation. 2006-11-14 Tavis Ormandy * Applied patch from Brian Lewis * Install signal handler to detach from target on sigint/sighup/sigquit. * Improved Makefile * Added initial man page 2006-11-13 Tavis Ormandy * Initial version scanmem-0.17/Makefile.am000066400000000000000000000022071317023271400151210ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 SUBDIRS = test if ENABLE_GUI SUBDIRS += po gui endif # '-O2 -g' are added by `configure`, unless the user overrides CFLAGS AM_CFLAGS = -std=gnu99 -Wall lib_LTLIBRARIES = libscanmem.la libscanmem_la_includedir = $(includedir)/scanmem libscanmem_la_include_HEADERS = commands.h \ list.h \ maps.h \ scanmem.h \ scanroutines.h \ sets.h \ show_message.h \ targetmem.h \ value.h libscanmem_la_SOURCES = commands.c \ common.h \ ptrace.c \ handlers.h \ handlers.c \ interrupt.h \ list.c \ licence.h \ maps.c \ scanmem.c \ scanroutines.c \ sets.c \ show_message.c \ targetmem.c \ value.c if !HAVE_GETLINE libscanmem_la_SOURCES += getline.h \ getline.c endif libscanmem_la_LDFLAGS = -version-info 1:0:0 \ -export-symbols-regex '^sm_|^show_' bin_PROGRAMS = scanmem scanmem_SOURCES = menu.h \ menu.c \ main.c if !WITH_READLINE scanmem_SOURCES += readline.h \ readline.c endif scanmem_LDADD = libscanmem.la dist_man_MANS = scanmem.1 dist_doc_DATA = README EXTRA_DIST = gpl-3.0.txt lgpl-3.0.txt scanmem-0.17/NEWS000066400000000000000000000000721317023271400135620ustar00rootroot00000000000000See https://github.com/scanmem/scanmem/releases for news. scanmem-0.17/README000066400000000000000000000047231317023271400137520ustar00rootroot00000000000000# ![](https://raw.githubusercontent.com/scanmem/scanmem/master/gui/GameConqueror_72x72.png)scanmem & GameConqueror [![Build Status](https://travis-ci.org/scanmem/scanmem.svg?branch=master)](https://travis-ci.org/scanmem/scanmem) [![Coverity Status](https://scan.coverity.com/projects/8565/badge.svg?flat=1")](https://scan.coverity.com/projects/scanmem) [![Chat on Slack](https://img.shields.io/badge/chat-on%20slack-blue.svg)](https://join.slack.com/t/scanmem/shared_invite/enQtMjU0OTE5NjczNjk4LWI1ZjEyNzMzZTdiMTZhOTUyNDRmMWFmNDQ3YTJhOWE5MDZmMjUzZmM1MzgzN2ViM2MzYmMzM2QzMWIxNDdiMjk) scanmem is a debugging utility designed to isolate the address of an arbitrary variable in an executing process. scanmem simply needs to be told the pid of the process and the value of the variable at several different times. After several scans of the process, scanmem isolates the position of the variable and allows you to modify its value. ## GUI GameConqueror is a GUI front-end for scanmem, providing more features, such as: * Flexible syntax for searching * Easier and multiple variable locking * Better process finder * Memory browser/editor See [gui/README](gui/README) for more details. ## Requirements scanmem requires libreadline to read commands interactively, and `/proc` must be mounted. GameConqueror requirements are documented in [gui/README](gui/README). ## Documentation To read documentation: * `man scanmem` * `man gameconqueror` * `scanmem --help` * enter `help` at the scanmem prompt * use the interactive help of GameConqueror ## Build Requirements The build requires autotools-dev, libtool, libreadline-dev, intltool, and python. ## Build and Install To generate files required for the build: ./autogen.sh To build with GUI: ./configure --prefix=/usr --enable-gui && make sudo make install To build without GUI: ./configure --prefix=/usr && make sudo make install scanmem and GameConqueror use static paths to libscanmem. So executing `ldconfig` is not required. Consider setting `--libdir=/usr/lib/scanmem` or `--libdir=/usr/lib64/scanmem` to avoid that libscanmem is in a library search path. Run `./configure --help` for more details. ## Android Build You need a [standalone toolchain of Android NDK](https://developer.android.com/ndk/guides/standalone_toolchain.html#itc) (Advanced method) to build interactive capabilities for Android. For more information, run: ./build_for_android.sh help ## License: GPLv3, LGPLv3 for libscanmem scanmem-0.17/README.md000077700000000000000000000000001317023271400152162READMEustar00rootroot00000000000000scanmem-0.17/TODO000066400000000000000000000011111317023271400135460ustar00rootroot00000000000000Current ======= * option: whether to search in readonly regions * reduce memory consumption (e.g. OVaMI padding) * completely centralize the printing in `show_message`, so that printing to some other output stream is easy to add * see: https://github.com/scanmem/scanmem/issues for further TODOs Future ====== * use PT_IO on freebsd, instead of PT_READ/WRITE_D * add working freebsd support * in targetmem.c, check return value of allocate_enough_to_reach everywhere * search for values in files? (eg saved state) * macro support * automatically support zero and one based values. scanmem-0.17/autogen.sh000077500000000000000000000016011317023271400150630ustar00rootroot00000000000000#!/bin/sh LIBTOOLIZE=libtoolize if [ "$(uname -s)" = "Darwin" ]; then LIBTOOLIZE=glibtoolize # install via brew fi echo "+ running $LIBTOOLIZE ..." $LIBTOOLIZE -c || { echo echo "$LIBTOOLIZE failed - check that it is present on system" exit 1 } echo "+ running aclocal ..." aclocal -I m4 || { echo echo "aclocal failed - check that all needed development files"\ "are present on system" exit 1 } echo "+ running autoheader ... " autoheader || { echo echo "autoheader failed" exit 1 } echo "+ running autoconf ... " autoconf || { echo echo "autoconf failed" exit 1 } echo "+ running intltoolize ..." intltoolize -f -c --automake || { echo echo "intltoolize failed - check that it is present on system" exit 1 } echo "+ running automake ... " automake -c --add-missing || { echo echo "automake failed" exit 1 } scanmem-0.17/build_for_android.sh000077500000000000000000000124401317023271400170710ustar00rootroot00000000000000#!/bin/sh if [ "x$1" = "x--help" ] || [ "x$1" = "xhelp" ] || [ "x$1" = "x-h" ]; then echo " ## Before building: # Run autogen.sh if \"./configure\" does not exist. # From scanmem source directory ./autogen.sh ## For libreadline support: # HOST is the compiler architecture for your toolchain and Android # device. (e.g. arm-linux-androideabi) # NDK_STANDALONE_TOOLCHAIN is the directory of your NDK standalone # toolchain. # Install libreadline to your NDK sysroot. # From libreadline source directory, execute: export PATH=\"\$NDK_STANDALONE_TOOLCHAIN/bin:\$PATH\" bash_cv_wcwidth_broken=false ./configure --host=\"\$HOST\" \\ --disable-shared --enable-static \\ --prefix=\"\$NDK_STANDALONE_TOOLCHAIN/sysroot/usr\" make make install # Install ncurses to your NDK sysgen. # From ncurses source directory, execute: export PATH=\"\$NDK_STANDALONE_TOOLCHAIN/bin:\$PATH\" ac_cv_header_locale_h=no ./configure --host=\"\$HOST\" \\ --disable-shared --enable-static \\ --prefix=\"\$NDK_STANDALONE_TOOLCHAIN/sysroot/usr\" make make install ## Building for Android 5.0 and above requires exporting PIE flags, such as: export CFLAGS="-fPIE" LDFLAGS="-pie" ## To build with standalone toolchain: export NDK_STANDALONE_TOOLCHAIN=\"/your/toolchain/path\" export HOST=\"your-androideabi\" # Default arm-linux-androideabi ./build_for_android.sh ## Advanced features and Environment variables that may be set... NDK_STANDALONE_TOOLCHAIN - A standalone toolchain is required to build full capabilities. HOST - Compiler architecture that will be used for cross-compiling, default is arm-linux-androideabi SCANMEM_HOME - Path which has scanmem sources, and will be used to build scanmem. Default current directory LIBREADLINE_DIR - Path which has libreadline sources to build automatically. Default is to download sources NCURSES_DIR - Path which has ncurses sources to build automatically. Default is to download sources " exit 0 fi # Resolve ndk toolchain or other if [ "x${NDK_STANDALONE_TOOLCHAIN}" = "x" ]; then echo "NDK_STANDALONE_TOOLCHAIN was not found. Please enter the toolchain path:" read NDK_STANDALONE_TOOLCHAIN # Nothing entered if [ "x${NDK_STANDALONE_TOOLCHAIN}" = "x" ]; then echo "Error: Please set \$NDK_STANDALONE_TOOLCHAIN env variable." 1>&2 exit 1 fi fi export SYSROOT="${NDK_STANDALONE_TOOLCHAIN}/sysroot" export PATH="${NDK_STANDALONE_TOOLCHAIN}/bin:${PATH}" # Host architecture if [ "x${HOST}" = "x" ]; then HOST=arm-linux-androideabi echo "Env variable \$HOST, host architecture, is not specified. Defaulting to ${HOST}" fi # Build and return directory if [ "x${SCANMEM_HOME}" = "x" ]; then export SCANMEM_HOME="$(pwd)" else cd "${SCANMEM_HOME}" fi # Processor count for make instructions procnum="$(getconf _NPROCESSORS_ONLN)" if [ "x${procnum}" = "x" ] || [ $procnum -eq 0 ]; then procnum=1 fi # Do not fail for source downloads, workarounds may be found for broken links if [ ! -f "${SYSROOT}/usr/lib/libreadline.a" ]; then # Build libreadline for android if [ "x${LIBREADLINE_DIR}" = "x" ]; then echo "LIBREADLINE_DIR was not found. Please enter the path where libreadline source is located, or press enter to try a source download:" read LIBREADLINE_DIR if [ "x${LIBREADLINE_DIR}" = "x" ]; then echo "Downloading libreadline..." if [ ! -f readline-6.3.tar.gz ]; then wget -c ftp://ftp.gnu.org/gnu/readline/readline-6.3.tar.gz fi tar xvf readline-6.3.tar.gz export LIBREADLINE_DIR="$(pwd)/readline-6.3" fi fi cd "${LIBREADLINE_DIR}" bash_cv_wcwidth_broken=false ./configure --host="${HOST}" \ --disable-shared --enable-static --prefix="${SYSROOT}/usr" make -j ${procnum} make install cd "${SCANMEM_HOME}" # To make sure headers can be found if [ ! -f readline ]; then ln -s "${LIBREADLINE_DIR}" readline fi fi # ncurses, same logic as libreadline if [ ! -f "${SYSROOT}/usr/lib/libncurses.a" ]; then # Build libncurses for android (needed by libreadline) if [ "x${NCURSES_DIR}" = "x" ]; then echo "NCURSES_DIR was not found. Please enter the path where ncurses source is located, or press enter to try a source download:" read NCURSES_DIR if [ "x${NCURSES_DIR}" = "x" ]; then echo "Downloading ncurses..." if [ ! -f ncurses-6.0.tar.gz ]; then wget -c http://invisible-mirror.net/archives/ncurses/ncurses-6.0.tar.gz fi tar xvf ncurses-6.0.tar.gz export NCURSES_DIR="$(pwd)/ncurses-6.0" fi fi cd "${NCURSES_DIR}" ac_cv_header_locale_h=no ./configure --host="${HOST}" \ --disable-shared --enable-static --prefix="${SYSROOT}/usr" make -j ${procnum} make install cd "${SCANMEM_HOME}" # To make sure headers can be found if [ ! -f ncurses ]; then ln -s "${NCURSES_DIR}" ncurses fi fi # Build scanmem for android if [ "$(uname -s)" = "Darwin" ]; then PATH=/usr/local/opt/gettext/bin:${PATH} # brew install gettext fi LIBS="-lncurses -lm" ./configure --host="${HOST}" --prefix="${SYSROOT}/usr" \ --enable-static --disable-shared [ "$?" != "0" ] && exit 1 make -j ${procnum} && make install scanmem-0.17/commands.c000066400000000000000000000105551317023271400150370ustar00rootroot00000000000000/* Registration and general execution of commands. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009,2010 WANG Lu This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include "commands.h" #include "common.h" #include "show_message.h" /* * sm_registercommand - add the command and a pointer to its handler to the commands list. * * So that free(data) works when destroying the list, I just concatenate the string * with the command structure. I could have used a static vector of commands, but this * way I can add aliases and macros at runtime (planned in future). * */ bool sm_registercommand(const char *command, handler_ptr handler, list_t *commands, char *shortdoc, char *longdoc) { command_t *data; assert(commands != NULL); if (command != NULL) { if ((data = malloc(sizeof(command_t) + strlen(command) + 1)) == NULL) { show_error("sorry, there was a memory allocation problem.\n"); return false; } data->command = (char *) data + sizeof(*data); /* command points to the extra space allocated after data */ strcpy(data->command, command); } else { if ((data = malloc(sizeof(command_t))) == NULL) { show_error("sorry, there was a memory allocation problem.\n"); return false; } data->command = NULL; } data->handler = handler; data->shortdoc = shortdoc; data->longdoc = longdoc; /* add new command to list */ if (l_append(commands, NULL, data) == -1) { free(data); return false; } return true; } bool sm_execcommand(globals_t *vars, const char *commandline) { unsigned argc; char *str = NULL, *tok = NULL; char **argv = NULL; command_t *err = NULL; bool ret = false; list_t *commands = vars->commands; element_t *np = NULL; assert(commandline != NULL); assert(commands != NULL); vars->current_cmdline = commandline; np = commands->head; str = tok = strdupa(commandline); /* tokenize command line into an argument vector */ for (argc = 0; tok; argc++, str = NULL) { /* make enough size for another pointer (+1 for NULL at end) */ if ((argv = realloc(argv, (argc + 1) * sizeof(char *))) == NULL) { show_error("sorry there was a memory allocation error.\n"); return false; } /* insert next token */ argv[argc] = tok = strtok(str, " \t"); } assert(argc >= 1); assert(argv != NULL); /* just leading whitespace? */ if (argv[0] == NULL) { free(argv); /* legal I guess, just don't do anything */ return true; } /* search commands list for appropriate handler */ while (np) { command_t *command = np->data; /* check if this command matches */ if (command->command == NULL) { /* the default handler has a NULL command */ err = command; } else if (strcasecmp(argv[0], command->command) == 0) { /* match found, execute handler */ ret = command->handler(vars, argv, argc - 1); free(argv); return ret; } np = np->next; } /* no match, if there was a default handler found, run it now */ if (err != NULL) { ret = err->handler(vars, argv, argc - 1); } free(argv); return ret; } scanmem-0.17/commands.h000066400000000000000000000023711317023271400150410ustar00rootroot00000000000000/* Registration and general execution of commands. This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef COMMANDS_H #define COMMANDS_H #include #include "scanmem.h" typedef bool (*handler_ptr)(globals_t *vars, char **argv, unsigned argc); typedef struct { handler_ptr handler; char *command; char *shortdoc; char *longdoc; } command_t; bool sm_registercommand(const char *command, handler_ptr handler, list_t *commands, char *shortdoc, char *longdoc); bool sm_execcommand(globals_t *vars, const char *commandline); #endif /* COMMANDS_H */ scanmem-0.17/common.h000066400000000000000000000042001317023271400145210ustar00rootroot00000000000000/* Common macro and helpers. Copyright (C) 2017 Andrea Stacchiotti This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef COMMON_H #define COMMON_H #ifndef MIN # define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif /* from string.h in glibc for Android/BSD */ #ifndef strdupa # include # include # define strdupa(s) \ ({ \ const char *__old = (s); \ size_t __len = strlen(__old) + 1; \ char *__new = (char *) alloca(__len); \ (char *) memcpy(__new, __old, __len); \ }) #endif #ifndef strndupa # include # include # define strndupa(s, n) \ ({ \ const char *__old = (s); \ size_t __len = strnlen(__old, (n)); \ char *__new = (char *) alloca(__len + 1); \ __new[__len] = '\0'; \ (char *) memcpy(__new, __old, __len); \ }) #endif #endif /* COMMON_H */ scanmem-0.17/configure.ac000066400000000000000000000107621317023271400153600ustar00rootroot00000000000000AC_INIT([scanmem],[0.17],[https://github.com/scanmem/scanmem]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([-Wall -Werror -Wno-extra-portability foreign]) AC_CONFIG_SRCDIR([main.c]) AC_CONFIG_HEADER([config.h]) AC_USE_SYSTEM_EXTENSIONS AC_HEADER_STDBOOL LT_INIT IT_PROG_INTLTOOL AC_CHECK_FUNCS(memset strcasecmp strchr strdup strerror strtoul getline) if test "x$ac_cv_func_getline" = "xno"; then AC_CHECK_FUNCS(fgetln) if test "x$ac_cv_func_fgetln" = "xno"; then AC_MSG_ERROR([Cannot build without working getline().]) else AC_MSG_NOTICE([Using the fgetln()-based getline() replacement.]) fi fi AM_CONDITIONAL([HAVE_GETLINE], [test "x$ac_cv_func_getline" != "xno"]) AC_CHECK_HEADERS(fcntl.h limits.h stddef.h sys/time.h) AC_FUNC_ALLOCA AC_FUNC_STRTOD AC_TYPE_INT8_T AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_C_BIGENDIAN # Detect the host OS android="no" AC_CANONICAL_HOST case "$host_os" in *android*) android="yes" AC_MSG_NOTICE([Android detected]) ;; linux*) AC_MSG_NOTICE([Linux detected]) ;; *) AC_MSG_NOTICE([Your platform is not currently supported]) ;; esac AS_IF([test "x$android" = "xno"], [ # also need to check if the file is zero'ed (some hardened systems) AC_CHECK_FILE([/proc/self/maps], [], [ echo "This system does not seem to have /proc/pid/maps files." exit 1 ]) # also need to check this file works AC_CHECK_FILE([/proc/self/mem], [ # LARGEFILE support required for this to work AC_SYS_LARGEFILE AC_DEFINE(HAVE_PROCMEM, [1], [Enable /proc/pid/mem support]) ],[ # This will hurt performance. echo "This system does not seem to have /proc/pid/mem files." echo "Falling back to ptrace() only support." AC_DEFINE(HAVE_PROCMEM, [0], [Enable /proc/pid/mem support]) ]) # malloc optimizations without Android AC_FUNC_MALLOC AC_FUNC_REALLOC ], [ # supported on Android AC_SYS_LARGEFILE # /proc/pid/mem is there but reading interesting data fails AC_DEFINE(HAVE_PROCMEM, [0], [Enable /proc/pid/mem support]) ]) # Check for termcap and readline or bypass checking for the libraries. AC_ARG_WITH([readline], [AS_HELP_STRING([--without-readline], [build without readline])]) AM_CONDITIONAL([WITH_READLINE], [test "x$with_readline" != "xno"]) AS_IF([test "x$with_readline" != "xno"], [ # termcap is sometimes required by readline AC_CHECK_LIB([termcap], [tgetent], [], []) AC_CHECK_LIB([readline], [readline], [], [ echo "libreadline could not be found, which is required to continue." echo "The libreadline-dev package may be required." exit 1 ]) ]) GETTEXT_PACKAGE=GameConqueror AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [The domain to use with gettext]) AC_CONFIG_FILES([ Makefile test/Makefile po/Makefile.in ]) # copied from ubuntu-tweak dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) dnl dnl example dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local AC_DEFUN([AS_AC_EXPAND], [ EXP_VAR=[$1] FROM_VAR=[$2] dnl first expand prefix and exec_prefix if necessary prefix_save=$prefix exec_prefix_save=$exec_prefix dnl if no prefix given, then use /usr/local, the default prefix if test "x$prefix" = "xNONE"; then prefix=$ac_default_prefix fi dnl if no exec_prefix given, then use prefix if test "x$exec_prefix" = "xNONE"; then exec_prefix=$prefix fi full_var="$FROM_VAR" dnl loop until it doesn't change anymore while true; do new_full_var="`eval echo $full_var`" if test "x$new_full_var" = "x$full_var"; then break; fi full_var=$new_full_var done dnl clean up full_var=$new_full_var AC_SUBST([$1], "$full_var") dnl restore prefix and exec_prefix prefix=$prefix_save exec_prefix=$exec_prefix_save ]) # end copy # GameConqueror configuration AC_ARG_ENABLE(gui, [AS_HELP_STRING([--enable-gui], [enable gameconqueror, the gui front-end of scanmem])]) AM_CONDITIONAL([ENABLE_GUI], [test "x$enable_gui" = "xyes"]) AS_IF([test "x$enable_gui" = "xyes"], [ AS_AC_EXPAND(PKGDATADIR, $datadir/gameconqueror) AS_AC_EXPAND(LOCALEDIR, $localedir) AS_AC_EXPAND(LIBDIR, $libdir) AM_PATH_PYTHON([2.7]) AC_CONFIG_FILES([ gui/Makefile gui/icons/Makefile gui/consts.py gui/gameconqueror gui/org.freedesktop.gameconqueror.policy.in ]) ]) AC_OUTPUT scanmem-0.17/endianness.h000066400000000000000000000054401317023271400153670ustar00rootroot00000000000000/* Endianness conversion. Copyright (C) 2014 Hraban Luyat Copyright (C) 2015 Sebastian Parschauer This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef ENDIANNESS_H #define ENDIANNESS_H #include "config.h" #include #include #include #include #include "value.h" /* true if host is big endian */ #ifdef WORDS_BIGENDIAN static const bool big_endian = true; #else static const bool big_endian = false; #endif static inline uint8_t swap_bytes8(uint8_t i) { return i; } static inline uint16_t swap_bytes16(uint16_t i) { uint16_t res = i & 0xff; res <<= 8; res |= i >> 8; return res; } static inline uint32_t swap_bytes32(uint32_t i) { uint32_t res = swap_bytes16(i); res <<= 16; res |= swap_bytes16(i >> 16); return res; } static inline uint64_t swap_bytes64(uint64_t i) { uint64_t res = swap_bytes32(i); res <<= 32; res |= swap_bytes32(i >> 32); return res; } // swap endianness of 2, 4 or 8 byte word in-place. static inline void swap_bytes_var(void *p, size_t num) { switch (num) { case sizeof(uint16_t): ; // empty statement to cheat the compiler uint16_t i16 = swap_bytes16(*((uint16_t *)p)); memcpy(p, &i16, sizeof(uint16_t)); return; case sizeof(uint32_t): ; uint32_t i32 = swap_bytes32(*((uint32_t *)p)); memcpy(p, &i32, sizeof(uint32_t)); return; case sizeof(uint64_t): ; uint64_t i64 = swap_bytes64(*((uint64_t *)p)); memcpy(p, &i64, sizeof(uint64_t)); return; } assert(false); return; } static inline void fix_endianness(value_t *data_value, bool reverse_endianness) { if (!reverse_endianness) { return; } if (data_value->flags & flags_64b) { data_value->uint64_value = swap_bytes64(data_value->uint64_value); } else if (data_value->flags & flags_32b) { data_value->uint32_value = swap_bytes32(data_value->uint32_value); } else if (data_value->flags & flags_16b) { data_value->uint16_value = swap_bytes16(data_value->uint16_value); } return; } #endif /* ENDIANNESS_H */ scanmem-0.17/getline.c000066400000000000000000000042561317023271400146660ustar00rootroot00000000000000/* Replace getline() with BSD specific fgetln(). Copyright (C) 2015 JianXiong Zhou Copyright (C) 2015 Jonathan Pelletier This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "getline.h" #if !defined(HAVE_GETLINE) && defined(HAVE_FGETLN) #include #include #include /* * Avoid frequent malloc()/free() calls * (determined by getline() test on Linux) */ #define BUF_MIN 120 ssize_t getline(char **lineptr, size_t *n, FILE *stream) { char *lptr; size_t len = 0; /* check for invalid arguments */ if (lineptr == NULL || n == NULL) { errno = EINVAL; return -1; } lptr = fgetln(stream, &len); if (lptr == NULL) { /* invalid stream */ errno = EINVAL; return -1; } /* * getline() returns a null byte ('\0') terminated C string, * but fgetln() returns characters without '\0' termination */ if (*lineptr == NULL) { *n = BUF_MIN; goto alloc_buf; } /* realloc the original pointer */ if (*n < len + 1) { free(*lineptr); *n = len + 1; alloc_buf: *lineptr = malloc(*n); if (*lineptr == NULL) { *n = 0; return -1; } } /* copy over the string */ memcpy(*lineptr, lptr, len); (*lineptr)[len] = '\0'; /* * getline() and fgetln() both return len including the * delimiter but without the null byte at the end */ return len; } #endif scanmem-0.17/getline.h000066400000000000000000000021011317023271400146560ustar00rootroot00000000000000/* Replace getline() with BSD specific fgetln(). Copyright (C) 2015 JianXiong Zhou Copyright (C) 2015 Jonathan Pelletier This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef GETLINE_H #define GETLINE_H #include "config.h" #ifndef HAVE_GETLINE #include ssize_t getline(char **lineptr, size_t *n, FILE *stream); #endif #endif /* GETLINE_H */ scanmem-0.17/gpl-3.0.txt000066400000000000000000001045131317023271400147110ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . scanmem-0.17/gui/000077500000000000000000000000001317023271400136505ustar00rootroot00000000000000scanmem-0.17/gui/COPYING000066400000000000000000001045131317023271400147070ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. 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. Copyright (C) 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 . 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: Copyright (C) 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 . 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 . scanmem-0.17/gui/GameConqueror.appdata.xml.in000066400000000000000000000024151317023271400211610ustar00rootroot00000000000000 GameConqueror.desktop CC0-1.0 GPL-3.0+ <_name>Game Conqueror <_summary>Game hacking tool. GUI front-end for scanmem. <_p> scanmem is a simple interactive debugging utility, used to locate the address of a variable in an executing process. This can be used for the analysis or modification of a hostile process on a compromised machine, reverse engineering, or as a "pokefinder" to cheat at video games. GameConqueror aims to provide a CheatEngine-alike interface for scanmem, it's user-friendly and easy to use. <_p>Features:
    <_li>Locking on multiple variables <_li>Memory Viewer/Editor <_li>... many to be done
https://raw.githubusercontent.com/scanmem/scanmem/master/gui/screenshot.png https://github.com/scanmem/scanmem i.gnatenko.brain@gmail.com
scanmem-0.17/gui/GameConqueror.desktop.in000066400000000000000000000003311317023271400204140ustar00rootroot00000000000000[Desktop Entry] _Name=Game Conqueror _Comment=A game hacking tool. A GUI front-end for scanmem. Exec=gameconqueror Terminal=false Type=Application Icon=GameConqueror Categories=Development;Utility; StartupNotify=true scanmem-0.17/gui/GameConqueror.py000066400000000000000000001443401317023271400167770ustar00rootroot00000000000000#!/usr/bin/env python """ Game Conqueror: a graphical game cheating tool, using scanmem as its backend Copyright (C) 2009-2011,2013 Wang Lu Copyright (C) 2010 Bryan Cain Copyright (C) 2013 Mattias Muenster Copyright (C) 2014-2016 Sebastian Parschauer Copyright (C) 2016-2017 Andrea Stacchiotti 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 . """ import sys import os import argparse import struct import platform import threading import json import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GObject from gi.repository import GLib from consts import * from hexview import HexView from backend import GameConquerorBackend import misc import locale import gettext # In some locale, ',' is used in float numbers locale.setlocale(locale.LC_NUMERIC, 'C') locale.bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); gettext.install(GETTEXT_PACKAGE, LOCALEDIR, names=('_')); CLIPBOARD = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) WORK_DIR = os.path.dirname(sys.argv[0]) PROGRESS_INTERVAL = 100 # for scan progress updates DATA_WORKER_INTERVAL = 500 # for read(update)/write(lock) HEXEDIT_SPAN = 1024 # hexview half-height SCAN_RESULT_LIST_LIMIT = 10000 # maximal number of entries that can be displayed SCAN_VALUE_TYPES = ['int', 'int8', 'int16', 'int32', 'int64', 'float', 'float32', 'float64', 'number', 'bytearray', 'string'] LOCK_FLAG_TYPES = misc.build_simple_str_liststore(['=', '+', '-']) MEMORY_TYPES = ['int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32', 'int64', 'uint64', 'float32', 'float64', 'bytearray', 'string'] SEARCH_SCOPE_NAMES = [_('Basic'), _('Normal'), _('Full')] # convert type names used by scanmem into ours TYPENAMES_S2G = {'I64':'int64' ,'I64s':'int64' ,'I64u':'uint64' ,'I32':'int32' ,'I32s':'int32' ,'I32u':'uint32' ,'I16':'int16' ,'I16s':'int16' ,'I16u':'uint16' ,'I8':'int8' ,'I8s':'int8' ,'I8u':'uint8' ,'F32':'float32' ,'F64':'float64' ,'bytearray':'bytearray' ,'string':'string' } # convert our typenames into struct format characters TYPENAMES_G2STRUCT = {'int8':'b' ,'uint8':'B' ,'int16':'h' ,'uint16':'H' ,'int32':'i' ,'uint32':'I' ,'int64':'q' ,'uint64':'Q' ,'float32':'f' ,'float64':'d' } # sizes in bytes of integer and float types TYPESIZES = {'int8':1 ,'uint8':1 ,'int16':2 ,'uint16':2 ,'int32':4 ,'uint32':4 ,'int64':8 ,'uint64':8 ,'float32':4 ,'float64':8 } class GameConqueror(): def __init__(self): ################################## # init GUI self.builder = Gtk.Builder() self.builder.set_translation_domain(GETTEXT_PACKAGE) self.builder.add_from_file(os.path.join(WORK_DIR, 'GameConqueror.ui')) self.main_window = self.builder.get_object('MainWindow') self.main_window.set_title('GameConqueror %s' % (VERSION,)) self.about_dialog = self.builder.get_object('AboutDialog') # set version self.about_dialog.set_version(VERSION) self.process_list_dialog = self.builder.get_object('ProcessListDialog') self.addcheat_dialog = self.builder.get_object('AddCheatDialog') # init memory editor self.memoryeditor_window = self.builder.get_object('MemoryEditor_Window') self.memoryeditor_hexview = HexView() self.memoryeditor_window.get_child().pack_start(self.memoryeditor_hexview, True, True, 0) self.memoryeditor_hexview.show_all() self.memoryeditor_address_entry = self.builder.get_object('MemoryEditor_Address_Entry') self.memoryeditor_hexview.connect('char-changed', self.memoryeditor_hexview_char_changed_cb) self.found_count_label = self.builder.get_object('FoundCount_Label') self.process_label = self.builder.get_object('Process_Label') self.value_input = self.builder.get_object('Value_Input') self.scanoption_frame = self.builder.get_object('ScanOption_Frame') self.scanprogress_progressbar = self.builder.get_object('ScanProgress_ProgressBar') self.input_box = self.builder.get_object('Value_Input') self.scan_button = self.builder.get_object('Scan_Button') self.stop_button = self.builder.get_object('Stop_Button') self.reset_button = self.builder.get_object('Reset_Button') ### # Set scan data type self.scan_data_type_combobox = self.builder.get_object('ScanDataType_ComboBoxText') for entry in SCAN_VALUE_TYPES: self.scan_data_type_combobox.append_text(entry) # apply setting misc.combobox_set_active_item(self.scan_data_type_combobox, SETTINGS['scan_data_type']) ### # set search scope self.search_scope_scale = self.builder.get_object('SearchScope_Scale') # apply setting self.search_scope_scale.set_value(SETTINGS['search_scope']) # init scanresult treeview # we may need a cell data func here # create model self.scanresult_tv = self.builder.get_object('ScanResult_TreeView') # liststore contents: addr, value, type, valid, offset, region type, match_id self.scanresult_liststore = Gtk.ListStore(GObject.TYPE_UINT64, str, str, bool, GObject.TYPE_UINT64, str, int) self.scanresult_tv.set_model(self.scanresult_liststore) # init columns misc.treeview_append_column(self.scanresult_tv, _('Address'), 0, hex_col=0, attributes=(('text',0),), properties = (('family', 'monospace'),) ) misc.treeview_append_column(self.scanresult_tv, _('Value'), 1, attributes=(('text',1),), properties = (('family', 'monospace'),) ) misc.treeview_append_column(self.scanresult_tv, _('Offset'), 4, hex_col=4, attributes=(('text',4),), properties = (('family', 'monospace'),) ) misc.treeview_append_column(self.scanresult_tv, _('Region Type'), 5, attributes=(('text',5),), properties = (('family', 'monospace'),) ) # init CheatList TreeView self.cheatlist_tv = self.builder.get_object('CheatList_TreeView') # cheatlist contents: lockflag, locked, description, addr, type, value, valid self.cheatlist_liststore = Gtk.ListStore(str, bool, str, GObject.TYPE_UINT64, str, str, bool) self.cheatlist_tv.set_model(self.cheatlist_liststore) self.cheatlist_editing = False # Lock Flag misc.treeview_append_column(self.cheatlist_tv, '', 0 ,renderer_class = Gtk.CellRendererCombo ,attributes = (('text',0),) ,properties = (('editable', True) ,('has-entry', False) ,('model', LOCK_FLAG_TYPES) ,('text-column', 0) ) ,signals = (('edited', self.cheatlist_toggle_lock_flag_cb), ('editing-started', self.cheatlist_edit_start), ('editing-canceled', self.cheatlist_edit_cancel),) ) # Lock misc.treeview_append_column(self.cheatlist_tv, _('Lock'), 1 ,renderer_class = Gtk.CellRendererToggle ,attributes = (('active',1),) ,properties = (('activatable', True) ,('radio', False) ,('inconsistent', False)) ,signals = (('toggled', self.cheatlist_toggle_lock_cb),) ) # Description misc.treeview_append_column(self.cheatlist_tv, _('Description'), 2 ,attributes = (('text',2),) ,properties = (('editable', True),) ,signals = (('edited', self.cheatlist_edit_description_cb), ('editing-started', self.cheatlist_edit_start), ('editing-canceled', self.cheatlist_edit_cancel),) ) # Address misc.treeview_append_column(self.cheatlist_tv, _('Address'), 3, hex_col=3 ,attributes = (('text',3),) ,properties = (('family', 'monospace'),) ) # Type misc.treeview_append_column(self.cheatlist_tv, _('Type'), 4 ,renderer_class = Gtk.CellRendererCombo ,attributes = (('text',4),) ,properties = (('editable', True) ,('has-entry', False) ,('model', misc.build_simple_str_liststore(MEMORY_TYPES)) ,('text-column', 0)) ,signals = (('edited', self.cheatlist_edit_type_cb), ('editing-started', self.cheatlist_edit_start), ('editing-canceled', self.cheatlist_edit_cancel),) ) # Value misc.treeview_append_column(self.cheatlist_tv, _('Value'), 5 ,attributes = (('text',5),) ,properties = (('editable', True) ,('family', 'monospace')) ,signals = (('edited', self.cheatlist_edit_value_cb), ('editing-started', self.cheatlist_edit_start), ('editing-canceled', self.cheatlist_edit_cancel),) ) # init ProcessList self.processfilter_input = self.builder.get_object('ProcessFilter_Input') self.userfilter_input = self.builder.get_object('UserFilter_Input') # init ProcessList_TreeView self.processlist_tv = self.builder.get_object('ProcessList_TreeView') self.processlist_liststore = Gtk.ListStore(int, str, str) self.processlist_filter = self.processlist_liststore.filter_new(root=None) self.processlist_filter.set_visible_func(self.processlist_filter_func, data=None) self.processlist_tv.set_model(Gtk.TreeModelSort(model=self.processlist_filter)) self.processlist_tv.set_search_column(2) # first col misc.treeview_append_column(self.processlist_tv, 'PID', 0 ,attributes = (('text',0),) ) # second col misc.treeview_append_column(self.processlist_tv, _('User'), 1 ,attributes = (('text',1),) ) # third col misc.treeview_append_column(self.processlist_tv, _('Process'), 2 ,attributes = (('text',2),) ) # get list of things to be disabled during scan self.disablelist = [self.cheatlist_tv, self.scanresult_tv, self.builder.get_object('processGrid'), self.value_input, self.reset_button, self.builder.get_object('buttonGrid'), self.memoryeditor_window] # init AddCheatDialog self.addcheat_address_input = self.builder.get_object('Address_Input') self.addcheat_address_input.override_font(gi.repository.Pango.FontDescription("Monospace")) self.addcheat_description_input = self.builder.get_object('Description_Input') self.addcheat_length_spinbutton = self.builder.get_object('Length_SpinButton') self.addcheat_type_combobox = self.builder.get_object('Type_ComboBoxText') for entry in MEMORY_TYPES: self.addcheat_type_combobox.append_text(entry) misc.combobox_set_active_item(self.addcheat_type_combobox, SETTINGS['lock_data_type']) self.Type_ComboBoxText_changed_cb(self.addcheat_type_combobox) # init popup menu for scanresult self.scanresult_popup = Gtk.Menu() misc.menu_append_item(self.scanresult_popup, _('Add to cheat list'), self.scanresult_popup_cb, 'add_to_cheat_list') misc.menu_append_item(self.scanresult_popup, _('Browse this address'), self.scanresult_popup_cb, 'browse_this_address') misc.menu_append_item(self.scanresult_popup, _('Scan for this address'), self.scanresult_popup_cb, 'scan_for_this_address') misc.menu_append_item(self.scanresult_popup, _('Remove this match'), self.scanresult_delete_selected_matches) self.scanresult_popup.show_all() # init popup menu for cheatlist self.cheatlist_popup = Gtk.Menu() misc.menu_append_item(self.cheatlist_popup, _('Browse this address'), self.cheatlist_popup_cb, 'browse_this_address') misc.menu_append_item(self.cheatlist_popup, _('Copy address'), self.cheatlist_popup_cb, 'copy_address') misc.menu_append_item(self.cheatlist_popup, _('Remove this entry'), self.cheatlist_popup_cb, 'remove_entry') self.cheatlist_popup.show_all() self.builder.connect_signals(self) self.main_window.connect('destroy', self.exit) ########################### # init others (backend, flag...) self.pid = 0 # target pid self.maps = [] self.is_scanning = False self.exit_flag = False # currently for data_worker only, other 'threads' may also use this flag self.backend = GameConquerorBackend(os.path.join(LIBDIR, 'libscanmem.so.1')) self.check_backend_version() self.is_first_scan = True GLib.timeout_add(DATA_WORKER_INTERVAL, self.data_worker) self.command_lock = threading.RLock() self.progress_watcher_id = None ########################### # GUI callbacks # Memory editor def MemoryEditor_Button_clicked_cb(self, button, data=None): if self.pid == 0: self.show_error(_('Please select a process')) return self.browse_memory() return True def MemoryEditor_Handle_Address_cb(self, widget, data=None): txt = self.memoryeditor_address_entry.get_text().strip() if txt == '': return try: addr = int(txt, 16) self.browse_memory(addr) except: self.show_error(_('Invalid address')) # Manually add cheat def ConfirmAddCheat_Button_clicked_cb(self, button, data=None): addr = self.addcheat_address_input.get_text() try: addr = int(addr, 16) addr = GObject.Value(GObject.TYPE_UINT64, addr) except (ValueError, OverflowError): self.show_error(_('Please enter a valid address.')) return False description = self.addcheat_description_input.get_text() if not description: description = _('No Description') typestr = self.addcheat_type_combobox.get_active_text() length = self.addcheat_length_spinbutton.get_value_as_int() if 'int' in typestr: value = 0 elif 'float' in typestr: value = 0.0 elif typestr == 'string': value = ' ' * length elif typestr == 'bytearray': value = '00 ' * length else: value = None self.add_to_cheat_list(addr, value, typestr, description) self.addcheat_dialog.hide() return True def CloseAddCheat_Button_clicked_cb(self, button, data=None): self.addcheat_dialog.hide() return True # Main window def ManuallyAddCheat_Button_clicked_cb(self, button, data=None): self.addcheat_dialog.show() return True def RemoveAllCheat_Button_clicked_cb(self, button, data=None): self.cheatlist_liststore.clear() return True def LoadCheat_Button_clicked_cb(self, button, data=None): dialog = Gtk.FileChooserDialog(title=_("Open.."), transient_for=self.main_window, action=Gtk.FileChooserAction.OPEN, buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) response = dialog.run() if response == Gtk.ResponseType.OK: try: with open(dialog.get_filename(), 'r') as f: obj = json.load(f) for row in obj['cheat_list']: self.add_to_cheat_list(row[3],row[5],row[4],row[2],True) except: pass dialog.destroy() return True def SaveCheat_Button_clicked_cb(self, button, data=None): dialog = Gtk.FileChooserDialog(title=_("Save.."), transient_for=self.main_window, action=Gtk.FileChooserAction.SAVE, buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) dialog.set_default_response(Gtk.ResponseType.OK) dialog.set_do_overwrite_confirmation(True) response = dialog.run() if response == Gtk.ResponseType.OK: try: with open(dialog.get_filename(), 'w') as f: obj = {'cheat_list' : [list(i) for i in self.cheatlist_liststore]} json.dump(obj, f); except: pass dialog.destroy() return True def SearchScope_Scale_format_value_cb(self, scale, value, data=None): return SEARCH_SCOPE_NAMES[int(value)] def Value_Input_activate_cb(self, entry, data=None): self.do_scan() return True def ScanResult_TreeView_popup_menu_cb(self, widget, data=None): pathlist = self.scanresult_tv.get_selection().get_selected_rows()[1] if len(pathlist): self.scanresult_popup.popup(None, None, None, None, 0, 0) return True return False def ScanResult_TreeView_button_press_event_cb(self, widget, event, data=None): # add to cheat list (model, pathlist) = self.scanresult_tv.get_selection().get_selected_rows() if event.button == 1 and event.get_click_count()[1] > 1: # left double click for path in pathlist: (addr, value, typestr) = model.get(model.get_iter(path), 0, 1, 2) self.add_to_cheat_list(addr, value, typestr) elif event.button == 3: # right click path = self.scanresult_tv.get_path_at_pos(int(event.x),int(event.y)) if path is not None: self.scanresult_popup.popup(None, None, None, None, event.button, event.get_time()) return path[0] in pathlist return False def CheatList_TreeView_button_press_event_cb(self, widget, event, data=None): if event.button == 3: # right click pathlist = self.cheatlist_tv.get_selection().get_selected_rows()[1] path = self.cheatlist_tv.get_path_at_pos(int(event.x),int(event.y)) if path is not None: self.cheatlist_popup.popup(None, None, None, None, event.button, event.get_time()) return path[0] in pathlist return False def CheatList_TreeView_popup_menu_cb(self, widget, data=None): pathlist = self.cheatlist_tv.get_selection().get_selected_rows()[1] if len(pathlist): self.cheatlist_popup.popup(None, None, None, None, 0, 0) return True return False def Scan_Button_clicked_cb(self, button, data=None): self.do_scan() return True def Stop_Button_clicked_cb(self, button, data=None): self.backend.set_stop_flag(True) return True def Reset_Button_clicked_cb(self, button, data=None): self.reset_scan() return True def Logo_EventBox_button_release_event_cb(self, widget, data=None): self.about_dialog.run() self.about_dialog.hide() return True # Process list def ProcessFilter_Input_changed_cb(self, widget, data=None): self.ProcessList_Refilter_Generic() def UserFilter_Input_changed_cb(self, widget, data=None): self.ProcessList_Refilter_Generic() def ProcessList_Refilter_Generic(self): self.processlist_filter.refilter() self.processlist_tv.set_cursor(0) def ProcessList_TreeView_row_activated_cb(self, treeview, path, view_column, data=None): (model, iter) = self.processlist_tv.get_selection().get_selected() if iter is not None: (pid, process) = model.get(iter, 0, 2) self.select_process(pid, process) self.process_list_dialog.response(Gtk.ResponseType.CANCEL) return True return False def SelectProcess_Button_clicked_cb(self, button, data=None): self.processlist_liststore.clear() pl = self.get_process_list() for p in pl: self.processlist_liststore.append([p[0], (p[1] if len(p) > 1 else _('')), (p[2] if len(p) > 2 else _(''))]) self.process_list_dialog.show() while True: res = self.process_list_dialog.run() if res == Gtk.ResponseType.OK: # -5 (model, iter) = self.processlist_tv.get_selection().get_selected() if iter is None: self.show_error(_('Please select a process')) continue else: (pid, process) = model.get(iter, 0, 2) self.select_process(pid, process) break else: # for None and Cancel break self.process_list_dialog.hide() return True ####################### # customed callbacks # (i.e. not standard event names are used) # Callback to hide window when 'X' button is pressed def hide_window_on_delete_event_cb(self, widget, event, data=None): widget.hide() return True # Memory editor def memoryeditor_hexview_char_changed_cb(self, hexview, offset, charval): addr = hexview.base_addr + offset self.write_value(addr, 'int8', charval) # return False such that the byte the default handler will be called, and will be displayed correctly return False def memoryeditor_key_press_event_cb(self, window, event, data=None): keycode = event.keyval pressedkey = Gdk.keyval_name(keycode) if pressedkey == 'w' and (event.state & Gdk.ModifierType.CONTROL_MASK): self.memoryeditor_window.hide() elif pressedkey == 'Escape': self.memoryeditor_window.hide() def MemoryEditor_Refresh_Button_clicked_cb(self, button, data=None): dlength = len(self.memoryeditor_hexview.payload) data = self.read_memory(self.memoryeditor_hexview.base_addr, dlength) if data is None: self.memoryeditor_window.hide() self.show_error(_('Cannot read memory')) return old_addr = self.memoryeditor_hexview.get_current_addr() self.memoryeditor_hexview.payload = misc.str2bytes(data) self.memoryeditor_hexview.show_addr(old_addr) # Manually add cheat def focus_on_next_widget_cb(self, widget, data=None): widget.get_toplevel().child_focus(Gtk.DirectionType.TAB_FORWARD) return True def Type_ComboBoxText_changed_cb(self, combo_box): data_type = combo_box.get_active_text() if data_type in TYPESIZES: self.addcheat_length_spinbutton.set_value(TYPESIZES[data_type]) self.addcheat_length_spinbutton.set_sensitive(False) else: self.addcheat_length_spinbutton.set_sensitive(True) # Main window def cheatlist_edit_start(self, a, b, c): self.cheatlist_editing = True def cheatlist_edit_cancel(self, a): self.cheatlist_editing = False def scanresult_delete_selected_matches(self, menuitem, data=None): (model, pathlist) = self.scanresult_tv.get_selection().get_selected_rows() match_id_list = ','.join(str(model.get_value(model.get_iter(path), 6)) for path in pathlist) self.command_lock.acquire() self.backend.send_command('delete {}'.format(match_id_list)) self.update_scan_result() self.command_lock.release() def scanresult_popup_cb(self, menuitem, data=None): (model, pathlist) = self.scanresult_tv.get_selection().get_selected_rows() if data == 'add_to_cheat_list': for path in reversed(pathlist): (addr, value, typestr) = model.get(model.get_iter(path), 0, 1, 2) self.add_to_cheat_list(addr, value, typestr) return True addr = model.get_value(model.get_iter(pathlist[0]), 0) if data == 'browse_this_address': self.browse_memory(addr) return True elif data == 'scan_for_this_address': self.scan_for_addr(addr) return True return False def value_input_key_press_event_cb(self, main_window, event, data=None): keycode = event.keyval pressedkey = Gdk.keyval_name(keycode) if pressedkey == 'j' and (event.state & Gdk.ModifierType.CONTROL_MASK): if self.cheatlist_tv.is_focus() == self.scanresult_tv.is_focus(): self.scanresult_tv.grab_focus() self.scanresult_tv.set_cursor(0) else: self.value_input.grab_focus() def ScanResult_TreeView_key_press_event_cb(self, scanresult_tv, event, data=None): keycode = event.keyval pressedkey = Gdk.keyval_name(keycode) if pressedkey == 'Return': (model, pathlist) = self.scanresult_tv.get_selection().get_selected_rows() for path in reversed(pathlist): (addr, value, typestr) = model.get(model.get_iter(path), 0, 1, 2) self.add_to_cheat_list(addr, value, typestr) elif pressedkey in {'Delete', 'BackSpace'}: self.scanresult_delete_selected_matches(None) elif pressedkey == 'j' and (event.state & Gdk.ModifierType.CONTROL_MASK): self.cheatlist_tv.grab_focus() if self.cheatlist_tv.get_cursor()[0] is not None: curpos = self.cheatlist_tv.get_cursor()[0] valcol = self.cheatlist_tv.get_column(5) self.cheatlist_tv.set_cursor(curpos, valcol) def CheatList_TreeView_key_press_event_cb(self, cheatlist_tv, event, data=None): keycode = event.keyval pressedkey = Gdk.keyval_name(keycode) if pressedkey in {'Delete', 'BackSpace'}: (model, pathlist) = self.cheatlist_tv.get_selection().get_selected_rows() for path in reversed(pathlist): self.cheatlist_liststore.remove(model.get_iter(path)) elif pressedkey == 'j' and (event.state & Gdk.ModifierType.CONTROL_MASK): self.scanresult_tv.grab_focus() if self.scanresult_tv.get_cursor()[0] is not None: self.scanresult_tv.set_cursor(0) def cheatlist_popup_cb(self, menuitem, data=None): self.cheatlist_editing = False (model, pathlist) = self.cheatlist_tv.get_selection().get_selected_rows() if data == 'remove_entry': for path in reversed(pathlist): self.cheatlist_liststore.remove(model.get_iter(path)) return True addr = model.get_value(model.get_iter(pathlist[0]), 3) if data == 'browse_this_address': self.browse_memory(addr) return True elif data == 'copy_address': addr = '%x' %(addr,) CLIPBOARD.set_text(addr, len(addr)) return True return False def cheatlist_toggle_lock(self, row): if self.cheatlist_liststore[row][6]: # valid locked = self.cheatlist_liststore[row][1] locked = not locked self.cheatlist_liststore[row][1] = locked if locked: #TODO: check value(valid number & not overflow), if failed, unlock it and do nothing pass else: #TODO: update its value? pass return True def cheatlist_toggle_lock_cb(self, cellrenderertoggle, row_str, data=None): pathlist = self.cheatlist_tv.get_selection().get_selected_rows()[1] if not row_str: return True cur_row = int(row_str) # check if the current row is part of the selection found = False for path in pathlist: row = path[0] if row == cur_row: found = True break if not found: self.cheatlist_toggle_lock(cur_row) return True # the current row is part of the selection for path in pathlist: row = path[0] self.cheatlist_toggle_lock(row) return True def cheatlist_toggle_lock_flag_cb(self, cell, path, new_text, data=None): self.cheatlist_editing = False # currently only one lock flag is supported return True row = int(path) self.cheatlist_liststore[row][0] = new_text # data_worker will handle this later return True def cheatlist_edit_description_cb(self, cell, path, new_text, data=None): self.cheatlist_editing = False pathlist = self.cheatlist_tv.get_selection().get_selected_rows()[1] for path in pathlist: row = path[0] self.cheatlist_liststore[row][2] = new_text return True def cheatlist_edit_value_cb(self, cell, path, new_text, data=None): self.cheatlist_editing = False # ignore empty value if new_text == '': return True pathlist = self.cheatlist_tv.get_selection().get_selected_rows()[1] for path in pathlist: row = path[0] if not self.cheatlist_liststore[row][6]: #not valid continue self.cheatlist_liststore[row][5] = new_text if self.cheatlist_liststore[row][1]: # locked # data_worker will handle this pass else: (addr, typestr, value) = self.cheatlist_liststore[row][3:6] self.write_value(addr, typestr, value) return True def cheatlist_edit_type_cb(self, cell, path, new_text, data=None): self.cheatlist_editing = False pathlist = self.cheatlist_tv.get_selection().get_selected_rows()[1] for path in pathlist: row = path[0] (addr, typestr, value) = self.cheatlist_liststore[row][3:6] if new_text == typestr: continue if new_text in {'bytearray', 'string'}: self.cheatlist_liststore[row][5] = self.bytes2value(new_text, self.read_memory(addr, self.get_type_size(typestr, value))) self.cheatlist_liststore[row][4] = new_text self.cheatlist_liststore[row][1] = False # unlock return True # Process list def processlist_filter_func(self, model, iter, data=None): (user, process) = model.get(iter, 1, 2) return process is not None and \ self.processfilter_input.get_text().lower() in process.lower() and \ user is not None and \ self.userfilter_input.get_text().lower() in user.lower() ############################ # core functions def show_error(self, msg): dialog = Gtk.MessageDialog(transient_for=self.main_window, modal=True, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text=msg) dialog.run() dialog.destroy() # return None if unknown def get_pointer_width(self): bits = platform.architecture()[0] if not bits.endswith('bit'): return None try: bitn = int(bits[:-len('bit')]) if bitn not in {8,16,32,64}: return None else: return bitn except: return None # return the size in bytes of the value in memory def get_type_size(self, typename, value): if typename in TYPESIZES: # int or float type; fixed length return TYPESIZES[typename] elif typename == 'bytearray': return (len(value.strip())+1)/3 elif typename == 'string': return len(misc.encode(value)) return None # parse bytes dumped by scanmem into number, string, etc. def bytes2value(self, typename, databytes): if databytes is None: return None if typename in TYPENAMES_G2STRUCT: return struct.unpack(TYPENAMES_G2STRUCT[typename], databytes)[0] elif typename == 'string': return misc.decode(databytes, 'replace') elif typename == 'bytearray': databytes = misc.str2bytes(databytes) return ' '.join(['%02x' %(i,) for i in databytes]) else: return databytes def scan_for_addr(self, addr): bits = self.get_pointer_width() if bits is None: self.show_error(_('Unknown architecture, you may report to developers')) return self.reset_scan() self.value_input.set_text('%#x'%(addr,)) misc.combobox_set_active_item(self.scan_data_type_combobox, 'int%d'%(bits,)) self.do_scan() def browse_memory(self, addr=None): # select a region contains addr try: self.read_maps() except: self.show_error(_('Cannot retrieve memory maps of that process, maybe it has ' 'exited (crashed), or you don\'t have enough privileges')) return selected_region = None if addr is not None: for m in self.maps: if m['start_addr'] <= addr and addr < m['end_addr']: selected_region = m break if selected_region: if selected_region['flags'][0] != 'r': # not readable self.show_error(_('Address %x is not readable') % (addr,)) return else: self.show_error(_('Address %x is not valid') % (addr,)) return else: # just select the first readable region for m in self.maps: if m['flags'][0] == 'r': selected_region = m break if selected_region is None: self.show_error(_('Cannot find a readable region')) return addr = selected_region['start_addr'] # read region if possible start_addr = max(addr - HEXEDIT_SPAN, selected_region['start_addr']) end_addr = min(addr + HEXEDIT_SPAN, selected_region['end_addr']) data = self.read_memory(start_addr, end_addr - start_addr) if data is None: self.show_error(_('Cannot read memory')) return self.memoryeditor_hexview.payload = misc.str2bytes(data) self.memoryeditor_hexview.base_addr = start_addr # set editable flag self.memoryeditor_hexview.editable = (selected_region['flags'][1] == 'w') if addr is not None: self.memoryeditor_hexview.show_addr(addr) self.memoryeditor_window.show() # this callback will be called from other thread def progress_watcher(self): Gdk.threads_enter() self.scanprogress_progressbar.set_fraction(self.backend.get_scan_progress()) Gdk.threads_leave() return True def add_to_cheat_list(self, addr, value, typestr, description=_('No Description'), at_end=False): # determine longest possible type types = typestr.split() vt = typestr for t in types: if t in TYPENAMES_S2G: vt = TYPENAMES_S2G[t] break if at_end: self.cheatlist_liststore.append(['=', False, description, addr, vt, str(value), True]) else: self.cheatlist_liststore.prepend(['=', False, description, addr, vt, str(value), True]) def get_process_list(self): plist = [] for proc in os.popen('ps -wweo pid=,user:16=,command= --sort=-pid').readlines(): (pid, user, pname) = [tok.strip() for tok in proc.split(None, 2)] plist.append((int(pid), user, pname)) return plist def select_process(self, pid, process_name): # ask backend for attaching the target process # update 'current process' # reset flags # for debug/log self.pid = pid try: self.read_maps() except: self.pid = 0 self.process_label.set_text(_('No process selected')) self.process_label.set_property('tooltip-text', _('Select a process')) self.show_error(_('Cannot retrieve memory maps of that process, maybe it has ' 'exited (crashed), or you don\'t have enough privileges')) else: self.process_label.set_text('%d - %s' % (pid, process_name)) self.process_label.set_property('tooltip-text', process_name) self.command_lock.acquire() self.backend.send_command('pid %d' % (pid,)) self.reset_scan() self.command_lock.release() # unlock all entries in cheat list for i in range(len(self.cheatlist_liststore)): self.cheatlist_liststore[i][1] = False def read_maps(self): lines = open('/proc/%d/maps' % (self.pid,)).readlines() self.maps = [] for l in lines: item = {} info = l.split(' ', 5) addr = info[0] idx = addr.index('-') item['start_addr'] = int(addr[:idx],16) item['end_addr'] = int(addr[idx+1:],16) item['size'] = item['end_addr'] - item['start_addr'] item['flags'] = info[1] item['offset'] = info[2] item['dev'] = info[3] item['inode'] = int(info[4]) if len(info) < 6: item['pathname'] = '' else: item['pathname'] = info[5].lstrip() # don't use strip self.maps.append(item) def reset_scan(self): # reset search type and value type self.scanresult_liststore.clear() self.command_lock.acquire() self.backend.send_command('reset') self.update_scan_result() self.command_lock.release() self.scanprogress_progressbar.set_fraction(0.0) self.scanoption_frame.set_sensitive(True) self.is_first_scan = True self.value_input.grab_focus() def apply_scan_settings (self): # scan data type assert(self.scan_data_type_combobox.get_active() >= 0) datatype = self.scan_data_type_combobox.get_active_text() # Tell the scanresult sort function if a numeric cast is needed isnumeric = ('int' in datatype or 'float' in datatype or 'number' in datatype) self.scanresult_liststore.set_sort_func(1, misc.value_compare, (1, isnumeric)) self.command_lock.acquire() self.backend.send_command('option scan_data_type %s' % (datatype,)) # search scope self.backend.send_command('option region_scan_level %d' %(1 + int(self.search_scope_scale.get_value()),)) # TODO: ugly, reset to make region_scan_level taking effect self.backend.send_command('reset') self.command_lock.release() # perform scanning through backend # set GUI if needed def do_scan(self): if self.pid == 0: self.show_error(_('Please select a process')) return assert(self.scan_data_type_combobox.get_active() >= 0) data_type = self.scan_data_type_combobox.get_active_text() cmd = self.value_input.get_text() try: cmd = misc.check_scan_command(data_type, cmd, self.is_first_scan) except Exception as e: # this is not quite good self.show_error(e.args[0]) return # disable the window before perform scanning, such that if result come so fast, we won't mess it up self.scanoption_frame.set_sensitive(False) # disable set of widgets interfering with the scan for wid in self.disablelist: wid.set_sensitive(False) # Replace scan_button with stop_button self.scan_button.set_visible(False) self.stop_button.set_visible(True) self.is_scanning = True # set scan options only when first scan, since this will reset backend if self.is_first_scan: self.apply_scan_settings() self.is_first_scan = False self.progress_watcher_id = GLib.timeout_add(PROGRESS_INTERVAL, self.progress_watcher, priority=GLib.PRIORITY_DEFAULT_IDLE) threading.Thread(target=self.scan_thread_func, args=(cmd,)).start() def scan_thread_func(self, cmd): self.command_lock.acquire() self.backend.send_command(cmd) GLib.source_remove(self.progress_watcher_id) Gdk.threads_enter() self.scanprogress_progressbar.set_fraction(1.0) # enable set of widgets interfering with the scan for wid in self.disablelist: wid.set_sensitive(True) # Replace stop_button with scan_button self.stop_button.set_visible(False) self.scan_button.set_visible(True) self.value_input.grab_focus() self.is_scanning = False self.update_scan_result() Gdk.threads_leave() self.command_lock.release() def update_scan_result(self): match_count = self.backend.get_match_count() self.found_count_label.set_text(_('Found: %d') % (match_count,)) if (match_count > SCAN_RESULT_LIST_LIMIT): self.scanresult_liststore.clear() else: self.command_lock.acquire() list_bytes = self.backend.send_command('list', get_output=True) self.command_lock.release() lines = filter(None, misc.decode(list_bytes).split('\n')) self.scanresult_tv.set_model(None) # temporarily disable model for scanresult_liststore for the sake of performance self.scanresult_liststore.clear() if misc.PY3K: addr = GObject.Value(GObject.TYPE_UINT64) off = GObject.Value(GObject.TYPE_UINT64) for line in lines: (mid, line) = line.split(']', 1) mid = int(mid.strip(' []')) (addr_str, off_str, rt, val, t) = list(map(str.strip, line.split(',')[:5])) t = t.strip(' []') if t == 'unknown': continue # `insert_with_valuesv` has the same function of `append`, but it's 7x faster # PY3 has problems with int's, so we need a forced guint64 conversion # See: https://bugzilla.gnome.org/show_bug.cgi?id=769532 # Still 5x faster even with the extra baggage if misc.PY3K: addr.set_uint64(int(addr_str, 16)) off.set_uint64(int(off_str.split('+')[1], 16)) else: addr = long(addr_str, 16) off = long(off_str.split('+')[1], 16) self.scanresult_liststore.insert_with_valuesv(-1, [0, 1, 2, 3, 4, 5, 6], [addr, val, t, True, off, rt, mid]) # self.scanresult_liststore.append([addr, val, t, True, off, rt, mid]) self.scanresult_tv.set_model(self.scanresult_liststore) # return range(r1, r2) where all rows between r1 and r2 (EXCLUSIVE) are visible # return range(0, 0) if no row visible def get_visible_rows(self, treeview): _range = treeview.get_visible_range() try: r1 = _range[0][0] r2 = _range[1][0] + 1 except: r1 = r2 = 0 return range(r1, r2) # read/write data periodically def data_worker(self): if (not self.is_scanning) and (self.pid != 0) and self.command_lock.acquire(0): # non-blocking Gdk.threads_enter() # Write to memory locked values in cheat list for i in self.cheatlist_liststore: if i[1] and i[6]: # locked and valid self.write_value(i[3], i[4], i[5]) # addr, typestr, value # Update visible (and unlocked) cheat list rows rows = self.get_visible_rows(self.cheatlist_tv) for i in rows: lockflag, locked, desc, addr, typestr, value, valid = self.cheatlist_liststore[i] if valid and not locked: newvalue = self.read_value(addr, typestr, value) if newvalue is None: self.cheatlist_liststore[i] = (lockflag, False, desc, addr, typestr, '??', False) elif newvalue != value and not self.cheatlist_editing: self.cheatlist_liststore[i] = (lockflag, locked, desc, addr, typestr, str(newvalue), valid) # Update visible scanresult rows rows = self.get_visible_rows(self.scanresult_tv) for i in rows: row = self.scanresult_liststore[i] addr, cur_value, scanmem_type, valid = row[:4] if valid: new_value = self.read_value(addr, TYPENAMES_S2G[scanmem_type.split(' ', 1)[0]], cur_value) if new_value is not None: row[1] = str(new_value) else: row[1] = '??' row[3] = False Gdk.threads_leave() self.command_lock.release() return not self.exit_flag def read_value(self, addr, typestr, prev_value): return self.bytes2value(typestr, self.read_memory(addr, self.get_type_size(typestr, prev_value))) # addr could be int or str def read_memory(self, addr, length): if not isinstance(addr,str): addr = '%x'%(addr,) self.command_lock.acquire() data = self.backend.send_command('dump %s %d' %(addr, length), get_output=True) self.command_lock.release() # TODO raise Exception here isn't good if len(data) != length: # self.show_error('Cannot access target memory') data = None return data # addr could be int or str def write_value(self, addr, typestr, value): if not isinstance(addr,str): addr = '%x'%(addr,) self.command_lock.acquire() self.backend.send_command('write %s %s %s'%(typestr, addr, value)) self.command_lock.release() def exit(self, object, data=None): self.exit_flag = True self.backend.exit_cleanup() Gtk.main_quit() def check_backend_version(self): if self.backend.get_version() != VERSION: self.show_error(_('Version of scanmem mismatched, you may encounter problems. Please make sure you are using the same version of GameConqueror as scanmem.')) if __name__ == '__main__': # Parse cmdline arguments parser = argparse.ArgumentParser(prog='GameConqueror', description=_("A GUI for scanmem, a game hacking tool"), epilog=_('Report bugs to ' + PACKAGE_BUGREPORT + '.')) parser.add_argument('-s', '--search', metavar='val', dest='search_value', help=_('prefill the search box')) parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + VERSION) parser.add_argument("pid", nargs='?', type=int, help=_("PID of the process")) args = parser.parse_args() # Init application GObject.threads_init() Gdk.threads_init() gc_instance = GameConqueror() # Attach to given pid (if any) if (args.pid is not None) : process_name = os.popen('ps -p ' + str(args.pid) + ' -o command=').read().strip() gc_instance.select_process(args.pid, process_name) # Prefill the search box (if asked) if (args.search_value is not None) : gc_instance.input_box.set_text(args.search_value) # Start Gtk.main() scanmem-0.17/gui/GameConqueror.ui000066400000000000000000001666701317023271400167760ustar00rootroot00000000000000 1 1024 1 10 2 1 10 True False list-add True False edit-delete True False gtk-edit True False edit-find True False go-jump True False document-open True False view-refresh True False gtk-refresh 280 False GameConqueror - Memory Editor False GameConqueror_128x128.png utility True False 5 vertical True False True False 5 5 _Address: True MemoryEditor_Address_Entry False True 0 True True Target address here (CTRL+L) 5 True Enter address to view True True 1 28 28 True True True True Jump to address (CTRL+ENTER) 2 jumpto_image False True 2 28 28 True True True True _Refresh (CTRL+R) refresh_image2 False True 3 True False True True 4 False False 0 True False document-save True False stop True False computer True False GameConqueror center 550 550 GameConqueror_128x128.png True True vertical True False 200 True 200 True False vertical True False start 5 2 2 Found: 0 False False 0 True True True True True multiple True True 1 True False True False 5 5 True False True False Syntax: For numeric types: <span font_family="monospace"> <b>N</b> Exactly this number <b>N..M</b> Range between two numbers <b>?</b> Unknown initial value <b>&gt; or +</b> Increased values <b>&lt; or -</b> Decreased values <b>=</b> Unchanged values <b>!=</b> Changed values <b>&gt; N</b> Greater than N <b>&lt; N</b> Less than N <b>+ N</b> Increased by N <b>- N</b> Decreased by N <b>!= N</b> Other than N </span> where N could be a number of a simple math expression embraced by (), e.g. <span font_family="monospace"> 12 0x34 5.67 (- 8 * 9)</span> For bytearray: use 2-char hexadecimal notation for each byte, separated by spaces e.g. <span font_family="monospace">FE DC BA 98 76 54 32 10</span> <span font_family="monospace">??</span> can be used as a wildcard value For string: enter the string directly _Value: <span color="blue"><u>?</u></span> True True Value_Input True 0 0 True True Enter search value here (CTRL+K/L) 5 5 True True Insert value to search for (CTRL+K/L) 1 0 32 32 True True True True Scan (CTRL+ENTER) 1 find_image 2 0 32 32 True True False Reset (CTRL+R) 1 refresh_image 3 0 32 32 True True Stop scan 1 stop_image 2 0 0 1 True False 5 72 72 True False About False 72 72 True False GameConqueror_72x72.png 1 0 2 True False start 5 True True 0 1 True False 5 32 32 True True True Select a process (CTRL+F) system_image 0 0 True False Select a process No process selected end 1 0 0 0 0 0 True False 0 out True False 10 10 5 True False 5 True False 1 0 True False Specify type of target data start Data _Type: True ScanDataType_ComboBoxText 0 0 True False True 2 0 2 True False <b>Basic:</b> Fastest but may miss some values <b>Normal:</b> Works for most cases <b>Full:</b> Never miss values but slowest start _Search Scope: True SearchScope_Scale 0 1 120 True True SearchScope_Scale_Adjustment 0 bottom 1 1 True False 0 2 True False 2 32 32 True True True Memory Editor (CTRL+SHIFT+M) edit_image 5 0 32 32 True True True Save address list to file (CTRL+S) save_image 3 0 32 32 True True True Load address list from file (CTRL+O) load_image 2 0 32 32 True True True Remove all entries (CTRL+D) delete_image 1 0 32 32 True True True Manually add an entry (CTRL+M) add_image 0 0 True False True 4 0 0 3 True False False False True True 50 True True True multiple True False False 5 dialog MainWindow GameConqueror Copyright (C) 2009-2014 WANG Lu <coolwanglu@gmail.com> Copyright (C) 2010 Bryan Cain Copyright (C) 2015-2016 Sebastian Parschauer <s.parschauer@gmx.de> Copyright (C) 2016 Andrea Stacchiotti <andreastacchiotti(a)gmail.com> Special thanks: Igor Gnatenko, Mattias Muenster, Zhang Zhe A graphical frontend of scanmem https://github.com/scanmem/scanmem Homepage Wang Lu <coolwanglu(a)gmail.com> GameConqueror_128x128.png gpl-3-0 True False 2 True False end False True end 0 False 5 Manually add an entry False True GameConqueror_128x128.png dialog MainWindow True False vertical 5 True False end _Add True True False True False False 0 _Close True True False True False False 1 False True end 0 True False 2 5 True False Memory address of the value start Addres_s: True Address_Input 0 0 True False Give a brief description of the value start _Description: True Description_Input 0 1 True False Specify type of target data start _Type: True Type_ComboBoxText 0 2 True True 1 0 True True 1 1 True False 1 2 True False Specify data length (string/bytearray only) start _Length: True Length_SpinButton 0 3 True True True Length_SpinButton_Adjustment 1 3 True True 0 ConfirmAddCheat_Button CloseAddCheat_Button False 5 Type to search for a process True 400 500 GameConqueror_128x128.png dialog MainWindow True False vertical 5 True False end _OK True True True True True True False False 0 _Cancel True True True True False False 1 False True end 2 True False 0 in True False 10 True False 5 5 True True True 1 0 True True True 1 1 True False start _User: True UserFilter_Input 0 1 True False start _Process: True ProcessFilter_Input 0 0 True False Filter True False True 0 True True True True True True 1 ProcessListDialog_OK_Button ProcessListDialog_Cancel_Button scanmem-0.17/gui/GameConqueror_128x128.png000066400000000000000000002006351317023271400201500ustar00rootroot00000000000000PNG  IHDR>agAMA abKGD pHYs  tIME  tEXtCommentCreated with GIMPW IDATx@2+ f^%*  * F'9N/C] "/ɴ h7Pn1Fa ʶѼ4 .?Ec Ѽ|ZGg(9OĪqU- IDATz Y +;#01Hc!f XV} Zt"V[  +?Vư.!eY yE#u fX~  )$2 "(8X[ W~  ǭ  # 7/.A[ (:O (7 :Z )̹n{C dM"2E:Sr(hȳ~Mp"0BƬͶMlv+:>Y| &i/+ 3Ie/D]wӿ2ӿ6'6ju&VKm §7ζ' %dT. _ лç12 H Fd)U DZvwQ--|BB +B$$#.-.? ; QQ #2-[r. IDATD%2 (%! ($  _WW L**/ )G$3G͹ 68 >0 $'    i/ 9![[  ! (D(8λ+N P/+<&4 *$Z9{BB v@@*_ c%5J#3F (8 ZI]] K)) 8) 8 ?(:Q,@W %4ZW dd 8 7+< /  /9Ss ,>V ZV ii 0 B$$!  9".iZ T..m;;=^eps9  {">SS TBp~ϹV\B wbb Z?s^D_ ѼfCaȳӿ fۮ^^ ZCsc2C%%9 R_"2E!/@ʲ˶ c55 C%%HOgλN&Na3Kg-@X ˴d :JJ@m(8 O^ IDAT?.ϸηG __ R i0F_ DZW {CC*|m$4HW2v??M 2Gb  WW ZN U2Ie̴ Q,,: Y 6Mj  #eLL }C?3Jf̵ r>> yP  Ed л (Lt>>f $4I$3Gȯ !-9 yAA  z @DcҾ}!/A  P$Gl::j^Q~Sw  rо&7MRoj99 V| ů#1` al::  X*w[=Xy Y: "a^ )d5"1D H KK!'}(=TNp ¦ѿ_ j r˷N  z Ad"/«w-=  %2%5I _ =`U..AT..](Oq@_ l@6,8 IDAT1 g  ĭҾ yb55 5K)=U%ZF .B[LA^   %( cOSx(6ưsIK _ ¥ѽEʵ6);R Y?ì2$4G Ҿadӿ@"&]ȳI9 &4Ca !ĩx tv-?5Mi %4ưιf k%6J!/B%3 ˷.J K1   - R%K5EB$$   q B$$GJJ '+0 IDAT,&   9 o <rY11  S F%%EFFh88&(Y RRJ[22%  M[[ \222 O++3ee $ J((@JJa2z ĚTTYY &>nlXXe66/ <8  PP(ll &~6!F&&F&&+ Z;%c  j}DD%YY3Bkk 2 ! 12N**`33>""_CV uu  #2%GZ-ghh X 6Gcc rT : 8`` [f, IDAT &Cbb  r_ m\\ B$$F V.. KK  z al::r>> ! x]9]22E%%  M Y?'$  *<IDATϏcesQIENDB`scanmem-0.17/gui/GameConqueror_48x48.png000066400000000000000000000052671317023271400200160ustar00rootroot00000000000000PNG  IHDR00WbKGD pHYs  tIME;utEXtCommentCreated with GIMPW IDAThYkpT~s~n. l"A.@EAP:ТbU@Ǫe:*V"JafP@IH 7rfl6MsiAc|}۸.7n(F t:&%gє++%nm;c5|vp"9_~/)gY aX;؇nk}υSOW nѓf̞e*.6J1RR0[ZGSnz]m{w.T <0`/uy=.TTs"E^zP1M;)8ڛۿvZD.I;THZ @~3o@qǂu0pu?6o/)-6D٣wT_ LwwTU?r`X1>@3澓_T!|~5"B|W22hcerM(s(>1ypWP0/1GMu6RjThI\Mcg ==tXFa"7rEzf8e(˩B.qbS! qwxƃmz\R9ԡƹL<!8XSVVU`Q"KKnFI6{1C~WT*tyxyvl0$+Li}'s&nU + fV7.Օٹ?1s3>`]tctxxnfqvq+ /]Ley6"Tðp4G[mIϠYCz^E8B&e= 8PJ0,c=GK7K%rTG, A3B #kʿ){w[+Ytu4DžFdJ-0,@GG'G/_\ܶJxW*Qax*gb0d66OLf4Cd2^X\.4lo Dkh%\a3A.8@x|o6nO[0IENDB`scanmem-0.17/gui/GameConqueror_72x72.png000066400000000000000000000123531317023271400200020ustar00rootroot00000000000000PNG  IHDRHHUGgAMA abKGDC pHYs   vpAgHHyIDATxytUսǿ{鎹7H B"SPp`EN8U;<{j{9Zg}UZTBH $!;a?R!$]w{gٿopQ.E('l\ q Nbl̎P!a`$X@>nɗNj vwAfb+Ӹ/Ag]2oƘ1qi#A_p x]j_n T%D(4Qg]7go+je%5 G1hW ex7<|60MbM-q,Zs˃|9dKC´;DPn;[;vQGVGT%2c.fLKLv,:13ޢ ҫ-8is!qv~o?w7iP8^$CZs<uf K{ި9'Yiv9V?f#'d.rЇC۶tyH]f93#0Zǻ?d&߾ꖇ/\mw]>=KMN6>g""`4儌쒵甬3ENEOjodOTy&4&gݒWalCރ;_`l",쎴ܥ|jJհ秶uqi."W1P6WU(sN榡KnHZ$= oUۻ^0!g %qκE %T E@Ӹ/Ӹ=3yC< #!9צ+՝6G>8|gx1|o1k80@r*sr(IKXqQrw^6R^PQ ێ{=c!E嗳/3'+ ሪ_uǺAPxSv7ΧCWݸh%6y=pݟuvnyyX2!YFP}b3%@fm'5UE$RG*:c$XXr=ԥVߒoT\i # ٽl3,fk:1r40 @ ) BRE7l?M"SR1Q2Z$eBfi*"OS(^@uEʼaу`_ޖyzi<?Ưe-9\JJ1B9xQGLw={YϿ$lGK}}[7G)wֹȪ6zW5f٢Ko^3ƒCκw վ7{7|dmUū55_ 7\-V5rٻz'֖ f Ԗj1/0&tXS޼kF kÐ׵+([v\|gkkݻŮmdF-?QӤDB Dԫj (+ʔjJ(zU xB(wʡ>&-mOB8A>놂n.)ɜ0!ٽ{,}NE݄mO4) 9EB qB)DċQOChzwoYV/Yrev|<BUmͯmbiQMEO~ ȔDQW$Z(!MUc&2˻\#1Zw5zN')ݬȮpBÙ2 #!;FNcB)xAcYuSVܴ<+K6!Y;[z]ʫXo!th`z`Ա&PGA `O'sK* E2 Fcyyf.[kwkQ'jPGsBT>Ӏ=\5vd0fU.YðߓWq-Űqʬed*TMEmO?ޅk-[ot:9=o[=時Gu*N-+-`Glbf(e,౻U%B \pWd[^/ n!l~$2 LS|ρy'^g˘-c7__.Z9vB`T:놂΀խ,-Tm@[-H}FP92о}3yA:/= J90AfWNZ? VaƋz( `A2W$e/*6&VAy@xw{;[82ص5u-_hJQJ4[ ;b+ (fJSR$3ċ,Wkj?p`}Z~y@k 4ufK%^'رbxaDu +oB{{Iش@@)dYV m30U*PA{g7AE ڱ(!&QBl6B`a@y;c;\@߀;ay$qzO?)!`qPrGHWiImd)JN2'' _vw`96\`Ң'__lsAbQl%iQeNN5*Ek0;qOGǞX~0)֏)!`E"˯_]{Ҹ\yf0VQggUDU>@$b,MK )TƑ8$Lv08Ys|"!lh8p=o͚eK32NqR@W׉ݟ@(V۝M&$##N/*tE P $A4EAYdUǾ[Y_++KSBplhHC}\@T|d"wm*.>g\rhm}Jc`PC1F'RBξ6͏k 8=##xqM! 3y\b nKLc @)&PJM&x~lur0aE3a{Kˏ+srn?1`X{%vߨ۹?^sy¿Wf'kΝۜNi Fd$8(=0hs81MUcU==O̵َ;M~p&nUvs,]d6S(B8Ț@$FA922rix]]o"_]Ru٣9Nvbqj*ju! l”(<,wCF?: ÛbnzAe,H0V+$ &QGΈ.E( ).TzctEXtCommentCreated with GIMPW%tEXtcreate-date2009-12-12T20:08:57+08:00ţ%tEXtmodify-date2009-12-12T19:30:08+08:00IENDB`scanmem-0.17/gui/Makefile.am000066400000000000000000000020341317023271400157030ustar00rootroot00000000000000SUBDIRS = icons gameconquerordir=$(datadir)/gameconqueror python_PYTHON = backend.py consts.py misc.py hexview.py GameConqueror.py pythondir = $(gameconquerordir) dist_bin_SCRIPTS = gameconqueror dist_gameconqueror_DATA = GameConqueror.ui GameConqueror_128x128.png \ GameConqueror_72x72.png GameConqueror_48x48.png dist_desktop_in_files = GameConqueror.desktop.in dist_desktop_DATA = $(dist_desktop_in_files:.desktop.in=.desktop) @INTLTOOL_DESKTOP_RULE@ dist_polkit_in_files = org.freedesktop.gameconqueror.policy.in dist_polkit_DATA = $(dist_polkit_in_files:.policy.in=.policy) @INTLTOOL_POLICY_RULE@ dist_appdata_in_files = GameConqueror.appdata.xml.in dist_appdata_DATA = $(dist_appdata_in_files:.xml.in=.xml) @INTLTOOL_XML_RULE@ dist_man_MANS = gameconqueror.1 dist_doc_DATA = README TODO COPYING EXTRA_DIST = gameconqueror.in consts.py.in desktopdir=$(datadir)/applications polkitdir=$(datadir)/polkit-1/actions appdatadir=$(datadir)/appdata install-data-hook: chmod +x $(DESTDIR)$(gameconquerordir)/GameConqueror.py scanmem-0.17/gui/README000066400000000000000000000017141317023271400145330ustar00rootroot00000000000000# ![](https://raw.githubusercontent.com/scanmem/scanmem/master/gui/GameConqueror_48x48.png) GameConqueror Game Conqueror is a graphical game cheating tool under Linux, a frontend for scanmem. ## Requirements ``` libscanmem (latest version) Python (2.x / 3.x) PyGObject GTK+ 3 policykit ``` ## Usage The interface is similar to the Windows-only Cheat Engine. - Enter a value to search in the Value entry, then press Enter or the scan buttons * Check supported syntax hovering over the '?' mark * You scan choose the search rehion/type via the dropdowns. * Number of matches will be shown in 'Found: XX' at topleft * Matches will be displayed in the left list if there are not too many - Add a candidate address to the list below by double-click on it or use the context-menu - There you can edit description, value, and lock it. ## Authors WANG Lu Andrea Stacchiotti ## License GPLv3 scanmem-0.17/gui/README.md000077700000000000000000000000001317023271400160022READMEustar00rootroot00000000000000scanmem-0.17/gui/TODO000066400000000000000000000007561317023271400143500ustar00rootroot00000000000000TODOs ===== - show error messages - different color for different addresses (heap, stack, module etc) - limit size of bytes read for memory editor - popup in memory editor -- like `add entry for this address` - icon in processlist - custom address region scan - different types of locking ('+' & '-') - plugin-like system to support different datatypes (or actually representations) - different representation (dec, hex ...) for cheatlist - reasonable parsing of simple math in several inputs scanmem-0.17/gui/backend.py000066400000000000000000000054101317023271400156110ustar00rootroot00000000000000""" GameConquerorBackend: communication with scanmem Copyright (C) 2010,2011,2013 Wang Lu 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 . """ import sys import os import ctypes import tempfile import misc class GameConquerorBackend(): BACKEND_FUNCS = { 'sm_init' : (ctypes.c_bool, ), 'sm_cleanup' : (None, ), 'sm_set_backend' : (None, ), 'sm_backend_exec_cmd' : (None, ctypes.c_char_p), 'sm_get_num_matches' : (ctypes.c_ulong, ), 'sm_get_version' : (ctypes.c_char_p, ), 'sm_get_scan_progress' : (ctypes.c_double, ), 'sm_set_stop_flag' : (ctypes.c_bool, ) } def __init__(self, libpath='libscanmem.so'): self.lib = ctypes.CDLL(libpath) self.init_lib_functions() self.lib.sm_set_backend() self.lib.sm_init() self.send_command('reset') self.version = '' def init_lib_functions(self): for k,v in GameConquerorBackend.BACKEND_FUNCS.items(): f = getattr(self.lib, k) f.restype = v[0] f.argtypes = v[1:] # `get_output` will return in a string what libscanmem would print to stdout def send_command(self, cmd, get_output = False): if get_output: with tempfile.TemporaryFile() as directed_file: backup_stdout_fileno = os.dup(sys.stdout.fileno()) os.dup2(directed_file.fileno(), sys.stdout.fileno()) self.lib.sm_backend_exec_cmd(ctypes.c_char_p(misc.encode(cmd))) os.dup2(backup_stdout_fileno, sys.stdout.fileno()) os.close(backup_stdout_fileno) directed_file.seek(0) return directed_file.read() else: self.lib.sm_backend_exec_cmd(ctypes.c_char_p(misc.encode(cmd))) def get_match_count(self): return self.lib.sm_get_num_matches() def get_version(self): return misc.decode(self.lib.sm_get_version()) def get_scan_progress(self): return self.lib.sm_get_scan_progress() def set_stop_flag(self, stop_flag): self.lib.sm_set_stop_flag(stop_flag) def exit_cleanup(self): self.lib.sm_cleanup() scanmem-0.17/gui/consts.py.in000066400000000000000000000005111317023271400161350ustar00rootroot00000000000000# constants # should be defined by autotools VERSION = '@VERSION@' LIBDIR = '@LIBDIR@' LOCALEDIR = '@LOCALEDIR@' GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@' PACKAGE_BUGREPORT = '@PACKAGE_BUGREPORT@' SETTINGS = {'scan_data_type':'int32' ,'lock_data_type':'int32' ,'search_scope' : 1 # normal } scanmem-0.17/gui/gameconqueror.1000066400000000000000000000020411317023271400165760ustar00rootroot00000000000000.TH gameconqueror 1 "2017-10-11" "gameconqueror-0.17" .SH NAME gameconqueror \- A GUI for scanmem, a game hacking tool. .SH SYNOPSIS .B gameconqueror .RB [options] .IR [target-program-pid] .SH DESCRIPTION .PP Scanmem is a simple interactive debugging utility for Linux, used to locate the address of a variable in a running process. This can be used for the analysis or modification of a hostile process on a compromised machine, for reverse engineering, or as a "pokefinder" to cheat at video games. .PP GameConqueror is a GUI for scanmem, aims to provide more features than scanmem, and a CheatEngine-like user-friendly interface. .PP .SH OPTIONS .TP .B "\-v, \-\-version" Print version and exit. .TP .B "\-h, \-\-help" Print a short description of command line options and exit. .TP .BI "\-s, \-\-search=" value Fill the search box with the given .IR value "." .SH HOMEPAGE https://github.com/scanmem/scanmem .SH AUTHORS WANG Lu .br Andrea Stacchiotti .SH SEE ALSO scanmem(1) scanmem-0.17/gui/gameconqueror.in000066400000000000000000000002541317023271400170500ustar00rootroot00000000000000#!/bin/bash DATADIR=@PKGDATADIR@ PKEXEC=$(command -v "pkexec") if [ -n "$PKEXEC" ]; then $PKEXEC $DATADIR/GameConqueror.py "$@" else echo "install policykit!" fi scanmem-0.17/gui/hexview.py000066400000000000000000000560411317023271400157070ustar00rootroot00000000000000""" HexView: memory editor with hex and ascii output Extended by Wang Lu and Sebastian Parschauer * Added editing and cursor moving stuff * Port to GTK 3 Copyright (C) 2010,2011,2013 WANG Lu Copyright (C) 2015 Sebastian Parschauer First version Copyright (C) 2008, 2009 Adriano Monteiro Marques Author: Francesco Piccinno 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 . """ from gi.repository import Gtk from gi.repository import Pango from gi.repository import GObject from gi.repository import GLib class BaseText(Gtk.TextView): __gtype_name__ = 'BaseText' def __init__(self, parent): super(BaseText, self).__init__() self.buffer = self.get_buffer() self._parent = parent self.override_font(Pango.FontDescription(parent.font)) self.set_editable(False) self.texttag = self.buffer.create_tag(None) GObject.type_register(BaseText) class OffsetText(BaseText): __gtype_name__ = 'OffsetText' def __init__(self, parent): super(OffsetText, self).__init__(parent) self.off_len = 1 self.connect('button-press-event', self.__on_button_press) self.connect('realize', self.__on_realize) self.set_cursor_visible(False) self.texttag.set_property('weight', Pango.Weight.BOLD) def __on_button_press(self, widget, evt): return True def __on_realize(self, widget): """ TODO self.modify_base(Gtk.StateType.NORMAL, self.get_style().dark[Gtk.StateType.NORMAL]) """ return True def render(self, txt): self.buffer.set_text('') base_addr = self._parent.base_addr bpl = self._parent.bpl tot_lines = int(len(txt) / bpl) if len(txt) % bpl != 0: tot_lines += 1 self.off_len = len('%x'%(base_addr+len(txt),)) output = [] for i in range(tot_lines): output.append(("%0" + str(self.off_len) + "x") % (base_addr + i*bpl)) if output: self.buffer.insert_with_tags( self.buffer.get_end_iter(), "\n".join(output), self.texttag ) def do_get_preferred_width(self): ctx = self.get_pango_context() font = ctx.load_font(Pango.FontDescription(self._parent.font)) metric = font.get_metrics(ctx.get_language()) w = metric.get_approximate_char_width() / Pango.SCALE * (self.off_len + 1) w += 2 return w,w def do_get_preferred_height(self): return 0,0 class AsciiText(BaseText): __gtype_name__ = 'AsciiText' _printable = \ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%" \ "&'()*+,-./:;<=>?@[\]^_`{|}~ " def __init__(self, parent): super(AsciiText, self).__init__(parent) self.connect('key-press-event', self.__on_key_press) self.connect('button-release-event', self.__on_button_release) self.connect_after('move-cursor', self.__on_move_cursor) self.prev_start = None self.prev_end = None """ TODO self.texttag.set_property( 'background-gdk', self.get_style().text_aa[Gtk.StateType.NORMAL] ) """ def __on_move_cursor(self, textview, step_size, count, extend_selection, data=None): buffer = self.get_buffer() insert_mark = buffer.get_insert() insert_iter = buffer.get_iter_at_mark(insert_mark) insert_off = insert_iter.get_offset() if not extend_selection: if count > 0: # try to move forward if insert_iter.is_end() or \ step_size != Gtk.MovementStep.VISUAL_POSITIONS: # at end or line down: stay insert_iter.backward_char() else: # move forward if (insert_off+1) % (self._parent.bpl+1) == 0: insert_iter.forward_char() elif count < 0: # try to move backward if not insert_iter.is_start() and \ step_size == Gtk.MovementStep.VISUAL_POSITIONS: # move backward (at start or line up: stay) if (insert_off+1) % (self._parent.bpl+1) == 1: insert_iter.backward_char() insert_iter.backward_char() # set end always to one char in front of start end_iter = insert_iter.copy() end_iter.forward_char() # select one char buffer.select_range(insert_iter, end_iter) return True def __on_key_press(self, widget, evt, data=None): if not self._parent.editable: return False c = evt.keyval if c < 256 and (chr(c) in AsciiText._printable): buffer = self.get_buffer() bounds = buffer.get_selection_bounds() if bounds and (bounds[1].get_offset() - bounds[0].get_offset() > 1): self.select_a_char() else: iter = buffer.get_iter_at_mark(buffer.get_insert()) off = iter.get_offset() org_off = off - int(off / (self._parent.bpl + 1)) self._parent.emit('char-changed', org_off, c) self.select_a_char(buffer.get_iter_at_offset(off+1)) return True return False def __on_button_release(self, widget, event, data=None): buffer = self.get_buffer() bounds = buffer.get_selection_bounds() if (not bounds) or (bounds[1].get_offset() - bounds[0].get_offset() == 1): self.select_a_char() # return False in order to let other handler handle it return False # we want at least one char is selected all the time def select_a_char(self, insert_iter = None): buffer = self.get_buffer() if insert_iter is None: insert_iter = buffer.get_iter_at_mark(buffer.get_insert()) insert_off = insert_iter.get_offset() if (insert_off+1) % (self._parent.bpl+1) == 0: if insert_iter.is_end(): end_iter = insert_iter.copy() insert_iter.backward_char() else: insert_iter.forward_char() end_iter = insert_iter.copy() end_iter.forward_char() else: end_iter = insert_iter.copy() end_iter.forward_char() buffer.select_range(insert_iter, end_iter) self.scroll_to_iter(insert_iter, 0, False, 0, 0) def render(self, txt): self.buffer.set_text('') bpl = self._parent.bpl tot_lines = int(len(txt) / bpl) if len(txt) % bpl != 0: tot_lines += 1 output = [] convert = lambda i: "".join( [chr(x) if (chr(x) in AsciiText._printable) else ('.') for x in list(i)]) for i in range(tot_lines): if i * bpl + bpl > len(txt): output.append( convert(txt[i * bpl:]) ) else: output.append( convert(txt[i * bpl:(i * bpl) + bpl]) ) if output: self.buffer.insert_with_tags( self.buffer.get_end_iter(), "\n".join(output), self.texttag ) def do_get_preferred_width(self): ctx = self.get_pango_context() font = ctx.load_font(Pango.FontDescription(self._parent.font)) metric = font.get_metrics(ctx.get_language()) w = metric.get_approximate_char_width() / Pango.SCALE * self._parent.bpl w += 2 return w,w def do_get_preferred_height(self): return 0,0 # start and end are offset to the original text def select_blocks(self, start=None, end=None): if not start and not end: # deselect if self.prev_start and self.prev_end and \ self.prev_start != self.prev_end: self.buffer.remove_tag(self.texttag, self.buffer.get_iter_at_mark(self.prev_start), self.buffer.get_iter_at_mark(self.prev_end)) self.buffer.delete_mark(self.prev_start) self.prev_start = None self.buffer.delete_mark(self.prev_end) self.prev_end = None return bpl = self._parent.bpl start += start/bpl end += end/bpl if self.prev_start and self.prev_end: if self.buffer.get_iter_at_mark(self.prev_start).get_offset() == start \ and self.buffer.get_iter_at_mark(self.prev_end).get_offset() == end: # nothing to do return else: # remove old selection self.buffer.remove_tag(self.texttag, self.buffer.get_iter_at_mark(self.prev_start), self.buffer.get_iter_at_mark(self.prev_end)) # apply new selection start_iter = self.buffer.get_iter_at_offset(start) end_iter = self.buffer.get_iter_at_offset(end) self.buffer.apply_tag(self.texttag, start_iter, end_iter) if self.prev_start: self.buffer.move_mark(self.prev_start, start_iter) else: self.prev_start = self.buffer.create_mark(None, start_iter, True) if self.prev_end: self.buffer.move_mark(self.prev_end, end_iter) else: self.prev_end = self.buffer.create_mark(None, end_iter, False) class HexText(BaseText): __gtype_name__ = 'HexText' _hexdigits = '0123456789abcdefABCDEF' def __init__(self, parent): super(HexText, self).__init__(parent) self.connect('realize', self.__on_realize) self.connect('button-release-event', self.__on_button_release) self.connect('key-press-event', self.__on_key_press) self.connect_after('move-cursor', self.__on_move_cursor) self.prev_start = None self.prev_end = None """ TODO self.texttag.set_property( 'background-gdk', self.get_style().mid[Gtk.StateType.NORMAL] ) """ def __on_key_press(self, widget, evt, data=None): if not self._parent.editable: return False char = evt.keyval if char < 256 and (chr(char) in HexText._hexdigits): buffer = self.get_buffer() bounds = buffer.get_selection_bounds() if bounds and (bounds[1].get_offset() - bounds[0].get_offset() > 1): self.select_a_char() else: c = chr(char).upper() iter = buffer.get_iter_at_mark(buffer.get_insert()) off = iter.get_offset() pos = off % 3 org_off = int(off / 3) txt = buffer.get_text( buffer.get_iter_at_offset(org_off*3), buffer.get_iter_at_offset(org_off*3+2), True) if pos < 2: l = list(txt) l[pos] = c self._parent.emit('char-changed', org_off, int(''.join(l),16)) self.select_a_char(buffer.get_iter_at_offset(off+1)) return True return False def __on_button_release(self, widget, event, data=None): buffer = self.get_buffer() bounds = buffer.get_selection_bounds() if (not bounds) or (bounds[1].get_offset() - bounds[0].get_offset() == 1): self.select_a_char() # return False in order to let other handler handle it return False # we want at least one char is selected all the time def select_a_char(self, insert_iter = None): buffer = self.get_buffer() if insert_iter is None: insert_iter = buffer.get_iter_at_mark(buffer.get_insert()) insert_off = insert_iter.get_offset() if insert_off % 3 == 2: if insert_iter.is_end(): end_iter = insert_iter.copy() insert_iter.backward_char() else: insert_iter.forward_char() end_iter = insert_iter.copy() end_iter.forward_char() else: end_iter = insert_iter.copy() end_iter.forward_char() buffer.select_range(insert_iter, end_iter) self.scroll_to_iter(insert_iter, 0, False, 0, 0) def __on_move_cursor(self, textview, step_size, count, extend_selection, data=None): buffer = self.get_buffer() insert_mark = buffer.get_insert() insert_iter = buffer.get_iter_at_mark(insert_mark) insert_off = insert_iter.get_offset() if not extend_selection: if count > 0: # try to move forward if insert_iter.is_end() or \ step_size != Gtk.MovementStep.VISUAL_POSITIONS: # at end or line down: stay insert_iter.backward_char() else: # move forward if insert_off % 3 == 2: insert_iter.forward_char() elif count < 0: # try to move backward if not insert_iter.is_start() and \ step_size == Gtk.MovementStep.VISUAL_POSITIONS: # move backward (at start or line up: stay) if insert_off % 3 == 0: insert_iter.backward_char() insert_iter.backward_char() # set end always to one char in front of start end_iter = insert_iter.copy() end_iter.forward_char() # select one char buffer.select_range(insert_iter, end_iter) return True def __on_realize(self, widget): """ TODO self.modify_base(Gtk.StateType.NORMAL, self.get_style().mid[Gtk.StateType.NORMAL]) """ return True def render(self, txt): self.buffer.set_text('') bpl = self._parent.bpl tot_lines = int(len(txt) / bpl) if len(txt) % bpl != 0: tot_lines += 1 output = [] convert = lambda x: '%02X'%(x,) for i in range(tot_lines): if i * bpl + bpl > len(txt): output.append( " ".join(map(convert, txt[i * bpl:])) ) else: output.append( " ".join(map(convert, txt[i * bpl:(i * bpl) + bpl])) ) if output: self.buffer.insert_with_tags( self.buffer.get_end_iter(), "\n".join(output).upper(), self.texttag ) def do_get_preferred_width(self): ctx = self.get_pango_context() font = ctx.load_font(Pango.FontDescription(self._parent.font)) metric = font.get_metrics(ctx.get_language()) w = metric.get_approximate_char_width() / Pango.SCALE * \ (self._parent.bpl * 3 - 1) w += 2 return w,w def do_get_preferred_height(self): return 0,0 # start and end are offset to the original text def select_blocks(self, start=None, end=None): if not start and not end: # deselect if self.prev_start and self.prev_end and \ self.prev_start != self.prev_end: self.buffer.remove_tag(self.texttag, self.buffer.get_iter_at_mark(self.prev_start), self.buffer.get_iter_at_mark(self.prev_end)) self.buffer.delete_mark(self.prev_start) self.prev_start = None self.buffer.delete_mark(self.prev_end) self.prev_end = None return start *= 3 end = end * 3 -1 if self.prev_start and self.prev_end: if self.buffer.get_iter_at_mark(self.prev_start).get_offset() == start \ and self.buffer.get_iter_at_mark(self.prev_end).get_offset() == end: return else: # remove old selection self.buffer.remove_tag(self.texttag, self.buffer.get_iter_at_mark(self.prev_start), self.buffer.get_iter_at_mark(self.prev_end)) start_iter = self.buffer.get_iter_at_offset(start) end_iter = self.buffer.get_iter_at_offset(end) self.buffer.apply_tag(self.texttag, start_iter, end_iter) if self.prev_start: self.buffer.move_mark(self.prev_start, start_iter) else: self.prev_start = self.buffer.create_mark(None, start_iter, True) if self.prev_end: self.buffer.move_mark(self.prev_end, end_iter) else: self.prev_end = self.buffer.create_mark(None, end_iter, False) class HexView(Gtk.Box): __gtype_name__ = 'HexView' __gsignals__ = { 'char-changed' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_BOOLEAN, (int,int)) } def __init__(self): super(HexView, self).__init__(homogeneous=False, spacing=4) self.set_border_width(4) self._bpl = 16 self._font = "Monospace 10" self._payload = "" self._base_addr = 0; self.editable = False self.vadj = Gtk.Adjustment() self.vscroll = Gtk.Scrollbar.new(Gtk.Orientation.VERTICAL, self.vadj) self.offset_text = OffsetText(self) self.hex_text = HexText(self) self.ascii_text = AsciiText(self) self.offset_text.set_vadjustment(self.vadj) self.hex_text.set_vadjustment(self.vadj) self.ascii_text.set_vadjustment(self.vadj) self.hex_text.buffer.connect('mark-set', self.__on_hex_change) self.ascii_text.buffer.connect('mark-set', self.__on_ascii_change) def scroll(widget): widget.set_size_request(-1, 128) frame = Gtk.ScrolledWindow.new(None, self.vadj) # frame.set_shadow_type(Gtk.ShadowType.IN) frame.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER) frame.add(widget) frame.connect('scroll-event', self.__on_scroll_event) return frame self.pack_start(scroll(self.offset_text), expand=False, fill=False, padding=0) self.pack_start(scroll(self.hex_text), expand=False, fill=False, padding=0) self.pack_start(scroll(self.ascii_text), expand=False, fill=False, padding=0) self.pack_end(self.vscroll, False, False, 0) # self.connect('char-changed', self.do_char_changed) def __on_scroll_event(self, widget, event, data=None): self.vscroll.emit('scroll-event', event.copy()) return True # scroll to the addr # select the byte at addr # set focus def show_addr(self, addr): GLib.idle_add(self.show_addr_helper, addr) def show_addr_helper(self, addr): off = addr - self._base_addr off *= 3 buf = self.hex_text.get_buffer() off_iter = buf.get_iter_at_offset(off) self.hex_text.scroll_to_iter(off_iter, 0, True, 0, 0) iter2 = off_iter.copy() iter2.forward_char() buf.select_range(off_iter, iter2) self.hex_text.grab_focus() def get_current_addr(self): buf = self.hex_text.get_buffer() buf_iter = buf.get_iter_at_mark(buf.get_insert()) off = buf_iter.get_offset() return off//3 + self._base_addr def do_realize(self): Gtk.Box.do_realize(self) # set font self.modify_font(self._font) def __on_hex_change(self, buffer, iter, mark): return True def __on_ascii_change(self, buffer, iter, mark): return True def do_char_changed(self, offset, charval): hex_buffer = self.hex_text.get_buffer() ascii_buffer = self.ascii_text.get_buffer() # set text # set hex iter1 = hex_buffer.get_iter_at_offset(offset * 3) iter2 = hex_buffer.get_iter_at_offset(offset * 3 + 2) hex_buffer.delete(iter1, iter2) hex_buffer.insert(iter1, '%02X'%(charval,)) # set ascii iter1 = ascii_buffer.get_iter_at_offset(offset + offset / self._bpl) iter2 = ascii_buffer.get_iter_at_offset(offset + offset / self._bpl + 1) ascii_buffer.delete(iter1, iter2) char = chr(charval) ascii_buffer.insert(iter1, (char in AsciiText._printable and char or '.')) return True def get_payload(self): return self._payload def set_payload(self, val): self._payload = val for view in {self.offset_text, self.hex_text, self.ascii_text}: # Invalidate previous iters if hasattr(view, 'prev_start'): view.prev_start = None if hasattr(view, 'prev_end'): view.prev_end = None view.render(self._payload) def get_font(self): return self._font def modify_font(self, val): try: desc = Pango.FontDescription(val) self._font = val for view in {self.offset_text, self.hex_text, self.ascii_text}: view.override_font(desc) except Exception: pass def get_bpl(self): return self._bpl def set_bpl(self, val): self._bpl = val # Redraw! self.payload = self.payload def get_base_addr(self): return self._base_addr def set_base_addr(self, val): self._base_addr = val view = self.offset_text # Invalidate previous iters if hasattr(view, 'prev_start'): view.prev_start = None if hasattr(view, 'prev_end'): view.prev_end = None view.render(self._payload) payload = property(get_payload, set_payload) font = property(get_font, modify_font) bpl = property(get_bpl, set_bpl) base_addr = property(get_base_addr, set_base_addr) GObject.type_register(HexView) if __name__ == "__main__": def char_changed_handler(hexview, offset, charval): #print 'handler:','%X' % (offset,), chr(charval), '%02X' % (charval,) return False w = Gtk.Window() w.resize(500,500) view = HexView() view.payload = "Woo welcome this is a simple read/only HexView widget for PacketManipulator"*16 view.base_addr = 0x6fff000000000000; # view.connect('char-changed', char_changed_handler) view.editable = True w.add(view) w.show_all() w.connect('delete-event', lambda *w: Gtk.main_quit()) Gtk.main() scanmem-0.17/gui/icons/000077500000000000000000000000001317023271400147635ustar00rootroot00000000000000scanmem-0.17/gui/icons/128x128/000077500000000000000000000000001317023271400157205ustar00rootroot00000000000000scanmem-0.17/gui/icons/128x128/GameConqueror.png000077700000000000000000000000001317023271400261102../../GameConqueror_128x128.pngustar00rootroot00000000000000scanmem-0.17/gui/icons/48x48/000077500000000000000000000000001317023271400155625ustar00rootroot00000000000000scanmem-0.17/gui/icons/48x48/GameConqueror.png000077700000000000000000000000001317023271400256142../../GameConqueror_48x48.pngustar00rootroot00000000000000scanmem-0.17/gui/icons/72x72/000077500000000000000000000000001317023271400155545ustar00rootroot00000000000000scanmem-0.17/gui/icons/72x72/GameConqueror.png000077700000000000000000000000001317023271400256002../../GameConqueror_72x72.pngustar00rootroot00000000000000scanmem-0.17/gui/icons/Makefile.am000066400000000000000000000006611317023271400170220ustar00rootroot00000000000000dist_icon48_DATA = 48x48/GameConqueror.png dist_icon72_DATA = 72x72/GameConqueror.png dist_icon128_DATA = 128x128/GameConqueror.png EXTRA_DIST = $(icons48_DATA) $(icons72_DATA) $(icons128_DATA) icon48dir = $(datadir)/icons/hicolor/48x48/apps icon72dir = $(datadir)/icons/hicolor/72x72/apps icon128dir = $(datadir)/icons/hicolor/128x128/apps install-data-hook: @echo "Remember to run gtk-update-icon-cache if icons don't show up." scanmem-0.17/gui/misc.py000066400000000000000000000162411317023271400151610ustar00rootroot00000000000000""" Misc functions for Game Conqueror Copyright (C) 2010,2011,2013 Wang Lu Copyright (C) 2013 Mattias Copyright (C) 2016 Andrea Stacchiotti 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 . """ import sys from gi.repository import Gtk PY3K = sys.version_info >= (3, 0) # check command syntax, data range etc. # return a valid scanmem command # raise if something is invalid def check_scan_command (data_type, cmd, is_first_scan): if cmd == '': raise ValueError(_('No value provided')) if data_type == 'string': return '" ' + cmd cmd = cmd.strip() # hack for snapshot/update (TODO: make it possible with string) if cmd == '?': if is_first_scan: return 'snapshot' else: return 'update' if data_type == 'bytearray': bytes = cmd.split(' ') for byte in bytes: if byte.strip() == '': continue if len(byte) != 2: raise ValueError(_('Bad value: %s') % (byte, )) if byte == '??': continue try: _tmp = int(byte,16) except: raise ValueError(_('Bad value: %s') % (byte, )) return cmd else: # for numbers is_operator_cmd = cmd in {'=', '!=', '>', '<', '+', '-'} if not is_first_scan and is_operator_cmd: return cmd if is_first_scan and (is_operator_cmd or cmd[:2] in {'+ ', '- '}): raise ValueError(_('Command \"%s\" is not valid for the first scan') % (cmd[:2],)) # evaluating the command range_nums = cmd.split("..") if len(range_nums) == 2: # range detected num_1 = eval_operand(range_nums[0]) num_2 = eval_operand(range_nums[1]) cmd = str(num_1) + ".." + str(num_2) check_int(data_type, num_1) check_int(data_type, num_2) else: # regular command processing if cmd[:2] in {'+ ', '- ', '> ', '< '}: num = cmd[2:] cmd = cmd[:2] elif cmd[:3] == '!= ': num = cmd[3:] cmd = cmd[:3] else: num = cmd cmd = '' num = eval_operand(num) cmd += str(num) check_int(data_type, num) # finally return cmd # evaluate the expression def eval_operand(s): try: v = eval(s) py2_long = not PY3K and isinstance(v, long) if isinstance(v, int) or isinstance(v, float) or py2_long: return v except: pass raise ValueError(_('Bad value: %s') % (s,)) # check if a number is a valid integer # raise an exception if not def check_int (data_type, num): if data_type.startswith('int'): py2_long = not PY3K and isinstance(num, long) if not (isinstance(num, int) or py2_long): raise ValueError(_('%s is not an integer') % (num,)) if data_type == 'int': width = 64 else: width = int(data_type[len('int'):]) if num > ((1< val2: return 1 if val1 == val2: return 0 return -1 # format number in base16 (callback for TreeView) def format16(col, cell, model, iter, hex_col): cell.set_property("text", "%x" % model.get_value(iter, hex_col)) # append a column to `treeview`, with given `title` # keyword parameters # renderer_class -- default: Gtk.CellRendererText # attributes -- if not None, will be applied to renderer # properties -- if not None, will be applied to renderer # signals -- if not None, will be connected to renderer # the latter two should be a list of tuples, i.e. ((name1, value1), (name2, value2)) def treeview_append_column(treeview, title, sort_id=None, resizable=True, hex_col=None, **kwargs): renderer_class = kwargs.get('renderer_class', Gtk.CellRendererText) attributes = kwargs.get('attributes') properties = kwargs.get('properties') signals = kwargs.get('signals') column = Gtk.TreeViewColumn(title) treeview.append_column(column) if sort_id is not None: column.set_sort_column_id(sort_id) column.set_resizable(resizable) renderer = renderer_class() column.pack_start(renderer, True) if hex_col is not None: column.set_cell_data_func(renderer, format16, hex_col) if attributes: for k,v in attributes: column.add_attribute(renderer, k, v) if properties: for k,v in properties: renderer.set_property(k,v) if signals: for k,v in signals: renderer.connect(k,v) # data is optional data to callback def menu_append_item(menu, name, callback, data=None): item = Gtk.MenuItem(label=name) menu.append(item) item.connect('activate', callback, data) # Interface for bytes<>string conversion for py2/3 # Usage is the same you'd do in py3, call `decode` on external raw data # and `encode` to work with the memory representation def decode(raw_bytes, errors='strict'): if PY3K: return raw_bytes.decode(errors=errors) else: return str(raw_bytes) def encode(unicode_string, errors='strict'): if PY3K: return unicode_string.encode(errors=errors) else: return unicode_string # Convert codepoints to integers byte by byte def str2bytes(string): if PY3K: return bytes(string) else: return map(ord, string) scanmem-0.17/gui/org.freedesktop.gameconqueror.policy.in.in000066400000000000000000000016101317023271400240500ustar00rootroot00000000000000 Scanmem https://github.com/scanmem/scanmem <_description>Run Game Conqueror <_message>Authentication is required to run Game Conqueror GameConqueror auth_admin_keep auth_admin_keep auth_admin_keep @PKGDATADIR@/GameConqueror.py true scanmem-0.17/gui/screenshot.png000066400000000000000000001445631317023271400165500ustar00rootroot00000000000000PNG  IHDRs sBIT|dtEXtSoftwaregnome-screenshot> IDATxwXlwbET-h,i'՘nIrҋ'=GSL=1&&1&f)wE eX] bdgyg6 viڻweee@B!\qTӴ䰰Z*255uAǎ{v//B!8EII 6mȑ#bcc/NLLLW8`Yfڑ#G&B!D믿ӧ~С&$$غuk4M;۱ !B:RXXbcc%B!hAzjhIB!Z.:K9!Br !B<:.V!BJZB!Z0iB!h\-sB!呖9!BLwB! 'ݬBgN'( zݹYM4&lr*.)l6a4}`wr NNYh' U!AYq9EQv,BRU,HFLx\0ir5YϡtJex=3WCDXY\*KW۟NG\tnR:mSqť,/~^'ECoYn\ƍڴ#8+di!ZR-. &l/&^qlNN ϗ@2srYi 7_uw9o@iY;%o#ݮmǭڰ91 E=ݲk+j-+h:%V+o9z^%V+;x]BYh>f>Iݮ-qM[Hc5f#Snז~3Qᭀn|}O^]xk7oonF~AQ᭰͵[d2܃ҵS 2E93ޚd]`<3-˙1gage#h8AIi);GWߝnCCbeNab N}{**S?)))%$(!}{sEH9t&CHf&3 䢡Cؾwj* cQc|2;ރ'z'r1 KJY|%&_o/W\! @ѱbPVncLtڐt OM KzV6i3a0@vn_[vl{ά\ Ӿ7zrxغ{/S;zٹ||b6*מxָk;D6n`¨Ĵkz&^:7KT:uhd4pc$+t/rܷ~~/^"}!^{%Κn8t:|=^}9!Z֘0QU_}ÎI2{ ؟F0LJJ~l6%&56*V90#|bbڷ#S Sxu"/R^Iʡ4۶۫8}I&c{|ٸ}'L}{u-]H$s?^>YyD #0vbtO{6^uTlvF_0р3;YnQEF[hЦ5Y9d6d)ir<+Eu#h:*{n?ChTsD soה)Ihy̦R͠[7g'>+- =3 y;&}h~³ooٓB᱊sٝq +\%n1vZ~#Y wM?/qɅ#x/YlSrЫgEyʿ6n:ж5wp_YO>f`ZV^~J|eM&iݟO,t:h-\ƲB3thhh7 тDQRHf6ByDc۹{]em6^{#6VGYykM\]}>1|<ZVƑ,n[ˣ}(U>Ɵ @LvWѱm}Y9h,؎\ǟ'ܺ󟘈N''p'j*`o.۵fru33~WH#|8{.3no~Itg !j? 4(xyպP}Ș9!Z0/O ̯V0Ïs5t8޲.[杻_.B ;'=Ɇ͐`16/Vpk\ːgdҾuQaC( { *"θC+\I9x E:W&{'qWu1KZCL kY~="b:Z'~ EUE_ ɬV!ΤR>nGqwߝ5ΫĿܨNZh4Mcw i>^lݽU/8w,ϵ%ߟ)\;mlf$6^qW IW'`W̘3geˢz#df@?}6EK,f3}{$0~PƍʥcF/f%xe\|=^GQ3EQШv=7_ϻf:/-sBp&Wx//d+X1z^ _c_lٹi&ꔥ2e'#<-{>ޝ5׺J LF?8Šu%h&^ݺ:EGoen2sޥ37]y9BHN= z^}f};&G0MtN)qtyOt:Ov՝qצ/,85vj9ޠdgNMyVS qnV!3u.7 on ]\t:d!ϐBsHxX(CMdE! !+b1aB3'88z=J!2'Bт@ZB!Z*iB!heN!3xxxHB'B:sB!-,M"BтI˜B!D &ɜB!D &ɜB!D &ɜB!D &ɜB!D &ɜB!D f8!BN'G%''ݎvx$sB!e ޒD^L38EA1t tbdN!fcU,XbK[ۍ%" EW2iN,?߷&{зw"Zj%B!=[6: v%ahzB)op$~!q:l`!=p7[J殺zrN>ճgU5ksS;Ӊر]u EE{1lhS(B$77~E5@o0Ĺ4 MsxQس>M6{[{`؋ܶo׮MP՚=}m !9z(͜C)CNh((ZU.ih?zO #y| 7??3Ȉ3ނ*yy`0ZAAֲ24jِA^ih Y!-ױcǘWd:ӺI\E( N=fG,AU(/ ʄ <əض}CMI3Y]Rk)qɴo׎\&s(/X_ZVՓn`С^2<==yGؿ?Gyul߱"'NQQ[x~ rW5 !l̞VK(>I_ JpPV+iٝ{1Gt']&nYE`񢬬Y܂~fO?^kۖO>UUyg[nˋoxY}TؚV]7n'',,ioN0v;OOy$?ߗ.Ofء%B~gFIXhh}/_!Mu;q(*P" sø <ȭ!Wb"vmJ6ފG~`P{`?q7˵4x̜'۶`Ǯ] n]b$~XK/P @~}ؿ3>Eii)9iS @= ~>vI^ScBq~ٻw/?C>UNv8޵Z\EիF#:tIJJ_k&o'Fן H9!BV݈սw ;Ær88FlݬB!gCii)ww`IfcŤF#}G >>AuI2'Bڞ={s7* y3K$\drAR ?3[q170Ȟiő̙< XlX9!B׶؋GH'z29]8h,Ɩ͛ټt)48FMái;d&( va:IB!NdZٞA`O{Pt 9~u:)++fa6IOOg} FDбSD&iQUhϾtNiOĐdN!P%xQ=չXVٟr, ;((qjS>51$vA8Z3?ްdI g5Y387Y]IfV&&6 kEV3.IJNA@׻֖s:ӵ%5϶پ;#+(^;`Cŀn11x{W$qqy@uW2paN 2WIUh923m ?ySV^ޤdfgv BRʑ<<ʤJQ(+. USIKKcɟذeHBN0heX|I]\XmWd-%ԊS) ~ '4,F#͆ZּH2WI9wzf9^'6:VK2'[)++#-`Ў?h&ݺXv;,Z+wv#,^44rYRNy͕q]0 <#e V O53&5tO$s qV+flTUٌjms !Ĺcw[_ZMT\Г%KT42y6:ZH\ѥ &x"(,NJՀujh7V26~˽FoMۜBC/`(h*\cTpL\BwS\\37جEvs8r =;5DNY;k%=KW') Eeeٽ l?8U9+N߸q, bU&ev;ؠ^ByW%iͩ*kL~Bcu:5^ǭJiEj%Eѡ(:)M@_ ZڽԮ#0,S@qcǠU]{ /,iihªC ϓC-s99| t:wu/^^^_~ K-a! 4-dYQcٳ۾ `\0d#|ŧt͕W\qEQ4Mk2';*..6Muxzn0hʟ#IGbU)M#28ȕ3')bs*i(:=E9iĴvg67ig`U<:JNܶm[uOT͚-?;ݒ9I$vl1Cmݬ[ma?ZVF\HbbjdNwTUnKx lpl%=)LF(:ҫ,gb<-ąMvp8|okPiNˠ6k׎dt: j:9;tm[4+))a_^bb:iǎ#$8AYicUR%.˖pa  56QmkPE W[2_Kl6c21FlrmL~u!={&xγͪ*NJiT,MUJC:$sg?WQ\Gto+bhw0">G8pF#PM>n奘2;HѣGdn_^ʬHFF:Yu62%bYef׻WY~[+k֬XardJJJ8p ]<ֱuf "<<.GjR]2It%e _ػo]( 锕YK\<ۿ?oG\.\re+Haj]絋QS2gZ $zkIT4.4Méj}yMgYj,dh ˯\Ģ?'%gٰik1̋Ǎ&~sX,O>a-p[+N)WZZʼocDw7Tyy9?]|1#kzh z)QA)8'ohbbݶj IDAT|nh}-9EagVku:<"cdqMPYЪRܶm[??22ٲm+l̝;ܼ\BBB&+;u|]]v'@ף*9_|ڴnKII1k֮fc1OIf߱X_INI9sէ(((d{kFn^zCYSPUܼ<N]=8KwX232 !8J}p 1sر:?rVh0%~}ّtsT_L' £nM}Y13غUצt:ٵk 22:_m_AQ:QXXȆWFu#jW["TS2ڍְkv;p*|nVmk9:ŹΖʦłNdB=U'66:Ei9S=ƯKJp[q85s:য়fSOCX MӸ`}2}4mZC9\&Hك'CYv-eeexzz䣏0?ʶ;PՊڗN=w݉(,>l6f.7{U bOF~~F$-?ZVh*>zS,ł[ܮLR1[Ҿk7TAnJnjo:f7c6b\ՇEӉfc˶-$&?̬SN:ލm/%%%tOAb?شi!t] 52mn ]viZٸiS%+}VGUיTT\Rܠk-_ɜJ||1 `0odԣ:5N6MM(wTܞd.2ЮW\~sƲO]#FЯopP۶N11xxTL?W[|W]/cGګ>>˧&=6}qBtޘ[2w:أ]|1a!YOgA צr`EGrɶmEDD8O7yÇɸ[Ax DT4U7 ;-2Ɔ,rlẎ&=~6ټ}xfk՚:ۈ(Wbfj %e?%DGp7MFf]te͚Uڵ=ֵ&qOI`4Kii)( 11ؾc+,|} (0={wո6%sAADDD~=)-8ddUM3>|8o=1M/>EӓYk-_}[vCe(dzD ҹ3la%p-79[;xݶxjǸiVQQDFDȃpu׳b_: @~}ؿ3>Eii)]qu:OgDEF( ٔӳ{wO =Uҟ5kבGXhh#Lp?678 '@xGq`]vP֮%^b9tlHK㷴 <кe,^',ήZ\uX,xlj&s[+ז8pvB^Y/nEc1iҍ,[CҺuk׸\xzzrן^B@Viݖ,rrs _DEEy_-zWBdk^9vdd9?^پ}9Y. /_BNN6tbvZEQdeeG0M&F#z^T`u:q8vvƠ7Ūygr\fΚE׸8-`LKtzZ]]bWfҵ׸-YgՎkΪkPU!_> (0ȑTW7?.$'7umvmK/'}v~ ߯z-{5(E!>.~܊0ވ`>7M6nl66np'Nێ=1t/hgO^ ᚹMU+GHppesr턄ӭ?)9^y~]z &1_*;'{?o<_}i|ߕs,/ਊVM%(*̲^`#tOVN܇)7 rٝ_ʬBvZ"/CQtn:H+-1Yi6[%q^ãiou5O:9etQAomDwpzon֬l쫘SRRBFf&WDS2hGdİan̝5 qpt댟/vٳ_z4ıclܼ;n}{#@*RfΪH]0䴒džԿ/ 3jׄz莘F;td 2NX,Fܕ+ :ᱚFxt"%Yo3??Ç!F,r J` iR{t8Hݼǘtf95Yh*%s'1cLv|cO0LwqQP0MSIm۱^z-[]t:L'-QLݸy Xߥ2#u4kq8vލiDFDONnO^5^/:>pE*xzz}Wu2x ~LF&":v<4 /Ǡ:씗jtx7Q{asptpkJ 2gmĉ~sޤ]P7Y]7oXj-8Ͽ,Zn#Ud?#3 թa0hQF_8^ONN&1?+ÃӻCaQQXTDP:vРnߦDO=7<>xgz(mlԟ(vRPXX-|$|p!ޜZQ~݊߳)}="x"T>~Kz=E PRC־5SzӰ¢E*Ywyւ8 V}Pu.])zՕdz>ÄqtE}s~!Duؑム+?+*<}LUQB( Ӂ⣙ڊ-15|2u<9U5#hږ&@q]wT&8ZlV!hBBBs ݻ'yz (f?Fp:pJ(/.q,$b:q͠>q!AA_ۜdLUk~K/zkk}; !DKҥ ]t#GC~16o^s_2 !ؤlw/+2֭OA9!fms !8[W S1.ԩUnfL9!Tq%+M ь<\U-tgT̒:.-/ZVAM#6$]j*;vhP~z:tg|G;GVvN!h, Qlqn6βy=GsnfTǎz4}9wocnݬMOkl|S?n,1>&jyϽNLdtǑ ^: m2o.W]>I;|^Çp]w6:Fqn  22-X,ls !DK֫_?YaV+~47}ĕFpppvl ZKѴr`ш`hin74%pѥ[4gz&&b6]N'ܾk֭.S EGNN;V| >;wˋ,[Ό?&y AAum\vn_3?&3+Ȉn&.x[sb7g]_`+O6x@zNf1q:n ;g"$qX !9LQFg۷5;PWBW.ؑyy,;?TF//Tllyy$8NݻcR=$l`~NA\z%sﺃ XV>1Ͽ }E"{m<tOH +;OgqJOyqsv!)y?OOA zYŻߕNLy%nCp_zߙ)_ngJ M{ϣ=x"B!DM lp. &! LL¨΢ŋy鵩7Mbcbj,g4/&NdN!8( ǯbhӆ__tz}EjZPUlN'lfH!C~0g6: 7hn2;ub zAumݾA^o߹:v=u.akD[|t1U Bqn%Yr%\IPZ1f3LzJRsူ0:uL:/ẊK֮v+3xXf'$su+ϳOSٽ89W}gNRrkWϞ-fRR070n\3G+BŨ1c2l%%)yy8CЙLhߞ&tDhh(3dڴ7w唗ۈ =ͷ*3tN'onŒ"umu-73Ϲ&|M 89[NP}oS1Q:]ƬgsSp:DEFps5WBf3薐inM5:Um`!Z^4X!h*-jg !B'ɜB!D &ɜB!D &ɜB!D &ɜB!D 4ϑBKaΎ ;yV,%gBJFlsI2'D3z:FjduXu: ^M{GkqZr|dGxvMzs$sB4b|ٞZ@f9jDU`hBq$Ƒ%g61[ؕU=I2'D3 ֡K(,UhTMJ贺 q$ƑǧaJ Ѭ^G}Nvq}Bq*sG.8a4 IhF1&~hz=tzBiNqCi #̓B!Z0IB!Z:Yk,SΚu{ܽ*yV~ ׮nѶm*.?Q:u<6mق]۶\w5\<~M!OM2uS5Č5[ːzl/Ny_{A1ws/%%%L6e-)SX/|3B!9sh'N@}[5ysrj$ukab7tѯhzg;!Z-_ ?5`\q,l X7HACKTOC Rٌ3B!OZYCR~2xȱ),,$$8.q[<y1 @C<3DEE1_ߦJL}MƏ茝&sѣwcB!DWdGGÇs 9%]zÇ^3?;`Qt=_9c܉3:}d?Y/l)vlB!8?ԫ5[n]I9p^![<&Ǝ}*{^FVO_ˆU+;3<=g\'[ eޘ~~TlB!84x6NЄn>qާߐkhb4/~匞ʢŋyx׉9bB!^ݬ[ogЀwc/9V\vc+PGUfyҷ3퍷Wc+6!B2ٲmV;wڴizwVR֬[3Sg7yd{ivIYYǎcɲϻXOQn{^zn ƍFxxO>۱ !TuL2`-7q} IDAT 2Xg_|}I&:vnr n7s&vFU5z$$p׹U4ț[l6nތ$*2K/뮹^_R{Qykzŋ?c3G֙BqZhQɜw !8W{`!Bqn!BiٙdfeRj-m{zx*ٻʴÿIf&; %;XuUTDEUW\ZQ mPJCd2#2dL$乯+sL3o;a4LXOjL9!BHEMFƎV 9vnC[,'ɜ۹Ӷh1#ǠRTJٖ ci|~I=JAQ%ń`X]…LfEM:4 FaZh4t{,>K$Bt¢BBCFѸ:! l64% m}ll6DvřI2'vuuuVe}E!.p6Nn+) {  ɜEZm7(!oBݽ}=eL,Vk 9!DT2BZpl2W[oa\ҎWr$H/喉xjέ'jrIN>ũ'ZxlW^q5JDuu5?\na%%l36IB#9۵h)ϭHcJ|* 늿} ^}ӃwCcytf9q7kͩo&ӧw_ 5XVJ%:i7Jnnѽ;ndIRQ 3Sw39!Dc-j 5yn.˶ԭ=cǘ\:i2O̸+7:g6떟7s 42ӓA @ppph-dNl̸J%[fٸ{ ůXGqI 7ϸwy?΍7zc25VGx:$B8u Vi()*d1wI?` ̠'O;r[Z%;4jww"#(++u`1b fy&2OWz J椛UNPpXϤ\?Ǐ0¦F0$'Wv^L nvsMV+5x{yXjooI 893u:tѣ#H؏^ѸcXP$zt ^T ζ߿++AT7eeWЧw&ǙoK1;wYQMM W(.)!:*|IOټ'Z.99dرjNe石mG*&^z42~vLGlOMe8pPT3c[naꔫLRV 69$s6nBV;fjjj?z[\9j5,cᮻ ,%Ç}|KM&l__{1u<'ܽ hˋ;oM{1V|va4u:zx.{`6E``(JZ+:x'ByNSs6CTϡÇ?v=&ǙLZo4r0$%KhHY'Otr{2wx&/X/2d`ܹ\>{j+q|wv \?JZy |}}9|+>`r1 !\djh2rFVP*{B@VvWӓZ}-Jeý[^v[JitvfC̭Ӈg}׏%o,gqw0zHƌɝ3ffgy)uxݧwo^ӦϰQsLG|Yg>Gg?lӧw,a3\W&/7YÇI8ST\#|^f\?u*!lOǟ|doRjņM̌0s·]Nmg;nCMmM1lSZj=4Ffw5[fь3.blشɞcseƌcf+_+fcT^!|:57C __?JKK@P̌w[?Q̇_k"Nŗ_16f8/a7pM-'=qc8l?v,9BL[)s.{dvVFliخM|Owwwoed~;n ㉎b膄&DVϵ7#\.b Tö2^{V +WjXoW gZ~hZZ*wfQYU_}VBB.6//|n} K_/qTv1Z,+Z}-ؿFLd4Gs3fͭ+wf¯V+W&zooF[xi&]f?$ngt 67߲fGM:63]qelg>?(+/.;v8 믻;WL_/n3jQwΜ:]o`̼v5]f')1wwCHmiV[ɜLϯ}A_8^vصtX73AA~(gmQ~+2zm|~gbM4&\﮿'if򉉉k'&&1m&k%'9$%r"l׎Z' 1!~}<̳dN`4j|*j5zxu5W]1?^YFdnL&2Od{x]lܼ//d%6D5r0|}|O\}_Yy9TVVuvL,4ݗA < ^opζy~y 1 >|e:6ɗ]˯3'">Lx"_|%/ğ~}Y<;xxhYE<==yjsqϽʫ?x^:|x7ntc_򧟷SO3r0^x/jv[ZYÿ޼'C`~=kǣ7nv易kGzЩ|liܙ/V﮻l2vNx1 8y6J~A>v'*s;3©ތ ƍ9;y!A-%9'-sNgj=$e0[mw8n[6tH>za'|ܠ&/7fwH8ЩchlC2lW*<,lVޜ?__ݹK0n%4YG.W/^F__VXu6I k6~&JLlHr7m2΃+[Q?u0-$JחX NIn\z-><={Xp Qaͺ'Sδq :Ů]TUUSU];}c1מӺY_Y/>qqt zľi71"<qq:; qSer3=1$%=SV.?ǃo%%9  2W^c]3e{Lf Fĉ,\}utUfګM]ovB 8(aVmsLZg.-ݴܼEܵZ֍l^8ķ;w9܄Sxy]q9B.LcƲawZ:n~.gtDsOhqKO:,6u\BҶ߯]i4qRgDEe%'OȈpB`7gGNMʠ jPw^oshw`kvuN愸I2v撋.quBd MIJH1FC r%s?rY!p~9!ą3Y;z~hgY,&$t&9 !Bt3H]̣5e=U!u.\yngI2'p IO'z77nK Og$B!DTXhEx؟멪j !ΓJ\(//o TcA~a~-j  '?b9I.,4̬L+  22m}rEد œz`g83n$sBnw}A.*pZ\@5dd2X$sBnh&P=$sBBtH,BqdN!<&ɜB!yL9!BX RFbmVTSS+qKJ`ed{8t:7oKvNyt?vleʹim9ԗζFbbzqӸvʔszP[oOMe?>cOz:ؘnNcB!ąSZFLF#,{m6K _:`<9v<,Ϗ?O~Gs^DVV)W_d˄Ow4]sζe:^7X !¤Xfm-pen'rQ^?ϩTӫw>þ#7/9O?Pv-,Z/6}~Ω;o/;2ΙlXnMtN3B>'Mnqߺu[g/⃕((,$2"{SaV['=nacqVSSÂEx/Mf9ؚFD1)A!yonSܪ>y/?c3ynދ3a82(+/qt6tq-Z=!Y9( -yr!)0tfNddo1+YWNn>vulB!89= NI 9iO?8W'szUf?9y! ۧt0j,V *Th4 z{v糵m֗2b<6ac 6moeꏚsulB!mp,9iAj5<Ij˜gwh4F>pzZ̼^ff}{w`֟Xj{{o0yX,"#nv'Vo/}{ɱ__&B S qEe9!ۯ;g`!YV ͣV_p.H:O"# oBI2'K(.)"?W#\ 0¢"su8 NlX+2<sG!`l\pܼ|D΅|ۇܼ|W"D"-sB8!?@ W!\H{]= !B$Bq^Q(AE9!PoCաBSI2'**:IyyCB!!VEѺ:"2sBG ZFq#CZǗޱ Y_~uG IHpp",I8l@hh$;w80>m?T2lȐ0lN>MuM5I m>>We69q2А|}dH˜$'=55=X,fbcb[:js5ٶ#HLL/nyNb$L؞ʒqСfgb]'?KPpN~q@C7BȱTVUVdj2IcڛyMpP&%8򅅄m?SRZJ}0[,䤠Оut_W"mo:}z&&ʪ*3IiMm F 6)C<ط? fs.l6nj͍**+۫N9!:SZF$w !).[nOaʒ0ܼ|^Y:w,{]m%o.n99ƛwLG|C{d6@ƮM\ xzzǡÇ3ٻjQ1Q(DGFsD&ܼVPUUZ m_CuuX]BP`A @oTD$QQ JhHC~hp(l6SYUIyEƎgCV]fuBמDN> S~}̟o׏%o,y OX͝;g:$sm胿:ӻ7/}igؓ!gtߙkąhq!!dOz +Ju(oY^;sL{DGFsQ%4$7o3C5<=uT &]t=9IZK =bf֟f`{Yμ6lT4t{hߵ{b69!:Ωd/⃕((,$2"{Sa{dvu4_`1[rLfs9r]87z :4-Z-\S[K~A/)**qx{yST\ 4g4)-j=ytR$$$zcǴ ;``&euzL&&xxh APy+m С =ifFJpPA㩪bJͥ/Ou"}<TTVvz؝89̭c^8vWAAqty((?z*#x) w| {2Yj>,P(}ֹaf[֬ȾK.;ng@|jjjٱs'<I.x'K 793eRsxcy]~ɥmQ( ILB@l6E Føc1-I0 QMDP@ }^^^=łbnԍI]T|d2qAjnnn$'&6H+RɘcHۗ(  !\˩d.9iA:;VV̓O@\yi|f0,[>pWٿPso4I܁d" ^}MeJKbh΋б ym%e%l6#ݯ@NkJu^vFpPW\z9FՆVqHd[JܯיؘbcΎ-<7z,& BIaQ!P)۫NI˜:RqА?W4M[i/s߃dæTUUSo4r0k]e3Ĺ)+/C:͍'\Dtdt}gD'&HU=~-[e`s"g=g!+)֬Yc>}zRFbsyJ$u\PnM}NgguSa]3^Zz?gr145}a̙{o;- BTdMml?3e:khȱ3USVVl" 2߭goo[ƌlwܕVk+䟅ofWXn]ɜNK$=,ąęd('SIuuøUWWZBH2'N';7G:&;7pW"D^H!:I`@ eeۋ"]MFhH(EE9!pB^A.ZRqu8 N" p@nF2#oP5)~B:@wg᯦ķ_z9-Lv?6vv`>V:Jn/ a7*sJuCg.5wg)D`6)<,Ȉ ^L#wzg.'JxVv9_8UcgZFpY\Ϫdp^R J6o/cF"0 ??2d0<*77N:R"W/4j *BBB).m5@t:2`PBZҲs%8(ǎ`0="I}Ʉd&@Fz!( t:v!at:uz@a'= #)3eJ9.4+PmmX OԳt}!O_l27_ʜPm85Q1ct #{{Z۸qϙ2g(p0^_51ob3nS;8q20r9́ÇX,3~XVAIǏ6T*۵hJb԰Kg[94Z-AAM< 3}}9ln԰ݟ􎍵'sU];B!zfm}ZRmTi #!M}7\%&Qms)4apWU )2q֮1Ffq=/3b$-Kl  bUSjR%XGTD$a!apXZݡ.ؘbcΎcb ]u6ZLa( ""M %S/w7z ŌF8^nE;e琟: &?Tmy@L4OMq+2Pa_K௛.3b4dѺv9c>yn3X۫klB0~Nֈssss:kKIY 6;ݯ)PZxR$rBq!q~`{uW?LCge>['?LMAl?N F3^o9BKYfq=⏧FFɸ~^wQpl#=c3:™k|(FKBR7DX/ (;'?__YBEZp;x0$*cZZʟ. %OMiV=g=aF6_3x]>]ª{V)9^d xuZ3eng縮ܯ5p ;skvrJ &?Xŗ{+:5V!zz P" RYI0W!D;dlڅbPgCJyϖ*Rw0f,BB\Hk&Ouu T*^^E"󤺺5#ES'k ј$sBᄈpss$sjsswu(B(3%M!~ゃQ )>@t:"# f:!z I 6`e2l6,3KB%ɜB8bH"!qd̜B!yL9!B8.+pTVֽB!CZB!c !B$B!8I2'BqdNl\|_!z"Y4X@mm-{gPZVjDr <=mKdxS/4z**+?_?#t.PqLRFbmVTSS+qKJ`-++QZVPwʈQ֭V[~⭥ChGƏzk{j*KǁC}O'=LlL r S\m/ζFbbzqӸvyDhQ]ج6F ÃZ h4.FyE90Y_l6YNr<8QQDGF0J**+iۏ1r<)-sF#CR`ٻuK-o6[b\}?a{s e& X8E ̞t;˗ѧwoV~cfhv=CYb+_'??˴oGfGvN&99<)>T__OMM  ۇf˛fJ%JeQVل{U*ٌZn\uxxx)vs{[bرk'Vqc7NGDx81b؛bF ! NI~O3~Ҿ\?Iٍq`?/Ν$Z?C|wqG`̨9ck~ƜrXVϼ}~~C;ys0ΙdpL޽yqsL>C9,ZVtv6Ax4[Nד/r $%$֐mݱ2l6J^40:C?lXOlX 0 L;FCƁcZt: O]Cӧ8|VRIʠd"#;`h|>>&9Baq!f?? Co#( "f3A$%B:ެ߼]~? IV cFn5A3r4Ru$}b{wcB/)1i|WXozjjjXhy)<<<,bDF'=qc8?v,#8 '{ K Æ h2-}l6{]qɄ=b8n/ȰC?fq}u$55Cj4ܳ:ÆsHGK&ľ17gHݵ"Ȑl6l:C6 ^d$9q RUSX,_o_|}O27zIIJqMPnPٜj[Ǽ4qx&{@&@fVee46N&<̉,~-\ue7G&'\v$ 8y4ePZVJmm-aXP(PPXȀ4S(9vʪ*|}}$:* 2**+0v<~a  48¢" z<<펿󕖕RV^#  |{N>ML^0ǗLIi)*fؐ!r"tS#*2BoJ~q$4x+Vؓ9^ϪW3g׏\濼Bo`~=lSߗ_@>}XTh4 ;lش5?j$<6nLJ%aDEP^Q;8p cGE%m_1'qa u1q6Ts@Kwes-sjjk3j=m%ק~hqFYyEEEgՑ8 IDATqAJKK @Dx8>>Eéd.9iAʲV<$&4@e3O3ړy/$(0YV>S>FhDɇCmzcnq濼eK[ۻs&'x7Y2znSd2džMfdOz +Jm3kd"BK7w3-L55xh=ٌP(XO*UmYY'?:\GwXsje{N\V_Kvv$sB4)]JCCCB|&lgvN BʈQ/h~u@xxI  %7/a[n^{zҺ x7k;YE?VWWdwzYNr)vQQYIlat( {T*F lߙ ]MX "*2k77#`o>{}:QG[:3#8t0bcj8yǏ K67[\k @EU%&gϞ=-=wUTRQYŀ+*+Q*lݾ"n*7bcc]=wQTǿ[! 5"(".^Ϯ\ Wņtޓdwg~,Y)Y~G9{99gδ({ŗyчקaιW^v)O>,oMzF&O=,_8YȨ#>˹۷Ƀm>c[V 0 M׹̸r%'c6ʋq7Z^]2z(TU//zg1l66+FÉ^$FC!$KDUUǰfMkP_.1fVbWߟFpY ^4` !h4O7wn>L\lqqN˝jkJiY)qE _y<'%qӬY<ӤgdE/ګg:w-bu6O^ܥӏFi5F5,A'a limݟި".&-[h&3+3ƌAduƌ:!:b=mW񐁃Kեt`?aF"#"e`!:e?~ϔI/q|?!sѭ;Z 76| {6lވVG PR.yB*>>'QGz,uK`@cɒҲ2JJʤw]*dN!NC{AFV&Y޷?cƵW!D#ɜB|}} U!Ns2gN!ƒI2'B$B!` !Bx0IB!<$sB!L9!B&ɜB!dN!ƒI2'Њ+(-+kkcۖSUC&e?~ONnΩE,IRU;w`Nu(BNL9!褂C.ωjl6;_UUM&TUm]EQAQ#h?@!DÇ 8(rWj#(0Dwd6UtNn~.fǰ~"#+GUU !%-͆7kߴbTUE->y:M9!M`@wltUUU6o|&TUWx:g:N +'`2t4W'p?Iޝ\vޅF/Ux??`"&*%.+/cێ$%$=:蒺'ìB4 ɃcR]}b.\HpA޻l ٺcZn.wMM-FZK-ҏ9h}E2*B !%_b4 # $$}4 g% %6&vUgfQѤeV6K)h9͒%K3f8贊]Vϫ~fy\VTSSx{7LMfFVpe?~ϔISܾ|r3'eFc !O~B!` !Bx0IB!<$sB!L9!B&ɜB!dN!ƒI2'B$B!` !Bx0IB!<$sB!L9!B&ɜB!dN!ƒI2'B$B!` !Bx0IB!<$sB!L9!B&ɜB!dN!ƒI2'B$B!` !Bx0IB!<$sB!L9!B?WOuB!N# FS&M9!!80B!dN!ƒI2'B$B!` !Bx0IB!<,M"Jn~.yTݲO__"#" DH<<> ɜnKmv>5f>Hn~.QQ#p?OH2'1ftZ555n٧N_~lؼMAx 3'L&F#m`41L#p?OHϜn*}mx<'!u$sBush%ϊG~> ɜnhPUխMHx Ip]ߚxԬ,~e ={Pm~hpwwkQ9!ܬ-==8]WfSx-\5WxKb;|zBٹ}l<~wS};w㓔@ŎT67xSa/(͆R&8Mx\Q+ !:VKuIFEբUhP\RL 186zƼIl)Ipam)X6`)mJw!|. w"#t6_Ic<}sjCY8]ÈOo܁(hq5 ;{Ki)WZsh49r‚2EٿA̟Az;6x2*D'֖@UUʫj(,jxj-Cߵ J{`c. V;EQ^x9}l,6JŒ%>KQTsu(ͩf+>Iz:}edO:*R3'gk}@ү__탢VT´Ϩ U3H/Lvvk^kMGX6TU%Hij Z0sTUEpD?}%ڍ1bj(*.&--ǟxEUdfQ~}0_ߦT=sGY'MN$VU3Oxf`W$seXzq8f z%ɜK{Z?HA~3s=b!9p`/{Lɹ\-ɜ%7g(ͦ/jF ѠCQT*kxN;/>en㡸mʽwSO?AzF111vlTUE;`TVfn's6"ӬhYFOtRUYy|"r%sq̊'FY/X."Ӻ$siyϜD(*;ٱ;FF {d.y7[dN7SV<hj5hZZ ;: VK\FX{T!!RQK@Zg.>ZCvbƣQV UAUuE V=/9s!a<k tz,Z,},XέZj-v[qj&4h/xoݸɯ>R fmI:l uZ6%smFv]wQy",7},t كi";w<%9saVEeșe@QEfPRXAUUQVCYyE ܵnTUU`ڈHeE%iR Gӓ:Bk})tԏ]@h!Ah rb 4cָA2[&Ҷ$n/ף,*:/?_]@D8ew[񔕗c,ÌNAQA6P4*^fo~~3ұZ-(BIi `}f L0@^^!:߷U|z洠UT?ĵ>Ip]{^=Zrft)UK/}LZZFmI)"|g϶'r qxJJJ,'84 Z6 E\iieee6̚``0D=zleeU>CYY}M ѡWWQq!F \zj5pɓ1m؟x5L@W/m|Y<\ɪo?rk'_a֖ ɜnd %22ұ2xGSՊbx<<!y}@9!btO:[QpNcBΤ)QUDgO'ew:3dNxi/< $s#H{ٝt8!L$AQ81!Dgq\'awD}|۝Ay1!DgaK4Cgk/QSSu9eggNj֭;W^~UKhh(I4p^g=>XUUt:8K X;hZne~~~͖FVqcsƸ~4v^B lmؿޥ )L%9yXm˾}r=z^={s/wyBY0k /&S5,G>t VM?}K<ϑ#Ou('u׮rž)y0?ulzg^^^Yàػom6GگI;>8!@n,wMKmm-iReddefF7;^UUU:|>}*ÿ{wkV+{Aբ( ;w`.wSmr*?}Ϯ;9) 4ĝXcǐ8տ"++RHKKxv pwB:]2W]]ů&5-Z"pDOl}5i&HHH{ꩪÏQVVƄ3b1m`0зO?#|?H檪eϤMbBg;N[6svJJ#**/q'6n\OyE9L|nѺ!Ą$ )//#<,qcӣGO_.wR~DFD2 Y;u8`EеKWF)\{:|DdKFF:qq2UW9VA76Y֖ʛ c2UsW-;yy,ZAL;z8[cw>:|}2f8:@_}٘&HJ1y3p|'0s5ƹ옄TTì~=5x3ђ;}.}|ճ7PZVҠ/2 mȵ%8Z-՘LXV| 22 /7m`_8rUX F#Yٙ㨵XSҎҳgo=q$% g^>u܏ST\?}GqIqa{4^v 1!$vvO{.fxܟڤ=@sz<޹ط>9xp㳦'gMà{<.EQ8|:~ =&:*~}Z[-[7:WBJgnѯ__SX IDAThؾc?ѣGOӧ/\|ifgg**˾z9gOqI|UU2>{,2aY(6/;1iQcC;#""s?+wm%r1|$}mg|Q}x'o?cv22 rT{p,= ?bcڵ+3)x{{sA 7Ut{?ݻ| 4-!ظi`y*F _$*+=֖z艟EEal6ӯoF7Zٷͥ"ظiii4|jsn1 !ĩԩ"bcD:ij]YUI^^.lwTU%<<^OAa`8|P%$K-[79k$q6wmQW_]-7slkn{OSeU%Ͽsƾ}{:t8昘Fp)\dϏIص{'%,, u$s';zrva-cj ?M7ouC`O]?m+I!NN,;ޓ؞ΈK.L:l3~;BBC ms|?0b}]^1t0jhXz%۶m!<,Eyya{u2XAw(ٽ ool6jkkٱkC'00ܼuN7=@܃ yhڽ]v}WM`OcBS%so?\}7oT))t:ƎG``6oȑü]BC),*$:*1Wޜ9~"/\}>>>j[d 0Ą])G{ڥ+ՠЧO_vUХKwBDDd%rrh4{+F?lOV;w`Y9s}?)-=JI9JeU%Q\sukj̼ !)?7g߾=L^8pp_Z{Z*44=zBQqAAAwW)0 cm!$$t!C}c=K} s e ##?^N#/?QY !8e@fL&lˋW] 3+ݺsn>PYYCPl64@ii _Utӧw_z|}}{.֭_ۨ8cdd4qݰX,ą/&66U^EQq!LjakL$FRb4 EŅ`}9,[lƎ=F7Æ ٹ̙ 1!R> gۛҞkm=1HA {嚺kk8;4޶aCӽ{rrU`1lp-y\|߉ ښzI+I!N%͒%K3fcLUU?pjjTVU瘨m9O}C[ң{^Ok{{SFk+d4 z]'t5gښȁk3=Wn5R}=Q~N_y֔+ҕBtNΜ;uD}^<].iY^O||74 ۶oes?69-WYYɂwc˜ne lܼ%|ƶ;٬t֍c0cϓlܼmז;/ذi3Zu/iC!CK/?){%rrrY.$e[ (-+s~<;s`-6%op/wHFf&Ͽ Y~<!XK,Q;wZUUL-S]]ry!#G{k3չ溧^p:dhǟISSo{~f}[=p)= ?Rxf˼4UUQzLmW|-QYUy53+l-OT3ϞT[dکfl(EfxEG֥l6 MGk[f%K]b;rСosiWe1K6f2^]Z:{G!8o5ۚ5\;F= OR^^2'/7z41-9VkҙSc,^~9nI!i2w$8cvd`uoOSo(̾n ǟ|?yS"[XIIMfqܻORYYŜᝅsq|1y\t>>( **6E!#3޽z?9wx?2dByEg礎R 4QQ<3c&k7hzT[)wߋ,zmػo_5j:;؄bhJ Ͽ n'i2@Rb;bɜN㊫ay{"nΎ]xW;f4Csy/s[O5Eӡ*[mXz:7zLf3:~}mZ6t:-\f3#gԈܵd=$?1 &N=W_Olʙ 44Ҳ2ƍ(_[8LG3oRUU_Ys3 "|u#'7X="o6~}c민LLL4Fjc&LlUH߮cm__>̙pZ-Uݻ%* 4))XzyWY0u'&6ϖ[Yݷt-[Z]ŋ;[xMkp۪n6OSc:rB*No0|U[}[CUAQUZm~k4Zl6M˓9JV>ZޞXRRCUUU3u9-_UU^iN%'[헟7ynjɘQ#9tO?w7HKOgH``Orr00 v9i^|'}~}R{{~e3Oޤgd3r'-\ZoO׏,嚫f-.f: &B[[Ե=wВv̸r%'c6ʋq_Z!N%܀п}?-D~~q Fm6+99HZ'yˮD!Æ2zYv+~~~,3e023XrRVVokְfzfz {":*(3hh4=qh 0xyѯO_vًC≧;ҫWOR1lX73+ **+xdİr|:WsbkG[sn:cͬ匱c'%qӬY<ӤgdE/ګg:ʼ:œiPo+=8Ͽ _7jjjILS=u7(ے2.ZĬ\ks8o=j$1<#]]-E[Nx>{FlL4MUW\3To8{9X3:)6E_gҤn3u;xiZx>潊ApỄ6Y͛ٸy3]Я__x|Yn{'_Ƙѣ(yRV^?p?{{G|[W^3.|}~}+/;͸rn=O\l # ]g礱9YU 5[n =4WOs"#"xKݖX;NRή g^^^M6N4 a\-ZNC04񇷄tYd:cƌSe4;Ԫj cs4p#t p(`X7[Z A-Wݲ!?Æ&EDxC(tz֬[˿|_-,nҥg 1!ĄMf0 Z-!!~߿Mh59rs(u{8x^!%sBϲ~FJޭO>岿_N;ա !_$sBv9p ~ii2sƕ<럧:,!ːdNS^N!Z]?łשG!r7;ZqZDN!N IB!<$sB!L9!BSB!hågN!£I2'B$B!` !Bx0IB!8/x[mOJ>$7V+;?"Xd_Ozq-Vn4 m V5Z˼+9z~:ӬVU'ko?lMGUm[y=\uJ,lLN?r\.cTb@OZfO U-6LuVvkLghKg;)ScUѵQW!NN3gPWIDt8#yXy]\5m(5 k搟{wt{,`F{k \ZwVNz{ :7YؒVEfɉ:_Mbk|Q֦6YgRf5]Jkd >QG=%U6Jf2]6<~%!I+.@7fv#:@~w͎F]Sº-_p/^#_g֥?Yg7M̥;^!. gջ.ڪkvgmcЂ V$t@~?T6W_!L&sN /zCVeZQZ*b¡vj-V>r{q(>=huY 'K)asj%W eJf-grݸ0FtǠ`rCT /̾]Е6ŽS卢z7W9++׌ !5Vv3OI ^!gSixVOYʘ^y|8/y+rٞ^ű;=kٕib<݉ 2n\JUdފ\V/oY<'`d?T*1՝G5,\!ڭP1oE.;3U;ωdx?TkտӟI]46W_!茜&s+7d;.jB/(h贑<+fʆdep8& Ն>R5%9kgѺj.KqˇiV<)Pnl@E1[4| Z0+Fx}!=B;5m+a2Ba1^o)l >T٢PPnCO/xsu>>:.SO3xmeJDW/^_iV\eߨWGs礎Nó?_n厳#6ֿN晉18֦|VXUy~TAƪrKzڸ+yO{znnεcCo%kYIDATv?]VG[i4xtz h2qNR}#[[Ϝ#xT@ !Dgtɑ|3hϑ|J]2721.NQ^>Ulƪҳyj,CQl6+րMQy\B\Fhb|Ia|~=XU ږsRXh17=! }#9Vt"Q=k[3huio={N$19kZx}~W[4wMmdoϓYw9w@7e6x)9BY9MV(@n7ʬR׮(6 F ̶xee Wa99l A1=Hǜ !U_.^9\h]W ` »ڛ*i{FEO^϶{+á<3K7q9D(b_VCu?G-WDxUj.ܜ%`N eV+gΦ4wMvk[^YG~(_tY\?> c|wG)7\~~TKjߙxX'u!(*X*̔Tu8-YxoM!O ? |?a46sސ eH/fG =!h(Ǘ y,ɓ8b웬wvňf  >mյ.QʀnANiwMqI[es1AdI&z_'_w+"axŻ((c7e[a݆-am4\[CKOBIܙ]Bg;^^0Xinj^%y"l=z^Ӄ[z|wEhO+iQ&ST̳/=Ҫ&wu}[WcӢ9+:hcy0+u컚)8ݽvI:eCV02=%ҬQ)Hquvsڷ z2'I5c9DFR~FGa,2J,}kdLwfMJg.'7Cd7qa#rf4o`RY%Wc]εE:P?;zҙ0Kn^ŢP/)xmACL^Ն6 yGaɹiE X0`1<):vM،0`1s#X0`1s#X0`1 Copyright (C) 2009 Eli Dupree Copyright (C) 2009,2010 WANG Lu Copyright (C) 2014-2016 Sebastian Parschauer This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* to determine the word width */ #include #include #include #include "common.h" #include "commands.h" #include "endianness.h" #include "handlers.h" #include "interrupt.h" #include "scanmem.h" #include "scanroutines.h" #include "sets.h" #include "show_message.h" #define USEPARAMS() ((void) vars, (void) argv, (void) argc) /* macro to hide gcc unused warnings */ /* * This file defines all the command handlers used, each one is registered using * registercommand(). When a matching command is entered, the commandline is * tokenized and parsed into an argv/argc. * * argv[0] will contain the command entered, so one handler can handle multiple * commands by checking what's in there. You still need seperate documentation * for each command when you register it. * * Most commands will also need some documentation, see handlers.h for the format. * * Commands are allowed to read and modify settings in the vars structure. * */ #define calloca(x,y) (memset(alloca((x) * (y)), 0x00, (x) * (y))) /* try to determine the size of a pointer */ #ifndef ULONG_MAX #warning ULONG_MAX is not defined! #endif #if ULONG_MAX == 4294967295UL #define POINTER_FMT "%8lx" #elif ULONG_MAX == 18446744073709551615UL #define POINTER_FMT "%12lx" #else #define POINTER_FMT "%12lx" #endif /* pager support */ static FILE *get_pager(void) { const char *pager; pid_t pgpid; int pgret; int pgpipe[2]; bool pgcmdfail = false; FILE *retfd = NULL; char *const emptyvec[1] = { NULL }; if ((pager = getenv("PAGER")) == NULL || *pager == '\0') { show_warn("get_pager(): couldn't get $PAGER, falling back to `more`\n"); pager = "more"; } if (pipe2(pgpipe, O_NONBLOCK) == -1) { show_error("get_pager(): pipe2() error `%s`. falling back to normal output\n", strerror(errno)); return stderr; } /* * we write here to ensure we will always * have something to read() into pgcmdfail. */ write(pgpipe[1], "", 1); /* XXX: is $PATH modified prior? */ retry: switch ((pgpid = fork())) { case -1: show_warn("get_pager(): fork() failed. falling back to normal output\n"); return stderr; case 0: execvp(pager, emptyvec); /* * if we got here, it means execvp() failed. * errno contains the error, so pass it back * up to parent. we use a pipe here to let * the parent know that we are indeed returning * the return value of the failed execvp(). */ char nullbuf; /* read() to empty pipe */ read(pgpipe[0], &nullbuf, 1); write(pgpipe[1], "1", 2); exit(errno); /* NOTREACHED */ default: if (waitpid(pgpid, &pgret, 0) == -1) { show_debug("pager: waitpid() error `%s`\n", strerror(errno)); show_warn("pager: waitpid() error. falling back to normal output\n"); return stderr; } else { pgret = WEXITSTATUS(pgret); if (read(pgpipe[0], &pgcmdfail, 1) == -1) { show_error("pager: pipe read() error `%s`. falling back to normal output\n", strerror(errno)); return stderr; } if (pgcmdfail) { show_debug("pager: execvp(pg=%s) ret -> %d (%s)\n", pager, pgret, strerror(pgret)); if (!strcmp(pager, "more")) { show_warn("pager: sh failed to execute more. falling back to normal output\n"); return stderr; } else { show_warn("pager: sh failed to execute `%s`. trying `more`...\n", pager); pager = "more"; goto retry; } } } } if ((retfd = popen(pager, "w")) == NULL) { show_warn("pager: couldn't popen() pager, falling back to normal output\n"); return stderr; } assert(retfd != NULL); return retfd; } bool handler__set(globals_t * vars, char **argv, unsigned argc) { unsigned block, seconds = 1; char *delay = NULL; bool cont = false; struct setting { char *matchids; char *value; unsigned seconds; } *settings = NULL; assert(argc != 0); assert(argv != NULL); assert(vars != NULL); if (argc < 2) { show_error("expected an argument, type `help set` for details.\n"); return false; } /* supporting `set` for bytearray will cause annoying syntax problems... */ if ((vars->options.scan_data_type == BYTEARRAY) ||(vars->options.scan_data_type == STRING)) { show_error("`set` is not supported for bytearray or string, use `write` instead.\n"); return false; } /* check if there are any matches */ if (vars->num_matches == 0) { show_error("no matches are known.\n"); return false; } /* --- parse arguments into settings structs --- */ settings = calloca(argc - 1, sizeof(struct setting)); /* parse every block into a settings struct */ for (block = 0; block < argc - 1; block++) { /* first separate the block into matches and value, which are separated by '=' */ if ((settings[block].value = strchr(argv[block + 1], '=')) == NULL) { /* no '=' found, whole string must be the value */ settings[block].value = argv[block + 1]; } else { /* there is a '=', value+1 points to value string. */ /* use strndupa() to copy the matchids into a new buffer */ settings[block].matchids = strndupa(argv[block + 1], (size_t) (settings[block].value++ - argv[block + 1])); } /* value points to the value string, possibly with a delay suffix */ /* matchids points to the match-ids (possibly multiple) or NULL */ /* now check for a delay suffix (meaning continuous mode), eg 0xff/10 */ if ((delay = strchr(settings[block].value, '/')) != NULL) { char *end = NULL; /* parse delay count */ settings[block].seconds = strtoul(delay + 1, &end, 10); if (*(delay + 1) == '\0') { /* empty delay count, eg: 12=32/ */ show_error("you specified an empty delay count, `%s`, see `help set`.\n", settings[block].value); return false; } else if (*end != '\0') { /* parse failed before end, probably trailing garbage, eg 34=9/16foo */ show_error("trailing garbage after delay count, `%s`.\n", settings[block].value); return false; } else if (settings[block].seconds == 0) { /* 10=24/0 disables continuous mode */ show_info("you specified a zero delay, disabling continuous mode.\n"); } else { /* valid delay count seen and understood */ show_info("setting %s every %u seconds until interrupted...\n", settings[block].matchids ? settings[block]. matchids : "all", settings[block].seconds); /* continuous mode on */ cont = true; } /* remove any delay suffix from the value */ settings[block].value = strndupa(settings[block].value, (size_t) (delay - settings[block].value)); } /* if (strchr('/')) */ } /* for(block...) */ /* --- setup a longjmp to handle interrupt --- */ if (INTERRUPTABLE()) { /* control returns here when interrupted */ // settings is allocated with alloca, do not free it // free(settings); sm_detach(vars->target); ENDINTERRUPTABLE(); return true; } /* --- execute the parsed setting structs --- */ while (true) { uservalue_t userval; /* for every settings struct */ for (block = 0; block < argc - 1; block++) { /* check if this block has anything to do in this iteration */ if (seconds != 1) { /* not the first iteration (all blocks get executed first iteration) */ /* if settings.seconds is zero, then this block is only executed once */ /* if seconds % settings.seconds is zero, then this block must be executed */ if (settings[block].seconds == 0 || (seconds % settings[block].seconds) != 0) continue; } /* convert value */ if (!parse_uservalue_number(settings[block].value, &userval)) { show_error("bad number `%s` provided\n", settings[block].value); goto fail; } /* check if specific match(s) were specified */ if (settings[block].matchids != NULL) { struct set match_set; match_location loc; if (parse_uintset(settings[block].matchids, &match_set, vars->num_matches) == false) { show_error("failed to parse the set, try `help set`.\n"); goto fail; } foreach_set_fw(i, &match_set) { loc = nth_match(vars->matches, match_set.buf[i]); if (loc.swath) { value_t v; void *address = remote_address_of_nth_element(loc.swath, loc.index); v = data_to_val(loc.swath, loc.index); /* copy userval onto v */ /* XXX: valcmp? make sure the sizes match */ uservalue2value(&v, &userval); show_info("setting *%p to %#"PRIx64"...\n", address, v.int64_value); /* set the value specified */ fix_endianness(&v, vars->options.reverse_endianness); if (sm_setaddr(vars->target, address, &v) == false) { show_error("failed to set a value.\n"); set_cleanup(&match_set); goto fail; } } else { show_error("BUG: set: id <%zu> match failure\n", match_set.buf[i]); set_cleanup(&match_set); goto fail; } } set_cleanup(&match_set); } else { matches_and_old_values_swath *reading_swath_index = vars->matches->swaths; size_t reading_iterator = 0; /* user wants to set all matches */ while (reading_swath_index->first_byte_in_child) { /* only actual matches are considered */ if (reading_swath_index->data[reading_iterator].match_info != flags_empty) { void *address = remote_address_of_nth_element(reading_swath_index, reading_iterator); value_t v; v = data_to_val(reading_swath_index, reading_iterator); /* XXX: as above : make sure the sizes match */ uservalue2value(&v, &userval); show_info("setting *%p to %#"PRIx64"...\n", address, v.int64_value); fix_endianness(&v, vars->options.reverse_endianness); if (sm_setaddr(vars->target, address, &v) == false) { show_error("failed to set a value.\n"); goto fail; } } /* go on to the next one... */ ++reading_iterator; if (reading_iterator >= reading_swath_index->number_of_bytes) { reading_swath_index = local_address_beyond_last_element(reading_swath_index); reading_iterator = 0; } } } /* if (matchid != NULL) else ... */ } /* for(block) */ if (cont) { sleep(1); } else { break; } seconds++; } /* while(true) */ ENDINTERRUPTABLE(); return true; fail: ENDINTERRUPTABLE(); return false; } /* Accepts a numerical argument to print up to N matches, defaults to 10k * FORMAT (don't change, front-end depends on this): * [#no] addr, value, [possible types (separated by space)] */ bool handler__list(globals_t *vars, char **argv, unsigned argc) { unsigned long num = 0; size_t buf_len = 128; /* will be realloc'd later if necessary */ element_t *np = NULL; char *v; const char *bytearray_suffix = ", [bytearray]"; const char *string_suffix = ", [string]"; unsigned long max_to_print = 10000; if (argc > 1) { max_to_print = strtoul(argv[1], NULL, 0x00); if (max_to_print == 0) { show_error("`%s` is not a valid positive integer.\n", argv[1]); return false; } } if (vars->num_matches == 0) return false; if ((v = malloc(buf_len)) == NULL) { show_error("memory allocation failed.\n"); return false; } if (vars->regions) np = vars->regions->head; matches_and_old_values_swath *reading_swath_index = vars->matches->swaths; size_t reading_iterator = 0; /* list all known matches */ while (reading_swath_index->first_byte_in_child) { if (num == max_to_print) { if (num < vars->num_matches) show_user("[...]\n"); break; } match_flags flags = reading_swath_index->data[reading_iterator].match_info; /* only actual matches are considered */ if (flags != flags_empty) { switch(vars->options.scan_data_type) { case BYTEARRAY: buf_len = flags * 3 + 32; v = realloc(v, buf_len); /* for each byte and the suffix, this should be enough */ if (v == NULL) { show_error("memory allocation failed.\n"); return false; } data_to_bytearray_text(v, buf_len, reading_swath_index, reading_iterator, flags); assert(strlen(v) + strlen(bytearray_suffix) + 1 <= buf_len); /* or maybe realloc is better? */ strcat(v, bytearray_suffix); break; case STRING: buf_len = flags + strlen(string_suffix) + 32; /* for the string and suffix, this should be enough */ v = realloc(v, buf_len); if (v == NULL) { show_error("memory allocation failed.\n"); return false; } data_to_printable_string(v, buf_len, reading_swath_index, reading_iterator, flags); assert(strlen(v) + strlen(string_suffix) + 1 <= buf_len); /* or maybe realloc is better? */ strcat(v, string_suffix); break; default: /* numbers */ ; /* cheat gcc */ value_t val = data_to_val(reading_swath_index, reading_iterator); valtostr(&val, v, buf_len); break; } void *address = remote_address_of_nth_element(reading_swath_index, reading_iterator); unsigned long address_ul = (unsigned long)address; unsigned int region_id = 99; unsigned long match_off = 0; const char *region_type = "??"; /* get region info belonging to the match - * note: we assume the regions list and matches are sorted */ while (np) { region_t *region = np->data; unsigned long region_start = (unsigned long)region->start; if (address_ul < region_start + region->size && address_ul >= region_start) { region_id = region->id; match_off = address_ul - region->load_addr; region_type = region_type_names[region->type]; break; } np = np->next; } printf("[%2lu] "POINTER_FMT", %2u + "POINTER_FMT", %5s, %s\n", num++, address_ul, region_id, match_off, region_type, v); } /* go on to the next one... */ ++reading_iterator; if (reading_iterator >= reading_swath_index->number_of_bytes) { reading_swath_index = local_address_beyond_last_element(reading_swath_index); reading_iterator = 0; } } free(v); return true; } bool handler__delete(globals_t * vars, char **argv, unsigned argc) { struct set del_set; if (argc != 2) { show_error("was expecting one argument, see `help delete`.\n"); return false; } if (vars->num_matches == 0) { show_error("nothing to delete.\n"); return false; } if (!parse_uintset(argv[1], &del_set, (size_t)vars->num_matches)) { show_error("failed to parse the set, try `help delete`.\n"); return false; } size_t match_counter = 0; size_t set_idx = 0; matches_and_old_values_swath *reading_swath_index = vars->matches->swaths; size_t reading_iterator = 0; while (reading_swath_index->first_byte_in_child) { /* only actual matches are considered */ if (reading_swath_index->data[reading_iterator].match_info != flags_empty) { if (match_counter++ == del_set.buf[set_idx]) { /* It is not reasonable to check if the matches array can be * downsized after the deletion. * So just zero its flags, to mark it as not a REAL match */ reading_swath_index->data[reading_iterator].match_info = flags_empty; vars->num_matches--; if (set_idx++ == del_set.size - 1) { set_cleanup(&del_set); return true; } } } /* go on to the next one... */ ++reading_iterator; if (reading_iterator >= reading_swath_index->number_of_bytes) { reading_swath_index = local_address_beyond_last_element(reading_swath_index); reading_iterator = 0; } } show_error("BUG: delete: id <%zu> match failure\n", del_set.buf[set_idx]); set_cleanup(&del_set); return false; } bool handler__reset(globals_t * vars, char **argv, unsigned argc) { USEPARAMS(); /* reset scan progress */ vars->scan_progress = 0; if (vars->matches) { free(vars->matches); vars->matches = NULL; vars->num_matches = 0; } /* refresh list of regions */ l_destroy(vars->regions); /* create a new linked list of regions */ if ((vars->regions = l_init()) == NULL) { show_error("sorry, there was a problem allocating memory.\n"); return false; } /* read in maps if a pid is known */ if (vars->target && sm_readmaps(vars->target, vars->regions, vars->options.region_scan_level) != true) { show_error("sorry, there was a problem getting a list of regions to search.\n"); show_warn("the pid may be invalid, or you don't have permission.\n"); vars->target = 0; return false; } return true; } bool handler__pid(globals_t * vars, char **argv, unsigned argc) { char *resetargv[] = { "reset", NULL }; char *end = NULL; if (argc == 2) { vars->target = (pid_t) strtoul(argv[1], &end, 0x00); if (vars->target == 0) { show_error("`%s` does not look like a valid pid.\n", argv[1]); return false; } } else if (vars->target) { /* print the pid of the target program */ show_info("target pid is %u.\n", vars->target); return true; } else { show_info("no target is currently set.\n"); return false; } return handler__reset(vars, resetargv, 1); } bool handler__snapshot(globals_t *vars, char **argv, unsigned argc) { USEPARAMS(); /* check that a pid has been specified */ if (vars->target == 0) { show_error("no target set, type `help pid`.\n"); return false; } /* remove any existing matches */ if (vars->matches) { free(vars->matches); vars->matches = NULL; vars->num_matches = 0; } if (sm_searchregions(vars, MATCHANY, NULL) != true) { show_error("failed to save target address space.\n"); return false; } return true; } /* dregion */ bool handler__dregion(globals_t *vars, char **argv, unsigned argc) { struct set reg_set; /* need an argument */ if (argc < 2) { show_error("expected an argument, see `help dregion`.\n"); return false; } /* check that there is a process known */ if (vars->target == 0) { show_error("no target specified, see `help pid`\n"); return false; } /* check if there are any regions at all */ if (vars->regions->size == 0) { show_error("no regions are known.\n"); return false; } size_t last_region_id = ((region_t*)(vars->regions->tail->data))->id; if (!parse_uintset(argv[1], ®_set, last_region_id + 1)) { show_error("failed to parse the set, try `help dregion`.\n"); return false; } /* loop for every reg_id in the set */ for (size_t set_idx = 0; set_idx < reg_set.size; set_idx++) { size_t reg_id = reg_set.buf[set_idx]; /* initialize list pointers */ element_t *np = vars->regions->head; element_t *pp = NULL; /* find the correct region node */ while (np) { region_t *r = np->data; /* compare the node id to the id the user specified */ if (r->id == reg_id) break; pp = np; /* keep track of prev for l_remove() */ np = np->next; } /* check if a match was found */ if (np == NULL) { show_warn("no region matching %u, or already removed.\n", reg_id); continue; } /* check for any affected matches before removing it */ if(vars->num_matches > 0) { region_t *reg_to_delete = np->data; void *start_address = reg_to_delete->start; void *end_address = reg_to_delete->start + reg_to_delete->size; vars->matches = delete_in_address_range(vars->matches, &vars->num_matches, start_address, end_address); if (vars->matches == NULL) { show_error("memory allocation error while deleting matches\n"); } } l_remove(vars->regions, pp, NULL); } return true; } bool handler__lregions(globals_t * vars, char **argv, unsigned argc) { element_t *np = vars->regions->head; USEPARAMS(); if (vars->target == 0) { show_error("no target has been specified, see `help pid`.\n"); return false; } if (vars->regions->size == 0) { show_info("no regions are known.\n"); } /* print a list of regions that have been searched */ while (np) { region_t *region = np->data; fprintf(stderr, "[%2u] "POINTER_FMT", %7lu bytes, %5s, "POINTER_FMT", %c%c%c, %s\n", region->id, (unsigned long)region->start, region->size, region_type_names[region->type], region->load_addr, region->flags.read ? 'r' : '-', region->flags.write ? 'w' : '-', region->flags.exec ? 'x' : '-', region->filename[0] ? region->filename : "unassociated"); np = np->next; } return true; } /* handles every scan that starts with an operator */ bool handler__operators(globals_t * vars, char **argv, unsigned argc) { uservalue_t val; scan_match_type_t m; USEPARAMS(); if (argc == 1) { zero_uservalue(&val); } else if (argc > 2) { show_error("too many values specified, see `help %s`", argv[0]); return false; } else { if (!parse_uservalue_number(argv[1], &val)) { show_error("bad value specified, see `help %s`", argv[0]); return false; } } if (strcmp(argv[0], "=") == 0) { m = (argc == 1) ? MATCHNOTCHANGED : MATCHEQUALTO; } else if (strcmp(argv[0], "!=") == 0) { m = (argc == 1) ? MATCHCHANGED : MATCHNOTEQUALTO; } else if (strcmp(argv[0], "<") == 0) { m = (argc == 1) ? MATCHDECREASED : MATCHLESSTHAN; } else if (strcmp(argv[0], ">") == 0) { m = (argc == 1) ? MATCHINCREASED : MATCHGREATERTHAN; } else if (strcmp(argv[0], "+") == 0) { m = (argc == 1) ? MATCHINCREASED : MATCHINCREASEDBY; } else if (strcmp(argv[0], "-") == 0) { m = (argc == 1) ? MATCHDECREASED : MATCHDECREASEDBY; } else { show_error("unrecognized operator seen at handler_operators: \"%s\".\n", argv[0]); return false; } if (vars->matches) { if (vars->num_matches == 0) { show_error("there are currently no matches.\n"); return false; } if (sm_checkmatches(vars, m, &val) == false) { show_error("failed to search target address space.\n"); return false; } } else { /* Cannot be used on first scan: * =, !=, <, >, +, + N, -, - N * Can be used on first scan: * = N, != N, < N, > N */ if (m == MATCHNOTCHANGED || m == MATCHCHANGED || m == MATCHDECREASED || m == MATCHINCREASED || m == MATCHDECREASEDBY || m == MATCHINCREASEDBY ) { show_error("cannot use that search without matches\n"); return false; } else { if (sm_searchregions(vars, m, &val) != true) { show_error("failed to search target address space.\n"); return false; } } } if (vars->num_matches == 1) { show_info("match identified, use \"set\" to modify value.\n"); show_info("enter \"help\" for other commands.\n"); } return true; } bool handler__version(globals_t *vars, char **argv, unsigned argc) { USEPARAMS(); vars->printversion(stderr); return true; } bool handler__string(globals_t * vars, char **argv, unsigned argc) { /* test scan_data_type */ if (vars->options.scan_data_type != STRING) { show_error("scan_data_type is not string, see `help option`.\n"); return false; } /* test the length */ size_t cmdline_length = strlen(vars->current_cmdline); if (cmdline_length < 3) /* cmdline too short */ { show_error("please specify a string\n"); return false; } size_t string_length = cmdline_length-2; if (string_length > (uint16_t)(-1)) /* string too long */ { show_error("String length is limited to %u\n", (uint16_t)(-1)); return false; } /* Allocate a copy of the target string. While it would be possible to reuse * the incoming string, truncating the first 2 chars means it is aligned * at most at a 2 bytes boundary, which will generate unaligned accesses * when the string will be read as a sequence of int64 during a scan. * `malloc()` instead ensures enough alignment for any type. */ char *string_value = malloc((string_length+1)*sizeof(char)); if (string_value == NULL) { show_error("memory allocation for string failed.\n"); return false; } strcpy(string_value, vars->current_cmdline+2); uservalue_t val; val.string_value = string_value; val.flags = string_length; /* need a pid for the rest of this to work */ if (vars->target == 0) { goto fail; } /* user has specified an exact value of the variable to find */ if (vars->matches) { if (vars->num_matches == 0) { show_error("there are currently no matches.\n"); return false; } /* already know some matches */ if (sm_checkmatches(vars, MATCHEQUALTO, &val) != true) { show_error("failed to search target address space.\n"); goto fail; } } else { /* initial search */ if (sm_searchregions(vars, MATCHEQUALTO, &val) != true) { show_error("failed to search target address space.\n"); goto fail; } } /* check if we now know the only possible candidate */ if (vars->num_matches == 1) { show_info("match identified, use \"set\" to modify value.\n"); show_info("enter \"help\" for other commands.\n"); } free(string_value); return true; fail: free(string_value); return false; } static inline bool parse_uservalue_default(const char *str, uservalue_t *val) { bool ret = true; if (!parse_uservalue_number(str, val)) { show_error("unable to parse number `%s`\n", str); ret = false; } return ret; } bool handler__default(globals_t * vars, char **argv, unsigned argc) { uservalue_t vals[2]; uservalue_t *val = &vals[0]; scan_match_type_t m = MATCHEQUALTO; char *ustr = argv[0]; char *pos; bool ret = false; USEPARAMS(); zero_uservalue(val); switch(vars->options.scan_data_type) { case ANYNUMBER: case ANYINTEGER: case ANYFLOAT: case INTEGER8: case INTEGER16: case INTEGER32: case INTEGER64: case FLOAT32: case FLOAT64: /* attempt to parse command as a number */ if (argc != 1) { show_error("unknown command\n"); goto retl; } /* detect a range */ pos = strstr(ustr, ".."); if (pos) { *pos = '\0'; if (!parse_uservalue_default(ustr, &vals[0])) goto retl; ustr = pos + 2; if (!parse_uservalue_default(ustr, &vals[1])) goto retl; /* Check that the range is nonempty */ if (vals[0].float64_value > vals[1].float64_value) { show_error("Empty range\n"); goto retl; } /* Store the bitwise AND of both flags in the first value, * so that range scanroutines need only one flag testing. */ vals[0].flags &= vals[1].flags; m = MATCHRANGE; } else { if (!parse_uservalue_default(ustr, val)) goto retl; } break; case BYTEARRAY: /* attempt to parse command as a bytearray */ if (!parse_uservalue_bytearray(argv, argc, val)) { show_error("unable to parse command `%s`\n", ustr); goto retl; } break; case STRING: show_error("unable to parse command `%s`\nIf you want to scan" " for a string, use command `\"`.\n", ustr); goto retl; break; default: assert(false); break; } /* need a pid for the rest of this to work */ if (vars->target == 0) { goto retl; } /* user has specified an exact value of the variable to find */ if (vars->matches) { if (vars->num_matches == 0) { show_error("there are currently no matches.\n"); goto retl; } /* already know some matches */ if (sm_checkmatches(vars, m, val) != true) { show_error("failed to search target address space.\n"); goto retl; } } else { /* initial search */ if (sm_searchregions(vars, m, val) != true) { show_error("failed to search target address space.\n"); goto retl; } } /* check if we now know the only possible candidate */ if (vars->num_matches == 1) { show_info("match identified, use \"set\" to modify value.\n"); show_info("enter \"help\" for other commands.\n"); } ret = true; retl: free_uservalue(val); return ret; } bool handler__update(globals_t *vars, char **argv, unsigned argc) { USEPARAMS(); if (vars->num_matches) { if (sm_checkmatches(vars, MATCHUPDATE, NULL) == false) { show_error("failed to scan target address space.\n"); return false; } } else { show_error("cannot use that command without matches\n"); return false; } return true; } bool handler__exit(globals_t *vars, char **argv, unsigned argc) { USEPARAMS(); vars->exit = 1; return true; } #define DOC_COLUMN 11 /* which column descriptions start on with the help command */ bool handler__help(globals_t *vars, char **argv, unsigned argc) { bool ret = false; list_t *commands = vars->commands; element_t *np = NULL; command_t *def = NULL; FILE *outfd; assert(commands != NULL); assert(argc >= 1); np = commands->head; outfd = get_pager(); /* print version information for generic help */ if (argv[1] == NULL) { vars->printversion(outfd); fprintf(outfd, "\n"); } /* traverse the commands list, printing out the relevant documentation */ while (np) { command_t *command = np->data; /* remember the default command */ if (command->command == NULL) def = command; /* just `help` with no argument */ if (argv[1] == NULL) { /* NULL shortdoc means don't print in the help listing */ if (command->shortdoc == NULL) { np = np->next; continue; } /* print out command name */ fprintf(outfd, "%-*s%s\n", DOC_COLUMN, command->command ? command->command : "default", command->shortdoc); /* detailed information requested about specific command */ } else if (command->command && strcasecmp(argv[1], command->command) == 0) { fprintf(outfd, "%s\n", command->longdoc ? command-> longdoc : "missing documentation"); ret = true; goto retl; } np = np->next; } if (argc > 1) { show_error("unknown command `%s`\n", argv[1]); ret = false; } else if (def) { fprintf(outfd, "\n%s\n", def->longdoc ? def->longdoc : ""); ret = true; } ret = true; retl: if (outfd != stderr) pclose(outfd); return ret; } bool handler__eof(globals_t * vars, char **argv, unsigned argc) { show_user("exit\n"); return handler__exit(vars, argv, argc); } /* XXX: handle !ls style escapes */ bool handler__shell(globals_t * vars, char **argv, unsigned argc) { size_t len = argc; unsigned i; char *command; USEPARAMS(); if (argc < 2) { show_error("shell command requires an argument, see `help shell`.\n"); return false; } /* convert arg vector into single string, first calculate length */ for (i = 1; i < argc; i++) len += strlen(argv[i]); /* allocate space */ command = calloca(len, 1); /* concatenate strings */ for (i = 1; i < argc; i++) { strcat(command, argv[i]); strcat(command, " "); } /* finally execute command */ if (system(command) == -1) { // command is allocated with alloca, do not free it // free(command); show_error("system() failed, command was not executed.\n"); return false; } // command is allocated with alloca, do not free it // free(command); return true; } bool handler__watch(globals_t * vars, char **argv, unsigned argc) { size_t id; char *end = NULL, buf[128], timestamp[64]; time_t t; match_location loc; value_t val; void *address; scan_data_type_t data_type = vars->options.scan_data_type; scan_routine_t valuecmp_routine; if (argc != 2) { show_error("was expecting one argument, see `help watch`.\n"); return false; } if ((data_type == BYTEARRAY) || (data_type == STRING)) { show_error("`watch` is not supported for bytearray or string.\n"); return false; } /* parse argument */ id = strtoul(argv[1], &end, 0x00); /* check that strtoul() worked */ if (argv[1][0] == '\0' || *end != '\0') { show_error("sorry, couldn't parse `%s`, try `help watch`\n", argv[1]); return false; } loc = nth_match(vars->matches, id); /* check that this is a valid match-id */ if (!loc.swath) { show_error("you specified a non-existent match `%u`.\n", id); show_info("use \"list\" to list matches, or \"help\" for other commands.\n"); return false; } address = remote_address_of_nth_element(loc.swath, loc.index); val = data_to_val(loc.swath, loc.index); if (INTERRUPTABLE()) { (void) sm_detach(vars->target); ENDINTERRUPTABLE(); return true; } /* every entry is timestamped */ t = time(NULL); strftime(timestamp, sizeof(timestamp), "[%T]", localtime(&t)); show_info("%s monitoring %10p for changes until interrupted...\n", timestamp, address); valuecmp_routine = sm_get_scanroutine(ANYNUMBER, MATCHCHANGED, flags_empty, vars->options.reverse_endianness); while (true) { const mem64_t *memory_ptr; size_t memlength; if (sm_attach(vars->target) == false) return false; if (sm_peekdata(vars->target, address, sizeof(uint64_t), &memory_ptr, &memlength) == false) return false; /* check if the new value is different */ match_flags tmpflags = flags_empty; if ((*valuecmp_routine)(memory_ptr, memlength, &val, NULL, &tmpflags)) { memcpy(val.bytes, memory_ptr, memlength); valtostr(&val, buf, sizeof(buf)); /* fetch new timestamp */ t = time(NULL); strftime(timestamp, sizeof(timestamp), "[%T]", localtime(&t)); show_info("%s %10p -> %s\n", timestamp, address, buf); } /* detach after valuecmp_routine, since it may read more data (e.g. bytearray) */ sm_detach(vars->target); (void) sleep(1); } } #include "licence.h" bool handler__show(globals_t * vars, char **argv, unsigned argc) { USEPARAMS(); if (argv[1] == NULL) { show_error("expecting an argument.\n"); return false; } if (strcmp(argv[1], "copying") == 0) show_user(SM_COPYING); else if (strcmp(argv[1], "warranty") == 0) show_user(SM_WARRANTY); else if (strcmp(argv[1], "version") == 0) vars->printversion(stderr); else { show_error("unrecognized show command `%s`\n", argv[1]); return false; } return true; } bool handler__dump(globals_t * vars, char **argv, unsigned argc) { void *addr; char *endptr; char *buf = NULL; int len; bool dump_to_file = false; FILE *dump_f = NULL; if (argc < 3 || argc > 4) { show_error("bad argument, see `help dump`.\n"); return false; } /* check address */ errno = 0; addr = (void *)(strtoll(argv[1], &endptr, 16)); if ((errno != 0) || (*endptr != '\0')) { show_error("bad address, see `help dump`.\n"); return false; } /* check length */ errno = 0; len = strtoll(argv[2], &endptr, 0); if ((errno != 0) || (*endptr != '\0')) { show_error("bad length, see `help dump`.\n"); return false; } /* check filename */ if (argc == 4) { if((dump_f = fopen(argv[3], "wb")) == NULL) { show_error("failed to open file\n"); return false; } dump_to_file = true; } buf = malloc(len + sizeof(long)); if (buf == NULL) { if (dump_f) fclose(dump_f); show_error("memory allocation failed.\n"); return false; } if (!sm_read_array(vars->target, addr, buf, len)) { if (dump_f) fclose(dump_f); show_error("read memory failed.\n"); free(buf); return false; } if(dump_to_file) { size_t s = fwrite(buf,1,len,dump_f); fclose(dump_f); if (s != len) { show_error("write to file failed.\n"); free(buf); return false; } } else { if (vars->options.backend == 1) { /* dump raw memory to stdout, the front-end will handle it */ fwrite(buf, sizeof(char), len, stdout); } else { /* print it out nicely */ int i,j; int buf_idx = 0; for (i = 0; i + 16 < len; i += 16) { printf("%p: ", addr+i); for (j = 0; j < 16; ++j) { printf("%02X ", (unsigned char)(buf[buf_idx++])); } if(vars->options.dump_with_ascii == 1) { for (j = 0; j < 16; ++j) { char c = buf[i+j]; printf("%c", isprint(c) ? c : '.'); } } printf("\n"); } if (i < len) { printf("%p: ", addr+i); for (j = i; j < len; ++j) { printf("%02X ", (unsigned char)(buf[buf_idx++])); } if(vars->options.dump_with_ascii == 1) { while(j%16 !=0) // skip "empty" numbers { printf(" "); ++j; } for (j = 0; i+j < len; ++j) { char c = buf[i+j]; printf("%c", isprint(c) ? c : '.'); } } printf("\n"); } } } free(buf); return true; } /* Returns (scan_data_type_t)(-1) on parse failure */ static inline scan_data_type_t parse_scan_data_type(const char *str) { /* Anytypes */ if ((strcasecmp(str, "number") == 0) || (strcasecmp(str, "anynumber") == 0)) return ANYNUMBER; if ((strcasecmp(str, "int") == 0) || (strcasecmp(str, "anyint") == 0) || (strcasecmp(str, "integer") == 0) || (strcasecmp(str, "anyinteger") == 0)) return ANYINTEGER; if ((strcasecmp(str, "float") == 0) || (strcasecmp(str, "anyfloat") == 0)) return ANYFLOAT; /* Ints */ if ((strcasecmp(str, "i8") == 0) || (strcasecmp(str, "int8") == 0) || (strcasecmp(str, "integer8") == 0)) return INTEGER8; if ((strcasecmp(str, "i16") == 0) || (strcasecmp(str, "int16") == 0) || (strcasecmp(str, "integer16") == 0)) return INTEGER16; if ((strcasecmp(str, "i32") == 0) || (strcasecmp(str, "int32") == 0) || (strcasecmp(str, "integer32") == 0)) return INTEGER32; if ((strcasecmp(str, "i64") == 0) || (strcasecmp(str, "int64") == 0) || (strcasecmp(str, "integer64") == 0)) return INTEGER64; /* Floats */ if ((strcasecmp(str, "f32") == 0) || (strcasecmp(str, "float32") == 0)) return FLOAT32; if ((strcasecmp(str, "f64") == 0) || (strcasecmp(str, "float64") == 0) || (strcasecmp(str, "double") == 0)) return FLOAT64; /* VLT */ if (strcasecmp(str, "bytearray") == 0) return BYTEARRAY; if (strcasecmp(str, "string") == 0) return STRING; /* Not a valid type */ return (scan_data_type_t)(-1); } /* write value_type address value */ bool handler__write(globals_t * vars, char **argv, unsigned argc) { int data_width = 0; const char *fmt = NULL; void *addr; char *buf = NULL; char *endptr; int datatype; /* 0 for numbers, 1 for bytearray, 2 for string */ bool ret; const char *string_parameter = NULL; /* used by string type */ if (argc < 4) { show_error("bad arguments, see `help write`.\n"); ret = false; goto retl; } scan_data_type_t st = parse_scan_data_type(argv[1]); /* try int first */ if (st == INTEGER8) { data_width = 1; datatype = 0; fmt = "%"PRId8; } else if (st == INTEGER16) { data_width = 2; datatype = 0; fmt = "%"PRId16; } else if (st == INTEGER32) { data_width = 4; datatype = 0; fmt = "%"PRId32; } else if (st == INTEGER64) { data_width = 8; datatype = 0; fmt = "%"PRId64; } else if (st == FLOAT32) { data_width = 4; datatype = 0; fmt = "%f"; } else if (st == FLOAT64) { data_width = 8; datatype = 0; fmt = "%lf"; } else if (st == BYTEARRAY) { data_width = argc - 3; datatype = 1; } else if (st == STRING) { /* locate the string parameter, say locate the beginning of the 4th parameter (2 characters after the end of the 3rd paramter)*/ int i; string_parameter = vars->current_cmdline; for(i = 0; i < 3; ++i) { while(isspace(*string_parameter)) ++ string_parameter; while(!isspace(*string_parameter)) ++ string_parameter; } ++ string_parameter; data_width = strlen(string_parameter); datatype = 2; } /* may support more types here */ else { show_error("bad data_type, see `help write`.\n"); ret = false; goto retl; } /* check argc again */ if ((datatype == 0) && (argc != 4)) { show_error("bad arguments, see `help write`.\n"); ret = false; goto retl; } /* check address */ errno = 0; addr = (void *)strtoll(argv[2], &endptr, 16); if ((errno != 0) || (*endptr != '\0')) { show_error("bad address, see `help write`.\n"); ret = false; goto retl; } buf = malloc(data_width + 8); /* allocate a little bit more, just in case */ if (buf == NULL) { show_error("memory allocation failed.\n"); ret = false; goto retl; } /* load value into buffer */ switch(datatype) { case 0: // numbers if(sscanf(argv[3], fmt, buf) < 1) /* should be OK even for max uint64 */ { show_error("bad value, see `help write`.\n"); ret = false; goto retl; } if (1 < data_width && vars->options.reverse_endianness) { swap_bytes_var(buf, data_width); } break; case 1: // bytearray ; /* cheat gcc */ /* call parse_uservalue_bytearray */ uservalue_t val_buf; if(!parse_uservalue_bytearray(argv+3, argc-3, &val_buf)) { show_error("bad byte array specified.\n"); free_uservalue(&val_buf); ret = false; goto retl; } int i; { // if wildcard is provided in the bytearray, we need the original data. bool wildcard_used = false; for(i = 0; i < data_width; ++i) { if(val_buf.wildcard_value[i] == WILDCARD) { wildcard_used = true; break; } } if (wildcard_used) { if(!sm_read_array(vars->target, addr, buf, data_width)) { show_error("read memory failed.\n"); free_uservalue(&val_buf); ret = false; goto retl; } } } for(i = 0; i < data_width; ++i) { if(val_buf.wildcard_value[i] == FIXED) { buf[i] = val_buf.bytearray_value[i]; } } free_uservalue(&val_buf); break; case 2: //string strncpy(buf, string_parameter, data_width); break; default: assert(false); } /* write into memory */ ret = sm_write_array(vars->target, addr, buf, data_width); retl: if(buf) free(buf); return ret; } bool handler__option(globals_t * vars, char **argv, unsigned argc) { /* this might need to change */ if (argc != 3) { show_error("bad arguments, see `help option`.\n"); return false; } if (strcasecmp(argv[1], "scan_data_type") == 0) { scan_data_type_t st = parse_scan_data_type(argv[2]); if (st != (scan_data_type_t)(-1)) { vars->options.scan_data_type = st; } else { show_error("bad value for scan_data_type, see `help option`.\n"); return false; } } else if (strcasecmp(argv[1], "region_scan_level") == 0) { if (strcmp(argv[2], "1") == 0) {vars->options.region_scan_level = REGION_HEAP_STACK_EXECUTABLE; } else if (strcmp(argv[2], "2") == 0) {vars->options.region_scan_level = REGION_HEAP_STACK_EXECUTABLE_BSS; } else if (strcmp(argv[2], "3") == 0) {vars->options.region_scan_level = REGION_ALL; } else { show_error("bad value for region_scan_level, see `help option`.\n"); return false; } } else if (strcasecmp(argv[1], "dump_with_ascii") == 0) { if (strcmp(argv[2], "0") == 0) {vars->options.dump_with_ascii = 0; } else if (strcmp(argv[2], "1") == 0) {vars->options.dump_with_ascii = 1; } else { show_error("bad value for dump_with_ascii, see `help option`.\n"); return false; } } else if (strcasecmp(argv[1], "endianness") == 0) { // data is host endian: don't swap if (strcmp(argv[2], "0") == 0) {vars->options.reverse_endianness = 0; } // data is little endian: swap if host is big endian else if (strcmp(argv[2], "1") == 0) {vars->options.reverse_endianness = big_endian; } // data is big endian: swap if host is little endian else if (strcmp(argv[2], "2") == 0) {vars->options.reverse_endianness = !big_endian; } else { show_error("bad value for endianness, see `help option`.\n"); return false; } } else { show_error("unknown option specified, see `help option`.\n"); return false; } return true; } scanmem-0.17/handlers.h000066400000000000000000000453501317023271400150440ustar00rootroot00000000000000/* Specific command handling with help texts. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009,2010 WANG Lu Copyright (C) 2014-2016 Sebastian Parschauer This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef HANDLERS_H #define HANDLERS_H #include #include "scanmem.h" /* Common documentations */ #define SET_FORMAT_DOC \ "The Set Format:\n" \ "[!][..a](,b..c | d, ...)[e..]\n" \ "An optional `!` at the beginning inverts the defined set.\n" \ "The beginning and end of the set may include an optional \"open-ended\" range:\n" \ "`..a` means a range starting at `0` to `a`.\n" \ "`e..` means a range starting at `e` to the last valid value.\n" \ "The rest is the same as the standard format.\n\n" /* * SHRTDOC's are one line descriptions of the supported command (shown with `help`). * LONGDOC's are detailed descriptions (shown with `help command`) (wrap them before column 80). * * The DOC's are passed to the registercommand() routine, and are read by the help * command. You can define SHRTDOC as NULL and help will not print it. * However, a NULL longdoc will print "missing documentation" if specific help is requested. * If you really don't want it to print anything, use "". * * Each handler is passed an argv and argc, argv[0] being the command called. Also passed is * a pointer to the global settings structure, which contains various settings and lists * that handlers are allowed to change. One handler can handle multiple commands, but * you need register each command with its own documentation. */ #define SET_SHRTDOC "change known matches to specified value" #define SET_LONGDOC "usage: set <[match-id set=]n [...]>\n" \ "Inject the value `n` into all the matches in `match-id set`, or if just `n` is\n" \ "specified, all known matches. `n` can be specified in standard C language\n" \ "notation, a leading 0x for hexadecimal, leading 0 for octal, and everything\n" \ "else is assumed to be decimal. All known matches, along with their match-ids\n" \ "can be displayed with the `list` command. Multiple match-ids can be specified,\n" \ "as a set data-type, terminated with a '=' sign. See below for a description of\n" \ "the set data-type.\n" \ "To set a value continually, for example to prevent a counter from decreasing,\n" \ "suffix the command with '/', followed by the number of seconds to wait between\n" \ "sets. Interrupt scanmem with ^C to stop the setting.\n\n" \ "Note that this command cannot work for bytearray or string.\n\n" \ SET_FORMAT_DOC \ "Examples:\n" \ "\tset 10 - set all known matches to 10\n" \ "\tset 0=0x03 - set match 0 to 0x03.\n" \ "\tset ..7=0x32 - set matches 0 through 7 to 0x32\n" \ "\tset 0,3=42/8 - set matches 0 and 3 to 42 every 8 seconds\n" \ "\tset !4..12=5 - set all matches except 4 through 12 to 5\n" \ "\tset 12,13,14=0x23/2 19..23=0/8 6=4 0 - complex example, can be combined" \ bool handler__set(globals_t *vars, char **argv, unsigned argc); #define LIST_SHRTDOC "list currently known matches" #define LIST_LONGDOC "usage: list [max_to_print]\n" \ "Print currently known matches, along with details about the\n" \ "match, such as its type, location, and last known value. The number in\n" \ "the left column is the `match-id`, this can be passed to other commands\n" \ "such as `set`, `delete`, etc.\n" \ "By default `list` prints up to 10k matches, a numerical parameter" \ "can be given to change this limit.\n" \ "The flags displayed indicate the possible types of the variable.\n" \ "Also the region id, an offset and the region type belonging to a match\n" \ "are displayed. The offset is used from the code load address or region start.\n" \ "This helps bypassing address space layout randomization (ASLR).\n" bool handler__list(globals_t *vars, char **argv, unsigned argc); #define DELETE_SHRTDOC "delete known matches by match-id" #define DELETE_LONGDOC "usage: delete \n" \ "Remove a set of match-id's from the match list. The set format\n" \ "is described below. The list of match-id's can\n" \ "be found using the `list` command. To delete all matches, see\n" \ "the `reset` command.\n" \ "To delete all matches associated with a particular library, see the\n" \ "`dregion` command, which will also remove any associated matches.\n\n" \ SET_FORMAT_DOC \ "Examples:\n" \ "\tdelete ..5,10..15 - delete matches 0 through 5 and 10 through 15\n" \ "\tdelete ..5,14,20.. - delete matches 0 through 5, 14, and 20 through the last match\n" \ "\tdelete !4..8,11 - delete all matches except for 4 through 8 and 11\n\n" \ "NOTE: Match-ids may be recalculated after matches are removed or added. However, the set\n" \ " of matches is guaranteed to be deleted properly." bool handler__delete(globals_t *vars, char **argv, unsigned argc); #define RESET_SHRTDOC "forget all matches, and reinitialise regions" #define RESET_LONGDOC "usage: reset\n" \ "Forget all matches and regions, and reread regions from the relevant\n" \ "maps file. Useful if you have made an error, or want to find a new\n" \ "variable.\n" bool handler__reset(globals_t *vars, char **argv, unsigned argc); #define PID_SHRTDOC "print current pid, or attach to a new process" #define PID_LONGDOC "usage: pid [pid]\n" \ "If `pid` is specified, reset current session and then attach to new\n" \ "process `pid`. If `pid` is not specified, print information about\n" \ "current process." bool handler__pid(globals_t *vars, char **argv, unsigned argc); #define SNAPSHOT_SHRTDOC "take a snapshot of the current process state" #define SNAPSHOT_LONGDOC "usage: snapshot\n" \ "Take a snapshot of the entire process in its current state. This is useful\n" \ "if you don't know the exact value of the variable you are searching for, but\n" \ "can describe it in terms of higher, lower or equal (see commands `>`,`<` and\n" \ "`=`).\n\n" \ "NOTE: This can use a lot of memory with large processes." bool handler__snapshot(globals_t *vars, char **argv, unsigned argc); #define DREGION_SHRTDOC "delete a known region by region-id" #define DREGION_LONGDOC "usage: dregion \n" \ "Remove a set of region-id from the regions list,\n" \ "along with any matches affected from the match list.\n" \ "The `region-id` can be found using the `lregions` command.\n\n" \ SET_FORMAT_DOC bool handler__dregion(globals_t *vars, char **argv, unsigned argc); #define LREGIONS_SHRTDOC "list all known regions" #define LREGIONS_LONGDOC "usage: lregions\n" \ "Print all the currently known regions, along with details such as the\n" \ "start address, size, region type, load address, permissions and associated\n" \ "filename. The number in the left column is the `region-id`, this can be\n" \ "passed to other commands that process regions, such as `dregion`.\n" \ "The load address is the start of the .text region for the executable\n" \ "or libraries. Otherwise, it is the region start.\n" bool handler__lregions(globals_t *vars, char **argv, unsigned argc); #define GREATERTHAN_SHRTDOC "match values that have increased or greater than some number" #define LESSTHAN_SHRTDOC "match values that have decreased or less than some number" #define NOTCHANGED_SHRTDOC "match values that have not changed or equal to some number" #define CHANGED_SHRTDOC "match values that have changed or different from some number" #define INCREASED_SHRTDOC "match values that have increased at all or by some number" #define DECREASED_SHRTDOC "match values that have decreased at all or by some number" #define GREATERTHAN_LONGDOC "usage: > [n]\n" \ "If n is given, match values that are greater than n.\n" \ "Otherwise match all values that have increased. (same as `+`)\n" \ "You can use this in conjunction with `snapshot` if you never know its value." #define LESSTHAN_LONGDOC "usage: < [n]\n" \ "If n is given, match values that are less than n.\n" \ "Otherwise match all values that have decreased. (same as `-`)\n" \ "You can use this in conjunction with `snapshot` if you never know its value." #define NOTCHANGED_LONGDOC "usage: = [n]\n" \ "If n is given, match values that are equal to n. (same as `n`)\n" \ "Otherwise match all values that have not changed since the last scan.\n" \ "You can use this in conjunction with `snapshot` if you never know its value." #define CHANGED_LONGDOC "usage: != [n]\n" \ "If n is given, match values that are different from n.\n" \ "Otherwise match all values that have changed since the last scan.\n" \ "You can use this in conjunction with `snapshot` if you never know its value." #define INCREASED_LONGDOC "usage: + [n]\n" \ "If n is given, match values that have been increased by n\n" \ "Otherwise match all values that have increased. (same as `>`)\n" \ "You can use this in conjunction with `snapshot` if you never know its value." #define DECREASED_LONGDOC "usage: - [n]\n" \ "If n is given, match values that have been decreased by n\n" \ "Otherwise match all values that have decreased. (same as `<`)\n" \ "You can use this in conjunction with `snapshot` if you never know its value." bool handler__operators(globals_t *vars, char **argv, unsigned argc); #define VERSION_SHRTDOC "print current version" #define VERSION_LONGDOC "usage: version\n" \ "Display the current version of scanmem in use." bool handler__version(globals_t *vars, char **argv, unsigned argc); #define EXIT_SHRTDOC "exit the program immediately" #define EXIT_LONGDOC "usage: exit\n" \ "Exit scanmem immediately, zero will be returned." bool handler__exit(globals_t *vars, char **argv, unsigned argc); #define HELP_SHRTDOC "access online documentation, use `help command` for specific help" #define HELP_LONGDOC "usage: help [command]\n" \ "If `command` is specified, print detailed information about command `command`\n" \ "including options and usage summary. If `command` is not specified, print a\n" \ "one line description of each command supported." bool handler__help(globals_t *vars, char **argv, unsigned argc); #define DEFAULT_SHRTDOC NULL #define DEFAULT_LONGDOC "When searching for a number, use any notation in standard C language (leading 0x for\n" \ "hexadecimal, leading 0 for octal, everything else is assumed to be decimal).\n" \ "Float numbers are also acceptable, but will be rounded if scanning integers.\n" \ "Use \'..\' for a range, e.g. \'1..3\' searches between 1 and 3 inclusive.\n" \ "\n" \ "When searching for an array of byte, use 2-byte hexadecimal notation, \n" \ "separated by spaces, wildcard '?\?' is also supported. E.g. FF ?\? EE ?\? 02 01\n" \ "\n" \ "When searching for strings, use the \" command\n" \ "\n" \ "Scan the current process for variables with given value.\n" \ "By entering the value of the variable as it changes multiple times, scanmem can\n" \ "eliminate matches, eventually identifying where the variable is located.\n" \ "Once the variable is found, use `set` to change its value.\n" bool handler__default(globals_t *vars, char **argv, unsigned argc); #define STRING_SHRTDOC "match a given string" #define STRING_LONGDOC "usage \" \n" \ " is counted since the 2nd character following the leading \"\n" \ "This can only be used when scan_data_type is set to be string\n" \ "Example:\n" \ "\t\" Scan for string, spaces and ' \" are all acceptable.\n" bool handler__string(globals_t *vars, char **argv, unsigned argc); #define UPDATE_SHRTDOC "update match values without culling list" #define UPDATE_LONGDOC "usage: update\n" \ "Scans the current process, getting the current values of all matches.\n" \ "These values can be viewed with `list`, and are also the old values that\n" \ "scanmem compares to when using `>`, `<`, or `=`. This command is equivalent\n" \ "to a search command that all current results match.\n" bool handler__update(globals_t *vars, char **argv, unsigned argc); bool handler__eof(globals_t *vars, char **argv, unsigned argc); #define SHELL_SHRTDOC "execute a shell command without leaving scanmem" #define SHELL_LONGDOC "usage: ! | shell [shell-command]\n" \ "Execute `shell-command` using /bin/sh then return, useful for reading man\n" \ "pages, checking top, or making notes with an external editor.\n" \ "Examples:\n" \ "\t! vi notes.txt\n" \ "\tshell man scanmem\n" \ "\tshell cat > notes.txt\n" bool handler__shell(globals_t *vars, char **argv, unsigned argc); #define WATCH_SHRTDOC "monitor the value of a memory location as it changes" #define WATCH_LONGDOC "usage: watch [match-id]\n" \ "Monitors the match `match-id`, by testing its value every second. When the\n" \ "value changes, its new value is printed along with an timestamp. Interrupt\n" \ "with ^C to stop monitoring.\n" \ "Examples:\n" \ "\twatch 12 - watch match 12 for any changes.\n" bool handler__watch(globals_t *vars, char **argv, unsigned argc); /*XXX: improve this */ #define SHOW_SHRTDOC "display information about scanmem." #define SHOW_LONGDOC "usage: show \n" \ "Display information relating to .\n" \ "Possible values: `copying`, `warranty` or `version`\n" bool handler__show(globals_t *vars, char **argv, unsigned argc); #define DUMP_SHRTDOC "dump a memory region to screen or a file" #define DUMP_LONGDOC "usage: dump
[]\n" \ "\n" \ "If is given, save the region of memory to the file \n" \ "Otherwise display it in a human-readable format.\n" bool handler__dump(globals_t *vars, char **argv, unsigned argc); #define WRITE_SHRTDOC "change the value of a specific memory location" #define WRITE_LONGDOC "usage: write
\n" \ "\n" \ "Write into
\n" \ " should be one of:\n" \ "\tint{8|16|32|64} (or i{8|16|32|64} for short)\n" \ "\tfloat{32|64} (or f{32|64} for short)\n" \ "\tbytearray\n" \ "\tstring\n" \ "\n" \ "Example:\n" \ "\twrite i16 60103e 0\n" \ "\twrite float32 60103e 0\n" \ "\twrite bytearray 60103e ff 01 32\n" \ "\twrite string 60103e cheating\n" bool handler__write(globals_t *vars, char **argv, unsigned argc); #define OPTION_SHRTDOC "set runtime options of scanmem, see `help option`" #define OPTION_LONGDOC "usage: option \n" \ "\n" \ "Here are all options and their possible values\n" \ "\n" \ "scan_data_type\t\tspecify what type of data should be considered\n" \ "\t\t\tDefault:int\n" \ "\tMOST OF TIME YOU MUST EXECUTE `reset' IMMEDIATELY AFTER CHANGING scan_data_type\n" \ "\n" \ "\tPossible Values:\n" \ "\tnumber:\t\t\tinteger or float\n" \ "\tint:\t\t\tinteger of any width\n" \ "\tfloat:\t\t\tfloat of any width\n" \ "\tint{8|16|32|64}:\tinteger of given width\n" \ "\tfloat{32|64}:\t\tfloat of given width\n" \ "\tbytearray:\t\tan array of bytes\n" \ "\tstring:\t\t\tstring\n" \ "\n" \ "region_scan_level\tspecify which regions should be scanned\n" \ "\t\t\tDefault:2\n" \ "\n" \ "\tPossible Values:\n" \ "\t1:\theap, stack and executable only\n" \ "\t2:\theap, stack executable and bss only\n" \ "\t3:\teverything(e.g. other libs)\n" \ "\n" \ "dump_with_ascii\twhether to print ascii characters with a memory dump\n" \ "\t\t\tDefault:1\n" \ "\n" \ "\tpossible values:\n" \ "\t0:\tdisabled\n" \ "\t1:\tenabled\n" \ "\n" \ "endianness\tendianness of data (used by: set, write and comparisons)\n" \ "\t\t\tDefault:0\n" \ "\n" \ "\tpossible values:\n" \ "\t0:\thost endian\n" \ "\t1:\tlittle endian\n" \ "\t2:\tbig endian\n" \ "\n" \ "Example:\n" \ "\toption scan_data_type int32\n" bool handler__option(globals_t *vars, char **argv, unsigned argc); #endif /* HANDLERS_H */ scanmem-0.17/interrupt.h000066400000000000000000000030011317023271400152630ustar00rootroot00000000000000/* Interrupt handling to stop watching or locking variables. This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef INTERRUPT_H #define INTERRUPT_H #ifndef _GNU_SOURCE # define _GNU_SOURCE /* for sighandler_t */ #endif #include #include /* small header file to manage interrupted commands */ static sigjmp_buf jmpbuf; /* used when aborting a command due to an interrupt */ static sighandler_t oldsig; /* reinstalled before longjmp */ static unsigned intused; /* signal handler used to handle an interrupt during commands */ static void interrupted(int n) { (void) n; siglongjmp(jmpbuf, 1); } #define INTERRUPTABLE() ((oldsig = signal(SIGINT, interrupted)), intused = 1, sigsetjmp(jmpbuf, 1)) #define ENDINTERRUPTABLE() (intused ? ((void) signal(SIGINT, oldsig), intused = 0) : (intused = 0)) #endif /* INTERRUPT_H */ scanmem-0.17/lgpl-3.0.txt000066400000000000000000000167431317023271400150740ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser 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 Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. scanmem-0.17/licence.h000066400000000000000000000060661317023271400146470ustar00rootroot00000000000000/* Just some license texts. This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef LICENCE_H #define LICENCE_H #define SM_WARRANTY \ "15. Disclaimer of Warranty.\n\n" \ "THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\n" \ "APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\n" \ "HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\n" \ "OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\n" \ "THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n" \ "PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\n" \ "IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\n" \ "ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n" \ "\n" \ "16. Limitation of Liability.\n\n" \ "IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n" \ "WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\n" \ "THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\n" \ "GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\n" \ "USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\n" \ "DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\n" \ "PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\n" \ "EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\n" \ "SUCH DAMAGES.\n" #define SM_COPYING \ "This program is free software; you can redistribute it and/or modify\n" \ "it under the terms of the GNU General Public License as published by\n" \ "the Free Software Foundation; either version 3 of the License, or\n" \ "(at your option) any later version.\n" \ "\n" \ "This program is distributed in the hope that it will be useful,\n" \ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \ "GNU General Public License for more details.\n" \ "\n" \ "You should have received a copy of the GNU General Public License\n" \ "along with this program; if not, see .\n" #endif /* LICENCE_H */ scanmem-0.17/list.c000066400000000000000000000062241317023271400142070ustar00rootroot00000000000000/* A simple linked list implementation. Copyright (C) 2006,2007,2009 Tavis Ormandy This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include "list.h" /* create a new list */ list_t *l_init(void) { return calloc(1, sizeof(list_t)); } /* destroy the whole list */ void l_destroy(list_t * list) { void *data; if (list == NULL) return; /* remove every element */ while (list->size) { l_remove(list, NULL, &data); free(data); } free(list); } /* add a new element to the list */ int l_append(list_t * list, element_t * element, void *data) { element_t *n = calloc(1, sizeof(element_t)); if (n == NULL) return -1; n->data = data; /* insert at head or tail */ if (element == NULL) { if (list->size == 0) { list->tail = n; } n->next = list->head; list->head = n; } else { /* insertion at the middle of a list */ if (element->next == NULL) { list->tail = n; } n->next = element->next; element->next = n; } list->size++; return 0; } /* concatenate list src with list dst */ int l_concat(list_t *dst, list_t **src) { void *data; element_t *n; n = (*src)->head; while (n) { l_remove(*src, NULL, &data); if (l_append(dst, NULL, data) == -1) return -1; n = (*src)->head; } l_destroy(*src); *src = NULL; return 0; } /* remove the element at element->next */ void l_remove(list_t * list, element_t * element, void **data) { element_t *o; /* remove from head */ if (element == NULL) { if (data) { *data = list->head->data; } o = list->head; list->head = o->next; if (list->size == 1) { list->tail = NULL; } } else { if (data) { *data = element->next->data; } o = element->next; if ((element->next = element->next->next) == NULL) { list->tail = element; } } if (data == NULL) free(o->data); free(o); list->size--; return; } /* remove the nth element */ void l_remove_nth(list_t * list, size_t n, void **data) { element_t *np = list->head; /* traverse to correct element */ while (n--) { if ((np = np->next) == NULL) /* return */ abort(); } l_remove(list, np, data); } scanmem-0.17/list.h000066400000000000000000000027241317023271400142150ustar00rootroot00000000000000/* A very simple linked list implementation. This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef LIST_H #define LIST_H typedef struct element { void *data; struct element *next; } element_t; typedef struct { size_t size; element_t *head; element_t *tail; } list_t; /* create a new list */ list_t *l_init(void); /* destroy the whole list */ void l_destroy(list_t * list); /* add a new element to the list */ int l_append(list_t * list, element_t * element, void *data); /* remove the element at element->next */ void l_remove(list_t * list, element_t * element, void **data); /* remove the nth element from head */ void l_remove_nth(list_t * list, size_t n, void **data); /* remove all elements from *src, and append to dst */ int l_concat(list_t *dst, list_t **src); #endif /* LIST_H */ scanmem-0.17/main.c000066400000000000000000000163201317023271400141560ustar00rootroot00000000000000/* Scanmem main function, option parsing and help text. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009-2013 WANG Lu Copyright (C) 2016 Sebastian Parschauer This file is part of scanmem. 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 . */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "config.h" #include #include #include #include #include #include #include #include "scanmem.h" #include "commands.h" #include "show_message.h" #include "menu.h" static const char copy_text[] = "Copyright (C) 2006-2017 Scanmem authors\n" "See https://github.com/scanmem/scanmem/blob/master/AUTHORS for a full author list\n" "\n" "scanmem comes with ABSOLUTELY NO WARRANTY; for details type `show warranty'.\n" "This is free software, and you are welcome to redistribute it\n" "under certain conditions; type `show copying' for details.\n\n"; /* print scanmem and libscanmem version */ static void printversion(FILE *outfd) { fprintf(outfd, "scanmem version %s\n", PACKAGE_VERSION); sm_printversion(outfd); } /* print scanmem version and copyright info */ static void printcopyright(FILE *outfd) { printversion(outfd); fprintf(outfd, "\n%s", copy_text); } static const char help_text[] = "Usage: scanmem [OPTION]... [PID]\n" "Interactively locate and modify variables in an executing process.\n" "\n" "-p, --pid=pid\t\tset the target process pid\n" "-c, --command\t\trun given commands (separated by `;`)\n" "-h, --help\t\tprint this message\n" "-v, --version\t\tprint version information\n" "\n" "scanmem is an interactive debugging utility, enter `help` at the prompt\n" "for further assistance.\n" "\n" "Report bugs to <" PACKAGE_BUGREPORT ">.\n"; /* print quick usage message to stderr */ static void printhelp(void) { printcopyright(stderr); show_user("%s", help_text); return; } static inline void show_user_quick_help(pid_t target) { if (target == 0) { show_user("Enter the pid of the process to search using the \"pid\" command.\n"); show_user("Enter \"help\" for other commands.\n"); } else { show_user("Please enter current value, or \"help\" for other commands.\n"); } } static void parse_parameters(int argc, char **argv, char **initial_commands, bool *exit_on_error) { struct option longopts[] = { {"pid", 1, NULL, 'p'}, /* target pid */ {"command", 1, NULL, 'c'}, /* commands to run at the beginning */ {"version", 0, NULL, 'v'}, /* print version */ {"help", 0, NULL, 'h'}, /* print help summary */ {"debug", 0, NULL, 'd'}, /* enable debug mode */ {"errexit", 0, NULL, 'e'}, /* exit on initial command failure */ {NULL, 0, NULL, 0}, }; char *end; int optindex; bool done = false; globals_t *vars = &sm_globals; /* process command line */ while (!done) { switch (getopt_long(argc, argv, "vhdep:c:", longopts, &optindex)) { case 'p': vars->target = (pid_t) strtoul(optarg, &end, 0); /* check if that parsed correctly */ if (*end != '\0' || *optarg == '\0' || vars->target == 0) { show_error("invalid pid specified.\n"); exit(EXIT_FAILURE); } break; case 'c': *initial_commands = optarg; break; case 'v': printversion(stderr); exit(EXIT_FAILURE); case 'h': printhelp(); exit(EXIT_FAILURE); case 'd': vars->options.debug = 1; break; case 'e': *exit_on_error = true; break; case -1: done = true; break; default: printhelp(); exit(EXIT_FAILURE); } } /* parse any pid specified after arguments */ if (optind <= argc && argv[optind]) { vars->target = (pid_t) strtoul(argv[optind], &end, 0); /* check if that parsed correctly */ if (*end != '\0' || argv[optind][0] == '\0' || vars->target == 0) { show_error("invalid pid specified.\n"); exit(EXIT_FAILURE); } } } int main(int argc, char **argv) { char *initial_commands = NULL; bool exit_on_error = false; parse_parameters(argc, argv, &initial_commands, &exit_on_error); int ret = EXIT_SUCCESS; globals_t *vars = &sm_globals; printcopyright(stderr); vars->printversion = printversion; if (!sm_init()) { show_error("Initialization failed.\n"); ret = EXIT_FAILURE; goto end; } if (getuid() != 0) { show_warn("Run scanmem as root if memory regions are missing. " "See scanmem man page.\n\n"); } /* this will initialize matches and regions */ if (sm_execcommand(vars, "reset") == false) { vars->target = 0; } /* check if there is a target already specified */ show_user_quick_help(vars->target); /* execute commands passed by `-c`, if any */ if (initial_commands) { char *saveptr = NULL; const char sep[] = ";\n"; for (char *line = strtok_r(initial_commands, sep, &saveptr); line != NULL; line = strtok_r(NULL, sep, &saveptr)) { if (vars->matches) { show_user("%ld> %s\n", vars->num_matches, line); } else { show_user("> %s\n", line); } if (sm_execcommand(vars, line) == false) { if (exit_on_error) goto end; show_user_quick_help(vars->target); } fflush(stdout); fflush(stderr); } } /* main loop, read input and process commands */ while (!vars->exit) { char *line; /* reads in a commandline from the user and returns a pointer to it in *line */ if (getcommand(vars, &line) == false) { show_error("failed to read in a command.\n"); ret = EXIT_FAILURE; break; } /* sm_execcommand() returning failure is not fatal, it just means the command could not complete. */ if (sm_execcommand(vars, line) == false) { show_user_quick_help(vars->target); } free(line); fflush(stdout); fflush(stderr); } end: sm_cleanup(); return ret; } scanmem-0.17/maps.c000066400000000000000000000251541317023271400141770ustar00rootroot00000000000000/* Reading the data from /proc/pid/maps into a regions list. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009,2010 WANG Lu Copyright (C) 2014-2016 Sebastian Parschauer This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include "list.h" #include "maps.h" #include "getline.h" #include "show_message.h" const char *region_type_names[] = REGION_TYPE_NAMES; bool sm_readmaps(pid_t target, list_t *regions, region_scan_level_t region_scan_level) { FILE *maps; char name[128], *line = NULL; char exelink[128]; size_t len = 0; unsigned int code_regions = 0, exe_regions = 0; unsigned long prev_end = 0, load_addr = 0, exe_load = 0; bool is_exe = false; #define MAX_LINKBUF_SIZE 256 char linkbuf[MAX_LINKBUF_SIZE], *exename = linkbuf; int linkbuf_size; char binname[MAX_LINKBUF_SIZE]; /* check if target is valid */ if (target == 0) return false; /* construct the maps filename */ snprintf(name, sizeof(name), "/proc/%u/maps", target); /* attempt to open the maps file */ if ((maps = fopen(name, "r")) == NULL) { show_error("failed to open maps file %s.\n", name); return false; } show_info("maps file located at %s opened.\n", name); /* get executable name */ snprintf(exelink, sizeof(exelink), "/proc/%u/exe", target); linkbuf_size = readlink(exelink, exename, MAX_LINKBUF_SIZE - 1); if (linkbuf_size > 0) { exename[linkbuf_size] = 0; } else { /* readlink may fail for special processes, just treat as empty in order not to miss those regions */ exename[0] = 0; } /* read every line of the maps file */ while (getline(&line, &len, maps) != -1) { unsigned long start, end; region_t *map = NULL; char read, write, exec, cow, *filename; int offset, dev_major, dev_minor, inode; region_type_t type = REGION_TYPE_MISC; /* slight overallocation */ if ((filename = alloca(len)) == NULL) { show_error("failed to allocate %lu bytes for filename.\n", (unsigned long)len); goto error; } /* initialise to zero */ memset(filename, '\0', len); /* parse each line */ if (sscanf(line, "%lx-%lx %c%c%c%c %x %x:%x %u %[^\n]", &start, &end, &read, &write, &exec, &cow, &offset, &dev_major, &dev_minor, &inode, filename) >= 6) { /* * get the load address for regions of the same ELF file * * When the ELF loader loads an executable or a library into * memory, there is one region per ELF segment created: * .text (r-x), .rodata (r--), .data (rw-) and .bss (rw-). The * 'x' permission of .text is used to detect the load address * (region start) and the end of the ELF file in memory. All * these regions have the same filename. The only exception * is the .bss region. Its filename is empty and it is * consecutive with the .data region. But the regions .bss and * .rodata may not be present with some ELF files. This is why * we can't rely on other regions to be consecutive in memory. * There should never be more than these four regions. * The data regions use their variables relative to the load * address. So determining it makes sense as we can get the * variable address used within the ELF file with it. * But for the executable there is the special case that there * is a gap between .text and .rodata. Other regions might be * loaded via mmap() to it. So we have to count the number of * regions belonging to the exe separately to handle that. * References: * http://en.wikipedia.org/wiki/Executable_and_Linkable_Format * http://wiki.osdev.org/ELF * http://lwn.net/Articles/531148/ */ /* detect further regions of the same ELF file and its end */ if (code_regions > 0) { if (exec == 'x' || (strncmp(filename, binname, MAX_LINKBUF_SIZE) != 0 && (filename[0] != '\0' || start != prev_end)) || code_regions >= 4) { code_regions = 0; is_exe = false; /* exe with .text and without .data is impossible */ if (exe_regions > 1) exe_regions = 0; } else { code_regions++; if (is_exe) exe_regions++; } } if (code_regions == 0) { /* detect the first region belonging to an ELF file */ if (exec == 'x' && filename[0] != '\0') { code_regions++; if (strncmp(filename, exename, MAX_LINKBUF_SIZE) == 0) { exe_regions = 1; exe_load = start; is_exe = true; } strncpy(binname, filename, MAX_LINKBUF_SIZE); binname[MAX_LINKBUF_SIZE - 1] = '\0'; /* just to be sure */ /* detect the second region of the exe after skipping regions */ } else if (exe_regions == 1 && filename[0] != '\0' && strncmp(filename, exename, MAX_LINKBUF_SIZE) == 0) { code_regions = ++exe_regions; load_addr = exe_load; is_exe = true; strncpy(binname, filename, MAX_LINKBUF_SIZE); binname[MAX_LINKBUF_SIZE - 1] = '\0'; /* just to be sure */ } if (exe_regions < 2) load_addr = start; } prev_end = end; /* must have permissions to read and write, and be non-zero size */ if ((write == 'w') && (read == 'r') && ((end - start) > 0)) { bool useful = false; /* determine region type */ if (is_exe) type = REGION_TYPE_EXE; else if (code_regions > 0) type = REGION_TYPE_CODE; else if (!strcmp(filename, "[heap]")) type = REGION_TYPE_HEAP; else if (!strcmp(filename, "[stack]")) type = REGION_TYPE_STACK; /* determine if this region is useful */ switch (region_scan_level) { case REGION_ALL: useful = true; break; case REGION_HEAP_STACK_EXECUTABLE_BSS: if (filename[0] == '\0') { useful = true; break; } /* fall through */ case REGION_HEAP_STACK_EXECUTABLE: if (type == REGION_TYPE_HEAP || type == REGION_TYPE_STACK) { useful = true; break; } /* test if the region is mapped to the executable */ if (type == REGION_TYPE_EXE || strncmp(filename, exename, MAX_LINKBUF_SIZE) == 0) useful = true; break; } if (!useful) continue; /* allocate a new region structure */ if ((map = calloc(1, sizeof(region_t) + strlen(filename))) == NULL) { show_error("failed to allocate memory for region.\n"); goto error; } /* initialize this region */ map->flags.read = true; map->flags.write = true; map->start = (void *) start; map->size = (unsigned long) (end - start); map->type = type; map->load_addr = load_addr; /* setup other permissions */ map->flags.exec = (exec == 'x'); map->flags.shared = (cow == 's'); map->flags.private = (cow == 'p'); /* save pathname */ if (strlen(filename) != 0) { /* the pathname is concatenated with the structure */ strcpy(map->filename, filename); } /* add a unique identifier */ map->id = regions->size; /* okay, add this guy to our list */ if (l_append(regions, regions->tail, map) == -1) { show_error("failed to save region.\n"); goto error; } } } } show_info("%d suitable regions found.\n", regions->size); /* release memory allocated */ free(line); fclose(maps); return true; error: free(line); fclose(maps); return false; } scanmem-0.17/maps.h000066400000000000000000000047271317023271400142070ustar00rootroot00000000000000/* Reading the data from /proc/pid/maps into a regions list. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009,2010 WANG Lu Copyright (C) 2014-2016 Sebastian Parschauer This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef MAPS_H #define MAPS_H #include #include #include "list.h" /* determine which regions we need */ typedef enum { REGION_ALL, /* each of them */ REGION_HEAP_STACK_EXECUTABLE, /* heap, stack, executable */ REGION_HEAP_STACK_EXECUTABLE_BSS /* heap, stack, executable, bss */ } region_scan_level_t; typedef enum { REGION_TYPE_MISC, REGION_TYPE_CODE, REGION_TYPE_EXE, REGION_TYPE_HEAP, REGION_TYPE_STACK } region_type_t; #define REGION_TYPE_NAMES { "misc", "code", "exe", "heap", "stack" } extern const char *region_type_names[]; /* a region obtained from /proc/pid/maps, these are searched for matches */ typedef struct { void *start; /* Start address. Hack: If HAVE_PROCMEM is defined, this is actually an (unsigned long) offset into /proc/{pid}/mem */ unsigned long size; /* size */ region_type_t type; unsigned long load_addr; /* e.g. load address of the executable */ struct __attribute__((packed)) { unsigned read:1; unsigned write:1; unsigned exec:1; unsigned shared:1; unsigned private:1; } flags; unsigned id; /* unique identifier */ char filename[1]; /* associated file, must be last */ } region_t; bool sm_readmaps(pid_t target, list_t *regions, region_scan_level_t region_scan_level); #endif /* MAPS_H */ scanmem-0.17/menu.c000066400000000000000000000073351317023271400142040ustar00rootroot00000000000000/* Prompt and command completion. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2010,2011 Lu Wang This file is part of scanmem. 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 . */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include "config.h" #include #include #include #include #include #include #if HAVE_LIBREADLINE #include #include #else #include "readline.h" #endif #include "menu.h" #include "getline.h" #include "scanmem.h" #include "commands.h" #include "show_message.h" /* command generator for readline completion */ static char *commandgenerator(const char *text, int state) { static unsigned index = 0; unsigned i; size_t len; element_t *np; globals_t *vars = &sm_globals; /* reset generator if state == 0, otherwise continue from last time */ index = state ? index : 0; np = vars->commands ? vars->commands->head : NULL; len = strlen(text); /* skip to the last node checked */ for (i = 0; np && i < index; i++) np = np->next; /* traverse the commands list, checking for matches */ while (np) { command_t *command = np->data; np = np->next; /* record progress */ index++; /* if shortdoc is NULL, this is not supposed to be user visible */ if (command == NULL || command->command == NULL || command->shortdoc == NULL) continue; /* check if we have a match */ if (strncmp(text, command->command, len) == 0) { return strdup(command->command); } } return NULL; } /* custom completor program for readline */ static char **commandcompletion(const char *text, int start, int end) { (void) end; /* never use default completer (filenames), even if I dont generate any matches */ rl_attempted_completion_over = 1; /* only complete on the first word, the command */ return start ? NULL : rl_completion_matches(text, commandgenerator); } /* * getcommand() reads in a command using readline and places a pointer to * the read string into *line, which must be free'd by caller. * returns true on success, or false on error. */ bool getcommand(globals_t *vars, char **line) { char prompt[64]; bool success = true; assert(vars != NULL); if (vars->matches) { snprintf(prompt, sizeof(prompt), "%ld> ", vars->num_matches); } else { snprintf(prompt, sizeof(prompt), "> "); } rl_readline_name = "scanmem"; rl_attempted_completion_function = commandcompletion; while (true) { success = ((*line = readline(prompt)) != NULL); if (!success) { /* EOF */ if ((*line = strdup("__eof")) == NULL) { show_error("sorry, there was a memory allocation error.\n"); return false; } } if (strlen(*line)) { break; } free(*line); } /* record this line to readline history */ add_history(*line); return true; } scanmem-0.17/menu.h000066400000000000000000000021671317023271400142070ustar00rootroot00000000000000/* Prompt and command completion. Copyright (C) 2017 Andrea Stacchiotti This file is part of scanmem. 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 . */ #ifndef MENU_H #define MENU_H #include #include "scanmem.h" /* * getcommand() reads in a command using readline, and places a pointer to * the read string into *line, _which must be free'd by caller_. * returns true on success, or false on error. */ bool getcommand(globals_t *vars, char **line); #endif /* MENU_H */ scanmem-0.17/po/000077500000000000000000000000001317023271400135025ustar00rootroot00000000000000scanmem-0.17/po/LINGUAS000066400000000000000000000000251317023271400145240ustar00rootroot00000000000000de es it ja ru sr_ME scanmem-0.17/po/POTFILES.in000066400000000000000000000002701317023271400152560ustar00rootroot00000000000000[type: gettext/glade]gui/GameConqueror.ui gui/GameConqueror.py gui/misc.py gui/GameConqueror.desktop.in gui/org.freedesktop.gameconqueror.policy.in.in gui/GameConqueror.appdata.xml.in scanmem-0.17/po/POTFILES.skip000066400000000000000000000000541317023271400156160ustar00rootroot00000000000000gui/org.freedesktop.gameconqueror.policy.in scanmem-0.17/po/de.po000066400000000000000000000344551317023271400144450ustar00rootroot00000000000000# German translations for GameConqueror. # This file is distributed under the same license as GameConqueror. # msgid "" msgstr "" "Project-Id-Version: scanmem 0.16~pre\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-10-04 18:53+0200\n" "PO-Revision-Date: 2017-10-05 16:44+0200\n" "Last-Translator: Michael Klose \n" "Language-Team: german <>\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.0.2\n" #: ../gui/GameConqueror.ui.h:1 msgid "GameConqueror - Memory Editor" msgstr "GameConqueror - Speichereditor" #: ../gui/GameConqueror.ui.h:2 msgid "_Address:" msgstr "_Adresse:" #: ../gui/GameConqueror.ui.h:3 msgid "Target address here (CTRL+L)" msgstr "Zieladresse auswählen (STRG+L)" #: ../gui/GameConqueror.ui.h:4 msgid "Enter address to view" msgstr "Adresse zur Anzeige eingeben" #: ../gui/GameConqueror.ui.h:5 msgid "Jump to address (CTRL+ENTER)" msgstr "Springe zu Adresse (STRG+EINGABE)" #: ../gui/GameConqueror.ui.h:6 msgid "_Refresh (CTRL+R)" msgstr "Aktualisieren (STRG+R)" #: ../gui/GameConqueror.ui.h:7 msgid "GameConqueror" msgstr "GameConqueror" #: ../gui/GameConqueror.ui.h:8 msgid "Found: 0" msgstr "Gefunden: 0" #: ../gui/GameConqueror.ui.h:9 msgid "" "Syntax:\n" "\n" " For numeric types:\n" "\n" " N Exactly this number\n" " N..M Range between two numbers\n" " ? Unknown initial value\n" " > or + Increased values\n" " < or - Decreased values\n" " = Unchanged values\n" " != Changed values\n" " > N Greater than N\n" " < N Less than N\n" " + N Increased by N\n" " - N Decreased by N\n" " != N Other than N\n" "\n" " where N could be a number of a simple math expression embraced by (), e.g.\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " For bytearray: use 2-char hexadecimal notation for each byte, separated by " "spaces\n" " e.g. FE DC BA 98 76 54 32 10\n" " ?? can be used as a wildcard value\n" "\n" " For string: enter the string directly" msgstr "" "Syntax:\n" "\n" " Für Numerische Typen:\n" "\n" " N Genau diese Zahl\n" " N..M Bereich zwischen zwei Zahlen(inklusiv)\n" " ? Beliebiger Wert\n" " > or + Größerer Wert\n" " < or - Kleinerer Wert\n" " = Unveränderter Wert\n" " != Veränderter Wert\n" " > N Wert größer N\n" " < N Wert kleiner N\n" " + N Wert um N größer\n" " - N Wert um N kleiner\n" " != N Wert ungleich N\n" "\n" " N kann ein einfacher mathematischer Ausdruck in Klammern () sein" ", Beispiel:\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " Für Bytearrays: Hexadezimale Notation in Gruppen von 2 Zeichen getrennt " "durch Leerzeichen\n" " Bsp.: FE DC BA 98 76 54 32 10\n" " ?? kann als Platzhalter für " "beliebige Werte benutzt werden\n" "\n" "\n" " Für String: einfach den String eingeben" #: ../gui/GameConqueror.ui.h:37 msgid "_Value: ?" msgstr "_Wert: ?" #: ../gui/GameConqueror.ui.h:38 msgid "Enter search value here (CTRL+K/L)" msgstr "Suchwert hier eingeben (STRG+K/L)" #: ../gui/GameConqueror.ui.h:39 msgid "Insert value to search for (CTRL+K/L)" msgstr "Wert zum Suchen eingeben (STRG+K/L)" #: ../gui/GameConqueror.ui.h:40 msgid "Scan (CTRL+ENTER)" msgstr "Suchen (STRG+EINGABE)" #: ../gui/GameConqueror.ui.h:41 msgid "Reset (CTRL+R)" msgstr "Zurücksetzen (STRG+R)" #: ../gui/GameConqueror.ui.h:42 msgid "Stop scan" msgstr "Suche abbrechen" #: ../gui/GameConqueror.ui.h:43 msgid "About" msgstr "Info" #: ../gui/GameConqueror.ui.h:44 msgid "Select a process (CTRL+F)" msgstr "Prozess auswählen (STRG+F)" #: ../gui/GameConqueror.ui.h:45 ../gui/GameConqueror.py:928 msgid "Select a process" msgstr "Prozess auswählen" #: ../gui/GameConqueror.ui.h:46 ../gui/GameConqueror.py:927 msgid "No process selected" msgstr "Kein Prozess ausgewählt" #: ../gui/GameConqueror.ui.h:47 msgid "Specify type of target data" msgstr "Typ der Zieldaten angeben" #: ../gui/GameConqueror.ui.h:48 msgid "Data _Type:" msgstr "Daten_typ:" #: ../gui/GameConqueror.ui.h:49 msgid "" "Basic: Fastest but may miss some values\n" "Normal: Works for most cases\n" "Full: Never miss values but slowest" msgstr "" "Einfach: Schnellste Suche, findet nicht alle Werte\n" "Normal: Angemessen in den meisten Fällen\n" "Vollständig: Langsamste Suche, sollte alle Werte finden" #: ../gui/GameConqueror.ui.h:52 msgid "_Search Scope:" msgstr "_Suchumfang:" #: ../gui/GameConqueror.ui.h:53 msgid "Memory Editor (CTRL+SHIFT+M)" msgstr "Speichereditor (STRG+UMSCHALT+M)" #: ../gui/GameConqueror.ui.h:54 msgid "Save address list to file (CTRL+S)" msgstr "Adressliste in Datei speichern (STRG+S)" #: ../gui/GameConqueror.ui.h:55 msgid "Load address list from file (CTRL+O)" msgstr "Adressliste aus Datei lesen (STRG+O)" #: ../gui/GameConqueror.ui.h:56 msgid "Remove all entries (CTRL+D)" msgstr "Alle Einträge entfernen (STRG+D)" #: ../gui/GameConqueror.ui.h:57 msgid "Manually add an entry (CTRL+M)" msgstr "Eintrag manuell hinzufügen (STRG+M)" #: ../gui/GameConqueror.ui.h:58 msgid "" "Copyright (C) 2009-2014 WANG Lu \n" "Copyright (C) 2010 Bryan Cain\n" "Copyright (C) 2015-2016 Sebastian Parschauer \n" "Copyright (C) 2016 Andrea Stacchiotti \n" "\n" "Special thanks: Igor Gnatenko, Mattias Muenster, Zhang Zhe" msgstr "" "Copyright (C) 2009-2014 WANG Lu \n" "Copyright (C) 2010 Bryan Cain\n" "Copyright (C) 2015-2016 Sebastian Parschauer \n" "Copyright (C) 2016 Andrea Stacchiotti \n" "\n" "Besonderer Danksagung an: Igor Gnatenko, Mattias Muenster, Zhang Zhe" #: ../gui/GameConqueror.ui.h:64 msgid "A graphical frontend of scanmem" msgstr "Grafisches Frontend für scanmem" #: ../gui/GameConqueror.ui.h:65 msgid "Homepage" msgstr "Homepage" #: ../gui/GameConqueror.ui.h:66 msgid "Manually add an entry" msgstr "Eintrag manuell hinzufügen" #: ../gui/GameConqueror.ui.h:67 msgid "_Close" msgstr "S_chließen" #: ../gui/GameConqueror.ui.h:68 msgid "_Add" msgstr "Hinzufügen" #: ../gui/GameConqueror.ui.h:69 msgid "Memory address of the value" msgstr "Speicheradresse des Werts" #: ../gui/GameConqueror.ui.h:70 msgid "Addres_s:" msgstr "Adre_sse:" #: ../gui/GameConqueror.ui.h:71 msgid "Give a brief description of the value" msgstr "Kurze Beschreibung des Wertes" #: ../gui/GameConqueror.ui.h:72 msgid "_Description:" msgstr "Beschreibung:" #: ../gui/GameConqueror.ui.h:73 msgid "_Type:" msgstr "_Typ:" #: ../gui/GameConqueror.ui.h:74 msgid "Specify data length (string/bytearray only)" msgstr "Datenlänge angeben (nur für string/bytearray)" #: ../gui/GameConqueror.ui.h:75 msgid "_Length:" msgstr "_Länge:" #: ../gui/GameConqueror.ui.h:76 msgid "Type to search for a process" msgstr "Tippen um Prozesse zu filtern" #: ../gui/GameConqueror.ui.h:77 msgid "_OK" msgstr "_OK" #: ../gui/GameConqueror.ui.h:78 msgid "_Cancel" msgstr "_Abbrechen" #: ../gui/GameConqueror.ui.h:79 msgid "_User:" msgstr "N_utzer:" #: ../gui/GameConqueror.ui.h:80 msgid "_Process:" msgstr "_Prozess:" #: ../gui/GameConqueror.ui.h:81 msgid "Filter" msgstr "Filter" #: ../gui/GameConqueror.py:72 msgid "Basic" msgstr "Einfach" #: ../gui/GameConqueror.py:72 msgid "Normal" msgstr "Normal" #: ../gui/GameConqueror.py:72 msgid "Full" msgstr "Vollständig" #. init columns #. Address #: ../gui/GameConqueror.py:180 ../gui/GameConqueror.py:234 msgid "Address" msgstr "Adresse" #. Value #: ../gui/GameConqueror.py:184 ../gui/GameConqueror.py:251 msgid "Value" msgstr "Wert" #: ../gui/GameConqueror.py:188 msgid "Offset" msgstr "Offset" #: ../gui/GameConqueror.py:192 msgid "Region Type" msgstr "Regionstyp" #. Lock #: ../gui/GameConqueror.py:217 msgid "Lock" msgstr "Festhalten" #. Description #: ../gui/GameConqueror.py:226 msgid "Description" msgstr "Beschreibung" #. Type #: ../gui/GameConqueror.py:239 msgid "Type" msgstr "Typ" #. second col #: ../gui/GameConqueror.py:276 msgid "User" msgstr "Nutzer" #. third col #: ../gui/GameConqueror.py:281 msgid "Process" msgstr "Prozess" #: ../gui/GameConqueror.py:312 msgid "Add to cheat list" msgstr "Zur Cheatliste hinzufügen" #: ../gui/GameConqueror.py:313 ../gui/GameConqueror.py:320 msgid "Browse this address" msgstr "Adresse im Speichereditor anzeigen" #: ../gui/GameConqueror.py:314 msgid "Scan for this address" msgstr "Nach dieser Adresse suchen" #: ../gui/GameConqueror.py:315 msgid "Remove this match" msgstr "Ergebniss verwerfen" #: ../gui/GameConqueror.py:321 msgid "Copy address" msgstr "Adresse kopieren" #: ../gui/GameConqueror.py:322 msgid "Remove this entry" msgstr "Eintrag verwerfen" #: ../gui/GameConqueror.py:349 ../gui/GameConqueror.py:537 #: ../gui/GameConqueror.py:1001 msgid "Please select a process" msgstr "Bitte Prozess auswählen" #: ../gui/GameConqueror.py:362 msgid "Invalid address" msgstr "Adresse ungültig" #: ../gui/GameConqueror.py:372 msgid "Please enter a valid address." msgstr "Bitte gültige Adresse eingeben" #: ../gui/GameConqueror.py:376 ../gui/GameConqueror.py:897 msgid "No Description" msgstr "Keine Beschreibung" #: ../gui/GameConqueror.py:405 msgid "Open.." msgstr "Öffnen.." #: ../gui/GameConqueror.py:425 msgid "Save.." msgstr "Speichern.." #: ../gui/GameConqueror.py:530 msgid "" msgstr "" #: ../gui/GameConqueror.py:578 ../gui/GameConqueror.py:878 msgid "Cannot read memory" msgstr "Kann Speicher nicht lesen" #: ../gui/GameConqueror.py:834 msgid "Unknown architecture, you may report to developers" msgstr "Unbekannte Architektur" #: ../gui/GameConqueror.py:846 ../gui/GameConqueror.py:929 msgid "" "Cannot retrieve memory maps of that process, maybe it has exited (crashed), " "or you don't have enough privileges" msgstr "" "Speicherabbild kann nicht gelesen werden: Prozess ist verschwunden oder " "Sie haben unzureichend Zugriffsrechte" #. not readable #: ../gui/GameConqueror.py:857 #, python-format msgid "Address %x is not readable" msgstr "Adresse %x ist unlesbar" #: ../gui/GameConqueror.py:860 #, python-format msgid "Address %x is not valid" msgstr "Adresse %x ist ungültig" #: ../gui/GameConqueror.py:869 msgid "Cannot find a readable region" msgstr "Keine Lesbare Speicherregion gefunden" #: ../gui/GameConqueror.py:1061 #, python-format msgid "Found: %d" msgstr "Gefunden: %d" #: ../gui/GameConqueror.py:1177 msgid "" "Version of scanmem mismatched, you may encounter problems. Please make sure " "you are using the same version of GameConqueror as scanmem." msgstr "" "Version von scanmem passt nicht zu GameConqueror." #: ../gui/GameConqueror.py:1183 msgid "A GUI for scanmem, a game hacking tool" msgstr "Eine grafische Oberfläche für scanmem, ein Werkzeug zur Speichermanipulation" #: ../gui/GameConqueror.py:1184 msgid "Report bugs to " msgstr "Bug Reports an " #: ../gui/GameConqueror.py:1186 msgid "prefill the search box" msgstr "Suchbox vorfüllen" #: ../gui/GameConqueror.py:1188 msgid "PID of the process" msgstr "PID des Prozesses" #: ../gui/misc.py:33 msgid "No value provided" msgstr "Kein Wert eingegeben" #: ../gui/misc.py:43 ../gui/misc.py:49 ../gui/misc.py:104 #, python-format msgid "Bad value: %s" msgstr "Ungültiger Wert: %s" #: ../gui/misc.py:65 #, python-format msgid "Command \"%s\" is not valid for the first scan" msgstr "Befehl \"%s\" kann nicht für die erste Suche verwendet werden" #: ../gui/misc.py:112 #, python-format msgid "%s is not an integer" msgstr "%s ist keine Ganzzahl" #: ../gui/misc.py:118 #, python-format msgid "%s is too bulky for %s" msgstr "%s ist zu groß für %s" #: ../gui/misc.py:138 #, python-format msgid "Cannot locate item: %s" msgstr "Kann %s nicht finden" #: ../gui/GameConqueror.desktop.in.h:1 ../gui/GameConqueror.appdata.xml.in.h:1 msgid "Game Conqueror" msgstr "Game Conqueror" #: ../gui/GameConqueror.desktop.in.h:2 msgid "A game hacking tool. A GUI front-end for scanmem." msgstr "Eine grafische Oberfläche für scanmem, ein Werkzeug zur Speichermanipulation" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:1 msgid "Run Game Conqueror" msgstr "GameConqueror ausführen" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:2 msgid "Authentication is required to run Game Conqueror" msgstr "Authorisierung ist notwendig für Game Conqueror" #: ../gui/GameConqueror.appdata.xml.in.h:2 msgid "Game hacking tool. GUI front-end for scanmem." msgstr "Eine grafische Oberfläche für scanmem, ein Werkzeug zur Speichermanipulation" #: ../gui/GameConqueror.appdata.xml.in.h:3 msgid "" "scanmem is a simple interactive debugging utility, used to locate the " "address of a variable in an executing process. This can be used for the " "analysis or modification of a hostile process on a compromised machine, " "reverse engineering, or as a \"pokefinder\" to cheat at video games. " "GameConqueror aims to provide a CheatEngine-alike interface for scanmem, " "it's user-friendly and easy to use." msgstr "" "scanmem ist ein einfaches interaktives Dienstprogramm, das verwendet wird, um " "die Adresse einer Variablen in einem Prozess zu finden. Dies kann für die " "Analyse oder Veränderung eines feindlichen Prozesses auf einer kompromittierten Maschine, " "Reverse Engineering, oder als \"Pokefinder\", um bei Videospielen zu betrügen, verwendet werden. " "Das Ziel von GameConqueror ist eine benutzerfreundliche und einfach zu bedienende " "CheatEngine-ähnliche Schnittstelle für scanmem bereitzustellen. " #: ../gui/GameConqueror.appdata.xml.in.h:4 msgid "Features:" msgstr "Funktionen:" #: ../gui/GameConqueror.appdata.xml.in.h:5 msgid "Locking on multiple variables" msgstr "Mehrere Variablen einfrieren" #: ../gui/GameConqueror.appdata.xml.in.h:6 msgid "Memory Viewer/Editor" msgstr "Speichereditor" #: ../gui/GameConqueror.appdata.xml.in.h:7 msgid "... many to be done" msgstr "... viele weitere" scanmem-0.17/po/es.po000066400000000000000000000350151317023271400144550ustar00rootroot00000000000000# Spanish translations for Game Conqueror. # Copyright (C) 2009-2017 Wang Lu # This file is distributed under the same license as Game Conqueror. # Salvador Pardiñas , 2017. # msgid "" msgstr "" "Project-Id-Version: scanmem 0.17-pre\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-17 22:08+0200\n" "PO-Revision-Date: 2017-09-02 12:31+0300\n" "Last-Translator: Salvador Pardiñas \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../gui/GameConqueror.ui.h:1 msgid "GameConqueror - Memory Editor" msgstr "GameConqueror - Editor de memoria" #: ../gui/GameConqueror.ui.h:2 msgid "_Address:" msgstr "_Dirección: " #: ../gui/GameConqueror.ui.h:3 msgid "Target address here (CTRL+L)" msgstr "Dirección de destino aquí (CTRL+L)" #: ../gui/GameConqueror.ui.h:4 msgid "Enter address to view" msgstr "Ingrese dirección para ver" #: ../gui/GameConqueror.ui.h:5 msgid "Jump to address (CTRL+ENTER)" msgstr "Saltar a dirección (CTRL+ENTER)" #: ../gui/GameConqueror.ui.h:6 msgid "_Refresh (CTRL+R)" msgstr "_Recargar (CTRL+R)" #: ../gui/GameConqueror.ui.h:7 msgid "GameConqueror" msgstr "GameConqueror" #: ../gui/GameConqueror.ui.h:8 msgid "Found: 0" msgstr "Encontrados: 0" #: ../gui/GameConqueror.ui.h:9 msgid "" "Syntax:\n" "\n" " For numeric types:\n" "\n" " N Exactly this number\n" " N..M Range between two numbers\n" " ? Unknown initial value\n" " > or + Increased values\n" " < or - Decreased values\n" " = Unchanged values\n" " != Changed values\n" " > N Greater than N\n" " < N Less than N\n" " + N Increased by N\n" " - N Decreased by N\n" " != N Other than N\n" "\n" " where N could be a number of a simple math expression embraced by (), e.g.\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " For bytearray: use 2-char hexadecimal notation for each byte, separated by " "spaces\n" " e.g. FE DC BA 98 76 54 32 10\n" " ?? can be used as a wildcard value\n" "\n" " For string: enter the string directly" msgstr "" "Sintáxis: \n" "\n" "Para tipos numéricos:\n" "\n" " N Exactamente este número\n" " N..M Rango entre 2 números\n" " ? Valor inicial desconocido\n" " > or + Valores incrementados\n" " < or - Valores decrecidos\n" " = Valores sin cambiar\n" " != Valores cambiados\n" " > N Mayor que N\n" " < N Menor que N\n" " + N Incrementado por N\n" " - N Decrementado por N\n" " != N Que no sea N\n" "\n" " donde N podría ser un número de una simple expresión matemática encerrada por (), ej:\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " Para arrays de byte: use notación hexadecimal de 2 caracteres por cada byte, separado por " "espacios\n" " e.g. FE DC BA 98 76 54 32 10\n" " ?? puede ser usado como un comodín\n" "\n" " Para strings: inserte el string directamente" #: ../gui/GameConqueror.ui.h:37 msgid "_Value: ?" msgstr "_Valor: ?" #: ../gui/GameConqueror.ui.h:38 msgid "Enter search value here (CTRL+K/L)" msgstr "Inserte valor a buscar aquí (CTRL+K/L)" #: ../gui/GameConqueror.ui.h:39 msgid "Insert value to search for (CTRL+K/L)" msgstr "Inserte valor a buscar aquí (CTRL+K/L)" #: ../gui/GameConqueror.ui.h:40 msgid "Scan (CTRL+ENTER)" msgstr "Escanear (CTRL+ENTER)" #: ../gui/GameConqueror.ui.h:41 msgid "Reset (CTRL+R)" msgstr "Reset (CTRL+R)" #: ../gui/GameConqueror.ui.h:42 msgid "Stop scan" msgstr "Detener escaneo" #: ../gui/GameConqueror.ui.h:43 msgid "About" msgstr "Acerca de" #: ../gui/GameConqueror.ui.h:44 msgid "Select a process (CTRL+F)" msgstr "Seleccionar un proceso (CTRL+F)" #: ../gui/GameConqueror.ui.h:45 ../gui/GameConqueror.py:929 msgid "Select a process" msgstr "Seleccionar un proceso" #: ../gui/GameConqueror.ui.h:46 ../gui/GameConqueror.py:928 msgid "No process selected" msgstr "Ningún proceso seleccionado" #: ../gui/GameConqueror.ui.h:47 msgid "Specify type of target data" msgstr "Especifique tipo de dato a buscar" #: ../gui/GameConqueror.ui.h:48 msgid "Data _Type:" msgstr "_Tipo de dato:" #: ../gui/GameConqueror.ui.h:49 msgid "" "Basic: Fastest but may miss some values\n" "Normal: Works for most cases\n" "Full: Never miss values but slowest" msgstr "" "Básico: El más rápido pero puede no encontrar algunos valores\n" "Normal: Sirve para la mayoría de los casos\n" "Completo: Nunca pierde datos pero es el más lento" #: ../gui/GameConqueror.ui.h:52 msgid "_Search Scope:" msgstr "_Area de búsqueda:" #: ../gui/GameConqueror.ui.h:53 msgid "Memory Editor (CTRL+SHIFT+M)" msgstr "Editor de Memoria (CTRL+SHIFT+M)" #: ../gui/GameConqueror.ui.h:54 msgid "Save address list to file (CTRL+S)" msgstr "Guardar lista de direcciones a archivo (CTRL+S)" #: ../gui/GameConqueror.ui.h:55 msgid "Load address list from file (CTRL+O)" msgstr "Cargar lista de direcciones de archivo (CTRL+O)" #: ../gui/GameConqueror.ui.h:56 msgid "Remove all entries (CTRL+D)" msgstr "Eliminar todas las direcciones (CTRL+D)" #: ../gui/GameConqueror.ui.h:57 msgid "Manually add an entry (CTRL+M)" msgstr "Añadir una dirección manualmente (CTRL+M)" #: ../gui/GameConqueror.ui.h:58 msgid "" "Copyright (C) 2009-2014 WANG Lu \n" "Copyright (C) 2010 Bryan Cain\n" "Copyright (C) 2015-2016 Sebastian Parschauer \n" "Copyright (C) 2016 Andrea Stacchiotti \n" "\n" "Special thanks: Igor Gnatenko, Mattias Muenster, Zhang Zhe" msgstr "" "Copyright (C) 2009-2014 WANG Lu \n" "Copyright (C) 2010 Bryan Cain\n" "Copyright (C) 2015-2016 Sebastian Parschauer \n" "Copyright (C) 2016 Andrea Stacchiotti \n" "\n" "Agradecimientos especiales: Igor Gnatenko, Mattias Muenster, Zhang Zhe" #: ../gui/GameConqueror.ui.h:64 msgid "A graphical frontend of scanmem" msgstr "Una interfaz gráfica para scanmem" #: ../gui/GameConqueror.ui.h:65 msgid "Homepage" msgstr "Hogar" #: ../gui/GameConqueror.ui.h:66 msgid "Manually add an entry" msgstr "Añadir una dirección manualmente" #: ../gui/GameConqueror.ui.h:67 msgid "_Add" msgstr "_Añadir" #: ../gui/GameConqueror.ui.h:68 msgid "_Close" msgstr "_Cerrar" #: ../gui/GameConqueror.ui.h:69 msgid "Memory address of the value" msgstr "Dirección de memoria del valor:" #: ../gui/GameConqueror.ui.h:70 msgid "Addres_s:" msgstr "_Dirección:" #: ../gui/GameConqueror.ui.h:71 msgid "Give a brief description of the value" msgstr "Breve descripción del valor" #: ../gui/GameConqueror.ui.h:72 msgid "_Description:" msgstr "_Descripción:" #: ../gui/GameConqueror.ui.h:73 msgid "_Type:" msgstr "_Tipo:" #: ../gui/GameConqueror.ui.h:74 msgid "Specify data length (string/bytearray only)" msgstr "Especifique tamaño del dato (sólamente string/array de datos)" #: ../gui/GameConqueror.ui.h:75 msgid "_Length:" msgstr "_Tamaño:" #: ../gui/GameConqueror.ui.h:76 msgid "Type to search for a process" msgstr "Tipo de dato a buscar para un proceso" #: ../gui/GameConqueror.ui.h:77 msgid "_OK" msgstr "_OK" #: ../gui/GameConqueror.ui.h:78 msgid "_Cancel" msgstr "_Cancelar" #: ../gui/GameConqueror.ui.h:79 msgid "_User:" msgstr "_Usuario:" #: ../gui/GameConqueror.ui.h:80 msgid "_Process:" msgstr "_Proceso:" #: ../gui/GameConqueror.ui.h:81 msgid "Filter" msgstr "Filtro" #: ../gui/GameConqueror.py:72 msgid "Basic" msgstr "Básico" #: ../gui/GameConqueror.py:72 msgid "Normal" msgstr "Normal" #: ../gui/GameConqueror.py:72 msgid "Full" msgstr "Completo" #. init columns #. Address #: ../gui/GameConqueror.py:180 ../gui/GameConqueror.py:234 msgid "Address" msgstr "Dirección" #. Value #: ../gui/GameConqueror.py:184 ../gui/GameConqueror.py:251 msgid "Value" msgstr "Valor" #: ../gui/GameConqueror.py:188 msgid "Offset" msgstr "Offset" #: ../gui/GameConqueror.py:192 msgid "Region Type" msgstr "Tipo de región" #. Lock #: ../gui/GameConqueror.py:217 msgid "Lock" msgstr "Bloqueo" #. Description #: ../gui/GameConqueror.py:226 msgid "Description" msgstr "Descripción" #. Type #: ../gui/GameConqueror.py:239 msgid "Type" msgstr "Tipo" #. second col #: ../gui/GameConqueror.py:276 msgid "User" msgstr "Usuario" #. third col #: ../gui/GameConqueror.py:281 msgid "Process" msgstr "Proceso" #: ../gui/GameConqueror.py:312 msgid "Add to cheat list" msgstr "Añadir a lista de trucos" #: ../gui/GameConqueror.py:313 ../gui/GameConqueror.py:320 msgid "Browse this address" msgstr "Ver esta dirección" #: ../gui/GameConqueror.py:314 msgid "Scan for this address" msgstr "Escanear esta dirección" #: ../gui/GameConqueror.py:315 msgid "Remove this match" msgstr "Eliminar esta dirección" #: ../gui/GameConqueror.py:321 msgid "Copy address" msgstr "Copiar dirección" #: ../gui/GameConqueror.py:322 msgid "Remove this entry" msgstr "Eliminar esta dirección" #: ../gui/GameConqueror.py:350 ../gui/GameConqueror.py:538 #: ../gui/GameConqueror.py:1002 msgid "Please select a process" msgstr "Por favor seleccione un proceso" #: ../gui/GameConqueror.py:363 msgid "Invalid address" msgstr "Dirección inválida" #: ../gui/GameConqueror.py:373 msgid "Please enter a valid address." msgstr "Por favor ingrese una dirección válida." #: ../gui/GameConqueror.py:377 ../gui/GameConqueror.py:898 msgid "No Description" msgstr "Sin descripción" #: ../gui/GameConqueror.py:406 msgid "Open.." msgstr "Abrir.." #: ../gui/GameConqueror.py:426 msgid "Save.." msgstr "Guardar.." #: ../gui/GameConqueror.py:531 msgid "" msgstr "" #: ../gui/GameConqueror.py:579 ../gui/GameConqueror.py:879 msgid "Cannot read memory" msgstr "No se puede leer memoria" #: ../gui/GameConqueror.py:835 msgid "Unknown architecture, you may report to developers" msgstr "Arquitectura desconocida, puede reportarlo a los desarrolladores" #: ../gui/GameConqueror.py:847 ../gui/GameConqueror.py:930 msgid "" "Cannot retrieve memory maps of that process, maybe it has exited (crashed), " "or you don't have enough privileges" msgstr "" "No se puede recuperar mapa de memoria de ese proceso, tal vez ha salido (crasheado), " "o no tienes suficientes privilegios" #. not readable #: ../gui/GameConqueror.py:858 #, python-format msgid "Address %x is not readable" msgstr "Dirección %x no es legible" #: ../gui/GameConqueror.py:861 #, python-format msgid "Address %x is not valid" msgstr "Dirección %x no es válida" #: ../gui/GameConqueror.py:870 msgid "Cannot find a readable region" msgstr "No se puede encontrar una región legible" #: ../gui/GameConqueror.py:1062 #, python-format msgid "Found: %d" msgstr "Encontrado: %d" #: ../gui/GameConqueror.py:1178 msgid "" "Version of scanmem mismatched, you may encounter problems. Please make sure " "you are using the same version of GameConqueror as scanmem." msgstr "" "Versión de scanmem incorrecta, puede encontrar problemas. Por favor asegúrese " "de estar usando la misma versión de GameConqueror que scanmem." #: ../gui/GameConqueror.py:1184 msgid "A GUI for scanmem, a game hacking tool" msgstr "Una interfaz para scanmem, una herramienta para hackear juegos" #: ../gui/GameConqueror.py:1185 msgid "Report bugs to " msgstr "Reportar bugs a " #: ../gui/GameConqueror.py:1187 msgid "prefill the search box" msgstr "pre-llenar la caja de búsqueda" #: ../gui/GameConqueror.py:1189 msgid "PID of the process" msgstr "PID del proceso" #: ../gui/misc.py:33 msgid "No value provided" msgstr "Ningún valor proveído" #: ../gui/misc.py:43 ../gui/misc.py:49 ../gui/misc.py:104 #, python-format msgid "Bad value: %s" msgstr "Mal valor: %s" #: ../gui/misc.py:65 #, python-format msgid "Command \"%s\" is not valid for the first scan" msgstr "Comanod \"%s\" no es válido para el primer escaneo" #: ../gui/misc.py:112 #, python-format msgid "%s is not an integer" msgstr "%s no es un entero" #: ../gui/misc.py:118 #, python-format msgid "%s is too bulky for %s" msgstr "%s es muy grande para %s" #: ../gui/misc.py:138 #, python-format msgid "Cannot locate item: %s" msgstr "No puede encontrar item: %s" #: ../gui/GameConqueror.desktop.in.h:1 ../gui/GameConqueror.appdata.xml.in.h:1 msgid "Game Conqueror" msgstr "Game Conqueror" #: ../gui/GameConqueror.desktop.in.h:2 msgid "A game hacking tool. A GUI front-end for scanmem." msgstr "Una herramienta de hackeo de juegos. Una interfaz gráfica para scanmem." #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:1 msgid "Run Game Conqueror" msgstr "Ejecutar Game Conqueror" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:2 msgid "Authentication is required to run Game Conqueror" msgstr "Se requiere autenticación para correr Game Conqueror" #: ../gui/GameConqueror.appdata.xml.in.h:2 msgid "Game hacking tool. GUI front-end for scanmem." msgstr "Herramienta de hackeo de juegos. Interfaz gráfica para scanmem." #: ../gui/GameConqueror.appdata.xml.in.h:3 msgid "" "scanmem is a simple interactive debugging utility, used to locate the " "address of a variable in an executing process. This can be used for the " "analysis or modification of a hostile process on a compromised machine, " "reverse engineering, or as a \"pokefinder\" to cheat at video games. " "GameConqueror aims to provide a CheatEngine-alike interface for scanmem, " "it's user-friendly and easy to use." msgstr "" "scanmem es una utilidad interactiva de debuggeo, utilizada para localizar la " "dirección de una variable en un proceso. Esto se puede utilizar para el " "análisis o modificación de un proceso hostil en una máquina infiltrada, " "ingeniería inversa, o como un \"pokefinder\" para hacer trampa en videojuegos. " "GameConqueror busca proveer una interfaz estilo CheatEngine para scanmem, " "es amigable y fácil de usar." #: ../gui/GameConqueror.appdata.xml.in.h:4 msgid "Features:" msgstr "Características:" #: ../gui/GameConqueror.appdata.xml.in.h:5 msgid "Locking on multiple variables" msgstr "Bloqueo en mútliples variables" #: ../gui/GameConqueror.appdata.xml.in.h:6 msgid "Memory Viewer/Editor" msgstr "Visor/Editor de memoria" #: ../gui/GameConqueror.appdata.xml.in.h:7 msgid "... many to be done" msgstr "... muchas a completar" scanmem-0.17/po/it.po000066400000000000000000000350141317023271400144610ustar00rootroot00000000000000# Italian translations for GameConqueror. # Copyright (C) 2009-2014 Wang Lu # This file is distributed under the same license as GameConqueror. # Andrea Stacchiotti , 2016. # msgid "" msgstr "" "Project-Id-Version: scanmem 0.16~pre\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-17 22:08+0200\n" "PO-Revision-Date: 2017-07-17 22:22+0200\n" "Last-Translator: Andrea Stacchiotti \n" "Language-Team: Italiano <>\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.0.2\n" #: ../gui/GameConqueror.ui.h:1 msgid "GameConqueror - Memory Editor" msgstr "GameConqueror - Editor di memoria" #: ../gui/GameConqueror.ui.h:2 msgid "_Address:" msgstr "_Indirizzo:" #: ../gui/GameConqueror.ui.h:3 msgid "Target address here (CTRL+L)" msgstr "Indirizzo (CTRL+L)" #: ../gui/GameConqueror.ui.h:4 msgid "Enter address to view" msgstr "Digita l'indirizzo da mostrare" #: ../gui/GameConqueror.ui.h:5 msgid "Jump to address (CTRL+ENTER)" msgstr "Vai all'indirizzo (CTRL+INVIO)" #: ../gui/GameConqueror.ui.h:6 msgid "_Refresh (CTRL+R)" msgstr "Reset (CTRL+R)" #: ../gui/GameConqueror.ui.h:7 msgid "GameConqueror" msgstr "GameConqueror" #: ../gui/GameConqueror.ui.h:8 msgid "Found: 0" msgstr "Trovati: 0" #: ../gui/GameConqueror.ui.h:9 msgid "" "Syntax:\n" "\n" " For numeric types:\n" "\n" " N Exactly this number\n" " N..M Range between two numbers\n" " ? Unknown initial value\n" " > or + Increased values\n" " < or - Decreased values\n" " = Unchanged values\n" " != Changed values\n" " > N Greater than N\n" " < N Less than N\n" " + N Increased by N\n" " - N Decreased by N\n" " != N Other than N\n" "\n" " where N could be a number of a simple math expression embraced by (), e.g.\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " For bytearray: use 2-char hexadecimal notation for each byte, separated by " "spaces\n" " e.g. FE DC BA 98 76 54 32 10\n" " ?? can be used as a wildcard value\n" "\n" " For string: enter the string directly" msgstr "" "Sintassi:\n" "\n" " Per i tipi numerici:\n" "\n" " N Esattamente questo numero\n" " N..M Intervallo tra due numeri\n" " ? Valore iniziale sconosciuto\n" " > or + Valori aumentati\n" " < or - Valori diminuiti\n" " = Valori non cambiati\n" " != Valori cambiati\n" " > N Più grande di N\n" " < N Più piccolo di N\n" " + N Aumentato di N\n" " - N Diminuito di N\n" " != N Diverso da N\n" "\n" " dove N può essere un numero o una semplice espressione matematica racchiusa " "da (), e.g.\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " Per i bytearray: usa notazione esadecimale a 2 caratteri per ogni byte, " "separati da spazi\n" " e.g. FE DC BA 98 76 54 32 10\n" " ?? può essere usato per indicare " "qualsiasi valore\n" "\n" "\n" " Per le stringhe: inserisci la stringa direttamente" #: ../gui/GameConqueror.ui.h:37 msgid "_Value: ?" msgstr "_Valore: ?" #: ../gui/GameConqueror.ui.h:38 msgid "Enter search value here (CTRL+K/L)" msgstr "Digita il valore da cercare (CTRL+K/L)" #: ../gui/GameConqueror.ui.h:39 msgid "Insert value to search for (CTRL+K/L)" msgstr "Digita il valore da cercare (CTRL+K/L)" #: ../gui/GameConqueror.ui.h:40 msgid "Scan (CTRL+ENTER)" msgstr "Cerca (CTRL+INVIO)" #: ../gui/GameConqueror.ui.h:41 msgid "Reset (CTRL+R)" msgstr "Reset (CTRL+R)" #: ../gui/GameConqueror.ui.h:42 msgid "Stop scan" msgstr "Interrompi ricerca" #: ../gui/GameConqueror.ui.h:43 msgid "About" msgstr "Informazioni" #: ../gui/GameConqueror.ui.h:44 msgid "Select a process (CTRL+F)" msgstr "Seleziona un processo (CTRL+F)" #: ../gui/GameConqueror.ui.h:45 ../gui/GameConqueror.py:928 msgid "Select a process" msgstr "Seleziona un processo" #: ../gui/GameConqueror.ui.h:46 ../gui/GameConqueror.py:927 msgid "No process selected" msgstr "Nessun processo selezionato" #: ../gui/GameConqueror.ui.h:47 msgid "Specify type of target data" msgstr "Specifica il tipo di dato da cercare" #: ../gui/GameConqueror.ui.h:48 msgid "Data _Type:" msgstr "_Tipo di dato:" #: ../gui/GameConqueror.ui.h:49 msgid "" "Basic: Fastest but may miss some values\n" "Normal: Works for most cases\n" "Full: Never miss values but slowest" msgstr "" "Base: La più veloce, ma potrebbe non trovare alcuni valori\n" "Normale: Funziona per la maggior parte dei casi\n" "Completo: Trova sempre i valori, ma è il più lento" #: ../gui/GameConqueror.ui.h:52 msgid "_Search Scope:" msgstr "_Area di ricerca:" #: ../gui/GameConqueror.ui.h:53 msgid "Memory Editor (CTRL+SHIFT+M)" msgstr "Editor di memoria (CTRL+SHIFT+M)" #: ../gui/GameConqueror.ui.h:54 msgid "Save address list to file (CTRL+S)" msgstr "Salva la lista di indirizzi in un file (CTRL+S)" #: ../gui/GameConqueror.ui.h:55 msgid "Load address list from file (CTRL+O)" msgstr "Carica la lista di indirizzi da un file (CTRL+O)" #: ../gui/GameConqueror.ui.h:56 msgid "Remove all entries (CTRL+D)" msgstr "Rimuovi tutti gli indirizzi (CTRL+D)" #: ../gui/GameConqueror.ui.h:57 msgid "Manually add an entry (CTRL+M)" msgstr "Aggiungi un indirizzo manualmente (CTRL+M)" #: ../gui/GameConqueror.ui.h:58 msgid "" "Copyright (C) 2009-2014 WANG Lu \n" "Copyright (C) 2010 Bryan Cain\n" "Copyright (C) 2015-2016 Sebastian Parschauer \n" "Copyright (C) 2016 Andrea Stacchiotti \n" "\n" "Special thanks: Igor Gnatenko, Mattias Muenster, Zhang Zhe" msgstr "" "Copyright (C) 2009-2014 WANG Lu \n" "Copyright (C) 2010 Bryan Cain\n" "Copyright (C) 2015-2016 Sebastian Parschauer \n" "Copyright (C) 2016 Andrea Stacchiotti \n" "\n" "Si ringraziano: Igor Gnatenko, Mattias Muenster, Zhang Zhe" #: ../gui/GameConqueror.ui.h:64 msgid "A graphical frontend of scanmem" msgstr "Un'interfaccia grafica per scanmem" #: ../gui/GameConqueror.ui.h:65 msgid "Homepage" msgstr "Homepage" #: ../gui/GameConqueror.ui.h:66 msgid "Manually add an entry" msgstr "Aggiungi un indirizzo manualmente" #: ../gui/GameConqueror.ui.h:67 msgid "_Close" msgstr "_Chiudi" #: ../gui/GameConqueror.ui.h:68 msgid "_Add" msgstr "_Aggiungi" #: ../gui/GameConqueror.ui.h:69 msgid "Memory address of the value" msgstr "Indirizzo del valore" #: ../gui/GameConqueror.ui.h:70 msgid "Addres_s:" msgstr "_Indirizzo:" #: ../gui/GameConqueror.ui.h:71 msgid "Give a brief description of the value" msgstr "Breve descrizione del valore" #: ../gui/GameConqueror.ui.h:72 msgid "_Description:" msgstr "_Descrizione:" #: ../gui/GameConqueror.ui.h:73 msgid "_Type:" msgstr "_Tipo:" #: ../gui/GameConqueror.ui.h:74 msgid "Specify data length (string/bytearray only)" msgstr "Specifica la lunghezza (solo per string/bytearray)" #: ../gui/GameConqueror.ui.h:75 msgid "_Length:" msgstr "_Lunghezza:" #: ../gui/GameConqueror.ui.h:76 msgid "Type to search for a process" msgstr "Digita per cercare un processo" #: ../gui/GameConqueror.ui.h:77 msgid "_OK" msgstr "_OK" #: ../gui/GameConqueror.ui.h:78 msgid "_Cancel" msgstr "_Annulla" #: ../gui/GameConqueror.ui.h:79 msgid "_User:" msgstr "_Utente:" #: ../gui/GameConqueror.ui.h:80 msgid "_Process:" msgstr "_Processo:" #: ../gui/GameConqueror.ui.h:81 msgid "Filter" msgstr "Filtro" #: ../gui/GameConqueror.py:72 msgid "Basic" msgstr "Base" #: ../gui/GameConqueror.py:72 msgid "Normal" msgstr "Normale" #: ../gui/GameConqueror.py:72 msgid "Full" msgstr "Completo" #. init columns #. Address #: ../gui/GameConqueror.py:180 ../gui/GameConqueror.py:234 msgid "Address" msgstr "Indirizzo" #. Value #: ../gui/GameConqueror.py:184 ../gui/GameConqueror.py:251 msgid "Value" msgstr "Valore" #: ../gui/GameConqueror.py:188 msgid "Offset" msgstr "Offset" #: ../gui/GameConqueror.py:192 msgid "Region Type" msgstr "Tipo di regione" #. Lock #: ../gui/GameConqueror.py:217 msgid "Lock" msgstr "Blocco" #. Description #: ../gui/GameConqueror.py:226 msgid "Description" msgstr "Descrizione" #. Type #: ../gui/GameConqueror.py:239 msgid "Type" msgstr "Tipo" #. second col #: ../gui/GameConqueror.py:276 msgid "User" msgstr "Utente" #. third col #: ../gui/GameConqueror.py:281 msgid "Process" msgstr "Processo" #: ../gui/GameConqueror.py:312 msgid "Add to cheat list" msgstr "Aggiungi alla lista dei trucchi" #: ../gui/GameConqueror.py:313 ../gui/GameConqueror.py:320 msgid "Browse this address" msgstr "Visualizza questo indirizzo" #: ../gui/GameConqueror.py:314 msgid "Scan for this address" msgstr "Cerca questo indirizzo" #: ../gui/GameConqueror.py:315 msgid "Remove this match" msgstr "Rimuovi questo indirizzo" #: ../gui/GameConqueror.py:321 msgid "Copy address" msgstr "Copia indirizzo" #: ../gui/GameConqueror.py:322 msgid "Remove this entry" msgstr "Rimuovi questo indirizzo" #: ../gui/GameConqueror.py:349 ../gui/GameConqueror.py:537 #: ../gui/GameConqueror.py:1001 msgid "Please select a process" msgstr "Seleziona un processo" #: ../gui/GameConqueror.py:362 msgid "Invalid address" msgstr "Indirizzo non valido" #: ../gui/GameConqueror.py:372 msgid "Please enter a valid address." msgstr "Inserisci un indirizzo valido." #: ../gui/GameConqueror.py:376 ../gui/GameConqueror.py:897 msgid "No Description" msgstr "Nessuna descrizione" #: ../gui/GameConqueror.py:405 msgid "Open.." msgstr "Apri" #: ../gui/GameConqueror.py:425 msgid "Save.." msgstr "Salva" #: ../gui/GameConqueror.py:530 msgid "" msgstr "" #: ../gui/GameConqueror.py:578 ../gui/GameConqueror.py:878 msgid "Cannot read memory" msgstr "Impossibile leggere la memoria" #: ../gui/GameConqueror.py:834 msgid "Unknown architecture, you may report to developers" msgstr "Architettura sconosciuta, potresti segnalarlo agli sviluppatori" #: ../gui/GameConqueror.py:846 ../gui/GameConqueror.py:929 msgid "" "Cannot retrieve memory maps of that process, maybe it has exited (crashed), " "or you don't have enough privileges" msgstr "" "Impossibile recuperare la mappa di memoria del processo, forse è terminato " "(crashato) o non hai i permessi" #. not readable #: ../gui/GameConqueror.py:857 #, python-format msgid "Address %x is not readable" msgstr "L'indirizzo %x non è leggibile" #: ../gui/GameConqueror.py:860 #, python-format msgid "Address %x is not valid" msgstr "L'indirizzo %x non è valido" #: ../gui/GameConqueror.py:869 msgid "Cannot find a readable region" msgstr "Impossibile trovare una regione leggibile" #: ../gui/GameConqueror.py:1061 #, python-format msgid "Found: %d" msgstr "Trovati: %d" #: ../gui/GameConqueror.py:1177 msgid "" "Version of scanmem mismatched, you may encounter problems. Please make sure " "you are using the same version of GameConqueror as scanmem." msgstr "" "Versione di scanmem non corrispondente, potresti incontrare problemi. " "Assicurati di usare la stessa vesione di GameConqueror e scanmem." #: ../gui/GameConqueror.py:1183 msgid "A GUI for scanmem, a game hacking tool" msgstr "Una GUI per scanmem, uno strumento per barare ai videogiochi" #: ../gui/GameConqueror.py:1184 msgid "Report bugs to " msgstr "Segnala bug a " #: ../gui/GameConqueror.py:1186 msgid "prefill the search box" msgstr "riempi la barra di ricerca" #: ../gui/GameConqueror.py:1188 msgid "PID of the process" msgstr "PID del processo" #: ../gui/misc.py:33 msgid "No value provided" msgstr "Nessun valore fornito" #: ../gui/misc.py:43 ../gui/misc.py:49 ../gui/misc.py:104 #, python-format msgid "Bad value: %s" msgstr "Valore non valido: %s" #: ../gui/misc.py:65 #, python-format msgid "Command \"%s\" is not valid for the first scan" msgstr "Il comando \"%s\" non è valido per la prima ricerca" #: ../gui/misc.py:112 #, python-format msgid "%s is not an integer" msgstr "%s non è un intero" #: ../gui/misc.py:118 #, python-format msgid "%s is too bulky for %s" msgstr "%s è troppo grande per %s" #: ../gui/misc.py:138 #, python-format msgid "Cannot locate item: %s" msgstr "Impossibile trovare: %s" #: ../gui/GameConqueror.desktop.in.h:1 ../gui/GameConqueror.appdata.xml.in.h:1 msgid "Game Conqueror" msgstr "Game Conqueror" #: ../gui/GameConqueror.desktop.in.h:2 msgid "A game hacking tool. A GUI front-end for scanmem." msgstr "Uno strumento per barare ai videogiochi. Una GUI per scanmem." #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:1 msgid "Run Game Conqueror" msgstr "Avvia Game Conqueror" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:2 msgid "Authentication is required to run Game Conqueror" msgstr "È necessario autenticarsi per avviare Game Conqueror" #: ../gui/GameConqueror.appdata.xml.in.h:2 msgid "Game hacking tool. GUI front-end for scanmem." msgstr "Uno strumento per barare ai videogiochi. GUI per scanmem." #: ../gui/GameConqueror.appdata.xml.in.h:3 msgid "" "scanmem is a simple interactive debugging utility, used to locate the " "address of a variable in an executing process. This can be used for the " "analysis or modification of a hostile process on a compromised machine, " "reverse engineering, or as a \"pokefinder\" to cheat at video games. " "GameConqueror aims to provide a CheatEngine-alike interface for scanmem, " "it's user-friendly and easy to use." msgstr "" "scanmem è un semplice programma di debug, utile per trovare l'indirizzo di " "una variabile in un processo in esecuzione. Questo può essere usato per " "l'analisi o la modifica di un processo sospetto in una macchina compromessa, " "ingegneria inversa, o come un \"pokefinder\" per barare ai videogiochi. " "GameConqueror mira a fornire un'interfaccia per scanmem, simile a " "CheatEngine, è user-friendly e semplice da usare." #: ../gui/GameConqueror.appdata.xml.in.h:4 msgid "Features:" msgstr "Caratteristiche:" #: ../gui/GameConqueror.appdata.xml.in.h:5 msgid "Locking on multiple variables" msgstr "Blocco di multiple variabili" #: ../gui/GameConqueror.appdata.xml.in.h:6 msgid "Memory Viewer/Editor" msgstr "Editor di memoria" #: ../gui/GameConqueror.appdata.xml.in.h:7 msgid "... many to be done" msgstr "... e molto altro" scanmem-0.17/po/ja.po000066400000000000000000000344231317023271400144420ustar00rootroot00000000000000# Japanese translations for Game Conqueror. # Copyright (C) 2009-2013 Wang Lu # This file is distributed under the same license as Game Conqueror. # Kuina , 2013. # msgid "" msgstr "" "Project-Id-Version: scanmem 0.14\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-17 22:08+0200\n" "PO-Revision-Date: 2013-10-30 02:13+0900\n" "Last-Translator: Kuina \n" "Language-Team: Japanese\n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../gui/GameConqueror.ui.h:1 msgid "GameConqueror - Memory Editor" msgstr "GameConqueror - メモリエディタ" #: ../gui/GameConqueror.ui.h:2 #, fuzzy msgid "_Address:" msgstr "アドレス:" #: ../gui/GameConqueror.ui.h:3 msgid "Target address here (CTRL+L)" msgstr "" #: ../gui/GameConqueror.ui.h:4 #, fuzzy msgid "Enter address to view" msgstr "アドレス一覧をファイルへ保存" #: ../gui/GameConqueror.ui.h:5 msgid "Jump to address (CTRL+ENTER)" msgstr "" #: ../gui/GameConqueror.ui.h:6 msgid "_Refresh (CTRL+R)" msgstr "" #: ../gui/GameConqueror.ui.h:7 msgid "GameConqueror" msgstr "" #: ../gui/GameConqueror.ui.h:8 msgid "Found: 0" msgstr "0 件見つかりました" #: ../gui/GameConqueror.ui.h:9 msgid "" "Syntax:\n" "\n" " For numeric types:\n" "\n" " N Exactly this number\n" " N..M Range between two numbers\n" " ? Unknown initial value\n" " > or + Increased values\n" " < or - Decreased values\n" " = Unchanged values\n" " != Changed values\n" " > N Greater than N\n" " < N Less than N\n" " + N Increased by N\n" " - N Decreased by N\n" " != N Other than N\n" "\n" " where N could be a number of a simple math expression embraced by (), e.g.\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " For bytearray: use 2-char hexadecimal notation for each byte, separated by " "spaces\n" " e.g. FE DC BA 98 76 54 32 10\n" " ?? can be used as a wildcard value\n" "\n" " For string: enter the string directly" msgstr "" "Syntax:\n" "\n" " 数値を検索する場合: \n" "\n" " N N と等しい\n" " N..M 二つの数値の間の範囲\n" " ? 全ての値\n" " > or + 前回スキャン時より増加した\n" " < or - 前回スキャン時より減少した\n" " = 前回スキャン時より変化していない\n" " != 前回スキャン時より変化した\n" " > N N より大きい\n" " < N N より小さい\n" " + N 前回スキャン時より N 増加した\n" " - N 前回スキャン時より N 減少した\n" " != N N と異なる\n" "\n" " 数値の代わりに、()で囲った簡単な数式を入力することもできます。例:\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " バイト列を検索する場合: 16進表記を用い、1バイトずつ半角スペースで区切ってく" "ださい\n" " 例: FE DC BA 98 76 54 32 10\n" " ?? can be used as a wildcard value\n" "\n" " 文字列を検索する場合: 検索したい文字列をそのまま入力してください" #: ../gui/GameConqueror.ui.h:37 #, fuzzy msgid "_Value: ?" msgstr "値: ?" #: ../gui/GameConqueror.ui.h:38 msgid "Enter search value here (CTRL+K/L)" msgstr "" #: ../gui/GameConqueror.ui.h:39 msgid "Insert value to search for (CTRL+K/L)" msgstr "" #: ../gui/GameConqueror.ui.h:40 msgid "Scan (CTRL+ENTER)" msgstr "" #: ../gui/GameConqueror.ui.h:41 msgid "Reset (CTRL+R)" msgstr "" #: ../gui/GameConqueror.ui.h:42 msgid "Stop scan" msgstr "" #: ../gui/GameConqueror.ui.h:43 msgid "About" msgstr "" #: ../gui/GameConqueror.ui.h:44 #, fuzzy msgid "Select a process (CTRL+F)" msgstr "プロセスを選択してください" #: ../gui/GameConqueror.ui.h:45 ../gui/GameConqueror.py:928 msgid "Select a process" msgstr "プロセスを選択してください" #: ../gui/GameConqueror.ui.h:46 ../gui/GameConqueror.py:927 msgid "No process selected" msgstr "プロセスが選択されていません" #: ../gui/GameConqueror.ui.h:47 msgid "Specify type of target data" msgstr "データ型を指定します" #: ../gui/GameConqueror.ui.h:48 #, fuzzy msgid "Data _Type:" msgstr "データ型:" #: ../gui/GameConqueror.ui.h:49 msgid "" "Basic: Fastest but may miss some values\n" "Normal: Works for most cases\n" "Full: Never miss values but slowest" msgstr "" "Basic: 高速ですが、値が存在するにもかかわらず、アドレスを取得でき" "ない場合があります\n" "Normal: 通常はこれで充分です\n" "Full: 値が存在すれば必ずそのアドレスを取得できますが、遅いです" #: ../gui/GameConqueror.ui.h:52 #, fuzzy msgid "_Search Scope:" msgstr "検索範囲:" #: ../gui/GameConqueror.ui.h:53 #, fuzzy msgid "Memory Editor (CTRL+SHIFT+M)" msgstr "メモリエディタ" #: ../gui/GameConqueror.ui.h:54 #, fuzzy msgid "Save address list to file (CTRL+S)" msgstr "アドレス一覧をファイルへ保存" #: ../gui/GameConqueror.ui.h:55 #, fuzzy msgid "Load address list from file (CTRL+O)" msgstr "アドレス一覧をファイルから読み込み" #: ../gui/GameConqueror.ui.h:56 #, fuzzy msgid "Remove all entries (CTRL+D)" msgstr "全ての項目を削除" #: ../gui/GameConqueror.ui.h:57 #, fuzzy msgid "Manually add an entry (CTRL+M)" msgstr "手動入力" #: ../gui/GameConqueror.ui.h:58 msgid "" "Copyright (C) 2009-2014 WANG Lu \n" "Copyright (C) 2010 Bryan Cain\n" "Copyright (C) 2015-2016 Sebastian Parschauer \n" "Copyright (C) 2016 Andrea Stacchiotti \n" "\n" "Special thanks: Igor Gnatenko, Mattias Muenster, Zhang Zhe" msgstr "" #: ../gui/GameConqueror.ui.h:64 msgid "A graphical frontend of scanmem" msgstr "scanmem GUI フロントエンド" #: ../gui/GameConqueror.ui.h:65 msgid "Homepage" msgstr "ホームページ" #: ../gui/GameConqueror.ui.h:66 msgid "Manually add an entry" msgstr "手動入力" #: ../gui/GameConqueror.ui.h:67 msgid "_Close" msgstr "閉じる(_C)" #: ../gui/GameConqueror.ui.h:68 msgid "_Add" msgstr "追加(_A)" #: ../gui/GameConqueror.ui.h:69 msgid "Memory address of the value" msgstr "メモリアドレスを入力します" #: ../gui/GameConqueror.ui.h:70 #, fuzzy msgid "Addres_s:" msgstr "アドレス:" #: ../gui/GameConqueror.ui.h:71 msgid "Give a brief description of the value" msgstr "変数の簡単な説明を入力します" #: ../gui/GameConqueror.ui.h:72 #, fuzzy msgid "_Description:" msgstr "概要:" #: ../gui/GameConqueror.ui.h:73 #, fuzzy msgid "_Type:" msgstr "データ型:" #: ../gui/GameConqueror.ui.h:74 msgid "Specify data length (string/bytearray only)" msgstr "" #: ../gui/GameConqueror.ui.h:75 msgid "_Length:" msgstr "" #: ../gui/GameConqueror.ui.h:76 #, fuzzy msgid "Type to search for a process" msgstr "Ctrl+Fキーでプロセスを検索できます" #: ../gui/GameConqueror.ui.h:77 msgid "_OK" msgstr "OK(_O)" #: ../gui/GameConqueror.ui.h:78 msgid "_Cancel" msgstr "キャンセル(_C)" #: ../gui/GameConqueror.ui.h:79 #, fuzzy msgid "_User:" msgstr "ユーザー:" #: ../gui/GameConqueror.ui.h:80 #, fuzzy msgid "_Process:" msgstr "プロセス:" #: ../gui/GameConqueror.ui.h:81 msgid "Filter" msgstr "フィルタ" #: ../gui/GameConqueror.py:72 msgid "Basic" msgstr "" #: ../gui/GameConqueror.py:72 msgid "Normal" msgstr "" #: ../gui/GameConqueror.py:72 msgid "Full" msgstr "" #. init columns #. Address #: ../gui/GameConqueror.py:180 ../gui/GameConqueror.py:234 msgid "Address" msgstr "アドレス" #. Value #: ../gui/GameConqueror.py:184 ../gui/GameConqueror.py:251 msgid "Value" msgstr "値" #: ../gui/GameConqueror.py:188 msgid "Offset" msgstr "オフセット" #: ../gui/GameConqueror.py:192 msgid "Region Type" msgstr "地域のタイプ" #. Lock #: ../gui/GameConqueror.py:217 msgid "Lock" msgstr "ロック" #. Description #: ../gui/GameConqueror.py:226 msgid "Description" msgstr "概要" #. Type #: ../gui/GameConqueror.py:239 msgid "Type" msgstr "型" #. second col #: ../gui/GameConqueror.py:276 msgid "User" msgstr "ユーザー" #. third col #: ../gui/GameConqueror.py:281 msgid "Process" msgstr "プロセス" #: ../gui/GameConqueror.py:312 msgid "Add to cheat list" msgstr "チートリストに追加" #: ../gui/GameConqueror.py:313 ../gui/GameConqueror.py:320 msgid "Browse this address" msgstr "このアドレスのメモリ内容を表示" #: ../gui/GameConqueror.py:314 msgid "Scan for this address" msgstr "このアドレスを指すポインタをスキャン" #: ../gui/GameConqueror.py:315 #, fuzzy msgid "Remove this match" msgstr "この項目を削除" #: ../gui/GameConqueror.py:321 msgid "Copy address" msgstr "アドレスをコピー" #: ../gui/GameConqueror.py:322 msgid "Remove this entry" msgstr "この項目を削除" #: ../gui/GameConqueror.py:349 ../gui/GameConqueror.py:537 #: ../gui/GameConqueror.py:1001 msgid "Please select a process" msgstr "プロセスを選択してください" #: ../gui/GameConqueror.py:362 msgid "Invalid address" msgstr "無効なアドレスです" #: ../gui/GameConqueror.py:372 msgid "Please enter a valid address." msgstr "有効なメモリアドレスを入力してください." #: ../gui/GameConqueror.py:376 ../gui/GameConqueror.py:897 msgid "No Description" msgstr "説明なし" #: ../gui/GameConqueror.py:405 msgid "Open.." msgstr "開く.." #: ../gui/GameConqueror.py:425 msgid "Save.." msgstr "保存.." #: ../gui/GameConqueror.py:530 msgid "" msgstr "" #: ../gui/GameConqueror.py:578 ../gui/GameConqueror.py:878 msgid "Cannot read memory" msgstr "メモリを読み込めません" #: ../gui/GameConqueror.py:834 msgid "Unknown architecture, you may report to developers" msgstr "未知のアーキテクチャです。必要であれば開発者に報告してください。" #: ../gui/GameConqueror.py:846 ../gui/GameConqueror.py:929 msgid "" "Cannot retrieve memory maps of that process, maybe it has exited (crashed), " "or you don't have enough privileges" msgstr "" "指定されたプロセスのメモリマップを読み込めません。既に終了あるいはクラッシュ" "したか、この操作に必要な権限がない可能性があります。" #. not readable #: ../gui/GameConqueror.py:857 #, python-format msgid "Address %x is not readable" msgstr "アドレス %x を読み込めません" #: ../gui/GameConqueror.py:860 #, python-format msgid "Address %x is not valid" msgstr "アドレス %x は無効です" #: ../gui/GameConqueror.py:869 msgid "Cannot find a readable region" msgstr "読み込み可能なメモリ範囲が見つかりません" #: ../gui/GameConqueror.py:1061 #, python-format msgid "Found: %d" msgstr "%d 件見つかりました" #: ../gui/GameConqueror.py:1177 msgid "" "Version of scanmem mismatched, you may encounter problems. Please make sure " "you are using the same version of GameConqueror as scanmem." msgstr "" "scanmem のバージョンが違います。何らかの問題が発生している可能性があります。" "scanmem 本体と同じバージョンの Game Conqueror を使用しているか確認してくださ" "い。" #: ../gui/GameConqueror.py:1183 msgid "A GUI for scanmem, a game hacking tool" msgstr "" #: ../gui/GameConqueror.py:1184 msgid "Report bugs to " msgstr "" #: ../gui/GameConqueror.py:1186 msgid "prefill the search box" msgstr "" #: ../gui/GameConqueror.py:1188 msgid "PID of the process" msgstr "" #: ../gui/misc.py:33 msgid "No value provided" msgstr "値が入力されていません" #: ../gui/misc.py:43 ../gui/misc.py:49 ../gui/misc.py:104 #, python-format msgid "Bad value: %s" msgstr "無効な値: %s" #: ../gui/misc.py:65 #, python-format msgid "Command \"%s\" is not valid for the first scan" msgstr "演算子 \"%s\" は初回スキャン時には利用できません" #: ../gui/misc.py:112 #, python-format msgid "%s is not an integer" msgstr "%s は整数ではありません" #: ../gui/misc.py:118 #, python-format msgid "%s is too bulky for %s" msgstr "%s は %s で表現できる範囲を超えています" #: ../gui/misc.py:138 #, python-format msgid "Cannot locate item: %s" msgstr "%s という項目は見つかりません" #: ../gui/GameConqueror.desktop.in.h:1 ../gui/GameConqueror.appdata.xml.in.h:1 msgid "Game Conqueror" msgstr "" #: ../gui/GameConqueror.desktop.in.h:2 msgid "A game hacking tool. A GUI front-end for scanmem." msgstr "ゲーム向けプロセスメモリエディタ(scanmem GUI フロントエンド)" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:1 msgid "Run Game Conqueror" msgstr "" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:2 msgid "Authentication is required to run Game Conqueror" msgstr "" #: ../gui/GameConqueror.appdata.xml.in.h:2 msgid "Game hacking tool. GUI front-end for scanmem." msgstr "ゲーム向けプロセスメモリエディタ(scanmem GUI フロントエンド)" #: ../gui/GameConqueror.appdata.xml.in.h:3 msgid "" "scanmem is a simple interactive debugging utility, used to locate the " "address of a variable in an executing process. This can be used for the " "analysis or modification of a hostile process on a compromised machine, " "reverse engineering, or as a \"pokefinder\" to cheat at video games. " "GameConqueror aims to provide a CheatEngine-alike interface for scanmem, " "it's user-friendly and easy to use." msgstr "" #: ../gui/GameConqueror.appdata.xml.in.h:4 msgid "Features:" msgstr "" #: ../gui/GameConqueror.appdata.xml.in.h:5 msgid "Locking on multiple variables" msgstr "" #: ../gui/GameConqueror.appdata.xml.in.h:6 msgid "Memory Viewer/Editor" msgstr "メモリエディタ" #: ../gui/GameConqueror.appdata.xml.in.h:7 msgid "... many to be done" msgstr "" #~ msgid "Scan" #~ msgstr "スキャン" #~ msgid "Reset" #~ msgstr "リセット" scanmem-0.17/po/ru.po000066400000000000000000000364531317023271400145030ustar00rootroot00000000000000# Russian translations for Game Conqueror. # Copyright (C) 2009-2014 Wang Lu # This file is distributed under the same license as Game Conqueror. # Igor Gnatenko , 2014. # msgid "" msgstr "" "Project-Id-Version: scanmem 0.14\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-17 22:08+0200\n" "PO-Revision-Date: 2014-01-26 13:38+0400\n" "Last-Translator: Igor Gnatenko \n" "Language-Team: Russian Fedora\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: ../gui/GameConqueror.ui.h:1 msgid "GameConqueror - Memory Editor" msgstr "GameConqueror - Редактор Памяти" #: ../gui/GameConqueror.ui.h:2 #, fuzzy msgid "_Address:" msgstr "Адрес:" #: ../gui/GameConqueror.ui.h:3 msgid "Target address here (CTRL+L)" msgstr "" #: ../gui/GameConqueror.ui.h:4 #, fuzzy msgid "Enter address to view" msgstr "Сохранить список адресов в файл" #: ../gui/GameConqueror.ui.h:5 msgid "Jump to address (CTRL+ENTER)" msgstr "" #: ../gui/GameConqueror.ui.h:6 msgid "_Refresh (CTRL+R)" msgstr "" #: ../gui/GameConqueror.ui.h:7 msgid "GameConqueror" msgstr "" #: ../gui/GameConqueror.ui.h:8 msgid "Found: 0" msgstr "Найдено: 0" #: ../gui/GameConqueror.ui.h:9 msgid "" "Syntax:\n" "\n" " For numeric types:\n" "\n" " N Exactly this number\n" " N..M Range between two numbers\n" " ? Unknown initial value\n" " > or + Increased values\n" " < or - Decreased values\n" " = Unchanged values\n" " != Changed values\n" " > N Greater than N\n" " < N Less than N\n" " + N Increased by N\n" " - N Decreased by N\n" " != N Other than N\n" "\n" " where N could be a number of a simple math expression embraced by (), e.g.\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " For bytearray: use 2-char hexadecimal notation for each byte, separated by " "spaces\n" " e.g. FE DC BA 98 76 54 32 10\n" " ?? can be used as a wildcard value\n" "\n" " For string: enter the string directly" msgstr "" "Синтаксис:\n" "\n" " Для численных типов: \n" "\n" " N Равные N\n" " N..M Диапазон между двумя числами\n" " ? Неизвестное начальное значение\n" " > или + Увеличившиеся значения\n" " < или - Уменьшившиеся значения\n" " = Не изменившиеся значения\n" " != Изменившиеся значения\n" " > N Больше чем N\n" " < N Меньше чем N\n" " + N Увеличились на N\n" " - N Уменьшились by N\n" " != N Отличные от N\n" "\n" " где N может быть число или простое математическое выражение, выделенное в " "(), напр.\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " Для массива байтов: используйте 2-разрядный шестнадцатеричный формат для " "каждого байта, разделённые пробелами\n" " напр. FE DC BA 98 76 54 32 10\n" " ?? can be used as a wildcard value\n" "\n" " Для строк: вводите искомую строку" #: ../gui/GameConqueror.ui.h:37 #, fuzzy msgid "_Value: ?" msgstr "Значение: ?" #: ../gui/GameConqueror.ui.h:38 msgid "Enter search value here (CTRL+K/L)" msgstr "" #: ../gui/GameConqueror.ui.h:39 msgid "Insert value to search for (CTRL+K/L)" msgstr "" #: ../gui/GameConqueror.ui.h:40 msgid "Scan (CTRL+ENTER)" msgstr "" #: ../gui/GameConqueror.ui.h:41 msgid "Reset (CTRL+R)" msgstr "" #: ../gui/GameConqueror.ui.h:42 msgid "Stop scan" msgstr "" #: ../gui/GameConqueror.ui.h:43 msgid "About" msgstr "" #: ../gui/GameConqueror.ui.h:44 #, fuzzy msgid "Select a process (CTRL+F)" msgstr "Выбрать процесс" #: ../gui/GameConqueror.ui.h:45 ../gui/GameConqueror.py:928 msgid "Select a process" msgstr "Выбрать процесс" #: ../gui/GameConqueror.ui.h:46 ../gui/GameConqueror.py:927 msgid "No process selected" msgstr "Процесс не выбран" #: ../gui/GameConqueror.ui.h:47 msgid "Specify type of target data" msgstr "Укажите тип целевых данных" #: ../gui/GameConqueror.ui.h:48 #, fuzzy msgid "Data _Type:" msgstr "Тип данных:" #: ../gui/GameConqueror.ui.h:49 msgid "" "Basic: Fastest but may miss some values\n" "Normal: Works for most cases\n" "Full: Never miss values but slowest" msgstr "" "Быстрый: Быстрый, но может не содержать некоторых значений\n" "Обычный: Работает в большинстве случаев\n" "Полный: Медленный, но содержит все значения" #: ../gui/GameConqueror.ui.h:52 #, fuzzy msgid "_Search Scope:" msgstr "Область Поиска:" #: ../gui/GameConqueror.ui.h:53 #, fuzzy msgid "Memory Editor (CTRL+SHIFT+M)" msgstr "Редактор Памяти" #: ../gui/GameConqueror.ui.h:54 #, fuzzy msgid "Save address list to file (CTRL+S)" msgstr "Сохранить список адресов в файл" #: ../gui/GameConqueror.ui.h:55 #, fuzzy msgid "Load address list from file (CTRL+O)" msgstr "Загрузить список адресов из файла" #: ../gui/GameConqueror.ui.h:56 #, fuzzy msgid "Remove all entries (CTRL+D)" msgstr "Удалить все элементы" #: ../gui/GameConqueror.ui.h:57 #, fuzzy msgid "Manually add an entry (CTRL+M)" msgstr "Добавить элемент вручную" #: ../gui/GameConqueror.ui.h:58 msgid "" "Copyright (C) 2009-2014 WANG Lu \n" "Copyright (C) 2010 Bryan Cain\n" "Copyright (C) 2015-2016 Sebastian Parschauer \n" "Copyright (C) 2016 Andrea Stacchiotti \n" "\n" "Special thanks: Igor Gnatenko, Mattias Muenster, Zhang Zhe" msgstr "" #: ../gui/GameConqueror.ui.h:64 msgid "A graphical frontend of scanmem" msgstr "Графический интерфейс к scanmem" #: ../gui/GameConqueror.ui.h:65 msgid "Homepage" msgstr "Домашняя страница" #: ../gui/GameConqueror.ui.h:66 msgid "Manually add an entry" msgstr "Добавить элемент вручную" #: ../gui/GameConqueror.ui.h:67 msgid "_Close" msgstr "_Закрыть" #: ../gui/GameConqueror.ui.h:68 msgid "_Add" msgstr "_Добавить" #: ../gui/GameConqueror.ui.h:69 msgid "Memory address of the value" msgstr "Адрес значения в памяти" #: ../gui/GameConqueror.ui.h:70 #, fuzzy msgid "Addres_s:" msgstr "Адрес:" #: ../gui/GameConqueror.ui.h:71 msgid "Give a brief description of the value" msgstr "Дайте краткое описание значения" #: ../gui/GameConqueror.ui.h:72 #, fuzzy msgid "_Description:" msgstr "Описание:" #: ../gui/GameConqueror.ui.h:73 #, fuzzy msgid "_Type:" msgstr "Тип:" #: ../gui/GameConqueror.ui.h:74 msgid "Specify data length (string/bytearray only)" msgstr "" #: ../gui/GameConqueror.ui.h:75 msgid "_Length:" msgstr "" #: ../gui/GameConqueror.ui.h:76 #, fuzzy msgid "Type to search for a process" msgstr "Нажмите Ctrl+F, чтобы найти процесс" #: ../gui/GameConqueror.ui.h:77 msgid "_OK" msgstr "_OK" #: ../gui/GameConqueror.ui.h:78 msgid "_Cancel" msgstr "О_тменить" #: ../gui/GameConqueror.ui.h:79 #, fuzzy msgid "_User:" msgstr "Пользователь:" #: ../gui/GameConqueror.ui.h:80 #, fuzzy msgid "_Process:" msgstr "Процесс:" #: ../gui/GameConqueror.ui.h:81 msgid "Filter" msgstr "Фильтр" #: ../gui/GameConqueror.py:72 msgid "Basic" msgstr "" #: ../gui/GameConqueror.py:72 msgid "Normal" msgstr "" #: ../gui/GameConqueror.py:72 msgid "Full" msgstr "" #. init columns #. Address #: ../gui/GameConqueror.py:180 ../gui/GameConqueror.py:234 msgid "Address" msgstr "Адрес" #. Value #: ../gui/GameConqueror.py:184 ../gui/GameConqueror.py:251 msgid "Value" msgstr "Значение" #: ../gui/GameConqueror.py:188 msgid "Offset" msgstr "офсет" #: ../gui/GameConqueror.py:192 msgid "Region Type" msgstr "Тип Регион" #. Lock #: ../gui/GameConqueror.py:217 msgid "Lock" msgstr "Заблокировать" #. Description #: ../gui/GameConqueror.py:226 msgid "Description" msgstr "Описание" #. Type #: ../gui/GameConqueror.py:239 msgid "Type" msgstr "Тип" #. second col #: ../gui/GameConqueror.py:276 msgid "User" msgstr "Пользователь" #. third col #: ../gui/GameConqueror.py:281 msgid "Process" msgstr "Процесс" #: ../gui/GameConqueror.py:312 msgid "Add to cheat list" msgstr "Добавить в чит-лист" #: ../gui/GameConqueror.py:313 ../gui/GameConqueror.py:320 msgid "Browse this address" msgstr "Просмотреть этот адрес" #: ../gui/GameConqueror.py:314 msgid "Scan for this address" msgstr "Искать по этому адресу" #: ../gui/GameConqueror.py:315 #, fuzzy msgid "Remove this match" msgstr "Удалить этот элемент" #: ../gui/GameConqueror.py:321 msgid "Copy address" msgstr "Копировать адрес" #: ../gui/GameConqueror.py:322 msgid "Remove this entry" msgstr "Удалить этот элемент" #: ../gui/GameConqueror.py:349 ../gui/GameConqueror.py:537 #: ../gui/GameConqueror.py:1001 msgid "Please select a process" msgstr "Пожалуйста выберите процесс" #: ../gui/GameConqueror.py:362 msgid "Invalid address" msgstr "Некорректный адрес" #: ../gui/GameConqueror.py:372 msgid "Please enter a valid address." msgstr "Пожалуйста введите корректный адрес." #: ../gui/GameConqueror.py:376 ../gui/GameConqueror.py:897 msgid "No Description" msgstr "Нет Описания" #: ../gui/GameConqueror.py:405 msgid "Open.." msgstr "Открыть.." #: ../gui/GameConqueror.py:425 msgid "Save.." msgstr "Сохранить.." #: ../gui/GameConqueror.py:530 msgid "" msgstr "" #: ../gui/GameConqueror.py:578 ../gui/GameConqueror.py:878 msgid "Cannot read memory" msgstr "Невозможно прочитать память" #: ../gui/GameConqueror.py:834 msgid "Unknown architecture, you may report to developers" msgstr "Неизвестная архитектура, сообщите разработчикам" #: ../gui/GameConqueror.py:846 ../gui/GameConqueror.py:929 msgid "" "Cannot retrieve memory maps of that process, maybe it has exited (crashed), " "or you don't have enough privileges" msgstr "" "Невозможно получить маппинги памяти для этого процесса, возможно он больше " "не существует или у вас не хватает прав" #. not readable #: ../gui/GameConqueror.py:857 #, python-format msgid "Address %x is not readable" msgstr "Адрес %x не доступен для чтения" #: ../gui/GameConqueror.py:860 #, python-format msgid "Address %x is not valid" msgstr "Адрес %x не корректен" #: ../gui/GameConqueror.py:869 msgid "Cannot find a readable region" msgstr "Невозможно найти область для чтения" #: ../gui/GameConqueror.py:1061 #, python-format msgid "Found: %d" msgstr "Найдено: %d" #: ../gui/GameConqueror.py:1177 msgid "" "Version of scanmem mismatched, you may encounter problems. Please make sure " "you are using the same version of GameConqueror as scanmem." msgstr "" "Используемая версия scanmem несовместима, могут быть проблемы. Убедитесь, " "что вы используете одинаковые версии GameConqueror и scanmem." #: ../gui/GameConqueror.py:1183 msgid "A GUI for scanmem, a game hacking tool" msgstr "" #: ../gui/GameConqueror.py:1184 msgid "Report bugs to " msgstr "" #: ../gui/GameConqueror.py:1186 msgid "prefill the search box" msgstr "" #: ../gui/GameConqueror.py:1188 msgid "PID of the process" msgstr "" #: ../gui/misc.py:33 msgid "No value provided" msgstr "Значение не предоставлено" #: ../gui/misc.py:43 ../gui/misc.py:49 ../gui/misc.py:104 #, python-format msgid "Bad value: %s" msgstr "Плохое значение: %s" #: ../gui/misc.py:65 #, python-format msgid "Command \"%s\" is not valid for the first scan" msgstr "Комманда \"%s\" некорректна для первого поиска" #: ../gui/misc.py:112 #, python-format msgid "%s is not an integer" msgstr "%s не целочисленное" #: ../gui/misc.py:118 #, python-format msgid "%s is too bulky for %s" msgstr "%s слишком большой для %s" #: ../gui/misc.py:138 #, python-format msgid "Cannot locate item: %s" msgstr "Невозможно найти запись: %s" #: ../gui/GameConqueror.desktop.in.h:1 ../gui/GameConqueror.appdata.xml.in.h:1 msgid "Game Conqueror" msgstr "" #: ../gui/GameConqueror.desktop.in.h:2 msgid "A game hacking tool. A GUI front-end for scanmem." msgstr "Инструмент для взлома игр (графический интерфейс к scanmem)" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:1 msgid "Run Game Conqueror" msgstr "Запуск Game Conqueror" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:2 msgid "Authentication is required to run Game Conqueror" msgstr "" "Для запуска Game Conqueror требуется подтверждение подлинности пользователя" #: ../gui/GameConqueror.appdata.xml.in.h:2 msgid "Game hacking tool. GUI front-end for scanmem." msgstr "Инструмент для взлома игр (графический интерфейс к scanmem)" #: ../gui/GameConqueror.appdata.xml.in.h:3 msgid "" "scanmem is a simple interactive debugging utility, used to locate the " "address of a variable in an executing process. This can be used for the " "analysis or modification of a hostile process on a compromised machine, " "reverse engineering, or as a \"pokefinder\" to cheat at video games. " "GameConqueror aims to provide a CheatEngine-alike interface for scanmem, " "it's user-friendly and easy to use." msgstr "" #: ../gui/GameConqueror.appdata.xml.in.h:4 msgid "Features:" msgstr "Возможности:" #: ../gui/GameConqueror.appdata.xml.in.h:5 msgid "Locking on multiple variables" msgstr "Замораживание мнножества переменных" #: ../gui/GameConqueror.appdata.xml.in.h:6 msgid "Memory Viewer/Editor" msgstr "Редактор/Просмотрщик Памяти" #: ../gui/GameConqueror.appdata.xml.in.h:7 msgid "... many to be done" msgstr "" #~ msgid "Scan" #~ msgstr "Сканировать" #~ msgid "Reset" #~ msgstr "Сброс" scanmem-0.17/po/sr_ME.po000066400000000000000000000346351317023271400150620ustar00rootroot00000000000000# Serbian/Montenegrin translations for GameConqueror. # Copyright (C) 2017 Vidovic Slobodan # This file is distributed under the same license as GameConqueror. # Slobodan Vidovic , 2017. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-25 10:04+0200\n" "PO-Revision-Date: 2017-07-26 12:45+0200\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.8.7.1\n" "Last-Translator: \n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "Language: sr_ME@latin\n" #: ../gui/GameConqueror.ui.h:1 msgid "GameConqueror - Memory Editor" msgstr "GameConqueror - Urednik Memorije" #: ../gui/GameConqueror.ui.h:2 msgid "_Address:" msgstr "_Adresa:" #: ../gui/GameConqueror.ui.h:3 msgid "Target address here (CTRL+L)" msgstr "Ciljana adresa ovdje (CTRL+L)" #: ../gui/GameConqueror.ui.h:4 msgid "Enter address to view" msgstr "Unesi adresu da vidiš" #: ../gui/GameConqueror.ui.h:5 msgid "Jump to address (CTRL+ENTER)" msgstr "Skoči na adresu (CTRL+ENTER)" #: ../gui/GameConqueror.ui.h:6 msgid "_Refresh (CTRL+R)" msgstr "_Osveži (CTRL+R)" #: ../gui/GameConqueror.ui.h:7 msgid "GameConqueror" msgstr "GameConqueror" #: ../gui/GameConqueror.ui.h:8 msgid "Found: 0" msgstr "Pronađeno: 0" #: ../gui/GameConqueror.ui.h:9 msgid "" "Syntax:\n" "\n" " For numeric types:\n" "\n" " N Exactly this number\n" " N..M Range between two numbers\n" " ? Unknown initial value\n" " > or + Increased values\n" " < or - Decreased values\n" " = Unchanged values\n" " != Changed values\n" " > N Greater than N\n" " < N Less than N\n" " + N Increased by N\n" " - N Decreased by N\n" " != N Other than N\n" "\n" " where N could be a number of a simple math expression embraced by (), e.g.\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " For bytearray: use 2-char hexadecimal notation for each byte, separated by " "spaces\n" " e.g. FE DC BA 98 76 54 32 10\n" " ?? can be used as a wildcard value\n" "\n" " For string: enter the string directly" msgstr "" "Sintaksa:\n" "\n" "Za numeričke tipove:\n" "\n" " N Upravo ovaj broj\n" " N..M Raspon između dva broja\n" " ? Nepoznata početna vrijednost\n" " > or + Uvećana vrijednost\n" " < or - Umanjena vrijednost\n" " = Nepromijenjena vrijednost\n" " != Izmijenjena vrijednost\n" " > N Veće od N\n" " < N Manje od N\n" " + N Uvećano za N\n" " - N Umanjeno za N\n" " != N Osim N\n" "\n" " gdje N može biti broj jednostavnog matematičkog izraza obuhvaćenog (), na " "pr.\n" " 12\n" " 0x34\n" " 5.67\n" " (- 8 * 9)\n" "\n" " Za niz bajtova: koristite heksadecimalnu notaciju od 2 karaktera za svaki " "bajt, razdvojene razmacima\n" "na pr. FE DC BA 98 76 54 32 10\n" " ?? može se koristiti kao džoker " "vrijednost\n" "\n" "Za niz: unesite niz direktno" #: ../gui/GameConqueror.ui.h:37 msgid "_Value: ?" msgstr "_Vrijednost: ?" #: ../gui/GameConqueror.ui.h:38 msgid "Enter search value here (CTRL+K/L)" msgstr "Unesite traženu vrijednost ovdje (CTRL+K/L)" #: ../gui/GameConqueror.ui.h:39 msgid "Insert value to search for (CTRL+K/L)" msgstr "Unesite vrijednost za pretragu (CTRL+K/L)" #: ../gui/GameConqueror.ui.h:40 msgid "Scan (CTRL+ENTER)" msgstr "Skeniraj (CTRL+ENTER)" #: ../gui/GameConqueror.ui.h:41 msgid "Reset (CTRL+R)" msgstr "Resetuj (CTRL+R)" #: ../gui/GameConqueror.ui.h:42 msgid "Stop scan" msgstr "Stopiraj skeniranje" #: ../gui/GameConqueror.ui.h:43 msgid "About" msgstr "O programu" #: ../gui/GameConqueror.ui.h:44 msgid "Select a process (CTRL+F)" msgstr "Odaberi proces (CTRL+F)" #: ../gui/GameConqueror.ui.h:45 ../gui/GameConqueror.py:929 msgid "Select a process" msgstr "Odaberi proces" #: ../gui/GameConqueror.ui.h:46 ../gui/GameConqueror.py:928 msgid "No process selected" msgstr "Nijedan proces nije označen" #: ../gui/GameConqueror.ui.h:47 msgid "Specify type of target data" msgstr "Naznači tip targetnog podatka" #: ../gui/GameConqueror.ui.h:48 msgid "Data _Type:" msgstr "Tip _podatka:" #: ../gui/GameConqueror.ui.h:49 msgid "" "Basic: Fastest but may miss some values\n" "Normal: Works for most cases\n" "Full: Never miss values but slowest" msgstr "" "Osnovno: Najbrže, ali možda nedostaje određene vrednosti\n" "Normalno: Radi u većini slučajeva\n" "Potpuno: Nikada ne propustite vrednosti, već najsporije" #: ../gui/GameConqueror.ui.h:52 msgid "_Search Scope:" msgstr "_Pretraga:" #: ../gui/GameConqueror.ui.h:53 msgid "Memory Editor (CTRL+SHIFT+M)" msgstr "Editor memorije (CTRL+SHIFT+M)" #: ../gui/GameConqueror.ui.h:54 msgid "Save address list to file (CTRL+S)" msgstr "Sačuvaj listu adresa u datoteku (CTRL+S)" #: ../gui/GameConqueror.ui.h:55 msgid "Load address list from file (CTRL+O)" msgstr "Učitaj listu adresa iz datoteke" #: ../gui/GameConqueror.ui.h:56 msgid "Remove all entries (CTRL+D)" msgstr "Ukloni sve unose" #: ../gui/GameConqueror.ui.h:57 msgid "Manually add an entry (CTRL+M)" msgstr "Ručno dadaj novi unos (CTRL+M)" #: ../gui/GameConqueror.ui.h:58 msgid "" "Copyright (C) 2009-2014 WANG Lu \n" "Copyright (C) 2010 Bryan Cain\n" "Copyright (C) 2015-2016 Sebastian Parschauer \n" "Copyright (C) 2016 Andrea Stacchiotti \n" "\n" "Special thanks: Igor Gnatenko, Mattias Muenster, Zhang Zhe" msgstr "" "Autorsko pravo (C) 2009-2014 WANG Lu \n" "Autorsko pravo (C) 2010 Bryan Cain\n" "Autorsko pravo (C) 2015-2016 Sebastian Parschauer \n" "Autorsko pravo (C) 2016 Andrea Stacchiotti \n" "\n" "Specijalna zahvalnica: Igor Gnatenko, Mattias Muenster, Zhang Zhe" #: ../gui/GameConqueror.ui.h:64 msgid "A graphical frontend of scanmem" msgstr "Grafički prikaz od scanmem" #: ../gui/GameConqueror.ui.h:65 msgid "Homepage" msgstr "Početna stranica" #: ../gui/GameConqueror.ui.h:66 msgid "Manually add an entry" msgstr "Ručno dodaj unos" #: ../gui/GameConqueror.ui.h:67 msgid "_Add" msgstr "_Dodaj" #: ../gui/GameConqueror.ui.h:68 msgid "_Close" msgstr "_Zatvori" #: ../gui/GameConqueror.ui.h:69 msgid "Memory address of the value" msgstr "Memorijska adresa vrijednosti" #: ../gui/GameConqueror.ui.h:70 msgid "Addres_s:" msgstr "Adresa_s:" #: ../gui/GameConqueror.ui.h:71 msgid "Give a brief description of the value" msgstr "Navedite kratak opis vrijednosti" #: ../gui/GameConqueror.ui.h:72 msgid "_Description:" msgstr "_Opis:" #: ../gui/GameConqueror.ui.h:73 msgid "_Type:" msgstr "_Tip:" #: ../gui/GameConqueror.ui.h:74 msgid "Specify data length (string/bytearray only)" msgstr "Naveditie dužinu podatka (niz/niz bajtova)" #: ../gui/GameConqueror.ui.h:75 msgid "_Length:" msgstr "_Dužina:" #: ../gui/GameConqueror.ui.h:76 msgid "Type to search for a process" msgstr "Tip za pretragu procesa" #: ../gui/GameConqueror.ui.h:77 msgid "_OK" msgstr "_U redu" #: ../gui/GameConqueror.ui.h:78 msgid "_Cancel" msgstr "_Otkaži" #: ../gui/GameConqueror.ui.h:79 msgid "_User:" msgstr "_Korisnik" #: ../gui/GameConqueror.ui.h:80 msgid "_Process:" msgstr "_Procesi:" #: ../gui/GameConqueror.ui.h:81 msgid "Filter" msgstr "Filter" #: ../gui/GameConqueror.py:72 msgid "Basic" msgstr "Osnovno" #: ../gui/GameConqueror.py:72 msgid "Normal" msgstr "Normalno" #: ../gui/GameConqueror.py:72 msgid "Full" msgstr "Puno" #. init columns #. Address #: ../gui/GameConqueror.py:180 ../gui/GameConqueror.py:234 msgid "Address" msgstr "Adresa" #. Value #: ../gui/GameConqueror.py:184 ../gui/GameConqueror.py:251 msgid "Value" msgstr "Vrijednost" #: ../gui/GameConqueror.py:188 msgid "Offset" msgstr "Ofset" #: ../gui/GameConqueror.py:192 msgid "Region Type" msgstr "Tip regiona" #. Lock #: ../gui/GameConqueror.py:217 msgid "Lock" msgstr "Zaključaj" #. Description #: ../gui/GameConqueror.py:226 msgid "Description" msgstr "Opis" #. Type #: ../gui/GameConqueror.py:239 msgid "Type" msgstr "Tip" #. second col #: ../gui/GameConqueror.py:276 msgid "User" msgstr "Korisnik" #. third col #: ../gui/GameConqueror.py:281 msgid "Process" msgstr "Proces" #: ../gui/GameConqueror.py:312 msgid "Add to cheat list" msgstr "Dodaj na listu" #: ../gui/GameConqueror.py:313 ../gui/GameConqueror.py:320 msgid "Browse this address" msgstr "Pretraži ovu adresu" #: ../gui/GameConqueror.py:314 msgid "Scan for this address" msgstr "Skeniraj ovu adresu" #: ../gui/GameConqueror.py:315 msgid "Remove this match" msgstr "Ukloni ovo poklapanje" #: ../gui/GameConqueror.py:321 msgid "Copy address" msgstr "Kopiraj adresu" #: ../gui/GameConqueror.py:322 msgid "Remove this entry" msgstr "Ukloni ovaj unos" #: ../gui/GameConqueror.py:350 ../gui/GameConqueror.py:538 #: ../gui/GameConqueror.py:1002 msgid "Please select a process" msgstr "Molimo označite unos" #: ../gui/GameConqueror.py:363 msgid "Invalid address" msgstr "Pogrešna adresa" #: ../gui/GameConqueror.py:373 msgid "Please enter a valid address." msgstr "Unesite važeću adresu" #: ../gui/GameConqueror.py:377 ../gui/GameConqueror.py:898 msgid "No Description" msgstr "Bez opisa" #: ../gui/GameConqueror.py:406 msgid "Open.." msgstr "Otvori.." #: ../gui/GameConqueror.py:426 msgid "Save.." msgstr "Sačuvaj.." #: ../gui/GameConqueror.py:531 msgid "" msgstr "" #: ../gui/GameConqueror.py:579 ../gui/GameConqueror.py:879 msgid "Cannot read memory" msgstr "Ne mogu očitati memoriju" #: ../gui/GameConqueror.py:835 msgid "Unknown architecture, you may report to developers" msgstr "Nepoznata arhitektura, prijavite programerima" #: ../gui/GameConqueror.py:847 ../gui/GameConqueror.py:930 msgid "" "Cannot retrieve memory maps of that process, maybe it has exited (crashed), " "or you don't have enough privileges" msgstr "" "Ne mogu očitati mapu memorije tog procesa, možda je program zatvoren (srušio " "se), ili nemate potrenu privilegiju" #. not readable #: ../gui/GameConqueror.py:858 #, python-format msgid "Address %x is not readable" msgstr "Adresa %x nije čitljiva" #: ../gui/GameConqueror.py:861 #, python-format msgid "Address %x is not valid" msgstr "Adresa %x nije validna" #: ../gui/GameConqueror.py:870 msgid "Cannot find a readable region" msgstr "Ne mogu pronaći čitljiv region" #: ../gui/GameConqueror.py:1062 #, python-format msgid "Found: %d" msgstr "Pronađeno: %d" #: ../gui/GameConqueror.py:1178 msgid "" "Version of scanmem mismatched, you may encounter problems. Please make sure " "you are using the same version of GameConqueror as scanmem." msgstr "" "Verzija za scanmem se ne podudara, može doći do problema. Molimo provjerite " "da li koristite istu verziju GameConqueror i scanmem-a." #: ../gui/GameConqueror.py:1184 msgid "A GUI for scanmem, a game hacking tool" msgstr "Grafički prikaz za scanmem, alat za hakovanje igrica" #: ../gui/GameConqueror.py:1185 msgid "Report bugs to " msgstr "Prijavi grešku" #: ../gui/GameConqueror.py:1187 msgid "prefill the search box" msgstr "Popunite polje za pretragu" #: ../gui/GameConqueror.py:1189 msgid "PID of the process" msgstr "Redni broj procesa" #: ../gui/misc.py:33 msgid "No value provided" msgstr "Nije obezbjeđena vrijednost" #: ../gui/misc.py:43 ../gui/misc.py:49 ../gui/misc.py:104 #, python-format msgid "Bad value: %s" msgstr "Loša vrijednost: %s" #: ../gui/misc.py:65 #, python-format msgid "Command \"%s\" is not valid for the first scan" msgstr "Komanda \"%s\" nije validna za ovo skeniranje" #: ../gui/misc.py:112 #, python-format msgid "%s is not an integer" msgstr "%s nije broj" #: ../gui/misc.py:118 #, python-format msgid "%s is too bulky for %s" msgstr "%s je preveliki za %s" #: ../gui/misc.py:138 #, python-format msgid "Cannot locate item: %s" msgstr "Ne mogu da pronađem: %s" #: ../gui/GameConqueror.desktop.in.h:1 ../gui/GameConqueror.appdata.xml.in.h:1 msgid "Game Conqueror" msgstr "Game Conqueror" #: ../gui/GameConqueror.desktop.in.h:2 msgid "A game hacking tool. A GUI front-end for scanmem." msgstr "Alat za hakovanje igrica. Grafički prikaz za scanmem" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:1 msgid "Run Game Conqueror" msgstr "Pokreni Game Conqueror" #: ../gui/org.freedesktop.gameconqueror.policy.in.in.h:2 msgid "Authentication is required to run Game Conqueror" msgstr "Autentifikacija je potrebna za pokretanje" #: ../gui/GameConqueror.appdata.xml.in.h:2 msgid "Game hacking tool. GUI front-end for scanmem." msgstr "Alat za hakovanje igrica. Grafički prikaz za scanmem" #: ../gui/GameConqueror.appdata.xml.in.h:3 msgid "" "scanmem is a simple interactive debugging utility, used to locate the " "address of a variable in an executing process. This can be used for the " "analysis or modification of a hostile process on a compromised machine, " "reverse engineering, or as a \"pokefinder\" to cheat at video games. " "GameConqueror aims to provide a CheatEngine-alike interface for scanmem, " "it's user-friendly and easy to use." msgstr "" "Scanmem je jednostavan interaktivni uslužni program za otklanjanje grešaka, " "koji se koristi za lociranje adrese varijable u procesu izvršavanja. Ovo se " "može koristiti za analizu ili modifikaciju neprijateljskog procesa na " "kompromitovanoj mašini, obrnutom inžinjeringu ili kao \"pokefinder\" za " "varanje na video igricama. GameConkueror ima za cilj da obezbedi interfejs " "CheatEngine-alike za skeniranje, jednostavan za korišćenje i jednostavan za " "korišćenje." #: ../gui/GameConqueror.appdata.xml.in.h:4 msgid "Features:" msgstr "Karakteristike:" #: ../gui/GameConqueror.appdata.xml.in.h:5 msgid "Locking on multiple variables" msgstr "Zaključavam višestruke promijenjiljive" #: ../gui/GameConqueror.appdata.xml.in.h:6 msgid "Memory Viewer/Editor" msgstr "Izmjenjivač/pregledaš memorije" #: ../gui/GameConqueror.appdata.xml.in.h:7 msgid "... many to be done" msgstr "... Mnogi će biti učinjeni" scanmem-0.17/ptrace.c000066400000000000000000000634601317023271400145170ustar00rootroot00000000000000/* Functions to access the memory of the target process. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009,2010 WANG Lu Copyright (C) 2015 Sebastian Parschauer Copyright (C) 2017 Andrea Stacchiotti This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "config.h" /* for pread */ # ifdef _XOPEN_SOURCE # undef _XOPEN_SOURCE # endif # define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __GNUC__ # define EXPECT(x,y) __builtin_expect(x, y) #else # define EXPECT(x,y) x #endif // dirty hack for FreeBSD #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define PTRACE_ATTACH PT_ATTACH #define PTRACE_DETACH PT_DETACH #define PTRACE_PEEKDATA PT_READ_D #define PTRACE_POKEDATA PT_WRITE_D #endif #include "value.h" #include "scanroutines.h" #include "scanmem.h" #include "show_message.h" #include "targetmem.h" /* progress handling */ #define NUM_DOTS (10) #define NUM_SAMPLES (100) #define MAX_PROGRESS (1.0) /* 100% */ #if (!NUM_DOTS || !NUM_SAMPLES || NUM_SAMPLES % NUM_DOTS != 0) #error Invalid NUM_DOTS to NUM_SAMPLES proportion! #endif #define SAMPLES_PER_DOT (NUM_SAMPLES / NUM_DOTS) #define PROGRESS_PER_SAMPLE (MAX_PROGRESS / NUM_SAMPLES) /* ptrace peek buffer, used by peekdata() as a mirror of the process memory. * Max size is the maximum allowed rounded VLT scan length, aka UINT16_MAX, * plus a `long` for shifting efficiency */ #define MAX_PEEKBUF_SIZE ((1<<16) + sizeof(long)) static struct { uint8_t cache[MAX_PEEKBUF_SIZE]; /* read from ptrace() */ unsigned size; /* number of entries (in bytes) */ const char *base; /* base address of cached region */ pid_t pid; /* what pid this applies to */ } peekbuf; bool sm_attach(pid_t target) { int status; /* attach to the target application, which should cause a SIGSTOP */ if (ptrace(PTRACE_ATTACH, target, NULL, NULL) == -1L) { show_error("failed to attach to %d, %s\n", target, strerror(errno)); return false; } /* wait for the SIGSTOP to take place. */ if (waitpid(target, &status, 0) == -1 || !WIFSTOPPED(status)) { show_error("there was an error waiting for the target to stop.\n"); show_info("%s\n", strerror(errno)); return false; } /* flush the peek buffer */ memset(&peekbuf, 0x00, sizeof(peekbuf)); /* everything looks okay */ return true; } bool sm_detach(pid_t target) { // addr is ignored on Linux, but should be 1 on FreeBSD in order to let the child process continue execution where it had been interrupted return ptrace(PTRACE_DETACH, target, 1, 0) == 0; } /* * sm_peekdata - caches overlapping ptrace reads to improve performance. * * This routine calls `ptrace(PEEKDATA, ...)`, and fills the peekbuf cache, * to make a local mirror of the process memory we're interested in. */ extern inline bool sm_peekdata(pid_t pid, const void *addr, uint16_t length, const mem64_t **result_ptr, size_t *memlength) { const char *reqaddr = addr; int i, j; unsigned int missing_bytes = 0; assert(peekbuf.size <= MAX_PEEKBUF_SIZE); assert(result_ptr != NULL); assert(memlength != NULL); /* check if we have a cache hit */ if (pid == peekbuf.pid && reqaddr >= peekbuf.base && (unsigned long) (reqaddr + length - peekbuf.base) <= peekbuf.size) { *result_ptr = (mem64_t*)&peekbuf.cache[reqaddr - peekbuf.base]; *memlength = peekbuf.base - reqaddr + peekbuf.size; return true; } else if (pid == peekbuf.pid && reqaddr >= peekbuf.base && (unsigned long) (reqaddr - peekbuf.base) < peekbuf.size) { assert(peekbuf.size != 0); /* partial hit, we have some of the data but not all, so remove old entries - shift the frame by as far as is necessary */ /* missing bytes: round up to nearest long size for ptrace efficiency */ missing_bytes = (reqaddr + length) - (peekbuf.base + peekbuf.size); missing_bytes = sizeof(long) * (1 + (missing_bytes-1) / sizeof(long)); /* head shift if necessary */ if (peekbuf.size + missing_bytes > MAX_PEEKBUF_SIZE) { int shift_size = reqaddr-peekbuf.base; shift_size = sizeof(long) * (shift_size / sizeof(long)); memmove(peekbuf.cache, &peekbuf.cache[shift_size], peekbuf.size-shift_size); peekbuf.size -= shift_size; peekbuf.base += shift_size; } } else { /* cache miss, invalidate the cache */ missing_bytes = length; peekbuf.pid = pid; peekbuf.size = 0; peekbuf.base = addr; } /* we need a ptrace() to complete the request */ errno = 0; for (i = 0; i < missing_bytes; i += sizeof(long)) { const char *ptrace_address = peekbuf.base + peekbuf.size; long ptraced_long = ptrace(PTRACE_PEEKDATA, pid, ptrace_address, NULL); /* check if ptrace() succeeded */ if (EXPECT(ptraced_long == -1L && errno != 0, false)) { /* it's possible i'm trying to read partially oob */ if (errno == EIO || errno == EFAULT) { /* read backwards until we get a good read, then shift out the right value */ for (j = 1, errno = 0; j < sizeof(long); j++, errno = 0) { /* try for a shifted ptrace - 'continue' (i.e. try an increased shift) if it fails */ if ((ptraced_long = ptrace(PTRACE_PEEKDATA, pid, ptrace_address - j, NULL)) == -1L && (errno == EIO || errno == EFAULT)) continue; /* cache it with the appropriate offset */ if(peekbuf.size >= j) { memcpy(&peekbuf.cache[peekbuf.size - j], &ptraced_long, sizeof(long)); } else { memcpy(&peekbuf.cache[0], &ptraced_long, sizeof(long)); peekbuf.base -= j; } peekbuf.size += sizeof(long) - j; /* interrupt the gathering process */ break; } /* partial success */ goto end; } /* I wont print a message here, would be very noisy if a region is unmapped */ *result_ptr = NULL; *memlength = 0; return false; } /* otherwise, ptrace() worked - cache the data and increase the size */ memcpy(&peekbuf.cache[peekbuf.size], &ptraced_long, sizeof(long)); peekbuf.size += sizeof(long); } end: /* return result to caller */ *result_ptr = (mem64_t*)&peekbuf.cache[reqaddr - peekbuf.base]; *memlength = peekbuf.base - reqaddr + peekbuf.size; return true; } static inline void print_a_dot(void) { fprintf(stderr, "."); fflush(stderr); } static inline uint16_t flags_to_memlength(scan_data_type_t scan_data_type, match_flags flags) { switch(scan_data_type) { case BYTEARRAY: case STRING: return flags; break; default: /* numbers */ if (flags & flags_64b) return 8; else if (flags & flags_32b) return 4; else if (flags & flags_16b) return 2; else if (flags & flags_8b ) return 1; else /* it can't be a variable of any size */ return 0; break; } } /* This is the function that handles when you enter a value (or >, <, =) for the second or later time (i.e. when there's already a list of matches); * it reduces the list to those that still match. It returns false on failure to attach, detach, or reallocate memory, otherwise true. */ bool sm_checkmatches(globals_t *vars, scan_match_type_t match_type, const uservalue_t *uservalue) { matches_and_old_values_swath *reading_swath_index = vars->matches->swaths; matches_and_old_values_swath reading_swath = *reading_swath_index; unsigned long bytes_scanned = 0; unsigned long total_scan_bytes = 0; matches_and_old_values_swath *tmp_swath_index = reading_swath_index; unsigned int samples_remaining = NUM_SAMPLES; unsigned int samples_to_dot = SAMPLES_PER_DOT; size_t bytes_at_next_sample; size_t bytes_per_sample; if (sm_choose_scanroutine(vars->options.scan_data_type, match_type, uservalue, vars->options.reverse_endianness) == false) { show_error("unsupported scan for current data type.\n"); return false; } assert(sm_scan_routine); while(tmp_swath_index->number_of_bytes) { total_scan_bytes += tmp_swath_index->number_of_bytes; tmp_swath_index = (matches_and_old_values_swath *)(&tmp_swath_index->data[tmp_swath_index->number_of_bytes]); } bytes_per_sample = total_scan_bytes / NUM_SAMPLES; bytes_at_next_sample = bytes_per_sample; /* for user, just print the first dot */ print_a_dot(); size_t reading_iterator = 0; matches_and_old_values_swath *writing_swath_index = vars->matches->swaths; writing_swath_index->first_byte_in_child = NULL; writing_swath_index->number_of_bytes = 0; int required_extra_bytes_to_record = 0; vars->num_matches = 0; vars->scan_progress = 0.0; vars->stop_flag = false; /* stop and attach to the target */ if (sm_attach(vars->target) == false) return false; while (reading_swath.first_byte_in_child) { unsigned int match_length = 0; const mem64_t *memory_ptr; size_t memlength; match_flags checkflags; match_flags old_flags = reading_swath_index->data[reading_iterator].match_info; uint old_length = flags_to_memlength(vars->options.scan_data_type, old_flags); void *address = reading_swath.first_byte_in_child + reading_iterator; /* read value from this address */ if (EXPECT(sm_peekdata(vars->target, address, old_length, &memory_ptr, &memlength) == false, false)) { /* If we can't look at the data here, just abort the whole recording, something bad happened */ required_extra_bytes_to_record = 0; } else if (old_flags != flags_empty) /* Test only valid old matches */ { value_t old_val = data_to_val_aux(reading_swath_index, reading_iterator, reading_swath.number_of_bytes); memlength = old_length < memlength ? old_length : memlength; checkflags = flags_empty; match_length = (*sm_scan_routine)(memory_ptr, memlength, &old_val, uservalue, &checkflags); } if (match_length > 0) { assert(match_length <= memlength); /* Still a candidate. Write data. - We can get away with overwriting in the same array because it is guaranteed to take up the same number of bytes or fewer, and because we copied out the reading swath metadata already. - We can get away with assuming that the pointers will stay valid, because as we never add more data to the array than there was before, it will not reallocate. */ writing_swath_index = add_element(&(vars->matches), writing_swath_index, address, get_u8b(memory_ptr), checkflags); ++vars->num_matches; required_extra_bytes_to_record = match_length - 1; } else if (required_extra_bytes_to_record) { writing_swath_index = add_element(&(vars->matches), writing_swath_index, address, get_u8b(memory_ptr), flags_empty); --required_extra_bytes_to_record; } if (EXPECT(bytes_scanned >= bytes_at_next_sample, false)) { bytes_at_next_sample += bytes_per_sample; /* handle rounding */ if (EXPECT(--samples_remaining > 0, true)) { /* for front-end, update percentage */ vars->scan_progress += PROGRESS_PER_SAMPLE; if (EXPECT(--samples_to_dot == 0, false)) { samples_to_dot = SAMPLES_PER_DOT; /* for user, just print a dot */ print_a_dot(); } /* stop scanning if asked to */ if (vars->stop_flag) break; } } ++bytes_scanned; /* go on to the next one... */ ++reading_iterator; if (reading_iterator >= reading_swath.number_of_bytes) { reading_swath_index = (matches_and_old_values_swath *) (&reading_swath_index->data[reading_swath.number_of_bytes]); reading_swath = *reading_swath_index; reading_iterator = 0; required_extra_bytes_to_record = 0; /* just in case */ } } if (!(vars->matches = null_terminate(vars->matches, writing_swath_index))) { show_error("memory allocation error while reducing matches-array size\n"); return false; } show_user("ok\n"); /* tell front-end we've done */ vars->scan_progress = MAX_PROGRESS; show_info("we currently have %ld matches.\n", vars->num_matches); /* okay, detach */ return sm_detach(vars->target); } /* read region using /proc/pid/mem */ static inline ssize_t readregion(pid_t target, void *buf, size_t count, unsigned long offset) { char mem[32]; int fd; ssize_t len; /* print the path to mem file */ snprintf(mem, sizeof(mem), "/proc/%d/mem", target); /* attempt to open the file */ if ((fd = open(mem, O_RDONLY)) == -1) { show_error("unable to open %s.\n", mem); return -1; } /* try to honor the request */ len = pread(fd, buf, count, offset); /* clean up */ close(fd); return len; } /* sm_searchregions() performs an initial search of the process for values matching `uservalue` */ bool sm_searchregions(globals_t *vars, scan_match_type_t match_type, const uservalue_t *uservalue) { matches_and_old_values_swath *writing_swath_index; int required_extra_bytes_to_record = 0; unsigned long total_size = 0; unsigned regnum = 0; element_t *n = vars->regions->head; region_t *r; unsigned long total_scan_bytes = 0; unsigned char *data = NULL; if (sm_choose_scanroutine(vars->options.scan_data_type, match_type, uservalue, vars->options.reverse_endianness) == false) { show_error("unsupported scan for current data type.\n"); return false; } assert(sm_scan_routine); /* stop and attach to the target */ if (sm_attach(vars->target) == false) return false; /* make sure we have some regions to search */ if (vars->regions->size == 0) { show_warn("no regions defined, perhaps you deleted them all?\n"); show_info("use the \"reset\" command to refresh regions.\n"); return sm_detach(vars->target); } total_size = sizeof(matches_and_old_values_array); while (n) { total_size += ((region_t *)(n->data))->size * sizeof(old_value_and_match_info) + sizeof(matches_and_old_values_swath); n = n->next; } total_size += sizeof(matches_and_old_values_swath); /* for null terminate */ show_debug("allocate array, max size %ld\n", total_size); if (!(vars->matches = allocate_array(vars->matches, total_size))) { show_error("could not allocate match array\n"); return false; } writing_swath_index = vars->matches->swaths; writing_swath_index->first_byte_in_child = NULL; writing_swath_index->number_of_bytes = 0; /* get total number of bytes */ for(n = vars->regions->head; n; n = n->next) total_scan_bytes += ((region_t *)n->data)->size; vars->scan_progress = 0.0; vars->stop_flag = false; n = vars->regions->head; /* check every memory region */ while (n) { size_t nread = 0; int dots_remaining = NUM_DOTS; size_t bytes_at_next_dot; size_t bytes_per_dot; double progress_per_dot; /* load the next region */ r = n->data; bytes_per_dot = r->size / NUM_DOTS; bytes_at_next_dot = bytes_per_dot; progress_per_dot = (double)bytes_per_dot / total_scan_bytes; /* allocate data array */ if ((data = malloc(r->size * sizeof(char))) == NULL) { show_error("sorry, there was a memory allocation error.\n"); return false; } /* print a progress meter so user knows we haven't crashed */ /* cannot use show_info here because it'll append a '\n' */ show_user("%02u/%02u searching %#10lx - %#10lx", ++regnum, vars->regions->size, (unsigned long)r->start, (unsigned long)r->start + r->size); fflush(stderr); #if HAVE_PROCMEM ssize_t len = 0; /* keep reading until completed */ while (nread < r->size) { if ((len = readregion(vars->target, data+nread, r->size-nread, (unsigned long)(r->start+nread))) == -1) { /* no, continue with whatever data was read */ break; } else { /* some data was read */ nread += len; } } #else /* Read the region with `ptrace()`: the API specifies that `ptrace()` returns a `long`, which * is the size of a word for the current architecture, so this section will deal in `long`s */ for (nread = 0; nread < r->size; nread += sizeof(long)) { const char *ptrace_address = r->start + nread; long ptraced_long = ptrace(PTRACE_PEEKDATA, vars->target, ptrace_address, NULL); /* check if ptrace() succeeded */ if (EXPECT(ptraced_long == -1L && errno != 0, false)) { /* interrupt the gathering process */ break; } /* otherwise, ptrace() worked - store the data */ memcpy(data+nread, &ptraced_long, sizeof(long)); } #endif /* region has been read, tell user */ print_a_dot(); /* For every offset, check if we have a match. * Testing `memlength > 0` is much faster than `offset < nread` */ size_t memlength, offset; for (memlength = nread, offset = 0; memlength > 0; memlength--, offset++) { unsigned int match_length; const mem64_t* memory_ptr = (mem64_t*)(data+offset); match_flags checkflags; /* initialize checkflags */ checkflags = flags_empty; /* check if we have a match */ match_length = (*sm_scan_routine)(memory_ptr, memlength, NULL, uservalue, &checkflags); if (EXPECT(match_length > 0, false)) { assert(match_length <= memlength); writing_swath_index = add_element(&(vars->matches), writing_swath_index, r->start+offset, get_u8b(memory_ptr), checkflags); ++vars->num_matches; required_extra_bytes_to_record = match_length - 1; } else if (required_extra_bytes_to_record) { writing_swath_index = add_element(&(vars->matches), writing_swath_index, r->start+offset, get_u8b(memory_ptr), flags_empty); --required_extra_bytes_to_record; } /* print a simple progress meter */ if (EXPECT(offset >= bytes_at_next_dot, false)) { bytes_at_next_dot += bytes_per_dot; /* handle rounding */ if (EXPECT(--dots_remaining > 0, true)) { /* for user, just print a dot */ print_a_dot(); /* for front-end, update percentage */ vars->scan_progress += progress_per_dot; /* stop scanning if asked to */ if (vars->stop_flag) break; } } } free(data); vars->scan_progress += progress_per_dot; /* stop scanning if asked to */ if (vars->stop_flag) break; n = n->next; show_user("ok\n"); } /* tell front-end we've finished */ vars->scan_progress = MAX_PROGRESS; if (!(vars->matches = null_terminate(vars->matches, writing_swath_index))) { show_error("memory allocation error while reducing matches-array size\n"); return false; } show_info("we currently have %ld matches.\n", vars->num_matches); /* okay, detach */ return sm_detach(vars->target); } /* Needs to support only ANYNUMBER types */ bool sm_setaddr(pid_t target, void *addr, const value_t *to) { unsigned int i; const mem64_t *memory_ptr; size_t memlength; if (sm_attach(target) == false) { return false; } if (sm_peekdata(target, addr, sizeof(uint64_t), &memory_ptr, &memlength) == false) { show_error("couldn't access the target address %10p\n", addr); return false; } /* Assume `sizeof(uint64_t)` is a multiple of `sizeof(long)` */ long memarray[sizeof(uint64_t)/sizeof(long)] = {0}; uint val_length = flags_to_memlength(ANYNUMBER, to->flags); if (val_length > 0) { /* Basically, overwrite as much of the data as makes sense, and no more. */ memcpy(memarray, memory_ptr, memlength); memcpy(memarray, to->bytes, val_length); } else { show_error("could not determine type to poke.\n"); return false; } /* TODO: may use /proc//mem here */ for (i = 0; i < sizeof(uint64_t)/sizeof(long); i++) { if (ptrace(PTRACE_POKEDATA, target, addr + i*sizeof(long), memarray[i]) == -1L) { return false; } } return sm_detach(target); } bool sm_read_array(pid_t target, const void *addr, char *buf, int len) { if (sm_attach(target) == false) { return false; } #if HAVE_PROCMEM unsigned nread=0; ssize_t tmpl; while (nread < len) { if ((tmpl = readregion(target, buf+nread, len-nread, (unsigned long)(addr+nread))) == -1) { /* no, continue with whatever data was read */ break; } else { /* some data was read */ nread += tmpl; } } if (nread < len) { sm_detach(target); return false; } return sm_detach(target); #else int i; /* here we just read long by long, this should be ok for most of time */ /* partial hit is not handled */ for(i = 0; i < len; i += sizeof(long)) { errno = 0; *((long *)(buf+i)) = ptrace(PTRACE_PEEKDATA, target, addr+i, NULL); if (EXPECT((*((long *)(buf+i)) == -1L) && (errno != 0), false)) { sm_detach(target); return false; } } return sm_detach(target); #endif } /* TODO: may use /proc//mem here */ bool sm_write_array(pid_t target, void *addr, const void *data, int len) { int i,j; long peek_value; if (sm_attach(target) == false) { return false; } for (i = 0; i + sizeof(long) < len; i += sizeof(long)) { if (ptrace(PTRACE_POKEDATA, target, addr + i, *(long *)(data + i)) == -1L) { return false; } } if (len - i > 0) /* something left (shorter than a long) */ { if (len > sizeof(long)) /* rewrite last sizeof(long) bytes of the buffer */ { if (ptrace(PTRACE_POKEDATA, target, addr + len - sizeof(long), *(long *)(data + len - sizeof(long))) == -1L) { return false; } } else /* we have to play with bits... */ { /* try all possible shifting read and write */ for(j = 0; j <= sizeof(long) - (len - i); ++j) { errno = 0; if(((peek_value = ptrace(PTRACE_PEEKDATA, target, addr - j, NULL)) == -1L) && (errno != 0)) { if (errno == EIO || errno == EFAULT) /* may try next shift */ continue; else { show_error("%s failed.\n", __func__); return false; } } else /* peek success */ { /* write back */ memcpy(((int8_t*)&peek_value)+j, data+i, len-i); if (ptrace(PTRACE_POKEDATA, target, addr - j, peek_value) == -1L) { show_error("%s failed.\n", __func__); return false; } break; } } } } return sm_detach(target); } scanmem-0.17/readline.c000066400000000000000000000032141317023271400150130ustar00rootroot00000000000000/* Replace libreadline. Copyright (C) 2015 Jonathan Pelletier This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include "readline.h" #include "getline.h" int rl_attempted_completion_over = 0; const char *rl_readline_name = "scanmem"; rl_completion_func_t *rl_attempted_completion_function = NULL; /* always return NULL to show that there are no completions */ char **rl_completion_matches(const char *text, rl_compentry_func_t *entry_function) { return NULL; } /* show the prompt, then allocate, read and return a line with getline() */ char *readline(const char *prompt) { char *line = NULL; size_t n = 0; ssize_t bytes_read; printf("%s", prompt); fflush(stdout); bytes_read = getline(&line, &n, stdin); if (bytes_read > 0) line[bytes_read - 1] = '\0'; /* remove the trailing newline */ return line; } /* don't maintain a history */ void add_history(const char *line) { } scanmem-0.17/readline.h000066400000000000000000000024471317023271400150270ustar00rootroot00000000000000/* Replace libreadline. Copyright (C) 2015 Jonathan Pelletier This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef READLINE_H #define READLINE_H typedef char *rl_compentry_func_t(const char *, int); typedef char **rl_completion_func_t(const char *, int, int); extern int rl_attempted_completion_over; extern const char *rl_readline_name; extern rl_completion_func_t *rl_attempted_completion_function; char **rl_completion_matches(const char *text, rl_compentry_func_t *entry_function); char *readline(const char *prompt); void add_history(const char *line); #endif /* READLINE_H */ scanmem-0.17/scanmem.1000066400000000000000000000400421317023271400145710ustar00rootroot00000000000000.TH scanmem 1 "2017-10-11" "scanmem-0.17" .SH NAME scanmem \- locate and modify variables in an executing process. .SH SYNOPSIS .B scanmem .RB [options] .IR [target-program-pid] .SH DESCRIPTION .B scanmem is an interactive debugging utility that can be used to isolate the address of a variable in an executing process by successively scanning the process' address space looking for matching values. .br .RB "By informing " scanmem how the value of the variable changes over time, it can determine the actual location (or locations) of the variable by successively eliminating non-matches. .br .BR scanmem " determines where to look by searching for mappings with .IR read "/" write permission, these are referred to as regions. Users can eliminate regions they believe are likely unrelated to the target variable (for example, located in a shared library unrelated to the variable in question), this will improve the speed of the scan, which can initially be quite slow in large programs. Once a variable has been found, .B scanmem can monitor the variable, or change it to a user specified value, either once, or continually over a period of time. .B scanmem .RI "works similarly to the \(dq" pokefinders "\(dq once commonly used to cheat at video games," this function is a good demonstration of how to use .BR scanmem ", and is used in the documentation." .SH USAGE .B scanmem should be invoked with the process id of the program you wish to debug as an argument. .RB "Once started, " scanmem " accepts interactive commands. These are described below, however entering .BR help " at the .BR > " prompt will allow you to access .BR scanmem "'s online documentation. .RI The " target-program-pid can be specified in decimal, hexadecimal, or octal using the standard C language notation (leading 0x for hexadecimal, leading 0 for octal, anything else is assumed to be decimal). .TP .BI "\-p, \-\-pid=" pid Set the .IR "target-program-pid". .TP .BI "\-c, \-\-command=" cmd1[;cmd2][;...] Run given commands (separated by ";") before starting the interactive shell. .TP .B "\-v, \-\-version" Print version and exit. .TP .B "\-h, \-\-help" Print a short description of command line options then exit. .TP .B "\-d, \-\-debug" Run in debug mode, more information will be outputted. .TP .B "\-e, \-\-errexit" Exit on initial commands error, ignored during interactive mode. .SH COMMANDS While in interactive mode, .BR scanmem " prints a decimal number followed by " > ", the number is the current number of" possible candidates for the target variable that are known. The absence of said number indicates that no possible variables have been eliminated yet. .br The default scan data type is "int". .RB "It can be changed with the " option " command." .TP .B n Where .B n represents any number in decimal, octal or hexadecimal, this command tells .B scanmem that the current value of the target variable is exactly .BR n "." .B scanmem will begin a search of the entire address space, or the existing known matches (if any), eliminating any variable that does not have this value. .TP .B n..m This is like the .B n command but .B scanmem searches for a range of numbers between .B n and .B m inclusive instead. .TP .BR ">", " <", " +", " -", " =", " !=" The following commands are extremely useful for locating a variable whose exact value we cannot see, but we can see how it changes over time, e.g. an health bar. These commands usually cannot be used for the first scan but there are some exceptions: .BR "> " n, " < " n, " = " "n and" " != " n. .RS .TP .BI "> " [n] .RI "If " n " is given, match values that are greater than " n "." .RB "Otherwise match all values that have increased." .TP .BI "< " [n] .RI "If " n " is given, match values that are less than " n "." .RB "Otherwise match all values that have decreased." .TP .BI "+ " [n] .RI "If " n " is given, match values that have been increased by " n "." .RB "Otherwise match all values that have increased (same as " > ")." .TP .BI "- " [n] .RI "If " n " is given, match values that have been decreased by " n "." .RB "Otherwise match all values that have decreased (same as " < ")." .TP .BI "= " [n] .RI "If " n " is given, match values that are equal to " n " .RB "(same as " n "). Otherwise match all values that have not changed." .TP .BI "!= " [n] .RI "If " n " is given, match values that are different from " n "." .RB "Otherwise match all values that have changed." .RE .TP .B snapshot Match any value. This is useful when an initial value or range is not known for subsequent scans with .BR > ", " < ", " + ", " - ", " = ", and " != "." .TP .BI "\(dq " text Search for the provided .I text in memory if the scan data type is set to "string". .TP .B update Scans the current process, getting the current values of all matches. These values can be viewed with .BR list ", and are also the old values that " scanmem " compares to when using" .BR > ", " < ", or " = "." This command is equivalent to a search command that all current results match. .TP .BI list " [max_to_print] .RI "List up to " max_to_print " (default: " 10k ") possible candidates currently known, including their address, region id, match offset, region type, last known value and possible value types. The value in the first column is the match id, and can be used in conjunction with the .B delete command to eliminate matches. The match offset is determined by subtracting the load address of the associated ELF file or region from the address. It can be used to bypass Address Space Layout Randomization (ASLR). .TP .BI delete " match-id_set .RI "Delete matches in the " match-id_set ". .RI "The " match-ids " can be found from the output of the .BR list " command. .RI "Set notation: " "[!][..a](,b..c | d, ...)[e..]". .br .RB "To delete all known matches, see the " reset " command. .br To delete all the matches associated with a particular library, see the .BR dregion " command, which also removes any associated matches. .br Please note that match-ids may be recalculated after matches are removed or added. .TP .BI watch " match-id Monitor the value of .IR match-id ", and print its value as it changes. Every change is printed along with a timestamp," you can interrupt this command with ^C to stop monitoring. .TP .BI set " [match-id_set=]value[/delay] [...] .RI "Set the value " value " into the match numbers specified in " match-id_set ", .RI "or if just " value " is specified, all known matches." .IR value " can be specified in standard C language notation. All known matches, along with their match-id's can be displayed using the .BR list " command. .RI Multiple " match-id_set" "s can be specified, terminated with an " = " sign. .RI "Set notation: " "[!][..a](,b..c | d, ...)[e..]". .br .RI "To set a value continually, suffix the command with " / followed by the number of seconds to wait between sets. You can interrupt the set command with ^C to return to the .BR scanmem " prompt. This can be used to sustain the value of a variable which decreases over time, for example a timer that is decremented every second can be set to 100 every 10 seconds to prevent some property from ever changing. This command is used to change the value of the variable(s) once found by elimination. Please note, some applications will store values in multiple locations. .TP .BI write " value_type address value Manually set the value of the variable at the specified address. .br .RI "Names of " value_type are subject to change in different versions of .BR scanmem "," see more info using the `help write` command. .TP .BI dump " address length [filename] .RI "Dump the memory region starting from " address " of length " length in a human-readable format. .RI "If " filename " is given, data will be saved into the file, otherwise data will be displayed on stdout. .TP .BI pid " [new-pid] Print out the process id of the current target program, or change the target to .IR new-pid ", which will reset existing regions and matches." .TP .B reset Forget all known regions and matches and start again. .TP .B lregions List all the known regions, this can be used in combination with the .B dregion command to eliminate regions that the user believes are not related to the variable in question, thus reducing the address space required to search in. The value in the first column is the .I region-id which must be passed to the .B dregion command. Besides the start address, the size and path (if applicable) are also printed. This can be used to eliminate regions located in shared libraries that are unlikely to be relevant to the variable required. For experts: Also the region type and the load address are displayed. The types are "exe" (executable) "code" (library), "heap", "stack" or "misc" (everything else). The load address is the memory location where an ELF file (exe/lib) has been loaded to. This helps to convert between the addresses in memory and in the associated ELF file. If the region does not belong to an ELF file, then it is the same as the start address. .TP .BI dregion " region-id_set .RI "Delete the regions in " region-id_set ", along with any matches from the match list. .RI "Set notation: " "[!][..a](,b..c | d, ...)[e..]". .br .RI "The " region-id "'s can be found in the output of the .BR lregions " command. .TP .BI option " name value Change options at runtime. E.g. the scan data type can be changed. See `help option` for all possible names/values. .TP .BI shell " shell-command .RI "Execute " shell-command " using /bin/sh, then return. .TP .BI show " info Display information relating to .I info - see `help show` for details. .TP .B version Print the version of .B scanmem in use. .TP .B help Print a short summary of available commands. .TP .B exit Detach from the target program and exit immediately. .SH EXAMPLES Cheat at nethack, on systems where nethack is not installed sgid. .B ATTENTION: scanmem usually requires root privileges. See .B KNOWN ISSUES for details. .nf $ sudo scanmem `pgrep nethack` info: maps file located at /proc/14658/maps opened. info: 9 suitable regions found. Please enter current value, or "help" for other commands. > .fi I enter how much gold I currently have (58 pieces) and let .B scanmem find the potential candidates. .nf > 58 01/09 searching 0x79f000 - 0x7b0000..........ok 02/09 searching 0x7b0000 - 0x7cc000..........ok 03/09 searching 0x24d2000 - 0x24f3000..........ok 04/09 searching 0x7fcc04baa000 - 0x7fcc04bae000..........ok 05/09 searching 0x7fcc04de1000 - 0x7fcc04de2000..........ok 06/09 searching 0x7fcc051f7000 - 0x7fcc051fb000..........ok 07/09 searching 0x7fcc05227000 - 0x7fcc0522a000..........ok 08/09 searching 0x7fcc0522c000 - 0x7fcc0522d000..........ok 09/09 searching 0x7ffc8c113000 - 0x7ffc8c134000..........ok info: we currently have 16 matches. 16> list [ 0] 7b09e0, 1 + 3b09e0, exe, 58, [I64 I32 I16 I8 ] [ 1] 7b907a, 1 + 3b907a, exe, 58, [I8 ] [ 2] 24d4b6c, 2 + 2b6c, heap, 58, [I16 I8 ] [ 3] 24d567e, 2 + 367e, heap, 58, [I16 I8 ] [ 4] 24d5740, 2 + 3740, heap, 58, [I8 ] [ 5] 7fcc05229951, 6 + 2951, misc, 58, [I8 ] [ 6] 7ffc8c12ee28, 8 + 1be28, stack, 58, [I16 I8 ] [ 7] 7ffc8c132381, 8 + 1f381, stack, 58, [I8 ] [ 8] 7ffc8c132389, 8 + 1f389, stack, 58, [I8 ] [ 9] 7ffc8c132391, 8 + 1f391, stack, 58, [I8 ] [10] 7ffc8c132399, 8 + 1f399, stack, 58, [I8 ] [11] 7ffc8c1323a1, 8 + 1f3a1, stack, 58, [I8 ] [12] 7ffc8c1323a9, 8 + 1f3a9, stack, 58, [I8 ] [13] 7ffc8c1331a3, 8 + 201a3, stack, 58, [I8 ] [14] 7ffc8c13325f, 8 + 2025f, stack, 58, [I8 ] [15] 7ffc8c133264, 8 + 20264, stack, 58, [I8 ] 16> .fi 16 potential matches were found. This is also displayed in the prompt. Many of them are quite unrelated, as they are part of the stack, belong to libraries or miscellaneous memory-mapped files. Even the heap is unlikely for a very old command line game. We could make .B scanmem eliminate these manually using the .B delete command, however just waiting until the amount of gold changes and telling .B scanmem the new value should be enough. I find some more gold, and tell .B scanmem the new value, 83. .nf 16> 83 \[char46].........info: we currently have 1 matches. info: match identified, use "set" to modify value. info: enter "help" for other commands. 1> list [ 0] 7b09e0, 1 + 3b09e0, exe, 83, [I64 I32 I16 I8 ] .fi Only one of the 16 original candidates now has the value 83, so this must be where the amount of gold is stored. I'll try setting it to 10,000 pieces. .nf 1> set 10000 info: setting *0x7b09e0 to 0x2710... 1> .fi The resulting nethack status: .nf Dlvl:1 $:10000 HP:15(15) Pw:2(2) AC:7 Exp:1 .fi Conclusion: We've found and modified the gold value as I32 in static memory of the executable at virtual memory address 0x7b09e0. This address belongs to the region with id 1. Now it is important to know if this is a position-independent executable (PIE). We list the regions for this and check the load address of the executable. .nf 1> lregions [ 0] 79f000, 69632 bytes, exe, 400000, rw-, /usr/lib/nethack/nethack.tty [ 1] 7b0000, 114688 bytes, exe, 400000, rw-, unassociated [ 2] 24d2000, 135168 bytes, heap, 24d2000, rw-, [heap] [ 3] 7fcc04baa000, 16384 bytes, misc, 7fcc04baa000, rw-, unassociated [ 4] 7fcc04de1000, 4096 bytes, misc, 7fcc04de1000, rw-, unassociated [ 5] 7fcc051f7000, 16384 bytes, misc, 7fcc051f7000, rw-, unassociated [ 6] 7fcc05227000, 12288 bytes, misc, 7fcc05227000, rw-, unassociated [ 7] 7fcc0522c000, 4096 bytes, misc, 7fcc0522c000, rw-, unassociated [ 8] 7ffc8c113000, 135168 bytes, stack, 7ffc8c113000, rw-, [stack] .fi We are on x86_64 and 0x400000 is the static load address for executables there. This means that this is not a PIE and the gold is always stored at 0x7b09e0. This makes it easy to use a game trainer like GameConqueror which refills the gold value periodically. With a PIE we have to use the match offset (0x3b09e0 here) instead and an advanced game trainer with PIE support has to determine and add the current load address to it to get the current memory address of the gold value of the current game run. .SH NOTES .B scanmem has been tested on multiple large programs, including the 3d shoot-em-up quake3 linux. .B scanmem is also tested on ARM platforms and comes with Android support since version 0.16. Obviously, .B scanmem can crash your program if used incorrectly. Some programs store values in multiple locations, this is why .B set will change all known matches. Address Space Layout Randomization (ASLR) together with position-independent executables (PIE), position-independent code (PIC) or dynamic memory on the heap causes variables to be loaded to different memory addresses at every game start. Advanced game trainers like ugtrain are required to periodically refill variables is such memory regions. .SH KNOWN ISSUES .B scanmem usually requires root privileges for .BR ptrace (2) because security modules control ptrace() capabilities. On x86 and x86_64 there is usually the .B Yama security module providing the file .IR /proc/sys/kernel/yama/ptrace_scope "." It is available since Linux 3.4. If this file contains "1", then only parents may ptrace() their children without root privileges. This means that .B scanmem would have to run the game. This is not possible as this would require major design changes. So we run .B scanmem as root. The first scan can be very slow on large programs, this is not a problem for subsequent scans as huge portions of the address space are usually eliminated. This could be improved in future, perhaps by assuming all integers are aligned by default. Suggestions welcome. The .B snapshot command uses memory inefficiently, and should probably not be used on large programs. .SH HOMEPAGE https://github.com/scanmem/scanmem .SH AUTHORS Tavis Ormandy http://taviso.decsystem.org/ .br Eli Dupree .br WANG Lu .br Sebastian Parschauer .br Andrea Stacchiotti All bug reports, suggestions or feedback welcome. .SH SEE ALSO gameconqueror(1) ptrace(2) proc(5) nethack(6) pidof(8) scanmem-0.17/scanmem.c000066400000000000000000000206331317023271400146570ustar00rootroot00000000000000/* Provide interfaces for front-ends. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009-2013 WANG Lu Copyright (C) 2016 Sebastian Parschauer This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "config.h" #include #include #include #include #include #include #include "scanmem.h" #include "commands.h" #include "handlers.h" #include "show_message.h" void sm_printversion(FILE *outfd) { fprintf(outfd, "libscanmem version %s\n", PACKAGE_VERSION); } /* global settings */ globals_t sm_globals = { 0, /* exit flag */ 0, /* pid target */ NULL, /* matches */ 0, /* match count */ 0, /* scan progress */ false, /* stop flag */ NULL, /* regions */ NULL, /* commands */ NULL, /* current_cmdline */ sm_printversion, /* printversion() pointer */ /* options */ { 1, /* alignment */ 0, /* debug */ 0, /* backend */ ANYINTEGER, /* scan_data_type */ REGION_HEAP_STACK_EXECUTABLE_BSS, /* region_detail_level */ 1, /* dump_with_ascii */ 0, /* reverse_endianness */ } }; /* signal handler - use async-signal safe functions ONLY! */ static void sighandler(int n) { const char err_msg[] = "error: \nKilled by signal "; const char msg_end[] = ".\n"; char num_str[4] = {0}; ssize_t num_size; ssize_t wbytes; wbytes = write(STDERR_FILENO, err_msg, sizeof(err_msg) - 1); if (wbytes != sizeof(err_msg) - 1) goto out; /* manual int to str conversion */ if (n < 10) { num_str[0] = (char) (0x30 + n); num_size = 1; } else if (n >= 100) { goto out; } else { num_str[0] = (char) (0x30 + n / 10); num_str[1] = (char) (0x30 + n % 10); num_size = 2; } wbytes = write(STDERR_FILENO, num_str, num_size); if (wbytes != num_size) goto out; wbytes = write(STDERR_FILENO, msg_end, sizeof(msg_end) - 1); if (wbytes != sizeof(msg_end) - 1) goto out; out: _exit(EXIT_FAILURE); /* also detaches from tracee */ } bool sm_init(void) { globals_t *vars = &sm_globals; /* before attaching to target, install signal handler to detach on error */ if (vars->options.debug == 0) /* in debug mode, let it crash and see the core dump */ { (void) signal(SIGHUP, sighandler); (void) signal(SIGINT, sighandler); (void) signal(SIGSEGV, sighandler); (void) signal(SIGABRT, sighandler); (void) signal(SIGILL, sighandler); (void) signal(SIGFPE, sighandler); (void) signal(SIGTERM, sighandler); } /* linked list of commands and function pointers to their handlers */ if ((vars->commands = l_init()) == NULL) { show_error("sorry, there was a memory allocation error.\n"); return false; } /* NULL shortdoc means don't display this command in `help` listing */ sm_registercommand("set", handler__set, vars->commands, SET_SHRTDOC, SET_LONGDOC); sm_registercommand("list", handler__list, vars->commands, LIST_SHRTDOC, LIST_LONGDOC); sm_registercommand("delete", handler__delete, vars->commands, DELETE_SHRTDOC, DELETE_LONGDOC); sm_registercommand("reset", handler__reset, vars->commands, RESET_SHRTDOC, RESET_LONGDOC); sm_registercommand("pid", handler__pid, vars->commands, PID_SHRTDOC, PID_LONGDOC); sm_registercommand("snapshot", handler__snapshot, vars->commands, SNAPSHOT_SHRTDOC, SNAPSHOT_LONGDOC); sm_registercommand("dregion", handler__dregion, vars->commands, DREGION_SHRTDOC, DREGION_LONGDOC); sm_registercommand("dregions", handler__dregion, vars->commands, NULL, DREGION_LONGDOC); sm_registercommand("lregions", handler__lregions, vars->commands, LREGIONS_SHRTDOC, LREGIONS_LONGDOC); sm_registercommand("version", handler__version, vars->commands, VERSION_SHRTDOC, VERSION_LONGDOC); sm_registercommand("=", handler__operators, vars->commands, NOTCHANGED_SHRTDOC, NOTCHANGED_LONGDOC); sm_registercommand("!=", handler__operators, vars->commands, CHANGED_SHRTDOC, CHANGED_LONGDOC); sm_registercommand("<", handler__operators, vars->commands, LESSTHAN_SHRTDOC, LESSTHAN_LONGDOC); sm_registercommand(">", handler__operators, vars->commands, GREATERTHAN_SHRTDOC, GREATERTHAN_LONGDOC); sm_registercommand("+", handler__operators, vars->commands, INCREASED_SHRTDOC, INCREASED_LONGDOC); sm_registercommand("-", handler__operators, vars->commands, DECREASED_SHRTDOC, DECREASED_LONGDOC); sm_registercommand("\"", handler__string, vars->commands, STRING_SHRTDOC, STRING_LONGDOC); sm_registercommand("update", handler__update, vars->commands, UPDATE_SHRTDOC, UPDATE_LONGDOC); sm_registercommand("exit", handler__exit, vars->commands, EXIT_SHRTDOC, EXIT_LONGDOC); sm_registercommand("quit", handler__exit, vars->commands, NULL, EXIT_LONGDOC); sm_registercommand("q", handler__exit, vars->commands, NULL, EXIT_LONGDOC); sm_registercommand("help", handler__help, vars->commands, HELP_SHRTDOC, HELP_LONGDOC); sm_registercommand("shell", handler__shell, vars->commands, SHELL_SHRTDOC, SHELL_LONGDOC); sm_registercommand("!", handler__shell, vars->commands, NULL, SHELL_LONGDOC); sm_registercommand("watch", handler__watch, vars->commands, WATCH_SHRTDOC, WATCH_LONGDOC); sm_registercommand("show", handler__show, vars->commands, SHOW_SHRTDOC, SHOW_LONGDOC); sm_registercommand("dump", handler__dump, vars->commands, DUMP_SHRTDOC, DUMP_LONGDOC); sm_registercommand("write", handler__write, vars->commands, WRITE_SHRTDOC, WRITE_LONGDOC); sm_registercommand("option", handler__option, vars->commands, OPTION_SHRTDOC, OPTION_LONGDOC); /* commands beginning with __ have special meaning */ sm_registercommand("__eof", handler__eof, vars->commands, NULL, NULL); /* special value NULL means no other matches */ sm_registercommand(NULL, handler__default, vars->commands, DEFAULT_SHRTDOC, DEFAULT_LONGDOC); return true; } void sm_cleanup(void) { /* free any allocated memory used */ l_destroy(sm_globals.regions); l_destroy(sm_globals.commands); /* free matches array */ if (sm_globals.matches) free(sm_globals.matches); /* attempt to detach just in case */ sm_detach(sm_globals.target); } /* for front-ends */ void sm_set_backend(void) { sm_globals.options.backend = 1; } void sm_backend_exec_cmd(const char *commandline) { sm_execcommand(&sm_globals, commandline); fflush(stdout); fflush(stderr); } unsigned long sm_get_num_matches(void) { return sm_globals.num_matches; } const char *sm_get_version(void) { return PACKAGE_VERSION; } double sm_get_scan_progress(void) { return sm_globals.scan_progress; } void sm_set_stop_flag(bool stop_flag) { sm_globals.stop_flag = stop_flag; } scanmem-0.17/scanmem.h000066400000000000000000000061731317023271400146670ustar00rootroot00000000000000/* Provide interfaces for front-ends. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009-2013 WANG Lu Copyright (C) 2016 Sebastian Parschauer This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef SCANMEM_H #define SCANMEM_H #include #include #include #include #include "scanroutines.h" #include "list.h" #include "maps.h" #include "value.h" #include "targetmem.h" /* global settings */ typedef struct { unsigned exit:1; pid_t target; matches_and_old_values_array *matches; unsigned long num_matches; double scan_progress; bool stop_flag; list_t *regions; list_t *commands; /* command handlers */ const char *current_cmdline; /* the command being executed */ void (*printversion)(FILE *outfd); struct { unsigned short alignment; unsigned short debug; unsigned short backend; /* if 1, scanmem will work as a backend and output will be more machine-readable */ /* options that can be changed during runtime */ scan_data_type_t scan_data_type; region_scan_level_t region_scan_level; unsigned short dump_with_ascii; unsigned short reverse_endianness; } options; } globals_t; /* global settings */ extern globals_t sm_globals; bool sm_init(void); void sm_cleanup(void); void sm_printversion(FILE *outfd); void sm_set_backend(void); void sm_backend_exec_cmd(const char *commandline); unsigned long sm_get_num_matches(void); const char *sm_get_version(void); double sm_get_scan_progress(void); void sm_reset_scan_progress(void); void sm_set_stop_flag(bool stop_flag); /* ptrace.c */ bool sm_detach(pid_t target); bool sm_setaddr(pid_t target, void *addr, const value_t *to); bool sm_checkmatches(globals_t *vars, scan_match_type_t match_type, const uservalue_t *uservalue); bool sm_searchregions(globals_t *vars, scan_match_type_t match_type, const uservalue_t *uservalue); bool sm_peekdata(pid_t pid, const void *addr, uint16_t length, const mem64_t **result_ptr, size_t *memlength); bool sm_attach(pid_t target); bool sm_read_array(pid_t target, const void *addr, char *buf, int len); bool sm_write_array(pid_t target, void *addr, const void *data, int len); #endif /* SCANMEM_H */ scanmem-0.17/scanroutines.c000066400000000000000000000702061317023271400157520ustar00rootroot00000000000000/* Scanning routines for different data types. Copyright (C) 2009,2010 WANG Lu Copyright (C) 2017 Andrea Stacchiotti This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include "scanroutines.h" #include "common.h" #include "endianness.h" #include "value.h" /* for convenience */ #define SCAN_ROUTINE_ARGUMENTS (const mem64_t *memory_ptr, size_t memlength, const value_t *old_value, const uservalue_t *user_value, match_flags *saveflags) unsigned int (*sm_scan_routine) SCAN_ROUTINE_ARGUMENTS; #define MEMORY_COMP(value,field,op) (((value)->flags & flag_##field) && (get_##field(memory_ptr) op get_##field(value))) #define GET_FLAG(valptr, field) ((valptr)->flags & flag_##field) #define SET_FLAG(flagsptr, field) ((*(flagsptr)) |= flag_##field) /********************/ /* Integer specific */ /********************/ /* for MATCHANY */ #define DEFINE_INTEGER_MATCHANY_ROUTINE(DATAWIDTH) \ extern inline unsigned int scan_routine_INTEGER##DATAWIDTH##_ANY SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength >= (DATAWIDTH)/8) { \ SET_FLAG(saveflags, s##DATAWIDTH##b); \ SET_FLAG(saveflags, u##DATAWIDTH##b); \ return (DATAWIDTH)/8; \ } \ else { \ return 0; \ } \ } DEFINE_INTEGER_MATCHANY_ROUTINE( 8) DEFINE_INTEGER_MATCHANY_ROUTINE(16) DEFINE_INTEGER_MATCHANY_ROUTINE(32) DEFINE_INTEGER_MATCHANY_ROUTINE(64) #define DEFINE_INTEGER_MATCHUPDATE_ROUTINE(DATAWIDTH) \ extern inline unsigned int scan_routine_INTEGER##DATAWIDTH##_UPDATE SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength < (DATAWIDTH)/8) return 0; \ int ret = 0; \ if (GET_FLAG(old_value, s##DATAWIDTH##b)) { ret = (DATAWIDTH)/8; SET_FLAG(saveflags, s##DATAWIDTH##b); } \ if (GET_FLAG(old_value, u##DATAWIDTH##b)) { ret = (DATAWIDTH)/8; SET_FLAG(saveflags, u##DATAWIDTH##b); } \ return ret; \ } DEFINE_INTEGER_MATCHUPDATE_ROUTINE( 8) DEFINE_INTEGER_MATCHUPDATE_ROUTINE(16) DEFINE_INTEGER_MATCHUPDATE_ROUTINE(32) DEFINE_INTEGER_MATCHUPDATE_ROUTINE(64) #define DEFINE_INTEGER_ROUTINE(DATAWIDTH, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, REVENDIAN, REVEND_STR) \ extern inline unsigned int scan_routine_INTEGER##DATAWIDTH##_##MATCHTYPENAME##REVEND_STR SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength < (DATAWIDTH)/8) return 0; \ int ret = 0; \ mem64_t val; \ if (REVENDIAN) { \ val.uint##DATAWIDTH##_value = swap_bytes##DATAWIDTH (memory_ptr->uint##DATAWIDTH##_value); \ memory_ptr = &val; \ } \ if (MEMORY_COMP(VALUE_TO_COMPARE_WITH, s##DATAWIDTH##b, MATCHTYPE)) { \ SET_FLAG(saveflags, s##DATAWIDTH##b); \ ret = (DATAWIDTH)/8; \ } \ if (MEMORY_COMP(VALUE_TO_COMPARE_WITH, u##DATAWIDTH##b, MATCHTYPE)) { \ SET_FLAG(saveflags, u##DATAWIDTH##b); \ ret = (DATAWIDTH)/8; \ } \ return ret; \ } #define DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES(MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH) \ DEFINE_INTEGER_ROUTINE( 8, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 0, ) \ DEFINE_INTEGER_ROUTINE(16, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 0, ) \ DEFINE_INTEGER_ROUTINE(32, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 0, ) \ DEFINE_INTEGER_ROUTINE(64, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 0, ) #define DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES_AND_ENDIANS(MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH) \ DEFINE_INTEGER_ROUTINE( 8, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 0, ) \ DEFINE_INTEGER_ROUTINE(16, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 0, ) \ DEFINE_INTEGER_ROUTINE(32, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 0, ) \ DEFINE_INTEGER_ROUTINE(64, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 0, ) \ DEFINE_INTEGER_ROUTINE(16, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 1, _REVENDIAN) \ DEFINE_INTEGER_ROUTINE(32, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 1, _REVENDIAN) \ DEFINE_INTEGER_ROUTINE(64, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 1, _REVENDIAN) DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES_AND_ENDIANS(EQUALTO, ==, user_value) DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES_AND_ENDIANS(NOTEQUALTO, !=, user_value) DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES_AND_ENDIANS(GREATERTHAN, >, user_value) DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES_AND_ENDIANS(LESSTHAN, <, user_value) DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES(NOTCHANGED, ==, old_value) DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES(CHANGED, !=, old_value) DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES(INCREASED, >, old_value) DEFINE_INTEGER_ROUTINE_FOR_ALL_INTEGER_TYPES(DECREASED, <, old_value) /******************/ /* Float specific */ /******************/ /* for MATCHANY */ #define DEFINE_FLOAT_MATCHANY_ROUTINE(DATAWIDTH) \ extern inline unsigned int scan_routine_FLOAT##DATAWIDTH##_ANY SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength >= (DATAWIDTH)/8) { \ SET_FLAG(saveflags, f##DATAWIDTH##b); \ return (DATAWIDTH)/8; \ } \ else { \ return 0; \ } \ } DEFINE_FLOAT_MATCHANY_ROUTINE(32) DEFINE_FLOAT_MATCHANY_ROUTINE(64) #define DEFINE_FLOAT_MATCHUPDATE_ROUTINE(DATAWIDTH) \ extern inline unsigned int scan_routine_FLOAT##DATAWIDTH##_UPDATE SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength < (DATAWIDTH)/8) return 0; \ int ret = 0; \ if (GET_FLAG(old_value, f##DATAWIDTH##b)) { ret = (DATAWIDTH)/8; SET_FLAG(saveflags, f##DATAWIDTH##b); } \ return ret; \ } DEFINE_FLOAT_MATCHUPDATE_ROUTINE(32) DEFINE_FLOAT_MATCHUPDATE_ROUTINE(64) #define DEFINE_FLOAT_ROUTINE(DATAWIDTH, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, REVENDIAN, REVEND_STR) \ extern inline unsigned int scan_routine_FLOAT##DATAWIDTH##_##MATCHTYPENAME##REVEND_STR SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength < (DATAWIDTH)/8) return 0; \ int ret = 0; \ mem64_t val; \ if (REVENDIAN) { \ val.uint##DATAWIDTH##_value = swap_bytes##DATAWIDTH (memory_ptr->uint##DATAWIDTH##_value); \ memory_ptr = &val; \ } \ if (MEMORY_COMP(VALUE_TO_COMPARE_WITH, f##DATAWIDTH##b, MATCHTYPE)) { \ SET_FLAG(saveflags,f##DATAWIDTH##b); \ ret = (DATAWIDTH)/8; \ } \ return ret; \ } #define DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES(MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, REVENDIAN, REVEND_STR) \ DEFINE_FLOAT_ROUTINE(32, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, REVENDIAN, REVEND_STR) \ DEFINE_FLOAT_ROUTINE(64, MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, REVENDIAN, REVEND_STR) #define DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES_AND_ENDIANS(MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH) \ DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES(MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 0, ) \ DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES(MATCHTYPENAME, MATCHTYPE, VALUE_TO_COMPARE_WITH, 1, _REVENDIAN) DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES_AND_ENDIANS(EQUALTO, ==, user_value) DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES_AND_ENDIANS(NOTEQUALTO, !=, user_value) DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES_AND_ENDIANS(GREATERTHAN, >, user_value) DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES_AND_ENDIANS(LESSTHAN, <, user_value) DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES(NOTCHANGED, ==, old_value, 0, ) DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES(CHANGED, !=, old_value, 0, ) DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES(INCREASED, >, old_value, 0, ) DEFINE_FLOAT_ROUTINE_FOR_ALL_FLOAT_TYPES(DECREASED, <, old_value, 0, ) /********************/ /* Special routines */ /********************/ /*---------------------------------*/ /* for INCREASEDBY and DECREASEDBY */ /*---------------------------------*/ #define DEFINE_INTEGER_OPERATIONBY_ROUTINE(DATAWIDTH, NAME, OP) \ extern inline unsigned int scan_routine_INTEGER##DATAWIDTH##_##NAME SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength < (DATAWIDTH)/8) return 0; \ int ret = 0; \ if ((GET_FLAG(old_value, s##DATAWIDTH##b)) && (GET_FLAG(user_value, s##DATAWIDTH##b)) && \ (get_s##DATAWIDTH##b(memory_ptr) == get_s##DATAWIDTH##b(old_value) OP get_s##DATAWIDTH##b(user_value))) \ { ret = (DATAWIDTH)/8; SET_FLAG(saveflags, s##DATAWIDTH##b); } \ if ((GET_FLAG(old_value, u##DATAWIDTH##b)) && (GET_FLAG(user_value, u##DATAWIDTH##b)) && \ (get_u##DATAWIDTH##b(memory_ptr) == get_u##DATAWIDTH##b(old_value) OP get_u##DATAWIDTH##b(user_value))) \ { ret = (DATAWIDTH)/8; SET_FLAG(saveflags, u##DATAWIDTH##b); } \ return ret; \ } #define DEFINE_INTEGER_INCREASEDBY_DECREASEDBY_ROUTINE(DATAWIDTH) \ DEFINE_INTEGER_OPERATIONBY_ROUTINE(DATAWIDTH, INCREASEDBY, +) \ DEFINE_INTEGER_OPERATIONBY_ROUTINE(DATAWIDTH, DECREASEDBY, -) DEFINE_INTEGER_INCREASEDBY_DECREASEDBY_ROUTINE( 8) DEFINE_INTEGER_INCREASEDBY_DECREASEDBY_ROUTINE(16) DEFINE_INTEGER_INCREASEDBY_DECREASEDBY_ROUTINE(32) DEFINE_INTEGER_INCREASEDBY_DECREASEDBY_ROUTINE(64) #define DEFINE_FLOAT_OPERATIONBY_ROUTINE(DATAWIDTH, NAME, OP) \ extern inline unsigned int scan_routine_FLOAT##DATAWIDTH##_##NAME SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength < (DATAWIDTH)/8) return 0; \ int ret = 0; \ if ((GET_FLAG(old_value, f##DATAWIDTH##b)) && (GET_FLAG(user_value, f##DATAWIDTH##b)) && \ (get_f##DATAWIDTH##b(memory_ptr) == get_f##DATAWIDTH##b(old_value) OP get_f##DATAWIDTH##b(user_value))) \ { ret = (DATAWIDTH)/8; SET_FLAG(saveflags, f##DATAWIDTH##b); } \ return ret; \ } #define DEFINE_FLOAT_INCREASEDBY_DECREASEDBY_ROUTINE(DATAWIDTH) \ DEFINE_FLOAT_OPERATIONBY_ROUTINE(DATAWIDTH, INCREASEDBY, +) \ DEFINE_FLOAT_OPERATIONBY_ROUTINE(DATAWIDTH, DECREASEDBY, -) DEFINE_FLOAT_INCREASEDBY_DECREASEDBY_ROUTINE(32) DEFINE_FLOAT_INCREASEDBY_DECREASEDBY_ROUTINE(64) /*-----------*/ /* for RANGE */ /*-----------*/ #define DEFINE_INTEGER_RANGE_ROUTINE(DATAWIDTH, REVENDIAN, REVEND_STR) \ extern inline unsigned int scan_routine_INTEGER##DATAWIDTH##_RANGE##REVEND_STR SCAN_ROUTINE_ARGUMENTS \ { \ int ret = 0; \ mem64_t val; \ if (REVENDIAN) { \ val.uint##DATAWIDTH##_value = swap_bytes##DATAWIDTH (memory_ptr->uint##DATAWIDTH##_value); \ memory_ptr = &val; \ } \ if ((memlength >= (DATAWIDTH)/8) \ && (user_value[0].flags & flag_s##DATAWIDTH##b) \ && (get_s##DATAWIDTH##b(memory_ptr) >= get_s##DATAWIDTH##b(&user_value[0])) \ && (get_s##DATAWIDTH##b(memory_ptr) <= get_s##DATAWIDTH##b(&user_value[1]))) \ { ret = (DATAWIDTH)/8; SET_FLAG(saveflags, s##DATAWIDTH##b); } \ if ((memlength >= (DATAWIDTH)/8) \ && (user_value[0].flags & flag_u##DATAWIDTH##b) \ && (get_u##DATAWIDTH##b(memory_ptr) >= get_u##DATAWIDTH##b(&user_value[0])) \ && (get_u##DATAWIDTH##b(memory_ptr) <= get_u##DATAWIDTH##b(&user_value[1]))) \ { ret = (DATAWIDTH)/8; SET_FLAG(saveflags, u##DATAWIDTH##b); } \ return ret; \ } DEFINE_INTEGER_RANGE_ROUTINE( 8, 0, ) DEFINE_INTEGER_RANGE_ROUTINE(16, 0, ) DEFINE_INTEGER_RANGE_ROUTINE(16, 1, _REVENDIAN) DEFINE_INTEGER_RANGE_ROUTINE(32, 0, ) DEFINE_INTEGER_RANGE_ROUTINE(32, 1, _REVENDIAN) DEFINE_INTEGER_RANGE_ROUTINE(64, 0, ) DEFINE_INTEGER_RANGE_ROUTINE(64, 1, _REVENDIAN) #define DEFINE_FLOAT_RANGE_ROUTINE(DATAWIDTH, REVENDIAN, REVEND_STR) \ extern inline unsigned int scan_routine_FLOAT##DATAWIDTH##_RANGE##REVEND_STR SCAN_ROUTINE_ARGUMENTS \ { \ int ret = 0; \ mem64_t val; \ if (REVENDIAN) { \ val.uint##DATAWIDTH##_value = swap_bytes##DATAWIDTH (memory_ptr->uint##DATAWIDTH##_value); \ memory_ptr = &val; \ } \ if ((memlength >= (DATAWIDTH)/8) \ && (user_value[0].flags & flag_f##DATAWIDTH##b) \ && (get_f##DATAWIDTH##b(memory_ptr) >= get_f##DATAWIDTH##b(&user_value[0])) \ && (get_f##DATAWIDTH##b(memory_ptr) <= get_f##DATAWIDTH##b(&user_value[1]))) \ { ret = (DATAWIDTH)/8; SET_FLAG(saveflags, f##DATAWIDTH##b); } \ return ret; \ } DEFINE_FLOAT_RANGE_ROUTINE(32, 0, ) DEFINE_FLOAT_RANGE_ROUTINE(32, 1, _REVENDIAN) DEFINE_FLOAT_RANGE_ROUTINE(64, 0, ) DEFINE_FLOAT_RANGE_ROUTINE(64, 1, _REVENDIAN) /*------------------------*/ /* Any-xxx types specific */ /*------------------------*/ /* this is for anynumber, anyinteger, anyfloat */ #define DEFINE_ANYTYPE_ROUTINE(MATCHTYPENAME, REVEND_STR) \ extern inline unsigned int scan_routine_ANYINTEGER_##MATCHTYPENAME##REVEND_STR SCAN_ROUTINE_ARGUMENTS \ { \ int ret = scan_routine_INTEGER8_##MATCHTYPENAME (memory_ptr, memlength, old_value, user_value, saveflags); \ int tmp_ret; \ if ((tmp_ret = scan_routine_INTEGER16_##MATCHTYPENAME##REVEND_STR (memory_ptr, memlength, old_value, user_value, saveflags)) > ret) { ret = tmp_ret; } \ if ((tmp_ret = scan_routine_INTEGER32_##MATCHTYPENAME##REVEND_STR (memory_ptr, memlength, old_value, user_value, saveflags)) > ret) { ret = tmp_ret; } \ if ((tmp_ret = scan_routine_INTEGER64_##MATCHTYPENAME##REVEND_STR (memory_ptr, memlength, old_value, user_value, saveflags)) > ret) { ret = tmp_ret; } \ return ret; \ } \ extern inline unsigned int scan_routine_ANYFLOAT_##MATCHTYPENAME##REVEND_STR SCAN_ROUTINE_ARGUMENTS \ { \ int ret = scan_routine_FLOAT32_##MATCHTYPENAME##REVEND_STR (memory_ptr, memlength, old_value, user_value, saveflags); \ int tmp_ret; \ if ((tmp_ret = scan_routine_FLOAT64_##MATCHTYPENAME##REVEND_STR (memory_ptr, memlength, old_value, user_value, saveflags)) > ret) { ret = tmp_ret; } \ return ret; \ } \ extern inline unsigned int scan_routine_ANYNUMBER_##MATCHTYPENAME##REVEND_STR SCAN_ROUTINE_ARGUMENTS \ { \ int ret1 = scan_routine_ANYINTEGER_##MATCHTYPENAME##REVEND_STR (memory_ptr, memlength, old_value, user_value, saveflags); \ int ret2 = scan_routine_ANYFLOAT_##MATCHTYPENAME##REVEND_STR (memory_ptr, memlength, old_value, user_value, saveflags); \ return (ret1 > ret2 ? ret1 : ret2); \ } \ DEFINE_ANYTYPE_ROUTINE(ANY, ) DEFINE_ANYTYPE_ROUTINE(UPDATE, ) DEFINE_ANYTYPE_ROUTINE(EQUALTO, ) DEFINE_ANYTYPE_ROUTINE(NOTEQUALTO, ) DEFINE_ANYTYPE_ROUTINE(CHANGED, ) DEFINE_ANYTYPE_ROUTINE(NOTCHANGED, ) DEFINE_ANYTYPE_ROUTINE(INCREASED, ) DEFINE_ANYTYPE_ROUTINE(DECREASED, ) DEFINE_ANYTYPE_ROUTINE(GREATERTHAN, ) DEFINE_ANYTYPE_ROUTINE(LESSTHAN, ) DEFINE_ANYTYPE_ROUTINE(INCREASEDBY, ) DEFINE_ANYTYPE_ROUTINE(DECREASEDBY, ) DEFINE_ANYTYPE_ROUTINE(RANGE, ) DEFINE_ANYTYPE_ROUTINE(EQUALTO, _REVENDIAN) DEFINE_ANYTYPE_ROUTINE(NOTEQUALTO, _REVENDIAN) DEFINE_ANYTYPE_ROUTINE(GREATERTHAN, _REVENDIAN) DEFINE_ANYTYPE_ROUTINE(LESSTHAN, _REVENDIAN) DEFINE_ANYTYPE_ROUTINE(RANGE, _REVENDIAN) /*----------------------------------------*/ /* for generic VLT (Variable Length Type) */ /*----------------------------------------*/ extern inline unsigned int scan_routine_VLT_ANY SCAN_ROUTINE_ARGUMENTS { return *saveflags = MIN(memlength, (uint16_t)(-1)); } extern inline unsigned int scan_routine_VLT_UPDATE SCAN_ROUTINE_ARGUMENTS { /* memlength here is already MIN(memlength, old_value->flags.length) */ return *saveflags = memlength; } /*---------------*/ /* for BYTEARRAY */ /*---------------*/ /* Used only for length>8 */ extern inline unsigned int scan_routine_BYTEARRAY_EQUALTO SCAN_ROUTINE_ARGUMENTS { const uint8_t *bytes_array = user_value->bytearray_value; const wildcard_t *wildcards_array = user_value->wildcard_value; uint length = user_value->flags; if (memlength < length || *((uint64_t*)bytes_array) != (memory_ptr->uint64_value & *((uint64_t*)wildcards_array))) { /* not matched */ return 0; } unsigned int i, j; for(i = sizeof(uint64_t); i + sizeof(uint64_t) <= length; i += sizeof(uint64_t)) { if (*((uint64_t*)(bytes_array+i)) != (((mem64_t*)(memory_ptr->bytes+i))->uint64_value & *((uint64_t*)(wildcards_array+i)))) { /* not matched */ return 0; } } /* match bytes left */ if (i < length) { for(j = 0; j < length - i; ++j) { if ((bytes_array+i)[j] != (((mem64_t*)(memory_ptr->bytes+i))->bytes[j] & (wildcards_array+i)[j])) { /* not matched */ return 0; } } } /* matched */ *saveflags = length; return length; } /* optimized routines for small lengths careful: WIDTH = 8*LENGTH */ #define DEFINE_BYTEARRAY_POW2_EQUALTO_ROUTINE(WIDTH) \ extern inline unsigned int scan_routine_BYTEARRAY##WIDTH##_EQUALTO SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength >= (WIDTH)/8 && \ ((get_u##WIDTH##b(memory_ptr) & *(uint##WIDTH##_t*)user_value->wildcard_value) \ == *(uint##WIDTH##_t*)(user_value->bytearray_value))) \ { \ /* matched */ \ *saveflags = (WIDTH)/8; \ return (WIDTH)/8; \ } \ else \ { \ /* not matched */ \ return 0; \ } \ } DEFINE_BYTEARRAY_POW2_EQUALTO_ROUTINE(8) DEFINE_BYTEARRAY_POW2_EQUALTO_ROUTINE(16) DEFINE_BYTEARRAY_POW2_EQUALTO_ROUTINE(32) DEFINE_BYTEARRAY_POW2_EQUALTO_ROUTINE(64) #define DEFINE_BYTEARRAY_SMALLOOP_EQUALTO_ROUTINE(WIDTH) \ extern inline unsigned int scan_routine_BYTEARRAY##WIDTH##_EQUALTO SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength < (WIDTH)/8) \ { \ /* new_value is not actually a valid bytearray */ \ return 0; \ } \ const uint8_t *bytes_array = user_value->bytearray_value; \ const wildcard_t *wildcards_array = user_value->wildcard_value; \ int i; \ for(i = 0; i < (WIDTH)/8; ++i) \ { \ if(bytes_array[i] != (memory_ptr->bytes[i] & wildcards_array[i])) \ { \ /* not matched */ \ return 0; \ } \ } \ /* matched */ \ *saveflags = (WIDTH)/8; \ return (WIDTH)/8; \ } DEFINE_BYTEARRAY_SMALLOOP_EQUALTO_ROUTINE(24) DEFINE_BYTEARRAY_SMALLOOP_EQUALTO_ROUTINE(40) DEFINE_BYTEARRAY_SMALLOOP_EQUALTO_ROUTINE(48) DEFINE_BYTEARRAY_SMALLOOP_EQUALTO_ROUTINE(56) /*------------*/ /* for STRING */ /*------------*/ /* Used only for length>8 */ extern inline unsigned int scan_routine_STRING_EQUALTO SCAN_ROUTINE_ARGUMENTS { const char *scan_string = user_value->string_value; uint length = user_value->flags; if(memlength < length || memory_ptr->int64_value != *((int64_t*)scan_string)) { /* not matched */ return 0; } unsigned int i, j; for(i = sizeof(int64_t); i + sizeof(int64_t) <= length; i += sizeof(int64_t)) { if(((mem64_t*)(memory_ptr->chars+i))->int64_value != *((int64_t*)(scan_string+i))) { /* not matched */ return 0; } } /* match bytes left */ if (i < length) { for(j = 0; j < length - i; ++j) { if(((mem64_t*)(memory_ptr->chars+i))->chars[j] != (scan_string+i)[j]) { /* not matched */ return 0; } } } /* matched */ *saveflags = length; return length; } /* optimized routines for small strings careful: WIDTH = 8*LENGTH */ #define DEFINE_STRING_POW2_EQUALTO_ROUTINE(WIDTH) \ extern inline unsigned int scan_routine_STRING##WIDTH##_EQUALTO SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength >= (WIDTH)/8 && \ (get_s##WIDTH##b(memory_ptr) == *(int##WIDTH##_t*)(user_value->string_value))) \ { \ /* matched */ \ *saveflags = (WIDTH)/8; \ return (WIDTH)/8; \ } \ else \ { \ /* not matched */ \ return 0; \ } \ } DEFINE_STRING_POW2_EQUALTO_ROUTINE(8) DEFINE_STRING_POW2_EQUALTO_ROUTINE(16) DEFINE_STRING_POW2_EQUALTO_ROUTINE(32) DEFINE_STRING_POW2_EQUALTO_ROUTINE(64) #define DEFINE_STRING_SMALLOOP_EQUALTO_ROUTINE(WIDTH) \ extern inline unsigned int scan_routine_STRING##WIDTH##_EQUALTO SCAN_ROUTINE_ARGUMENTS \ { \ if (memlength < (WIDTH)/8) \ { \ /* new_value is not actually a valid string */ \ return 0; \ } \ const char *scan_string = user_value->string_value; \ int i; \ for(i = 0; i < (WIDTH)/8; ++i) \ { \ if(memory_ptr->chars[i] != scan_string[i]) \ { \ /* not matched */ \ return 0; \ } \ } \ /* matched */ \ *saveflags = (WIDTH)/8; \ return (WIDTH)/8; \ } DEFINE_STRING_SMALLOOP_EQUALTO_ROUTINE(24) DEFINE_STRING_SMALLOOP_EQUALTO_ROUTINE(40) DEFINE_STRING_SMALLOOP_EQUALTO_ROUTINE(48) DEFINE_STRING_SMALLOOP_EQUALTO_ROUTINE(56) /***************************************************************/ /* choose a routine according to scan_data_type and match_type */ /***************************************************************/ #define CHOOSE_ROUTINE(SCANDATATYPE, ROUTINEDATATYPENAME, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ if ((dt == SCANDATATYPE) && (mt == SCANMATCHTYPE)) \ { \ return &scan_routine_##ROUTINEDATATYPENAME##_##ROUTINEMATCHTYPENAME; \ } #define CHOOSE_ROUTINE_FOR_BOTH_ENDIANS(SCANDATATYPE, ROUTINEDATATYPENAME, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ if ((dt == SCANDATATYPE) && (mt == SCANMATCHTYPE)) { \ if (reverse_endianness) { \ return &scan_routine_##ROUTINEDATATYPENAME##_##ROUTINEMATCHTYPENAME##_REVENDIAN; \ } \ else { \ return &scan_routine_##ROUTINEDATATYPENAME##_##ROUTINEMATCHTYPENAME; \ } \ } #define CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES(SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(INTEGER8, INTEGER8, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(INTEGER16, INTEGER16, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(INTEGER32, INTEGER32, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(INTEGER64, INTEGER64, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(FLOAT32, FLOAT32, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(FLOAT64, FLOAT64, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(ANYINTEGER, ANYINTEGER, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(ANYFLOAT, ANYFLOAT, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(ANYNUMBER, ANYNUMBER, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) #define CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES_AND_ENDIANS(SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE(INTEGER8, INTEGER8, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE_FOR_BOTH_ENDIANS(INTEGER16, INTEGER16, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE_FOR_BOTH_ENDIANS(INTEGER32, INTEGER32, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE_FOR_BOTH_ENDIANS(INTEGER64, INTEGER64, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE_FOR_BOTH_ENDIANS(FLOAT32, FLOAT32, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE_FOR_BOTH_ENDIANS(FLOAT64, FLOAT64, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE_FOR_BOTH_ENDIANS(ANYINTEGER, ANYINTEGER, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE_FOR_BOTH_ENDIANS(ANYFLOAT, ANYFLOAT, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) \ CHOOSE_ROUTINE_FOR_BOTH_ENDIANS(ANYNUMBER, ANYNUMBER, SCANMATCHTYPE, ROUTINEMATCHTYPENAME) #define SELECTION_CASE(ROUTINEDATATYPENAME, WIDTH, ROUTINEMATCHTYPENAME) \ case (WIDTH): \ return &scan_routine_##ROUTINEDATATYPENAME##WIDTH##_##ROUTINEMATCHTYPENAME; \ break; \ #define CHOOSE_ROUTINE_VLT(SCANDATATYPE, ROUTINEDATATYPENAME, SCANMATCHTYPE, ROUTINEMATCHTYPENAME, WIDTH) \ if ((dt == SCANDATATYPE) && (mt == SCANMATCHTYPE)) \ { \ switch (WIDTH) \ { \ case 0: \ assert(false); \ break; \ SELECTION_CASE(ROUTINEDATATYPENAME, 8, ROUTINEMATCHTYPENAME) \ SELECTION_CASE(ROUTINEDATATYPENAME, 16, ROUTINEMATCHTYPENAME) \ SELECTION_CASE(ROUTINEDATATYPENAME, 24, ROUTINEMATCHTYPENAME) \ SELECTION_CASE(ROUTINEDATATYPENAME, 32, ROUTINEMATCHTYPENAME) \ SELECTION_CASE(ROUTINEDATATYPENAME, 40, ROUTINEMATCHTYPENAME) \ SELECTION_CASE(ROUTINEDATATYPENAME, 48, ROUTINEMATCHTYPENAME) \ SELECTION_CASE(ROUTINEDATATYPENAME, 56, ROUTINEMATCHTYPENAME) \ SELECTION_CASE(ROUTINEDATATYPENAME, 64, ROUTINEMATCHTYPENAME) \ default: \ return &scan_routine_##ROUTINEDATATYPENAME##_##ROUTINEMATCHTYPENAME; \ break; \ } \ } scan_routine_t sm_get_scanroutine(scan_data_type_t dt, scan_match_type_t mt, match_flags uflags, bool reverse_endianness) { CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES(MATCHANY, ANY) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES(MATCHUPDATE, UPDATE) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES_AND_ENDIANS(MATCHEQUALTO, EQUALTO) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES_AND_ENDIANS(MATCHNOTEQUALTO, NOTEQUALTO) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES_AND_ENDIANS(MATCHGREATERTHAN, GREATERTHAN) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES_AND_ENDIANS(MATCHLESSTHAN, LESSTHAN) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES(MATCHCHANGED, CHANGED) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES(MATCHNOTCHANGED, NOTCHANGED) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES(MATCHINCREASED, INCREASED) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES(MATCHDECREASED, DECREASED) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES(MATCHINCREASEDBY, INCREASEDBY) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES(MATCHDECREASEDBY, DECREASEDBY) CHOOSE_ROUTINE_FOR_ALL_NUMBER_TYPES_AND_ENDIANS(MATCHRANGE, RANGE) CHOOSE_ROUTINE(BYTEARRAY, VLT, MATCHANY, ANY) CHOOSE_ROUTINE(BYTEARRAY, VLT, MATCHUPDATE, UPDATE) CHOOSE_ROUTINE_VLT(BYTEARRAY, BYTEARRAY, MATCHEQUALTO, EQUALTO, uflags*8) CHOOSE_ROUTINE(STRING, VLT, MATCHANY, ANY) CHOOSE_ROUTINE(STRING, VLT, MATCHUPDATE, UPDATE) CHOOSE_ROUTINE_VLT(STRING, STRING, MATCHEQUALTO, EQUALTO, uflags*8) return NULL; } /* Possible flags per scan data type: if an incoming uservalue has none of the * listed flags we're sure it's not going to be matched by the scan, * so we reject it without even trying */ static match_flags possible_flags_for_scan_data_type[] = { [ANYNUMBER] = flags_all, [ANYINTEGER] = flags_integer, [ANYFLOAT] = flags_float, [INTEGER8] = flags_i8b, [INTEGER16] = flags_i16b, [INTEGER32] = flags_i32b, [INTEGER64] = flags_i64b, [FLOAT32] = flag_f32b, [FLOAT64] = flag_f64b, [BYTEARRAY] = flags_max, [STRING] = flags_max }; bool sm_choose_scanroutine(scan_data_type_t dt, scan_match_type_t mt, const uservalue_t* uval, bool reverse_endianness) { match_flags uflags = uval ? uval->flags : flags_empty; /* Check scans that need an uservalue */ if (mt == MATCHEQUALTO || mt == MATCHNOTEQUALTO || mt == MATCHGREATERTHAN || mt == MATCHLESSTHAN || mt == MATCHRANGE || mt == MATCHINCREASEDBY || mt == MATCHDECREASEDBY) { match_flags possible_flags = possible_flags_for_scan_data_type[dt]; if ((possible_flags & uflags) == flags_empty) { /* There's no possibility to have a match, just abort */ sm_scan_routine = NULL; return false; } } sm_scan_routine = sm_get_scanroutine(dt, mt, uflags, reverse_endianness); return (sm_scan_routine != NULL); } scanmem-0.17/scanroutines.h000066400000000000000000000054361317023271400157620ustar00rootroot00000000000000/* Definition of routines of scanning for different data types. Copyright (C) 2009,2010 WANG Lu Copyright (C) 2015 Vyacheslav Shegai This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef SCANROUTINES_H #define SCANROUTINES_H #include #include "value.h" typedef enum { ANYNUMBER, /* ANYINTEGER or ANYFLOAT */ ANYINTEGER, /* INTEGER of whatever width */ ANYFLOAT, /* FLOAT of whatever width */ INTEGER8, INTEGER16, INTEGER32, INTEGER64, FLOAT32, FLOAT64, BYTEARRAY, STRING } scan_data_type_t; typedef enum { MATCHANY, /* for snapshot */ /* following: compare with a given value */ MATCHEQUALTO, MATCHNOTEQUALTO, MATCHGREATERTHAN, MATCHLESSTHAN, MATCHRANGE, /* following: compare with the old value */ MATCHUPDATE, MATCHNOTCHANGED, MATCHCHANGED, MATCHINCREASED, MATCHDECREASED, /* following: compare with both given value and old value */ MATCHINCREASEDBY, MATCHDECREASEDBY } scan_match_type_t; /* Matches a memory area given by `memory_ptr` and `memlength` against `user_value` or `old_value` * (or both, depending on the matching type), stores the result into saveflags. * NOTE: saveflags must be set to 0, since only useful bits are set, but extra bits are not cleared! * Returns the number of bytes needed to store said match, 0 for not matched */ typedef unsigned int (*scan_routine_t)(const mem64_t *memory_ptr, size_t memlength, const value_t *old_value, const uservalue_t *user_value, match_flags *saveflags); extern scan_routine_t sm_scan_routine; /* * Choose the global scanroutine according to the given parameters, sm_scan_routine will be set. * Returns whether a proper routine has been found. */ bool sm_choose_scanroutine(scan_data_type_t dt, scan_match_type_t mt, const uservalue_t* uval, bool reverse_endianness); scan_routine_t sm_get_scanroutine(scan_data_type_t dt, scan_match_type_t mt, match_flags uflags, bool reverse_endianness); #endif /* SCANROUTINES_H */ scanmem-0.17/sets.c000066400000000000000000000250341317023271400142120ustar00rootroot00000000000000/* Create sets. Copyright (C) 2016-2017 Bijan Kazemi This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include #include #include #include #include #include "show_message.h" #include "sets.h" #define DEFAULT_UINTLS_SZ (64) /* * -= The Set Data-Type =- * * A 'set' is a type similar in vein to the mathematical definition of a set. * See: https://foldoc.org/set * * Sets support ranges. See the `delete` longdoc for more information. * * Restrictions of our set data-type: * - No infinite sets. * - No empty sets. * - No duplicate elements. * - All the elements must be of the same type. */ /* * Used with `qsort()`. * * This function uses the less elegant branching solution * to deal with possible overflow. */ static int _size_t_cmp(const void *_i1, const void *_i2) { const size_t i1 = *(const size_t *)_i1, i2 = *(const size_t *)_i2; if (i1 < i2) return -1; else if (i1 == i2) return 0; else return 1; } /* * Function used to grow the set array. * * We must use a separate pointer with realloc(), in case it fails. When that * happens and we re-assign valarr, valarr is _not_ free()'d by realloc(), and * valarr is then assigned to NULL, leaving the original valarr address lost. */ static inline bool inc_arr_sz(size_t **valarr, size_t *arr_maxsz, size_t maxsz) { size_t *valarr_tmpptr; if (*arr_maxsz > maxsz / 2) *arr_maxsz = maxsz; else *arr_maxsz *= 2; if ((valarr_tmpptr = realloc(*valarr, *arr_maxsz * sizeof(size_t))) == NULL) return false; *valarr = valarr_tmpptr; return true; } bool parse_uintset(const char *lptr, struct set *set, size_t maxsz) { const char *tok, *tmpnum = NULL, *tmpnum_end; char *tmpnum_endptr = NULL, *fail_reason = "BUG"; bool got_num, is_hex = false, invert = false; size_t last_num = 0, arr_szfilled, arr_maxsz; size_t *valarr; size_t vaidx = 0; unsigned long long toknum; assert(lptr && set); /* ensure set is initialized */ memset(set, 0, sizeof(*set)); enum { NIL = -1, NUMBER_TOK, RANGE_TOK, COMMA_TOK, } last_type = NIL; /* allocate space for value pointers */ if ((valarr = malloc(DEFAULT_UINTLS_SZ * sizeof(size_t))) == NULL) { show_error("%s(): OOM (Out Of Memory)\n", __func__); return false; } arr_szfilled = 0; arr_maxsz = DEFAULT_UINTLS_SZ; got_num = false; for (tok = lptr; *tok; tok++) { /* skip spaces */ while (tok && isspace(*tok)) tok++; /* check if we just went over trailing space */ if (!tok || !*tok) break; assert(arr_szfilled <= arr_maxsz); // should never fail if (arr_szfilled == arr_maxsz) if (!inc_arr_sz(&valarr, &arr_maxsz, maxsz)) { fail_reason = "OOM (Out Of Memory)"; goto error; } switch (*tok) { case '!': if (last_type != NIL) { fail_reason = "inversion only allowed at beginning of set"; goto error; } invert = true; continue; case ',': if (last_type == RANGE_TOK) { fail_reason = "invalid range"; goto error; } last_type = COMMA_TOK; continue; case '.': if (last_type == COMMA_TOK || last_type == RANGE_TOK) { fail_reason = "invalid range"; goto error; } /* check for consecutive `..` */ if (tok[1] != '.') { fail_reason = "bad token"; goto error; } last_type = RANGE_TOK; tok++; continue; } if (isdigit(*tok)) { /* check for 0x hex prefix */ if (*tok == '0' && tolower(tok[1]) == 'x') { if (!isxdigit(tok[2])) continue; is_hex = true; tok += 2; } /* find the end of this number in string */ tmpnum_end = tok; for ( ; isxdigit(*tmpnum_end); tmpnum_end++) ; tmpnum = strndup(tok, tmpnum_end - tok); errno = 0; toknum = strtoull(tmpnum, &tmpnum_endptr, is_hex ? 16 : 10); if (is_hex) is_hex = false; if (errno || *tmpnum_endptr != '\0') { fail_reason = "strtoull() failed"; goto error; } free((void *)tmpnum); tmpnum = NULL; if (last_type == RANGE_TOK) { if (!got_num) { /* we've got a {0 .. n} range */ last_num = toknum; last_type = NUMBER_TOK; got_num = true; /* move token position to last number found (needed for 2+ digit nums */ tok = tmpnum_end-1; /* bounds check */ if (last_num >= maxsz) { fail_reason = "0..n range OOB (Out Of Bounds)"; goto error; } /* pre-set the new size filled and grow array if necessary */ if ((arr_szfilled += last_num+1) > arr_maxsz) // +1 for `0` while (arr_szfilled > arr_maxsz) { if (!inc_arr_sz(&valarr, &arr_maxsz, maxsz)) { fail_reason = "OOM (Out Of Memory)"; goto error; } } /* fill up array with range */ for (size_t i = 0; i <= last_num; i++) valarr[vaidx++] = i; continue; } if (toknum <= last_num || toknum >= maxsz) { fail_reason = "invalid range"; goto error; } /* pre-set the new size filled and grow array if necessary */ if ((arr_szfilled += (toknum - last_num)) > arr_maxsz) while (arr_szfilled > arr_maxsz) { if (!inc_arr_sz(&valarr, &arr_maxsz, maxsz)) { fail_reason = "OOM (Out Of Memory)"; goto error; } } /* fill up array with range */ for (size_t i = last_num+1; i <= toknum; i++) valarr[vaidx++] = i; } else if (last_type == NUMBER_TOK) { /* sanity check */ fail_reason = "impossible condition (last_type == NUMBER_TOK)"; goto error; } else { valarr[vaidx++] = toknum; arr_szfilled++; } last_num = toknum; last_type = NUMBER_TOK; if (!got_num) got_num = true; /* move token position to last number found (needed for 2+ digit nums) */ tok = tmpnum_end-1; } else { fail_reason = "bad token"; goto error; } } /* check for {n .. end} range */ if (last_type == RANGE_TOK) { if (!got_num) { fail_reason = "invalid range"; goto error; } if (last_num >= maxsz) { fail_reason = "n..end range OOB (Out Of Bounds)"; goto error; } if ((arr_szfilled += (maxsz-1 - last_num)) > arr_maxsz) while (arr_szfilled > arr_maxsz) { if (!inc_arr_sz(&valarr, &arr_maxsz, maxsz)) { fail_reason = "OOM (Out Of Memory)"; goto error; } } /* fill up array with range */ for (size_t i = last_num+1; i < maxsz; i++) valarr[vaidx++] = i; } /* consider empty sets invalid */ if (arr_szfilled == 0) { fail_reason = "empty set"; goto error; } /* sort the value array */ qsort(valarr, arr_szfilled, sizeof(valarr[0]), _size_t_cmp); /* check if there are any duplicates */ for (size_t i = 0; i+1 < arr_szfilled; i++) if (valarr[i] == valarr[i+1]) { fail_reason = "duplicate element"; goto error; } /* check range (for individual entries, faster than checking each one) */ if (valarr[arr_szfilled-1] >= maxsz) { fail_reason = "OOB (Out Of Bounds) element(s)"; goto error; } /* handle inverted sets */ if (invert) { size_t *inv_valarr; if (arr_szfilled == maxsz) { fail_reason = "cannot invert the entire set!"; goto error; } if ((inv_valarr = malloc((maxsz - arr_szfilled) * sizeof(size_t))) == NULL) { fail_reason = "OOM (Out Of Memory)"; goto error; } for (size_t matchid = 0, va_idx = 0, inv_idx = 0; matchid < maxsz; matchid++) { if (va_idx == arr_szfilled || valarr[va_idx] > matchid) inv_valarr[inv_idx++] = matchid; else va_idx++; } free(valarr); valarr = inv_valarr; arr_szfilled = maxsz - arr_szfilled; } else { size_t *tmp_vaptr; if ((tmp_vaptr = realloc(valarr, arr_szfilled * sizeof(size_t))) == NULL) { fail_reason = "couldn't deallocate possibly unused end of the buffer"; goto error; } valarr = tmp_vaptr; } set->buf = valarr; set->size = arr_szfilled; return true; error: show_error("%s(): %s\n", __func__, fail_reason); free((void *)tmpnum); free(valarr); set_cleanup(set); return false; } scanmem-0.17/sets.h000066400000000000000000000032401317023271400142120ustar00rootroot00000000000000/* Create sets. Copyright (C) 2016-2017 Bijan Kazemi This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef SETS_H #define SETS_H #include struct set { size_t size; /* size of set (used) */ size_t *buf; /* value buffer */ }; static inline void set_cleanup(struct set *set) { if (set) free(set->buf); } /* iterate over set from front (forwards) */ #define foreach_set_fw(i, set) \ for (size_t i = 0; i < (set)->size; i++) /* * iterate over set from back (backwards) * * NOTE: `_reserved_for_set_iteration_zzaw2_df_` is reserved, named randomly to avoid namespace crash */ #define foreach_set_bw(i, set) \ for (size_t _reserved_for_set_iteration_zzaw2_df_ = 0, i = (set)->size-1; \ _reserved_for_set_iteration_zzaw2_df_ < (set)->size; \ _reserved_for_set_iteration_zzaw2_df_++, i--) bool parse_uintset(const char *, struct set *, size_t); #endif /* SETS_H */ scanmem-0.17/show_message.c000066400000000000000000000034461317023271400157230ustar00rootroot00000000000000/* Message printing helper functions. Copyright (C) 2010 WANG Lu This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include "show_message.h" #include "scanmem.h" void show_info(const char *fmt, ...) { va_list args; va_start (args, fmt); fprintf(stderr, "info: "); vfprintf(stderr, fmt, args); va_end (args); } void show_error(const char *fmt, ...) { va_list args; va_start (args, fmt); fprintf(stderr, "error: "); vfprintf(stderr, fmt, args); va_end (args); } void show_warn(const char *fmt, ...) { va_list args; va_start (args, fmt); fprintf(stderr, "warn: "); vfprintf(stderr, fmt, args); va_end (args); } void show_user(const char *fmt, ...) { va_list args; va_start (args, fmt); if (!(sm_globals.options.backend)) { vfprintf(stderr, fmt, args); } va_end (args); } void show_debug(const char *fmt, ...) { va_list args; va_start (args, fmt); if (sm_globals.options.debug) { fprintf(stderr, "debug: "); vfprintf(stderr, fmt, args); } va_end (args); } scanmem-0.17/show_message.h000066400000000000000000000037121317023271400157240ustar00rootroot00000000000000/* Message printing helper functions. Copyright (C) 2010 WANG Lu This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ /* * This file declares all types of output functions, in order to provide well-formatted messages that a front-end can understand. * * Basically, all data goes through stdout, and all messages (to user or front-end) go through stderr. * * In stderr: * all messages prefixed with 'error:' will be considered a fatal error; front-end may notify user to restart backend * all messages prefixed with 'warn:' will be considered a nonfatal error. * all messages prefixed with 'info:' will be ignored (by the front-end) * * To display messages to user only, use show_user; nothing will be prepended, and the message will be ignored if scanmem is running as a backend. */ #ifndef SHOW_MESSAGE_H #define SHOW_MESSAGE_H /* prepend 'info: ', output to stderr */ void show_info(const char *fmt, ...); /* prepend 'error: ', output to stderr */ void show_error(const char *fmt, ...); /* prepend 'warn: ', output to stderr */ void show_warn(const char *fmt, ...); /* display message only when in debug mode */ void show_debug(const char *fmt, ...); /* display message only when not running as a backend */ void show_user(const char *fmt, ...); #endif /* SHOW_MESSAGE_H */ scanmem-0.17/targetmem.c000066400000000000000000000156571317023271400152330ustar00rootroot00000000000000/* The target memory information array (storage of matches). Copyright (C) 2009 Eli Dupree Copyright (C) 2010 WANG Lu This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include #include #include #include #include "targetmem.h" #include "show_message.h" #include "value.h" matches_and_old_values_array * allocate_array (matches_and_old_values_array *array, size_t max_bytes) { /* make enough space for the array header and a null first swath */ size_t bytes_to_allocate = sizeof(matches_and_old_values_array) + sizeof(matches_and_old_values_swath); if (!(array = realloc(array, bytes_to_allocate))) return NULL; array->bytes_allocated = bytes_to_allocate; array->max_needed_bytes = max_bytes; return array; } matches_and_old_values_array * null_terminate (matches_and_old_values_array *array, matches_and_old_values_swath *swath) { size_t bytes_needed; if (swath->number_of_bytes == 0) { assert(swath->first_byte_in_child == NULL); } else { swath = local_address_beyond_last_element(swath ); array = allocate_enough_to_reach(array, ((void *)swath) + sizeof(matches_and_old_values_swath), &swath); swath->first_byte_in_child = NULL; swath->number_of_bytes = 0; } bytes_needed = ((void *)swath + sizeof(matches_and_old_values_swath) - (void *)array); if (bytes_needed < array->bytes_allocated) { /* reduce array to its final size */ if (!(array = realloc(array, bytes_needed))) return NULL; array->bytes_allocated = bytes_needed; } return array; } void data_to_printable_string (char *buf, int buf_length, matches_and_old_values_swath *swath, size_t index, int string_length) { long swath_length = swath->number_of_bytes - index; /* TODO: what if length is too large ? */ long max_length = (swath_length >= string_length) ? string_length : swath_length; int i; for (i = 0; i < max_length; ++i) { uint8_t byte = swath->data[index+i].old_value; buf[i] = isprint(byte) ? byte : '.'; } buf[i] = 0; /* null-terminate */ } void data_to_bytearray_text (char *buf, int buf_length, matches_and_old_values_swath *swath, size_t index, int bytearray_length) { int i; int bytes_used = 0; long swath_length = swath->number_of_bytes - index; /* TODO: what if length is too large ? */ long max_length = (swath_length >= bytearray_length) ? bytearray_length : swath_length; for (i = 0; i < max_length; ++i) { uint8_t byte = swath->data[index+i].old_value; /* TODO: check error here */ snprintf(buf+bytes_used, buf_length-bytes_used, (iswaths; size_t reading_iterator = 0; if (!matches) return (match_location){NULL, 0}; while (reading_swath_index->first_byte_in_child) { /* only actual matches are considered */ if (reading_swath_index->data[reading_iterator].match_info != flags_empty) { if (i == n) return (match_location){reading_swath_index, reading_iterator}; ++i; } /* go on to the next one... */ ++reading_iterator; if (reading_iterator >= reading_swath_index->number_of_bytes) { reading_swath_index = local_address_beyond_last_element(reading_swath_index); reading_iterator = 0; } } /* I guess this is not a valid match-id */ return (match_location){ NULL, 0 }; } /* deletes matches in [start, end) and resizes the matches array */ matches_and_old_values_array * delete_in_address_range (matches_and_old_values_array *array, unsigned long *num_matches, void *start_address, void *end_address) { assert(array); size_t reading_iterator = 0; matches_and_old_values_swath *reading_swath_index = array->swaths; matches_and_old_values_swath reading_swath = *reading_swath_index; matches_and_old_values_swath *writing_swath_index = array->swaths; writing_swath_index->first_byte_in_child = NULL; writing_swath_index->number_of_bytes = 0; *num_matches = 0; while (reading_swath.first_byte_in_child) { void *address = reading_swath.first_byte_in_child + reading_iterator; if (address < start_address || address >= end_address) { old_value_and_match_info old_byte; old_byte = reading_swath_index->data[reading_iterator]; /* Still a candidate. Write data. (We can get away with overwriting in the same array because it is guaranteed to take up the same number of bytes or fewer, and because we copied out the reading swath metadata already.) (We can get away with assuming that the pointers will stay valid, because as we never add more data to the array than there was before, it will not reallocate.) */ writing_swath_index = add_element(&array, writing_swath_index, address, old_byte.old_value, old_byte.match_info); /* actual matches are recorded */ if (old_byte.match_info != flags_empty) ++(*num_matches); } /* go on to the next one... */ ++reading_iterator; if (reading_iterator >= reading_swath.number_of_bytes) { reading_swath_index = (matches_and_old_values_swath *) (&reading_swath_index->data[reading_swath.number_of_bytes]); reading_swath = *reading_swath_index; reading_iterator = 0; } } return null_terminate(array, writing_swath_index); } scanmem-0.17/targetmem.h000066400000000000000000000243121317023271400152240ustar00rootroot00000000000000/* The target memory information array (storage of matches). Copyright (C) 2009 Eli Dupree Copyright (C) 2010 WANG Lu Copyright (C) 2015 Sebastian Parschauer This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef TARGETMEM_H #define TARGETMEM_H #include #include #include #include #include #include "value.h" #include "show_message.h" /* Public structs */ /* Single match struct */ typedef struct { uint8_t old_value; match_flags match_info; } old_value_and_match_info; /* Array that contains a consecutive (in memory) sequence of matches (= swath). - the first_byte_in_child pointer refers to locations in the child, it cannot be followed except using ptrace() - the number_of_bytes refers to the number of bytes in the child process's memory that are covered, not the number of bytes the struct takes up. It's the length of data. */ typedef struct __attribute__((packed,aligned(sizeof(old_value_and_match_info)))) { void *first_byte_in_child; size_t number_of_bytes; old_value_and_match_info data[0]; } matches_and_old_values_swath; /* Master matches array, smartly resized, contains swaths. Both `bytes` values refer to real struct bytes this time. */ typedef struct { size_t bytes_allocated; size_t max_needed_bytes; matches_and_old_values_swath swaths[0]; } matches_and_old_values_array; /* Location of a match in a matches_and_old_values_array */ typedef struct { matches_and_old_values_swath *swath; size_t index; } match_location; /* Public functions */ matches_and_old_values_array *allocate_array (matches_and_old_values_array *array, size_t max_bytes); matches_and_old_values_array *null_terminate (matches_and_old_values_array *array, matches_and_old_values_swath *swath); /* for printable text representation */ void data_to_printable_string (char *buf, int buf_length, matches_and_old_values_swath *swath, size_t index, int string_length); /* for bytearray representation */ void data_to_bytearray_text (char *buf, int buf_length, matches_and_old_values_swath *swath, size_t index, int bytearray_length); match_location nth_match (matches_and_old_values_array *matches, size_t n); /* deletes matches in [start, end) and resizes the matches array */ matches_and_old_values_array * delete_in_address_range (matches_and_old_values_array *array, unsigned long *num_matches, void *start_address, void *end_address); /* The following functions are called in the hot scanning path and were moved to this header from the .c file so that they could be inlined */ static inline size_t index_of_last_element (matches_and_old_values_swath *swath) { return swath->number_of_bytes - 1; } static inline void * remote_address_of_nth_element (matches_and_old_values_swath *swath, size_t n) { return swath->first_byte_in_child + n; } static inline void * remote_address_of_last_element (matches_and_old_values_swath *swath) { return (remote_address_of_nth_element(swath, index_of_last_element(swath))); } static inline void * local_address_beyond_nth_element (matches_and_old_values_swath *swath, size_t n) { return &(swath->data[n + 1]); } static inline void * local_address_beyond_last_element (matches_and_old_values_swath *swath) { return (local_address_beyond_nth_element(swath, index_of_last_element(swath))); } static inline matches_and_old_values_array * allocate_enough_to_reach (matches_and_old_values_array *array, void *last_byte_to_reach_plus_one, matches_and_old_values_swath **swath_pointer_to_correct) { size_t bytes_needed = last_byte_to_reach_plus_one - (void *)array; if (bytes_needed <= array->bytes_allocated) { return array; } else { matches_and_old_values_array *original_location = array; /* allocate twice as much each time, so we don't have to do it too often */ size_t bytes_to_allocate = array->bytes_allocated; while (bytes_to_allocate < bytes_needed) bytes_to_allocate *= 2; show_debug("to_allocate %ld, max %ld\n", bytes_to_allocate, array->max_needed_bytes); /* sometimes we know an absolute max that we will need */ if (array->max_needed_bytes < bytes_to_allocate) { assert(array->max_needed_bytes >= bytes_needed); bytes_to_allocate = array->max_needed_bytes; } if (!(array = realloc(array, bytes_to_allocate))) return NULL; array->bytes_allocated = bytes_to_allocate; /* Put the swath pointer back where it should be, if needed. We cast everything to void pointers in this line to make sure the math works out. */ if (swath_pointer_to_correct) { (*swath_pointer_to_correct) = (matches_and_old_values_swath *) (((void *)(*swath_pointer_to_correct)) + ((void *)array - (void *)original_location)); } return array; } } /* returns a pointer to the swath to which the element was added - i.e. the last swath in the array after the operation */ static inline matches_and_old_values_swath * add_element (matches_and_old_values_array **array, matches_and_old_values_swath *swath, void *remote_address, uint8_t new_byte, match_flags new_flags) { if (swath->number_of_bytes == 0) { assert(swath->first_byte_in_child == NULL); /* we have to overwrite this as a new swath */ *array = allocate_enough_to_reach(*array, (void *)swath + sizeof(matches_and_old_values_swath) + sizeof(old_value_and_match_info), &swath); swath->first_byte_in_child = remote_address; } else { size_t local_index_excess = remote_address - remote_address_of_last_element(swath); size_t local_address_excess = local_index_excess * sizeof(old_value_and_match_info); size_t needed_size_for_a_new_swath = sizeof(matches_and_old_values_swath) + sizeof(old_value_and_match_info); if (local_address_excess >= needed_size_for_a_new_swath) { /* It is more memory-efficient to start a new swath. * The equal case is decided for a new swath, so that * later we don't needlessly iterate through a bunch * of empty values */ *array = allocate_enough_to_reach(*array, local_address_beyond_last_element(swath) + needed_size_for_a_new_swath, &swath); swath = local_address_beyond_last_element(swath); swath->first_byte_in_child = remote_address; swath->number_of_bytes = 0; } else { /* It is more memory-efficient to write over the intervening space with null values */ *array = allocate_enough_to_reach(*array, local_address_beyond_last_element(swath) + local_address_excess, &swath); switch (local_index_excess) { case 1: /* do nothing, the new value is right after the old */ break; case 2: memset(local_address_beyond_last_element(swath), 0, sizeof(old_value_and_match_info)); break; default: /* slow due to unknown size to be zeroed */ memset(local_address_beyond_last_element(swath), 0, local_address_excess - sizeof(old_value_and_match_info)); break; } swath->number_of_bytes += local_index_excess - 1; } } /* add me */ old_value_and_match_info *dataptr = local_address_beyond_last_element(swath); dataptr->old_value = new_byte; dataptr->match_info = new_flags; ++swath->number_of_bytes; return swath; } /* only at most sizeof(int64_t) bytes will be read, if more bytes are needed (e.g. bytearray), read them separately (for performance) */ static inline value_t data_to_val_aux (const matches_and_old_values_swath *swath, size_t index, size_t swath_length) { uint i; value_t val; size_t max_bytes = swath_length - index; /* Init all possible flags in a single go. * Also init length to the maximum possible value */ val.flags = 0xffffu; /* NOTE: This does the right thing for VLT because the flags are in * the same order as the number representation (for both endians), so * that the zeroing of a flag does not change useful bits of `length`. */ if (max_bytes > 8) max_bytes = 8; if (max_bytes < 8) val.flags &= ~flags_64b; if (max_bytes < 4) val.flags &= ~flags_32b; if (max_bytes < 2) val.flags &= ~flags_16b; if (max_bytes < 1) val.flags = flags_empty; for (i = 0; i < max_bytes; ++i) { /* Both uint8_t, no explicit casting needed */ val.bytes[i] = swath->data[index + i].old_value; } /* Truncate to the old flags, which are stored with the first matched byte */ val.flags &= swath->data[index].match_info; return val; } static inline value_t data_to_val (const matches_and_old_values_swath *swath, size_t index) { return data_to_val_aux(swath, index, swath->number_of_bytes); } #endif /* TARGETMEM_H */ scanmem-0.17/test/000077500000000000000000000000001317023271400140435ustar00rootroot00000000000000scanmem-0.17/test/.gitignore000066400000000000000000000000621317023271400160310ustar00rootroot00000000000000# memfake exe memfake # Test results *.log *.trs scanmem-0.17/test/Makefile.am000066400000000000000000000001531317023271400160760ustar00rootroot00000000000000TESTS = sm_test.sh check_PROGRAMS = memfake memfake_SOURCES = memfake.c memfake_CFLAGS = -std=gnu99 -Wall scanmem-0.17/test/memfake.c000066400000000000000000000027751317023271400156270ustar00rootroot00000000000000/* Provide a simple program to run test scans on Copyright (C) 2017 Andrea Stacchiotti This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include #include #include int main(int argc, char **argv) { uint MB_to_allocate = 1; bool add_randomness = false; if (argc >= 2) MB_to_allocate = atoi(argv[1]); if (argc >= 3) add_randomness = atoi(argv[2]); if (argc >= 4) return 1; size_t array_size = MB_to_allocate * 1024 * 1024 / sizeof(int); int* array = calloc(array_size, sizeof(int)); // Fill half with random values and leave an half of zeroes, if asked to if (add_randomness) { srand(time(NULL)); for (size_t i = 0; i < array_size/2; i++) { array[i] = rand(); } } pause(); free(array); return 0; } scanmem-0.17/test/sm_test.sh000077500000000000000000000014431317023271400160620ustar00rootroot00000000000000#!/bin/bash set -ev # Start memfake ./memfake 4 1 & memfake_pid=$! # Test runs test_sm () { ../scanmem -p $memfake_pid -e -c "$1" } test_sm "option scan_data_type int8;0;exit" test_sm "option scan_data_type int8;snapshot;exit" test_sm "option scan_data_type int8;snapshot;1;exit" test_sm "option scan_data_type int8;1;delete 0;1;exit" test_sm "option scan_data_type int;1;exit" test_sm "option scan_data_type float;1;exit" test_sm "option scan_data_type number;1;exit" huge_bytearray="" huge_string="" # 257 not a typo, forces full scan routine use for ((i=0; i < 257; i++)); do huge_bytearray+="00 ?? " huge_string+="a" done test_sm "option scan_data_type bytearray;${huge_bytearray};exit" test_sm "option scan_data_type string;\" ${huge_string};exit" # Clean up kill $memfake_pid scanmem-0.17/value.c000066400000000000000000000233101317023271400143430ustar00rootroot00000000000000/* Simple routines for working with the value_t data structure. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009,2010 WANG Lu Copyright (C) 2015 Sebastian Parschauer This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include #include #include #include #include #include #include #include #include #include /* for fixed-width formatters */ #include "value.h" #include "show_message.h" void valtostr(const value_t *val, char *str, size_t n) { char buf[128]; int np = 0; #define FLAG_MACRO(bytes, string) \ (val->flags & flag_u##bytes##b && val->flags & flag_s##bytes##b) ? (string " ") : \ (val->flags & flag_u##bytes##b) ? (string "u ") : \ (val->flags & flag_s##bytes##b) ? (string "s ") : "" /* set the flags */ np = snprintf(buf, sizeof(buf), "[%s%s%s%s%s%s]", FLAG_MACRO(64, "I64"), FLAG_MACRO(32, "I32"), FLAG_MACRO(16, "I16"), FLAG_MACRO(8, "I8"), (val->flags & flag_f64b) ? "F64 " : "", (val->flags & flag_f32b) ? "F32 " : ""); /* handle having no type at all */ if (np <= 2) { show_debug("BUG: No type\n"); goto err; } if (val->flags & flag_u64b) np = snprintf(str, n, "%" PRIu64 ", %s", get_u64b(val), buf); else if (val->flags & flag_s64b) np = snprintf(str, n, "%" PRId64 ", %s", get_s64b(val), buf); else if (val->flags & flag_u32b) np = snprintf(str, n, "%" PRIu32 ", %s", get_u32b(val), buf); else if (val->flags & flag_s32b) np = snprintf(str, n, "%" PRId32 ", %s", get_s32b(val), buf); else if (val->flags & flag_u16b) np = snprintf(str, n, "%" PRIu16 ", %s", get_u16b(val), buf); else if (val->flags & flag_s16b) np = snprintf(str, n, "%" PRId16 ", %s", get_s16b(val), buf); else if (val->flags & flag_u8b) np = snprintf(str, n, "%" PRIu8 ", %s", get_u8b(val), buf); else if (val->flags & flag_s8b) np = snprintf(str, n, "%" PRId8 ", %s", get_s8b(val), buf); else if (val->flags & flag_f64b) np = snprintf(str, n, "%lg, %s", get_f64b(val), buf); else if (val->flags & flag_f32b) np = snprintf(str, n, "%g, %s", get_f32b(val), buf); else { show_debug("BUG: No formatting found\n"); goto err; } if (np <= 0 || np >= (n - 1)) goto err; return; err: /* always print a value and a type to not crash front-ends */ strncpy(str, "unknown, [unknown]", n); } void valcpy(value_t * dst, const value_t * src) { memcpy(dst, src, sizeof(value_t)); return; } /* dst.flags must be set beforehand. Prefer setting floats to ints */ void uservalue2value(value_t *dst, const uservalue_t *src) { /* Zero whole value union, in case high bytes won't be set */ dst->uint64_value = 0; if (dst->flags & flag_f64b) set_f64b(dst, get_f64b(src)); else if (dst->flags & flag_u64b) set_u64b(dst, get_u64b(src)); else if (dst->flags & flag_s64b) set_s64b(dst, get_s64b(src)); else if (dst->flags & flag_f32b) set_f32b(dst, get_f32b(src)); else if (dst->flags & flag_u32b) set_u32b(dst, get_u32b(src)); else if (dst->flags & flag_s32b) set_s32b(dst, get_s32b(src)); else if (dst->flags & flag_u16b) set_u16b(dst, get_u16b(src)); else if (dst->flags & flag_s16b) set_s16b(dst, get_s16b(src)); else if (dst->flags & flag_u8b) set_u8b (dst, get_u8b(src)); else if (dst->flags & flag_s8b) set_s8b (dst, get_s8b(src)); else assert(false); } /* parse bytearray, it will allocate the arrays itself, then needs to be free'd by `free_uservalue()` */ bool parse_uservalue_bytearray(char *const *argv, unsigned argc, uservalue_t *val) { int i,j; uint8_t *bytes_array = malloc(argc*sizeof(uint8_t)); wildcard_t *wildcards_array = malloc(argc*sizeof(wildcard_t)); if (bytes_array == NULL || wildcards_array == NULL) { show_error("memory allocation for bytearray failed.\n"); goto err; } const char *cur_str; char *endptr; for(i = 0; i < argc; ++i) { /* get current string */ cur_str = argv[i]; /* test its length */ for(j = 0; (j < 3) && (cur_str[j]); ++j) {} if (j != 2) /* length is not 2 */ goto err; if (strcmp(cur_str, "??") == 0) { wildcards_array[i] = WILDCARD; bytes_array[i] = 0x00; } else { /* parse as hex integer */ uint8_t cur_byte = (uint8_t)strtoul(cur_str, &endptr, 16); if (*endptr != '\0') goto err; wildcards_array[i] = FIXED; bytes_array[i] = cur_byte; } } /* everything is ok */ val->bytearray_value = bytes_array; val->wildcard_value = wildcards_array; val->flags = argc; return true; err: if (bytes_array) free(bytes_array); if (wildcards_array) free(wildcards_array); zero_uservalue(val); return false; } bool parse_uservalue_number(const char *nptr, uservalue_t * val) { if (parse_uservalue_int(nptr, val)) { val->flags |= flags_float; if (val->flags & flag_s64b) { val->float32_value = (float) val->int64_value; val->float64_value = (double) val->int64_value; } else { val->float32_value = (float) val->uint64_value; val->float64_value = (double) val->uint64_value; } return true; } else if(parse_uservalue_float(nptr, val)) { double num = val->float64_value; if (num >= 0 && num <= UINT8_MAX) { val->flags |= flag_u8b; set_u8b(val, (uint8_t)num); } if (num >= INT8_MIN && num <= INT8_MAX) { val->flags |= flag_s8b; set_s8b(val, (int8_t)num); } if (num >= 0 && num <= UINT16_MAX) { val->flags |= flag_u16b; set_u16b(val, (uint16_t)num); } if (num >= INT16_MIN && num <= INT16_MAX) { val->flags |= flag_s16b; set_s16b(val, (int16_t)num); } if (num >= 0 && num <= UINT32_MAX) { val->flags |= flag_u32b; set_u32b(val, (uint32_t)num); } if (num >= INT32_MIN && num <= INT32_MAX) { val->flags |= flag_s32b; set_s32b(val, (int32_t)num); } if (num >= 0 && num <= UINT64_MAX) { val->flags |= flag_u64b; set_u64b(val, (uint64_t)num); } if (num >= INT64_MIN && num <= INT64_MAX) { val->flags |= flag_s64b; set_s64b(val, (int64_t)num); } return true; } return false; } bool parse_uservalue_int(const char *nptr, uservalue_t * val) { int64_t snum; bool valid_sint; uint64_t unum; bool valid_uint; char *endptr; assert(nptr != NULL); assert(val != NULL); zero_uservalue(val); /* skip past any whitespace */ while (isspace(*nptr)) ++nptr; /* parse it as signed int */ errno = 0; snum = strtoll(nptr, &endptr, 0); valid_sint = (errno == 0) && (*endptr == '\0'); /* parse it as unsigned int */ errno = 0; unum = strtoull(nptr, &endptr, 0); valid_uint = (*nptr != '-') && (errno == 0) && (*endptr == '\0'); if (!valid_sint && !valid_uint) return false; /* determine correct flags */ if (valid_uint && unum <= UINT8_MAX) { val->flags |= flag_u8b; set_u8b(val, (uint8_t)unum); } if (valid_sint && snum >= INT8_MIN && snum <= INT8_MAX) { val->flags |= flag_s8b; set_s8b(val, (int8_t)snum); } if (valid_uint && unum <= UINT16_MAX) { val->flags |= flag_u16b; set_u16b(val, (uint16_t)unum); } if (valid_sint && snum >= INT16_MIN && snum <= INT16_MAX) { val->flags |= flag_s16b; set_s16b(val, (int16_t)snum); } if (valid_uint && unum <= UINT32_MAX) { val->flags |= flag_u32b; set_u32b(val, (uint32_t)unum); } if (valid_sint && snum >= INT32_MIN && snum <= INT32_MAX) { val->flags |= flag_s32b; set_s32b(val, (int32_t)snum); } if (valid_uint && unum <= UINT64_MAX) { val->flags |= flag_u64b; set_u64b(val, (uint64_t)unum); } if (valid_sint && snum >= INT64_MIN && snum <= INT64_MAX) { val->flags |= flag_s64b; set_s64b(val, (int64_t)snum); } return true; } bool parse_uservalue_float(const char *nptr, uservalue_t * val) { double num; char *endptr; assert(nptr); assert(val); zero_uservalue(val); while (isspace(*nptr)) ++nptr; errno = 0; num = strtod(nptr, &endptr); if ((errno != 0) || (*endptr != '\0')) return false; /* I'm not sure how to distinguish between float and double, but I guess it's not necessary here */ val->flags |= flags_float; val->float32_value = (float) num; val->float64_value = num; return true; } void free_uservalue(uservalue_t *uval) { /* bytearray arrays are dynamically allocated and have to be freed, strings are not */ if (uval->bytearray_value) free((void*)uval->bytearray_value); if (uval->wildcard_value) free((void*)uval->wildcard_value); } scanmem-0.17/value.h000066400000000000000000000166321317023271400143610ustar00rootroot00000000000000/* Simple routines for working with the value_t data structure. Copyright (C) 2006,2007,2009 Tavis Ormandy Copyright (C) 2009 Eli Dupree Copyright (C) 2009,2010 WANG Lu Copyright (C) 2015 Sebastian Parschauer Copyright (C) 2017 Andrea Stacchiotti This file is part of libscanmem. This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef VALUE_H #define VALUE_H #include #include #include #include #include /* some routines for working with value_t structures */ /* match_flags: they MUST be implemented as an `uint16_t`, the `__packed__` ensures so. * They are reinterpreted as a normal integer when scanning for VLT, which is * valid for both endians, as the flags are ordered from smaller to bigger. * NAMING: Primitive, single-bit flags are called `flag_*`, while aggregates, * defined for convenience, are called `flags_*`*/ typedef enum __attribute__((__packed__)) { flags_empty = 0, flag_u8b = 1 << 0, /* could be an unsigned 8-bit variable (e.g. unsigned char) */ flag_s8b = 1 << 1, /* could be a signed 8-bit variable (e.g. signed char) */ flag_u16b = 1 << 2, /* could be an unsigned 16-bit variable (e.g. unsigned short) */ flag_s16b = 1 << 3, /* could be a signed 16-bit variable (e.g. short) */ flag_u32b = 1 << 4, /* could be an unsigned 32-bit variable (e.g. unsigned int) */ flag_s32b = 1 << 5, /* could be a signed 32-bit variable (e.g. int) */ flag_u64b = 1 << 6, /* could be an unsigned 64-bit variable (e.g. unsigned long long) */ flag_s64b = 1 << 7, /* could be a signed 64-bit variable (e.g. long long) */ flag_f32b = 1 << 8, /* could be a 32-bit floating point variable (i.e. float) */ flag_f64b = 1 << 9, /* could be a 64-bit floating point variable (i.e. double) */ flags_i8b = flag_u8b | flag_s8b, flags_i16b = flag_u16b | flag_s16b, flags_i32b = flag_u32b | flag_s32b, flags_i64b = flag_u64b | flag_s64b, flags_integer = flags_i8b | flags_i16b | flags_i32b | flags_i64b, flags_float = flag_f32b | flag_f64b, flags_all = flags_integer | flags_float, flags_8b = flags_i8b, flags_16b = flags_i16b, flags_32b = flags_i32b | flag_f32b, flags_64b = flags_i64b | flag_f64b, flags_max = 0xffffU /* ensures we're using an uint16_t */ } match_flags; /* this struct describes matched values */ typedef struct { union { int8_t int8_value; uint8_t uint8_value; int16_t int16_value; uint16_t uint16_value; int32_t int32_value; uint32_t uint32_value; int64_t int64_value; uint64_t uint64_value; float float32_value; double float64_value; uint8_t bytes[sizeof(int64_t)]; char chars[sizeof(int64_t)]; }; match_flags flags; } value_t; /* This union describes 8 bytes retrieved from target memory. * Pointers to this union are the only ones that are allowed to be unaligned: * to avoid performance degradation/crashes on arches that don't support unaligned access * (e.g. ARM) we access unaligned memory only through the attributes of this packed union. * As described in http://www.alfonsobeato.net/arm/how-to-access-safely-unaligned-data/ , * a packed structure forces the compiler to write general access methods to its members * that don't depend on alignment. * So NEVER EVER dereference a mem64_t*, but use its accessors to obtain the needed type. */ typedef union __attribute__((packed)) { int8_t int8_value; uint8_t uint8_value; int16_t int16_value; uint16_t uint16_value; int32_t int32_value; uint32_t uint32_value; int64_t int64_value; uint64_t uint64_value; float float32_value; double float64_value; uint8_t bytes[sizeof(int64_t)]; char chars[sizeof(int64_t)]; } mem64_t; /* bytearray wildcards: they must be uint8_t. They are ANDed with the incoming * memory before the comparison, so that '??' wildcards always return true * It's possible to extend them to fully granular wildcard-ing, if needed */ typedef enum __attribute__ ((__packed__)) { FIXED = 0xffu, WILDCARD = 0x00u, } wildcard_t; /* this struct describes values provided by users */ typedef struct { int8_t int8_value; uint8_t uint8_value; int16_t int16_value; uint16_t uint16_value; int32_t int32_value; uint32_t uint32_value; int64_t int64_value; uint64_t uint64_value; float float32_value; double float64_value; const uint8_t *bytearray_value; const wildcard_t *wildcard_value; const char *string_value; match_flags flags; } uservalue_t; /* used when outputting values to user */ /* only works for numbers */ void valtostr(const value_t *val, char *str, size_t n); /* parse bytearray, it will allocate the arrays itself, then needs to be free'd by `free_uservalue()` */ bool parse_uservalue_bytearray(char *const *argv, unsigned argc, uservalue_t *val); bool parse_uservalue_number(const char *nptr, uservalue_t * val); /* parse int or float */ bool parse_uservalue_int(const char *nptr, uservalue_t * val); bool parse_uservalue_float(const char *nptr, uservalue_t * val); void free_uservalue(uservalue_t *uval); void valcpy(value_t * dst, const value_t * src); void uservalue2value(value_t * dst, const uservalue_t * src); /* dst.flags must be set beforehand */ #define get_s8b(val) ((val)->int8_value) #define get_u8b(val) ((val)->uint8_value) #define get_s16b(val) ((val)->int16_value) #define get_u16b(val) ((val)->uint16_value) #define get_s32b(val) ((val)->int32_value) #define get_u32b(val) ((val)->uint32_value) #define get_s64b(val) ((val)->int64_value) #define get_u64b(val) ((val)->uint64_value) #define get_f32b(val) ((val)->float32_value) #define get_f64b(val) ((val)->float64_value) #define set_s8b(val, v) (((val)->int8_value) = v) #define set_u8b(val, v) (((val)->uint8_value) = v) #define set_s16b(val, v) (((val)->int16_value) = v) #define set_u16b(val, v) (((val)->uint16_value) = v) #define set_s32b(val, v) (((val)->int32_value) = v) #define set_u32b(val, v) (((val)->uint32_value) = v) #define set_s64b(val, v) (((val)->int64_value) = v) #define set_u64b(val, v) (((val)->uint64_value) = v) #define set_f32b(val, v) (((val)->float32_value) = v) #define set_f64b(val, v) (((val)->float64_value) = v) static inline void zero_value(value_t *val) { /* zero components separately - 10 bytes memset() is too slow */ val->int64_value = 0; /* zero the whole union */ val->flags = flags_empty; } static inline void zero_uservalue(uservalue_t *val) { memset(val, 0, sizeof(*val)); } #endif /* VALUE_H */