diff options
| author | Fuwn <[email protected]> | 2022-01-03 03:20:12 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2022-01-03 03:20:12 -0800 |
| commit | 85db2b507f3f69b32811c54a89d9ac7bbbc46121 (patch) | |
| tree | 2efd66da452f8a6a2cc6c91584c925f237506ddf | |
| download | driver-85db2b507f3f69b32811c54a89d9ac7bbbc46121.tar.xz driver-85db2b507f3f69b32811c54a89d9ac7bbbc46121.zip | |
feat(driver): commit primer
55 files changed, 4558 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..775acd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# IDE +/.idea/ +/driver.iml + +# Development +/src/_*.* +/crates/**/target/ + +# NPM +/node_modules/ diff --git a/.license_template b/.license_template new file mode 100644 index 0000000..e8cad8b --- /dev/null +++ b/.license_template @@ -0,0 +1,2 @@ +// Copyright (C) {20\d{2}(-20\d{2})?} Fuwn +// SPDX-License-Identifier: GPL-3.0-only diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..42f026d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] +members = [ + "crates/driver", + "crates/windows-kernel-build", + "crates/windows-kernel-rs", + "crates/windows-kernel-sys", + "crates/winioctl" +] + +[profile.dev] +panic = "abort" + +[profile.release] +lto = "fat" +codegen-units = 1 +panic = "abort" @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU 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 <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 0000000..5e838d3 --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,69 @@ +# ------------- +# | Variables | +# ------------- +[env] +CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true +VC_BUILD_DIR="C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat" + +[env.development] +TARGET_PATH = "target/debug" + +[env.production] +TARGET_PATH = "target/release" +# BUILD_RELEASE = "--release" + +# ------------ +# | Wrappers | +# ------------ +[tasks.fmt] +command = "cargo" +args = ["fmt"] +private = true + +[tasks.check] +command = "cargo" +args = ["check"] +private = true + +[tasks.clippy] +command = "cargo" +args = ["clippy"] +private = true + +# ------------- +# | Executors | +# ------------- +[tasks.checkf] +workspace = false +dependencies = ["fmt", "check"] + +[tasks.checkfc] +workspace = false +dependencies = ["fmt", "check", "clippy"] + +[tasks.build-driver] +script = ''' +cargo b %BUILD_FLAGS% +''' + +[tasks.rename-driver] + +ignore_errors = true +script = ''' +cd %TARGET_PATH% +rename driver.dll driver.sys +''' + +[tasks.clean-cert] +script = ''' +# Remove the self-signed certificate. +if exist DriverCertificate.cer ( rm DriverCertificate.cer ) +''' + +[tasks.sign] +dependencies = ["build-driver", "rename"] +script = ''' +call "%VC_BUILD_DIR%" +if not exist DriverCertificate.cer ( makecert -r -pe -ss PrivateCertStore -n CN=DriverCertificate DriverCertificate.cer ) else ( echo Certificate already exists. ) +signtool sign /a /v /s PrivateCertStore /n DriverCertificate /fd certHash /t http://timestamp.digicert.com %TARGET_PATH%/driver.sys +''' diff --git a/crates/driver/Cargo.toml b/crates/driver/Cargo.toml new file mode 100644 index 0000000..1ffeb53 --- /dev/null +++ b/crates/driver/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "driver" +version = "0.1.0" +authors = ["Fuwn <[email protected]>"] +edition = "2021" +description = "" +homepage = "https://github.com/rawaccel/driver" +repository = "https://github.com/rawaccel/driver" +license = "GPL-3.0-only" +publish = false + +[lib] +crate-type = ["cdylib"] + +# test = false +# bench = false + +[build-dependencies] +windows-kernel-build = { path = "../windows-kernel-build" } + +[dependencies.winapi] +git = "https://github.com/Trantect/winapi-rs.git" +branch = "feature/km" +features = [ + "wdm", + "ntstatus", + "ntdef" +] diff --git a/crates/driver/build.rs b/crates/driver/build.rs new file mode 100644 index 0000000..f0f2215 --- /dev/null +++ b/crates/driver/build.rs @@ -0,0 +1,4 @@ +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +fn main() { windows_kernel_build::build().unwrap() } diff --git a/crates/driver/src/lib.rs b/crates/driver/src/lib.rs new file mode 100644 index 0000000..09b17fc --- /dev/null +++ b/crates/driver/src/lib.rs @@ -0,0 +1,43 @@ +// Copyright (C) 2022-2022 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#![no_std] +#![feature(lang_items, const_extern_fn)] +#![deny( + warnings, + nonstandard_style, + unused, + future_incompatible, + rust_2018_idioms +)] +#![deny(clippy::all, clippy::nursery, clippy::pedantic)] + +use winapi::{ + km::wdm::{DbgPrint, DRIVER_OBJECT}, + shared::ntdef::{NTSTATUS, UNICODE_STRING}, +}; + +#[panic_handler] +const fn panic(_info: &core::panic::PanicInfo<'_>) -> ! { loop {} } + +// https://users.rust-lang.org/t/solved-hello-world-no-std-build-problem/23122/4 +#[lang = "eh_personality"] +const extern "C" fn eh_personality() {} + +#[no_mangle] +pub extern "system" fn driver_entry(driver: &mut DRIVER_OBJECT, _: &UNICODE_STRING) -> NTSTATUS { + unsafe { + DbgPrint("driver_entry()\0".as_ptr()); + } + + driver.DriverUnload = Some(driver_exit); + + winapi::shared::ntstatus::STATUS_SUCCESS +} + +#[no_mangle] +pub extern "system" fn driver_exit(_driver: &mut DRIVER_OBJECT) { + unsafe { + DbgPrint("driver_exit()\0".as_ptr()); + } +} diff --git a/crates/windows-kernel-build/Cargo.toml b/crates/windows-kernel-build/Cargo.toml new file mode 100644 index 0000000..8938bc0 --- /dev/null +++ b/crates/windows-kernel-build/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "windows-kernel-build" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +thiserror = "1.0" +winreg = "0.10" diff --git a/crates/windows-kernel-build/LICENSE b/crates/windows-kernel-build/LICENSE new file mode 100644 index 0000000..68bb87c --- /dev/null +++ b/crates/windows-kernel-build/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 S.J.R. van Schaik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/windows-kernel-build/src/lib.rs b/crates/windows-kernel-build/src/lib.rs new file mode 100644 index 0000000..d312b82 --- /dev/null +++ b/crates/windows-kernel-build/src/lib.rs @@ -0,0 +1,92 @@ +use std::path::PathBuf; + +use thiserror::Error; +use winreg::{enums::HKEY_LOCAL_MACHINE, RegKey}; + +#[derive(Debug, Error)] +pub enum Error { + #[error(transparent)] + IoError(#[from] std::io::Error), + #[error("cannot find the directory")] + DirectoryNotFound, +} + +pub enum DirectoryType { + Include, + Library, +} + +/// Retrieves the path to the Windows Kits directory. The default should be +/// `C:\Program Files (x86)\Windows Kits\10`. +pub fn get_windows_kits_dir() -> Result<PathBuf, Error> { + let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); + let key = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots"; + let dir: String = hklm.open_subkey(key)?.get_value("KitsRoot10")?; + + Ok(dir.into()) +} + +/// Retrieves the path to the kernel mode libraries. The path may look something +/// like: `C:\Program Files (x86)\Windows Kits\10\lib\10.0.18362.0\km`. +pub fn get_km_dir(dir_type: DirectoryType) -> Result<PathBuf, Error> { + // We first append lib to the path and read the directory.. + let dir = get_windows_kits_dir()? + .join(match dir_type { + DirectoryType::Include => "Include", + DirectoryType::Library => "Lib", + }) + .read_dir()?; + + // In the lib directory we may have one or more directories named after the + // version of Windows, we will be looking for the highest version number. + let dir = dir + .filter_map(|dir| dir.ok()) + .map(|dir| dir.path()) + .filter(|dir| { + dir + .components() + .last() + .and_then(|c| c.as_os_str().to_str()) + .map(|c| c.starts_with("10.") && dir.join("km").is_dir()) + .unwrap_or(false) + }) + .max() + .ok_or_else(|| Error::DirectoryNotFound)?; + + // Finally append km to the path to get the path to the kernel mode libraries. + Ok(dir.join("km")) +} + +pub fn build() -> Result<(), Error> { + // Get the path to the kernel libraries. + let dir = get_km_dir(DirectoryType::Library).unwrap(); + + // Append the architecture based on our target. + let target = std::env::var("TARGET").unwrap(); + + let arch = if target.contains("x86_64") { + "x64" + } else if target.contains("i686") { + "x86" + } else { + panic!("The target {} is currently not supported.", target); + }; + + let dir = dir.join(arch); + + // Specify the link path. + println!("cargo:rustc-link-search=native={}", dir.to_str().unwrap()); + + // Ensure the right linker flags are passed for building a driver. + println!("cargo:rustc-link-arg=/NODEFAULTLIB"); + println!("cargo:rustc-link-arg=/SUBSYSTEM:NATIVE"); + println!("cargo:rustc-link-arg=/DRIVER"); + println!("cargo:rustc-link-arg=/DYNAMICBASE"); + println!("cargo:rustc-link-arg=/MANIFEST:NO"); + println!("cargo:rustc-link-arg=/ENTRY:driver_entry"); + println!("cargo:rustc-link-arg=/MERGE:.edata=.rdata"); + println!("cargo:rustc-link-arg=/MERGE:.rustc=.data"); + println!("cargo:rustc-link-arg=/INTEGRITYCHECK"); + + Ok(()) +} diff --git a/crates/windows-kernel-rs/Cargo.toml b/crates/windows-kernel-rs/Cargo.toml new file mode 100644 index 0000000..e8f4660 --- /dev/null +++ b/crates/windows-kernel-rs/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "windows-kernel-rs" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + +[features] +default = ["alloc"] +alloc = [] +system = [] + +[dependencies] +bitflags = "1.3" +cty = "0.2" +lazy_static = { version = "1.4", features = ["spin_no_std"] } +widestring = { version = "0.4", default-features = false, features = ["alloc"]} +windows-kernel-sys = { path = "../windows-kernel-sys" } diff --git a/crates/windows-kernel-rs/LICENSE b/crates/windows-kernel-rs/LICENSE new file mode 100644 index 0000000..68bb87c --- /dev/null +++ b/crates/windows-kernel-rs/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 S.J.R. van Schaik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/windows-kernel-rs/rust-toolchain b/crates/windows-kernel-rs/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/crates/windows-kernel-rs/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/crates/windows-kernel-rs/src/affinity.rs b/crates/windows-kernel-rs/src/affinity.rs new file mode 100644 index 0000000..bf2615f --- /dev/null +++ b/crates/windows-kernel-rs/src/affinity.rs @@ -0,0 +1,108 @@ +//! This module provides functions to get information about the logical CPUs in +//! the system, and to run closures on specific or all CPUs. + +use windows_kernel_sys::{ + base::{ALL_PROCESSOR_GROUPS, GROUP_AFFINITY, PROCESSOR_NUMBER, ULONG_PTR}, + ntoskrnl::{ + KeGetCurrentProcessorNumberEx, + KeGetProcessorNumberFromIndex, + KeIpiGenericCall, + KeQueryActiveProcessorCountEx, + KeRevertToUserGroupAffinityThread, + KeSetSystemGroupAffinityThread, + }, +}; + +use crate::error::{Error, IntoResult}; + +/// Uses [`KeGetCurrentProcessorNumberEx`] to get the logical number associated +/// with the CPU that is currently running our code. +pub fn get_current_cpu_num() -> u32 { + unsafe { KeGetCurrentProcessorNumberEx(core::ptr::null_mut()) } +} + +/// Uses [`KeQueryActiveProcessorCountEx`] to get the number of CPUs in the +/// system, that is all the CPUs from all the different CPU groups are counted, +/// such that each of them has a logical number. +pub fn get_cpu_count() -> u32 { + unsafe { KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS as _) } +} + +/// This is the callback used by [`run_on_each_cpu_parallel`] to run the closure +/// on all CPUs. +unsafe extern "C" fn broadcast_callback<F>(context: ULONG_PTR) -> ULONG_PTR +where F: FnMut() { + let f = &mut *(context as *mut F); + f(); + + 0 +} + +/// Runs the given closure on all CPUs in the system without interrupting all +/// CPUs to force them to switch to kernel mode. Instead, this is a more +/// graceful version that simply relies on [`run_on_cpu`] to switch to all the +/// possible CPUs by configuring the affinity, and execute the closure on the +/// selected CPU. Upon executing the closure on all CPUs, the affinity is +/// restored. Also see [`run_on_each_cpu_parallel`] which is a more aggressive +/// version that relies on an IPI to run a given closure on all CPUs in +/// parallel. +pub fn run_on_each_cpu<F>(f: &mut F) -> Result<(), Error> +where F: FnMut() -> Result<(), Error> { + for cpu_num in 0..get_cpu_count() { + run_on_cpu(cpu_num, f)?; + } + + Ok(()) +} + +/// Runs the given closure on all CPUs in the system by broadcasting an +/// Inter-Processor Interrupt (IPI) to interrupt all CPUs to force them to +/// switch to kernel mode to run the given closure. Upon execution of the +/// closure, these CPUs resume their work. Also see [`run_on_each_cpu`] which is +/// a friendlier version that does not rely on an IPI but instead configures the +/// affinity to run a given a closure on all CPUs. +pub fn run_on_each_cpu_parallel<F>(f: &F) +where F: Fn() { + unsafe { + KeIpiGenericCall(Some(broadcast_callback::<F>), f as *const _ as ULONG_PTR); + } +} + +/// Runs the given closure on the CPU with the given CPU number by temporarily +/// configuring the CPU affinity to only contain the given CPU number. Upon +/// switching to the selected CPU, the CPU executes the closure. Then the +/// original affinity is restored. +pub fn run_on_cpu<F>(cpu_num: u32, f: &mut F) -> Result<(), Error> +where F: FnMut() -> Result<(), Error> { + let mut processor_num = PROCESSOR_NUMBER { + Group: 0, + Number: 0, + Reserved: 0, + }; + + unsafe { KeGetProcessorNumberFromIndex(cpu_num, &mut processor_num) }.into_result()?; + + let mut previous = GROUP_AFFINITY { + Mask: 0, + Group: 0, + Reserved: [0; 3], + }; + + let mut affinity = GROUP_AFFINITY { + Mask: 1 << processor_num.Number, + Group: processor_num.Group, + Reserved: [0; 3], + }; + + unsafe { + KeSetSystemGroupAffinityThread(&mut affinity, &mut previous); + } + + let result = f(); + + unsafe { + KeRevertToUserGroupAffinityThread(&mut previous); + } + + result +} diff --git a/crates/windows-kernel-rs/src/allocator.rs b/crates/windows-kernel-rs/src/allocator.rs new file mode 100644 index 0000000..7b1efc8 --- /dev/null +++ b/crates/windows-kernel-rs/src/allocator.rs @@ -0,0 +1,72 @@ +//! This module provides an allocator to use with the [`alloc`] crate. You can +//! define your own global allocator with the `#[global_allocator]` attribute +//! when not using the `alloc` feature, in case you want to specify your own tag +//! to use with [`ExAllocatePool2`] and [`ExAllocatePoolWithTag`]. + +use core::alloc::{GlobalAlloc, Layout}; + +use lazy_static::lazy_static; +use windows_kernel_sys::{ + base::_POOL_TYPE as POOL_TYPE, + ntoskrnl::{ExAllocatePool2, ExAllocatePoolWithTag, ExFreePool}, +}; + +use crate::version::VersionInfo; + +/// See issue #52191. +#[alloc_error_handler] +fn alloc_error(_: Layout) -> ! { loop {} } + +lazy_static! { + /// The version of Microsoft Windows that is currently running. This is used by + /// [`KernelAllocator`] to determine whether to use [`ExAllocatePool2`] or + /// [`ExAllocatePoolWithTag`]. + static ref VERSION_INFO: VersionInfo = { + VersionInfo::query().unwrap() + }; +} + +/// Represents a kernel allocator that relies on the `ExAllocatePool` family of +/// functions to allocate and free memory for the `alloc` crate. +pub struct KernelAllocator { + /// The 32-bit tag to use for the pool, this is usually derived from a + /// quadruplet of ASCII bytes, e.g. by invoking + /// `u32::from_ne_bytes(*b"rust")`. + tag: u32, +} + +impl KernelAllocator { + /// Sets up a new kernel allocator with the 32-bit tag specified. The tag is + /// usually derived from a quadruplet of ASCII bytes, e.g. by invoking + /// `u32::from_ne_bytes(*b"rust")`. + pub const fn new(tag: u32) -> Self { + Self { + tag, + } + } +} + +unsafe impl GlobalAlloc for KernelAllocator { + /// Uses [`ExAllocatePool2`] on Microsoft Windows 10.0.19041 and later, and + /// [`ExAllocatePoolWithTag`] on older versions of Microsoft Windows to + /// allocate memory. + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let use_ex_allocate_pool2 = VERSION_INFO.major() > 10 + || (VERSION_INFO.major() == 10 && VERSION_INFO.build_number() == 19041); + + let ptr = if use_ex_allocate_pool2 { + ExAllocatePool2(POOL_TYPE::NonPagedPool as _, layout.size() as u64, self.tag) + } else { + ExAllocatePoolWithTag(POOL_TYPE::NonPagedPool, layout.size() as u64, self.tag) + }; + + if ptr.is_null() { + panic!("[kernel-alloc] failed to allocate pool."); + } + + ptr as _ + } + + /// Uses [`ExFreePool`] to free allocated memory. + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { ExFreePool(ptr as _) } +} diff --git a/crates/windows-kernel-rs/src/device.rs b/crates/windows-kernel-rs/src/device.rs new file mode 100644 index 0000000..88ae85b --- /dev/null +++ b/crates/windows-kernel-rs/src/device.rs @@ -0,0 +1,443 @@ +use alloc::boxed::Box; + +use bitflags::bitflags; +use windows_kernel_sys::{ + base::{ + DEVICE_OBJECT, + IRP, + IRP_MJ_CLEANUP, + IRP_MJ_CLOSE, + IRP_MJ_CREATE, + IRP_MJ_DEVICE_CONTROL, + IRP_MJ_READ, + IRP_MJ_WRITE, + NTSTATUS, + STATUS_SUCCESS, + }, + ntoskrnl::{IoDeleteDevice, IoGetCurrentIrpStackLocation}, +}; + +use crate::{ + error::Error, + request::{IoControlRequest, IoRequest, ReadRequest, WriteRequest}, +}; + +#[derive(Copy, Clone, Debug)] +pub enum Access { + NonExclusive, + Exclusive, +} + +impl Access { + pub fn is_exclusive(&self) -> bool { + match *self { + Access::Exclusive => true, + _ => false, + } + } +} + +bitflags! { + pub struct DeviceFlags: u32 { + const SECURE_OPEN = windows_kernel_sys::base::FILE_DEVICE_SECURE_OPEN; + } +} + +bitflags! { + pub struct DeviceDoFlags: u32 { + const DO_BUFFERED_IO = windows_kernel_sys::base::DO_BUFFERED_IO; + const DO_DIRECT_IO = windows_kernel_sys::base::DO_DIRECT_IO; + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum DeviceType { + Port8042, + Acpi, + Battery, + Beep, + BusExtender, + Cdrom, + CdromFileSystem, + Changer, + Controller, + DataLink, + Dfs, + DfsFileSystem, + DfsVolume, + Disk, + DiskFileSystem, + Dvd, + FileSystem, + Fips, + FullscreenVideo, + InportPort, + Keyboard, + Ks, + Ksec, + Mailslot, + MassStorage, + MidiIn, + MidiOut, + Modem, + Mouse, + MultiUncProvider, + NamedPipe, + Network, + NetworkBrowser, + NetworkFileSystem, + NetworkRedirector, + Null, + ParallelPort, + PhysicalNetcard, + Printer, + Scanner, + Screen, + Serenum, + SerialPort, + SerialMousePort, + Smartcard, + Smb, + Sound, + Streams, + Tape, + TapeFileSystem, + Termsrv, + Transport, + Unknown, + Vdm, + Video, + VirtualDisk, + WaveIn, + WaveOut, +} + +impl Into<u32> for DeviceType { + fn into(self) -> u32 { + match self { + DeviceType::Port8042 => windows_kernel_sys::base::FILE_DEVICE_8042_PORT, + DeviceType::Acpi => windows_kernel_sys::base::FILE_DEVICE_ACPI, + DeviceType::Battery => windows_kernel_sys::base::FILE_DEVICE_BATTERY, + DeviceType::Beep => windows_kernel_sys::base::FILE_DEVICE_BEEP, + DeviceType::BusExtender => windows_kernel_sys::base::FILE_DEVICE_BUS_EXTENDER, + DeviceType::Cdrom => windows_kernel_sys::base::FILE_DEVICE_CD_ROM, + DeviceType::CdromFileSystem => windows_kernel_sys::base::FILE_DEVICE_CD_ROM_FILE_SYSTEM, + DeviceType::Changer => windows_kernel_sys::base::FILE_DEVICE_CHANGER, + DeviceType::Controller => windows_kernel_sys::base::FILE_DEVICE_CONTROLLER, + DeviceType::DataLink => windows_kernel_sys::base::FILE_DEVICE_DATALINK, + DeviceType::Dfs => windows_kernel_sys::base::FILE_DEVICE_DFS, + DeviceType::DfsFileSystem => windows_kernel_sys::base::FILE_DEVICE_DFS_FILE_SYSTEM, + DeviceType::DfsVolume => windows_kernel_sys::base::FILE_DEVICE_DFS_VOLUME, + DeviceType::Disk => windows_kernel_sys::base::FILE_DEVICE_DISK, + DeviceType::DiskFileSystem => windows_kernel_sys::base::FILE_DEVICE_DISK_FILE_SYSTEM, + DeviceType::Dvd => windows_kernel_sys::base::FILE_DEVICE_DVD, + DeviceType::FileSystem => windows_kernel_sys::base::FILE_DEVICE_FILE_SYSTEM, + DeviceType::Fips => windows_kernel_sys::base::FILE_DEVICE_FIPS, + DeviceType::FullscreenVideo => windows_kernel_sys::base::FILE_DEVICE_FULLSCREEN_VIDEO, + DeviceType::InportPort => windows_kernel_sys::base::FILE_DEVICE_INPORT_PORT, + DeviceType::Keyboard => windows_kernel_sys::base::FILE_DEVICE_KEYBOARD, + DeviceType::Ks => windows_kernel_sys::base::FILE_DEVICE_KS, + DeviceType::Ksec => windows_kernel_sys::base::FILE_DEVICE_KSEC, + DeviceType::Mailslot => windows_kernel_sys::base::FILE_DEVICE_MAILSLOT, + DeviceType::MassStorage => windows_kernel_sys::base::FILE_DEVICE_MASS_STORAGE, + DeviceType::MidiIn => windows_kernel_sys::base::FILE_DEVICE_MIDI_IN, + DeviceType::MidiOut => windows_kernel_sys::base::FILE_DEVICE_MIDI_OUT, + DeviceType::Modem => windows_kernel_sys::base::FILE_DEVICE_MODEM, + DeviceType::Mouse => windows_kernel_sys::base::FILE_DEVICE_MOUSE, + DeviceType::MultiUncProvider => windows_kernel_sys::base::FILE_DEVICE_MULTI_UNC_PROVIDER, + DeviceType::NamedPipe => windows_kernel_sys::base::FILE_DEVICE_NAMED_PIPE, + DeviceType::Network => windows_kernel_sys::base::FILE_DEVICE_NETWORK, + DeviceType::NetworkBrowser => windows_kernel_sys::base::FILE_DEVICE_NETWORK_BROWSER, + DeviceType::NetworkFileSystem => windows_kernel_sys::base::FILE_DEVICE_NETWORK_FILE_SYSTEM, + DeviceType::NetworkRedirector => windows_kernel_sys::base::FILE_DEVICE_NETWORK_REDIRECTOR, + DeviceType::Null => windows_kernel_sys::base::FILE_DEVICE_NULL, + DeviceType::ParallelPort => windows_kernel_sys::base::FILE_DEVICE_PARALLEL_PORT, + DeviceType::PhysicalNetcard => windows_kernel_sys::base::FILE_DEVICE_PHYSICAL_NETCARD, + DeviceType::Printer => windows_kernel_sys::base::FILE_DEVICE_PRINTER, + DeviceType::Scanner => windows_kernel_sys::base::FILE_DEVICE_SCANNER, + DeviceType::Screen => windows_kernel_sys::base::FILE_DEVICE_SCREEN, + DeviceType::Serenum => windows_kernel_sys::base::FILE_DEVICE_SERENUM, + DeviceType::SerialMousePort => windows_kernel_sys::base::FILE_DEVICE_SERIAL_MOUSE_PORT, + DeviceType::SerialPort => windows_kernel_sys::base::FILE_DEVICE_SERIAL_PORT, + DeviceType::Smartcard => windows_kernel_sys::base::FILE_DEVICE_SMARTCARD, + DeviceType::Smb => windows_kernel_sys::base::FILE_DEVICE_SMB, + DeviceType::Sound => windows_kernel_sys::base::FILE_DEVICE_SOUND, + DeviceType::Streams => windows_kernel_sys::base::FILE_DEVICE_STREAMS, + DeviceType::Tape => windows_kernel_sys::base::FILE_DEVICE_TAPE, + DeviceType::TapeFileSystem => windows_kernel_sys::base::FILE_DEVICE_TAPE_FILE_SYSTEM, + DeviceType::Termsrv => windows_kernel_sys::base::FILE_DEVICE_TERMSRV, + DeviceType::Transport => windows_kernel_sys::base::FILE_DEVICE_TRANSPORT, + DeviceType::Unknown => windows_kernel_sys::base::FILE_DEVICE_UNKNOWN, + DeviceType::Vdm => windows_kernel_sys::base::FILE_DEVICE_VDM, + DeviceType::Video => windows_kernel_sys::base::FILE_DEVICE_VIDEO, + DeviceType::VirtualDisk => windows_kernel_sys::base::FILE_DEVICE_VIRTUAL_DISK, + DeviceType::WaveIn => windows_kernel_sys::base::FILE_DEVICE_WAVE_IN, + DeviceType::WaveOut => windows_kernel_sys::base::FILE_DEVICE_WAVE_OUT, + } + } +} + +impl From<u32> for DeviceType { + fn from(value: u32) -> Self { + match value { + windows_kernel_sys::base::FILE_DEVICE_8042_PORT => DeviceType::Port8042, + windows_kernel_sys::base::FILE_DEVICE_ACPI => DeviceType::Acpi, + windows_kernel_sys::base::FILE_DEVICE_BATTERY => DeviceType::Battery, + windows_kernel_sys::base::FILE_DEVICE_BEEP => DeviceType::Beep, + windows_kernel_sys::base::FILE_DEVICE_BUS_EXTENDER => DeviceType::BusExtender, + windows_kernel_sys::base::FILE_DEVICE_CD_ROM => DeviceType::Cdrom, + windows_kernel_sys::base::FILE_DEVICE_CD_ROM_FILE_SYSTEM => DeviceType::CdromFileSystem, + windows_kernel_sys::base::FILE_DEVICE_CHANGER => DeviceType::Changer, + windows_kernel_sys::base::FILE_DEVICE_CONTROLLER => DeviceType::Controller, + windows_kernel_sys::base::FILE_DEVICE_DATALINK => DeviceType::DataLink, + windows_kernel_sys::base::FILE_DEVICE_DFS => DeviceType::Dfs, + windows_kernel_sys::base::FILE_DEVICE_DFS_FILE_SYSTEM => DeviceType::DfsFileSystem, + windows_kernel_sys::base::FILE_DEVICE_DFS_VOLUME => DeviceType::DfsVolume, + windows_kernel_sys::base::FILE_DEVICE_DISK => DeviceType::Disk, + windows_kernel_sys::base::FILE_DEVICE_DISK_FILE_SYSTEM => DeviceType::DiskFileSystem, + windows_kernel_sys::base::FILE_DEVICE_DVD => DeviceType::Dvd, + windows_kernel_sys::base::FILE_DEVICE_FILE_SYSTEM => DeviceType::FileSystem, + windows_kernel_sys::base::FILE_DEVICE_FIPS => DeviceType::Fips, + windows_kernel_sys::base::FILE_DEVICE_FULLSCREEN_VIDEO => DeviceType::FullscreenVideo, + windows_kernel_sys::base::FILE_DEVICE_INPORT_PORT => DeviceType::InportPort, + windows_kernel_sys::base::FILE_DEVICE_KEYBOARD => DeviceType::Keyboard, + windows_kernel_sys::base::FILE_DEVICE_KS => DeviceType::Ks, + windows_kernel_sys::base::FILE_DEVICE_KSEC => DeviceType::Ksec, + windows_kernel_sys::base::FILE_DEVICE_MAILSLOT => DeviceType::Mailslot, + windows_kernel_sys::base::FILE_DEVICE_MASS_STORAGE => DeviceType::MassStorage, + windows_kernel_sys::base::FILE_DEVICE_MIDI_IN => DeviceType::MidiIn, + windows_kernel_sys::base::FILE_DEVICE_MIDI_OUT => DeviceType::MidiOut, + windows_kernel_sys::base::FILE_DEVICE_MODEM => DeviceType::Modem, + windows_kernel_sys::base::FILE_DEVICE_MOUSE => DeviceType::Mouse, + windows_kernel_sys::base::FILE_DEVICE_MULTI_UNC_PROVIDER => DeviceType::MultiUncProvider, + windows_kernel_sys::base::FILE_DEVICE_NAMED_PIPE => DeviceType::NamedPipe, + windows_kernel_sys::base::FILE_DEVICE_NETWORK => DeviceType::Network, + windows_kernel_sys::base::FILE_DEVICE_NETWORK_BROWSER => DeviceType::NetworkBrowser, + windows_kernel_sys::base::FILE_DEVICE_NETWORK_FILE_SYSTEM => DeviceType::NetworkFileSystem, + windows_kernel_sys::base::FILE_DEVICE_NETWORK_REDIRECTOR => DeviceType::NetworkRedirector, + windows_kernel_sys::base::FILE_DEVICE_NULL => DeviceType::Null, + windows_kernel_sys::base::FILE_DEVICE_PARALLEL_PORT => DeviceType::ParallelPort, + windows_kernel_sys::base::FILE_DEVICE_PHYSICAL_NETCARD => DeviceType::PhysicalNetcard, + windows_kernel_sys::base::FILE_DEVICE_PRINTER => DeviceType::Printer, + windows_kernel_sys::base::FILE_DEVICE_SCANNER => DeviceType::Scanner, + windows_kernel_sys::base::FILE_DEVICE_SCREEN => DeviceType::Screen, + windows_kernel_sys::base::FILE_DEVICE_SERENUM => DeviceType::Serenum, + windows_kernel_sys::base::FILE_DEVICE_SERIAL_MOUSE_PORT => DeviceType::SerialMousePort, + windows_kernel_sys::base::FILE_DEVICE_SERIAL_PORT => DeviceType::SerialPort, + windows_kernel_sys::base::FILE_DEVICE_SMARTCARD => DeviceType::Smartcard, + windows_kernel_sys::base::FILE_DEVICE_SMB => DeviceType::Smb, + windows_kernel_sys::base::FILE_DEVICE_SOUND => DeviceType::Sound, + windows_kernel_sys::base::FILE_DEVICE_STREAMS => DeviceType::Streams, + windows_kernel_sys::base::FILE_DEVICE_TAPE => DeviceType::Tape, + windows_kernel_sys::base::FILE_DEVICE_TAPE_FILE_SYSTEM => DeviceType::TapeFileSystem, + windows_kernel_sys::base::FILE_DEVICE_TERMSRV => DeviceType::Termsrv, + windows_kernel_sys::base::FILE_DEVICE_TRANSPORT => DeviceType::Transport, + windows_kernel_sys::base::FILE_DEVICE_UNKNOWN => DeviceType::Unknown, + windows_kernel_sys::base::FILE_DEVICE_VDM => DeviceType::Vdm, + windows_kernel_sys::base::FILE_DEVICE_VIDEO => DeviceType::Video, + windows_kernel_sys::base::FILE_DEVICE_VIRTUAL_DISK => DeviceType::VirtualDisk, + windows_kernel_sys::base::FILE_DEVICE_WAVE_IN => DeviceType::WaveIn, + windows_kernel_sys::base::FILE_DEVICE_WAVE_OUT => DeviceType::WaveOut, + _ => DeviceType::Unknown, + } + } +} + +#[repr(C)] +pub struct device_operations { + dispatch: Option<extern "C" fn(*mut DEVICE_OBJECT, *mut IRP, u8) -> NTSTATUS>, + release: Option<extern "C" fn(*mut DEVICE_OBJECT)>, +} + +pub struct Device { + raw: *mut DEVICE_OBJECT, +} + +unsafe impl Send for Device {} +unsafe impl Sync for Device {} + +impl Device { + pub unsafe fn from_raw(raw: *mut DEVICE_OBJECT) -> Self { + Self { + raw, + } + } + + pub unsafe fn as_raw(&self) -> *const DEVICE_OBJECT { self.raw as *const _ } + + pub unsafe fn as_raw_mut(&self) -> *mut DEVICE_OBJECT { self.raw } + + pub fn into_raw(mut self) -> *mut DEVICE_OBJECT { + core::mem::replace(&mut self.raw, core::ptr::null_mut()) + } + + pub(crate) fn extension(&self) -> &DeviceExtension { + unsafe { &*((*self.raw).DeviceExtension as *const DeviceExtension) } + } + + pub(crate) fn extension_mut(&self) -> &mut DeviceExtension { + unsafe { &mut *((*self.raw).DeviceExtension as *mut DeviceExtension) } + } + + pub(crate) fn device_type(&self) -> DeviceType { self.extension().device_type } + + pub(crate) fn vtable(&self) -> &device_operations { + unsafe { &*(self.extension().vtable as *const _) } + } + + pub fn data<T: DeviceOperations>(&self) -> &T { unsafe { &*(self.extension().data as *const T) } } + + pub fn data_mut<T: DeviceOperations>(&self) -> &mut T { + unsafe { &mut *(self.extension().data as *mut T) } + } +} + +impl Drop for Device { + fn drop(&mut self) { + if self.raw.is_null() { + return; + } + + unsafe { + if let Some(release) = self.vtable().release { + release(self.raw); + } + + IoDeleteDevice(self.raw); + } + } +} + +pub struct RequestError(pub Error, pub IoRequest); + +pub enum Completion { + Complete(u32, IoRequest), +} + +pub trait DeviceOperations: Sync + Sized { + fn create(&mut self, _device: &Device, request: IoRequest) -> Result<Completion, RequestError> { + Ok(Completion::Complete(0, request)) + } + + fn close(&mut self, _device: &Device, request: IoRequest) -> Result<Completion, RequestError> { + Ok(Completion::Complete(0, request)) + } + + fn cleanup(&mut self, _device: &Device, request: IoRequest) -> Result<Completion, RequestError> { + Ok(Completion::Complete(0, request)) + } + + fn read(&mut self, _device: &Device, request: ReadRequest) -> Result<Completion, RequestError> { + Ok(Completion::Complete(0, request.into())) + } + + fn write(&mut self, _device: &Device, request: WriteRequest) -> Result<Completion, RequestError> { + Ok(Completion::Complete(0, request.into())) + } + + fn ioctl( + &mut self, + _device: &Device, + request: IoControlRequest, + ) -> Result<Completion, RequestError> { + Ok(Completion::Complete(0, request.into())) + } +} + +extern "C" fn dispatch_callback<T: DeviceOperations>( + device: *mut DEVICE_OBJECT, + irp: *mut IRP, + major: u8, +) -> NTSTATUS { + let device = unsafe { Device::from_raw(device) }; + let data: &mut T = device.data_mut(); + let request = unsafe { IoRequest::from_raw(irp) }; + + let result = match major as _ { + IRP_MJ_CREATE => data.create(&device, request), + IRP_MJ_CLOSE => data.close(&device, request), + IRP_MJ_CLEANUP => data.cleanup(&device, request), + IRP_MJ_READ => { + let read_request = ReadRequest { + inner: request + }; + + data.read(&device, read_request) + } + IRP_MJ_WRITE => { + let write_request = WriteRequest { + inner: request + }; + + data.write(&device, write_request) + } + IRP_MJ_DEVICE_CONTROL => { + let control_request = IoControlRequest { + inner: request + }; + + if device.device_type() == control_request.control_code().device_type() { + data.ioctl(&device, control_request) + } else { + Err(RequestError( + Error::INVALID_PARAMETER, + control_request.into(), + )) + } + } + _ => Err(RequestError(Error::INVALID_PARAMETER, request)), + }; + + device.into_raw(); + + match result { + Ok(Completion::Complete(size, request)) => { + request.complete(Ok(size)); + STATUS_SUCCESS + } + Err(RequestError(e, request)) => { + let status = e.to_ntstatus(); + request.complete(Err(e)); + status + } + } +} + +extern "C" fn release_callback<T: DeviceOperations>(device: *mut DEVICE_OBJECT) { + unsafe { + let extension = (*device).DeviceExtension as *mut DeviceExtension; + + let ptr = core::mem::replace(&mut (*extension).data, core::ptr::null_mut()); + Box::from_raw(ptr as *mut T); + } +} + +pub(crate) struct DeviceOperationsVtable<T>(core::marker::PhantomData<T>); + +impl<T: DeviceOperations> DeviceOperationsVtable<T> { + pub(crate) const VTABLE: device_operations = device_operations { + dispatch: Some(dispatch_callback::<T>), + release: Some(release_callback::<T>), + }; +} + +#[repr(C)] +pub struct DeviceExtension { + pub(crate) vtable: *const device_operations, + pub(crate) data: *mut cty::c_void, + pub(crate) device_type: DeviceType, +} + +pub extern "C" fn dispatch_device(device: *mut DEVICE_OBJECT, irp: *mut IRP) -> NTSTATUS { + let stack_location = unsafe { &*IoGetCurrentIrpStackLocation(irp) }; + let device = unsafe { Device::from_raw(device) }; + let vtable = device.vtable(); + + match vtable.dispatch { + Some(dispatch) => dispatch(device.into_raw(), irp, stack_location.MajorFunction), + _ => { + device.into_raw(); + STATUS_SUCCESS + } + } +} diff --git a/crates/windows-kernel-rs/src/driver.rs b/crates/windows-kernel-rs/src/driver.rs new file mode 100644 index 0000000..2bdac4e --- /dev/null +++ b/crates/windows-kernel-rs/src/driver.rs @@ -0,0 +1,85 @@ +use alloc::boxed::Box; + +use widestring::U16CString; +use windows_kernel_sys::{base::DRIVER_OBJECT, ntoskrnl::IoCreateDevice}; + +use crate::{ + device::{ + Access, + Device, + DeviceDoFlags, + DeviceExtension, + DeviceFlags, + DeviceOperations, + DeviceOperationsVtable, + DeviceType, + }, + error::{Error, IntoResult}, + string::create_unicode_string, +}; + +pub struct Driver { + pub(crate) raw: *mut DRIVER_OBJECT, +} + +impl Driver { + pub unsafe fn from_raw(raw: *mut DRIVER_OBJECT) -> Self { + Self { + raw, + } + } + + pub unsafe fn as_raw(&self) -> *const DRIVER_OBJECT { self.raw as _ } + + pub unsafe fn as_raw_mut(&mut self) -> *mut DRIVER_OBJECT { self.raw as _ } + + pub fn create_device<T>( + &mut self, + name: &str, + device_type: DeviceType, + device_flags: DeviceFlags, + device_do_flags: DeviceDoFlags, + access: Access, + data: T, + ) -> Result<Device, Error> + where + T: DeviceOperations, + { + // Box the data. + let data = Box::new(data); + + // Convert the name to UTF-16 and then create a UNICODE_STRING. + let name = U16CString::from_str(name).unwrap(); + let mut name = create_unicode_string(name.as_slice()); + + // Create the device. + let mut device = core::ptr::null_mut(); + + unsafe { + IoCreateDevice( + self.raw, + core::mem::size_of::<DeviceExtension>() as u32, + &mut name, + device_type.into(), + device_flags.bits(), + access.is_exclusive() as _, + &mut device, + ) + } + .into_result()?; + + unsafe { + (*device).Flags |= device_do_flags.bits(); + } + + let device = unsafe { Device::from_raw(device) }; + + // Store the boxed data and vtable. + let extension = device.extension_mut(); + extension.device_type = device_type; + extension.vtable = &DeviceOperationsVtable::<T>::VTABLE; + extension.data = Box::into_raw(data) as *mut cty::c_void; + + Ok(device) + } +} diff --git a/crates/windows-kernel-rs/src/error.rs b/crates/windows-kernel-rs/src/error.rs new file mode 100644 index 0000000..075287f --- /dev/null +++ b/crates/windows-kernel-rs/src/error.rs @@ -0,0 +1,87 @@ +use windows_kernel_sys::base::{ + NTSTATUS, + STATUS_ACCESS_VIOLATION, + STATUS_ARRAY_BOUNDS_EXCEEDED, + STATUS_BREAKPOINT, + STATUS_DATATYPE_MISALIGNMENT, + STATUS_END_OF_FILE, + STATUS_FLOAT_DENORMAL_OPERAND, + STATUS_FLOAT_DIVIDE_BY_ZERO, + STATUS_FLOAT_INEXACT_RESULT, + STATUS_FLOAT_INVALID_OPERATION, + STATUS_FLOAT_OVERFLOW, + STATUS_FLOAT_STACK_CHECK, + STATUS_FLOAT_UNDERFLOW, + STATUS_GUARD_PAGE_VIOLATION, + STATUS_ILLEGAL_INSTRUCTION, + STATUS_INSUFFICIENT_RESOURCES, + STATUS_INTEGER_DIVIDE_BY_ZERO, + STATUS_INTEGER_OVERFLOW, + STATUS_INVALID_DISPOSITION, + STATUS_INVALID_HANDLE, + STATUS_INVALID_PARAMETER, + STATUS_INVALID_USER_BUFFER, + STATUS_IN_PAGE_ERROR, + STATUS_NONCONTINUABLE_EXCEPTION, + STATUS_NOT_IMPLEMENTED, + STATUS_NO_MEMORY, + STATUS_PRIVILEGED_INSTRUCTION, + STATUS_SINGLE_STEP, + STATUS_STACK_OVERFLOW, + STATUS_SUCCESS, + STATUS_UNSUCCESSFUL, + STATUS_UNWIND_CONSOLIDATE, +}; + +#[derive(Clone, Copy, Debug)] +pub struct Error(NTSTATUS); + +impl Error { + pub const ACCESS_VIOLATION: Error = Error(STATUS_ACCESS_VIOLATION); + pub const ARRAY_BOUNDS_EXCEEDED: Error = Error(STATUS_ARRAY_BOUNDS_EXCEEDED); + pub const BREAKPOINT: Error = Error(STATUS_BREAKPOINT); + pub const DATATYPE_MISALIGNMENT: Error = Error(STATUS_DATATYPE_MISALIGNMENT); + pub const END_OF_FILE: Error = Error(STATUS_END_OF_FILE); + pub const FLOAT_DENORMAL_OPERAND: Error = Error(STATUS_FLOAT_DENORMAL_OPERAND); + pub const FLOAT_DIVIDE_BY_ZERO: Error = Error(STATUS_FLOAT_DIVIDE_BY_ZERO); + pub const FLOAT_INEXACT_RESULT: Error = Error(STATUS_FLOAT_INEXACT_RESULT); + pub const FLOAT_INVALID_OPERATION: Error = Error(STATUS_FLOAT_INVALID_OPERATION); + pub const FLOAT_OVERFLOW: Error = Error(STATUS_FLOAT_OVERFLOW); + pub const FLOAT_STACK_CHECK: Error = Error(STATUS_FLOAT_STACK_CHECK); + pub const FLOAT_UNDERFLOW: Error = Error(STATUS_FLOAT_UNDERFLOW); + pub const GUARD_PAGE_VIOLATION: Error = Error(STATUS_GUARD_PAGE_VIOLATION); + pub const ILLEGAL_INSTRUCTION: Error = Error(STATUS_ILLEGAL_INSTRUCTION); + pub const INSUFFICIENT_RESOURCES: Error = Error(STATUS_INSUFFICIENT_RESOURCES); + pub const INTEGER_DIVIDE_BY_ZERO: Error = Error(STATUS_INTEGER_DIVIDE_BY_ZERO); + pub const INTEGER_OVERFLOW: Error = Error(STATUS_INTEGER_OVERFLOW); + pub const INVALID_DISPOSITION: Error = Error(STATUS_INVALID_DISPOSITION); + pub const INVALID_HANDLE: Error = Error(STATUS_INVALID_HANDLE); + pub const INVALID_PARAMETER: Error = Error(STATUS_INVALID_PARAMETER); + pub const INVALID_USER_BUFFER: Error = Error(STATUS_INVALID_USER_BUFFER); + pub const IN_PAGE_ERROR: Error = Error(STATUS_IN_PAGE_ERROR); + pub const NONCONTINUABLE_EXCEPTION: Error = Error(STATUS_NONCONTINUABLE_EXCEPTION); + pub const NOT_IMPLEMENTED: Error = Error(STATUS_NOT_IMPLEMENTED); + pub const NO_MEMORY: Error = Error(STATUS_NO_MEMORY); + pub const PRIVILEGED_INSTRUCTION: Error = Error(STATUS_PRIVILEGED_INSTRUCTION); + pub const SINGLE_STEP: Error = Error(STATUS_SINGLE_STEP); + pub const STACK_OVERFLOW: Error = Error(STATUS_STACK_OVERFLOW); + pub const UNSUCCESSFUL: Error = Error(STATUS_UNSUCCESSFUL); + pub const UNWIND_CONSOLIDATE: Error = Error(STATUS_UNWIND_CONSOLIDATE); + + pub fn from_ntstatus(status: NTSTATUS) -> Error { Error(status) } + + pub fn to_ntstatus(&self) -> NTSTATUS { self.0 } +} + +pub trait IntoResult { + fn into_result(self) -> Result<(), Error>; +} + +impl IntoResult for NTSTATUS { + fn into_result(self) -> Result<(), Error> { + match self { + STATUS_SUCCESS => Ok(()), + status => Err(Error::from_ntstatus(status)), + } + } +} diff --git a/crates/windows-kernel-rs/src/intrin.rs b/crates/windows-kernel-rs/src/intrin.rs new file mode 100644 index 0000000..f415f8d --- /dev/null +++ b/crates/windows-kernel-rs/src/intrin.rs @@ -0,0 +1,25 @@ +use windows_kernel_sys::intrin::{read_msr_safe, write_msr_safe}; + +use crate::error::{Error, IntoResult}; + +/// Attempts to read the given model-specific register. Accessing an invalid +/// model-specific register would normally result in a CPU exception. This +/// function uses Structured Exception Handling (SEH) to safely catch CPU +/// exceptions and to turn them into an [`Error`]. This prevents a hang. +pub fn read_msr(register: u32) -> Result<u64, Error> { + let mut value = 0; + + unsafe { read_msr_safe(register, &mut value) }.into_result()?; + + Ok(value) +} + +/// Attempts to write the given value to the given model-specific register. +/// Accessing an invalid model-specific register would normally result in a CPU +/// exception. This function uses Structured Handling (SEH) to safely catch CPU +/// exceptions and to turn them into an [`Error`]. This prevents a hang. +pub fn write_msr(register: u32, value: u64) -> Result<(), Error> { + unsafe { write_msr_safe(register, value) }.into_result()?; + + Ok(()) +} diff --git a/crates/windows-kernel-rs/src/io.rs b/crates/windows-kernel-rs/src/io.rs new file mode 100644 index 0000000..7d783ce --- /dev/null +++ b/crates/windows-kernel-rs/src/io.rs @@ -0,0 +1,31 @@ +use windows_kernel_sys::{base::ANSI_STRING, ntoskrnl::DbgPrint}; + +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))); +} + +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); +} + +#[doc(hidden)] +pub fn _print(args: core::fmt::Arguments) { + // Format the string using the `alloc::format!` as this is guaranteed to return + // a `String` instead of a `Result` that we would have to `unwrap`. This + // ensures that this code stays panic-free. + let s = alloc::format!("{}", args); + + // Print the string. We must make sure to not pass this user-supplied string as + // the format string, as `DbgPrint` may then format any format specifiers it + // contains. This could potentially be an attack vector. + let s = ANSI_STRING { + Length: s.len() as u16, + MaximumLength: s.len() as u16, + Buffer: s.as_ptr() as _, + }; + + unsafe { DbgPrint("%Z\0".as_ptr() as _, &s) }; +} diff --git a/crates/windows-kernel-rs/src/ioctl.rs b/crates/windows-kernel-rs/src/ioctl.rs new file mode 100644 index 0000000..c1d493f --- /dev/null +++ b/crates/windows-kernel-rs/src/ioctl.rs @@ -0,0 +1,111 @@ +use bitflags::bitflags; +use windows_kernel_sys::base::{ + FILE_ANY_ACCESS, + FILE_READ_DATA, + FILE_WRITE_DATA, + METHOD_BUFFERED, + METHOD_IN_DIRECT, + METHOD_NEITHER, + METHOD_OUT_DIRECT, +}; + +use crate::device::DeviceType; + +bitflags! { + pub struct RequiredAccess: u32 { + const ANY_ACCESS = FILE_ANY_ACCESS; + const READ_DATA = FILE_READ_DATA; + const WRITE_DATA = FILE_WRITE_DATA; + const READ_WRITE_DATA = FILE_READ_DATA | FILE_WRITE_DATA; + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum TransferMethod { + Neither = METHOD_NEITHER, + InputDirect = METHOD_IN_DIRECT, + OutputDirect = METHOD_OUT_DIRECT, + Buffered = METHOD_BUFFERED, +} + +impl From<u32> for TransferMethod { + fn from(value: u32) -> Self { + match value & 0x3 { + METHOD_NEITHER => Self::Neither, + METHOD_IN_DIRECT => Self::InputDirect, + METHOD_OUT_DIRECT => Self::OutputDirect, + METHOD_BUFFERED => Self::Buffered, + _ => unreachable!(), + } + } +} + +impl Into<u32> for TransferMethod { + fn into(self) -> u32 { + match self { + Self::Neither => METHOD_NEITHER, + Self::InputDirect => METHOD_IN_DIRECT, + Self::OutputDirect => METHOD_OUT_DIRECT, + Self::Buffered => METHOD_BUFFERED, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ControlCode( + pub DeviceType, + pub RequiredAccess, + pub u32, + pub TransferMethod, +); + +impl ControlCode { + const ACCESS_BITS: usize = 2; + const ACCESS_MASK: u32 = (1 << Self::ACCESS_BITS) - 1; + const ACCESS_SHIFT: usize = Self::NUM_SHIFT + Self::NUM_BITS; + const METHOD_BITS: usize = 2; + const METHOD_MASK: u32 = (1 << Self::METHOD_BITS) - 1; + const METHOD_SHIFT: usize = 0; + const NUM_BITS: usize = 12; + const NUM_MASK: u32 = (1 << Self::NUM_BITS) - 1; + const NUM_SHIFT: usize = Self::METHOD_SHIFT + Self::METHOD_BITS; + const TYPE_BITS: usize = 16; + const TYPE_MASK: u32 = (1 << Self::TYPE_BITS) - 1; + const TYPE_SHIFT: usize = Self::ACCESS_SHIFT + Self::ACCESS_BITS; + + pub fn device_type(&self) -> DeviceType { self.0 } + + pub fn required_access(&self) -> RequiredAccess { self.1 } + + pub fn number(&self) -> u32 { self.2 } + + pub fn transfer_method(&self) -> TransferMethod { self.3 } +} + +impl From<u32> for ControlCode { + fn from(value: u32) -> Self { + let method = (value >> Self::METHOD_SHIFT) & Self::METHOD_MASK; + let num = (value >> Self::NUM_SHIFT) & Self::NUM_MASK; + let access = (value >> Self::ACCESS_SHIFT) & Self::ACCESS_MASK; + let ty = (value >> Self::TYPE_SHIFT) & Self::TYPE_MASK; + + Self( + ty.into(), + RequiredAccess::from_bits(access).unwrap_or(RequiredAccess::READ_DATA), + num, + method.into(), + ) + } +} + +impl Into<u32> for ControlCode { + fn into(self) -> u32 { + let method = Into::<u32>::into(self.3) << Self::METHOD_SHIFT; + let num = self.2 << Self::NUM_SHIFT; + let access = self.1.bits() << Self::ACCESS_SHIFT; + let ty = Into::<u32>::into(self.0) << Self::TYPE_SHIFT; + + ty | access | num | method + } +} diff --git a/crates/windows-kernel-rs/src/lib.rs b/crates/windows-kernel-rs/src/lib.rs new file mode 100644 index 0000000..a0ddf9a --- /dev/null +++ b/crates/windows-kernel-rs/src/lib.rs @@ -0,0 +1,119 @@ +#![no_std] +#![feature(alloc_error_handler)] + +extern crate alloc; + +pub mod affinity; +pub mod allocator; +pub mod device; +pub mod driver; +pub mod error; +pub mod intrin; +pub mod io; +pub mod ioctl; +pub mod mdl; +pub mod memory; +pub mod process; +pub mod request; +pub mod section; +pub mod string; +pub mod symbolic_link; +pub mod sync; +pub mod user_ptr; +pub mod version; + +pub use widestring::U16CString; +pub use windows_kernel_sys::base::{ + DRIVER_OBJECT, + IRP_MJ_MAXIMUM_FUNCTION, + NTSTATUS, + STATUS_SUCCESS, + UNICODE_STRING, +}; + +pub use crate::{ + affinity::{get_cpu_count, get_current_cpu_num, run_on_cpu, run_on_each_cpu}, + device::{ + dispatch_device, + Access, + Completion, + Device, + DeviceDoFlags, + DeviceFlags, + DeviceOperations, + DeviceType, + RequestError, + }, + driver::Driver, + error::Error, + ioctl::{ControlCode, RequiredAccess, TransferMethod}, + request::{IoControlRequest, IoRequest, ReadRequest, WriteRequest}, + symbolic_link::SymbolicLink, + user_ptr::UserPtr, +}; + +#[cfg(feature = "alloc")] +#[global_allocator] +static ALLOCATOR: allocator::KernelAllocator = + allocator::KernelAllocator::new(u32::from_ne_bytes(*b"rust")); + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } + +#[used] +#[no_mangle] +pub static _fltused: i32 = 0; + +#[no_mangle] +pub extern "system" fn __CxxFrameHandler3() -> i32 { 0 } + +#[macro_export] +macro_rules! kernel_module { + ($module:ty) => { + static mut __MOD: Option<$module> = None; + + #[no_mangle] + pub extern "system" fn driver_entry( + driver: &mut $crate::DRIVER_OBJECT, + registry_path: &$crate::UNICODE_STRING, + ) -> $crate::NTSTATUS { + unsafe { + driver.DriverUnload = Some(driver_exit); + + for i in 0..$crate::IRP_MJ_MAXIMUM_FUNCTION { + driver.MajorFunction[i as usize] = Some($crate::dispatch_device); + } + } + + let driver = unsafe { Driver::from_raw(driver) }; + + let registry_path = unsafe { $crate::U16CString::from_ptr_str(registry_path.Buffer) }; + let registry_path = registry_path.to_string_lossy(); + + match <$module as $crate::KernelModule>::init(driver, registry_path.as_str()) { + Ok(m) => { + unsafe { + __MOD = Some(m); + } + + $crate::STATUS_SUCCESS + } + Err(e) => e.to_ntstatus(), + } + } + + pub unsafe extern "C" fn driver_exit(driver: *mut $crate::DRIVER_OBJECT) { + let driver = unsafe { Driver::from_raw(driver) }; + + match __MOD.take() { + Some(mut m) => m.cleanup(driver), + _ => (), + } + } + }; +} + +pub trait KernelModule: Sized + Sync { + fn init(driver: Driver, registry_path: &str) -> Result<Self, Error>; + fn cleanup(&mut self, _driver: Driver) {} +} diff --git a/crates/windows-kernel-rs/src/mdl.rs b/crates/windows-kernel-rs/src/mdl.rs new file mode 100644 index 0000000..7d002f0 --- /dev/null +++ b/crates/windows-kernel-rs/src/mdl.rs @@ -0,0 +1,117 @@ +use crate::{error::Error, memory::MemoryCaching}; + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum AccessMode { + KernelMode = windows_kernel_sys::base::_MODE::KernelMode, + UserMode = windows_kernel_sys::base::_MODE::UserMode, +} + +pub struct MemoryDescriptorList { + raw: *mut windows_kernel_sys::base::MDL, +} + +unsafe impl Send for MemoryDescriptorList {} +unsafe impl Sync for MemoryDescriptorList {} + +impl MemoryDescriptorList { + pub fn new(addr: *mut core::ffi::c_void, size: usize) -> Result<Self, Error> { + use windows_kernel_sys::ntoskrnl::IoAllocateMdl; + + let raw = unsafe { + IoAllocateMdl( + addr, + size as _, + false as _, + false as _, + core::ptr::null_mut(), + ) + }; + + if raw.is_null() { + return Err(Error::INSUFFICIENT_RESOURCES); + } + + Ok(Self { + raw, + }) + } + + pub fn build_for_non_paged_pool(&mut self) { + use windows_kernel_sys::ntoskrnl::MmBuildMdlForNonPagedPool; + + unsafe { + MmBuildMdlForNonPagedPool(self.raw); + } + } + + pub fn map_locked_pages( + self, + access: AccessMode, + caching: MemoryCaching, + desired_addr: Option<*mut core::ffi::c_void>, + ) -> Result<LockedMapping, Error> { + use windows_kernel_sys::ntoskrnl::MmMapLockedPagesSpecifyCache; + + let ptr = unsafe { + MmMapLockedPagesSpecifyCache( + self.raw, + access as _, + caching as _, + desired_addr.unwrap_or(core::ptr::null_mut()), + false as _, + 0, + ) + }; + + Ok(LockedMapping { + raw: self.raw, + ptr, + }) + } +} + +impl Drop for MemoryDescriptorList { + fn drop(&mut self) { + use windows_kernel_sys::ntoskrnl::IoFreeMdl; + + unsafe { + IoFreeMdl(self.raw); + } + } +} + +pub struct LockedMapping { + raw: *mut windows_kernel_sys::base::MDL, + ptr: *mut core::ffi::c_void, +} + +unsafe impl Send for LockedMapping {} +unsafe impl Sync for LockedMapping {} + +impl LockedMapping { + pub fn ptr(&self) -> *mut core::ffi::c_void { self.ptr } + + pub fn unlock(self) -> MemoryDescriptorList { + use windows_kernel_sys::ntoskrnl::MmUnmapLockedPages; + + unsafe { + MmUnmapLockedPages(self.ptr, self.raw); + } + + MemoryDescriptorList { + raw: self.raw + } + } +} + +impl Drop for LockedMapping { + fn drop(&mut self) { + use windows_kernel_sys::ntoskrnl::{IoFreeMdl, MmUnmapLockedPages}; + + unsafe { + MmUnmapLockedPages(self.ptr, self.raw); + IoFreeMdl(self.raw); + } + } +} diff --git a/crates/windows-kernel-rs/src/memory.rs b/crates/windows-kernel-rs/src/memory.rs new file mode 100644 index 0000000..cb5d2e9 --- /dev/null +++ b/crates/windows-kernel-rs/src/memory.rs @@ -0,0 +1,173 @@ +use windows_kernel_sys::base::{ + MM_COPY_ADDRESS, + MM_COPY_MEMORY_PHYSICAL, + MM_COPY_MEMORY_VIRTUAL, + PHYSICAL_ADDRESS, + _MEMORY_CACHING_TYPE as MEMORY_CACHING_TYPE, +}; + +use crate::error::{Error, IntoResult}; + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MemoryCaching { + NonCached = MEMORY_CACHING_TYPE::MmNonCached, + Cached = MEMORY_CACHING_TYPE::MmCached, + WriteCombined = MEMORY_CACHING_TYPE::MmWriteCombined, + #[cfg(feature = "system")] + HardwareCoherentCached = MEMORY_CACHING_TYPE::MmHardwareCoherentCached, + #[cfg(feature = "system")] + NonCachedUnordered = MEMORY_CACHING_TYPE::MmNonCachedUnordered, + #[cfg(feature = "system")] + USWCCached = MEMORY_CACHING_TYPE::MmUSWCCached, + NotMapped = MEMORY_CACHING_TYPE::MmNotMapped, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct PhysicalAddress(u64); + +impl From<u64> for PhysicalAddress { + fn from(value: u64) -> Self { Self(value) } +} + +impl Into<u64> for PhysicalAddress { + fn into(self) -> u64 { self.0 } +} + +impl From<PHYSICAL_ADDRESS> for PhysicalAddress { + fn from(value: PHYSICAL_ADDRESS) -> Self { Self(unsafe { value.QuadPart } as _) } +} + +impl Into<PHYSICAL_ADDRESS> for PhysicalAddress { + fn into(self) -> PHYSICAL_ADDRESS { + let mut addr: PHYSICAL_ADDRESS = unsafe { core::mem::zeroed() }; + + addr.QuadPart = self.0 as _; + + addr + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum CopyAddress { + Virtual(*mut core::ffi::c_void), + Physical(PhysicalAddress), +} + +unsafe impl Send for CopyAddress {} +unsafe impl Sync for CopyAddress {} + +impl Into<(u32, MM_COPY_ADDRESS)> for CopyAddress { + fn into(self) -> (u32, MM_COPY_ADDRESS) { + let mut copy_addr: MM_COPY_ADDRESS = unsafe { core::mem::zeroed() }; + + let flags = match self { + CopyAddress::Virtual(addr) => { + copy_addr.__bindgen_anon_1.VirtualAddress = addr as _; + MM_COPY_MEMORY_VIRTUAL + } + CopyAddress::Physical(addr) => { + copy_addr.__bindgen_anon_1.PhysicalAddress = addr.into(); + MM_COPY_MEMORY_PHYSICAL + } + }; + + (flags, copy_addr) + } +} + +pub struct IoMapping { + ptr: *mut core::ffi::c_void, + size: usize, +} + +unsafe impl Send for IoMapping {} +unsafe impl Sync for IoMapping {} + +impl IoMapping { + pub fn new(addr: PhysicalAddress, size: usize, caching: MemoryCaching) -> Result<Self, Error> { + use windows_kernel_sys::ntoskrnl::MmMapIoSpace; + + let ptr = unsafe { MmMapIoSpace(addr.into(), size as _, caching as _) }; + + if ptr.is_null() { + return Err(Error::INVALID_PARAMETER); + } + + Ok(Self { + ptr, + size, + }) + } + + pub fn ptr(&self) -> *mut core::ffi::c_void { self.ptr } + + pub fn size(&self) -> usize { self.size } +} + +impl Drop for IoMapping { + fn drop(&mut self) { + use windows_kernel_sys::ntoskrnl::MmUnmapIoSpace; + + unsafe { + MmUnmapIoSpace(self.ptr, self.size as _); + } + } +} + +#[cfg(feature = "system")] +pub fn get_virtual_for_physical(addr: PhysicalAddress) -> *mut core::ffi::c_void { + use windows_kernel_sys::ntoskrnl::MmGetVirtualForPhysical; + + let virt_addr = unsafe { MmGetVirtualForPhysical(addr.into()) }; + + virt_addr as _ +} + +pub fn read_memory(buffer: &mut [u8], source: CopyAddress) -> Result<usize, Error> { + use windows_kernel_sys::ntoskrnl::MmCopyMemory; + + let (flags, copy_addr) = source.into(); + let mut bytes = 0; + + unsafe { + MmCopyMemory( + buffer.as_mut_ptr() as _, + copy_addr, + buffer.len() as _, + flags, + &mut bytes, + ) + } + .into_result()?; + + Ok(bytes as _) +} + +#[cfg(feature = "system")] +pub fn write_memory(target: CopyAddress, buffer: &[u8]) -> Result<usize, Error> { + use windows_kernel_sys::ntoskrnl::MmCopyMemory; + + let mut copy_addr: MM_COPY_ADDRESS = unsafe { core::mem::zeroed() }; + let mut bytes = 0; + + let target = match target { + CopyAddress::Virtual(addr) => addr, + CopyAddress::Physical(addr) => get_virtual_for_physical(addr), + }; + + copy_addr.__bindgen_anon_1.VirtualAddress = buffer.as_ptr() as _; + + unsafe { + MmCopyMemory( + target as _, + copy_addr, + buffer.len() as _, + MM_COPY_MEMORY_VIRTUAL, + &mut bytes, + ) + } + .into_result()?; + + Ok(bytes as _) +} diff --git a/crates/windows-kernel-rs/src/process.rs b/crates/windows-kernel-rs/src/process.rs new file mode 100644 index 0000000..e0133c0 --- /dev/null +++ b/crates/windows-kernel-rs/src/process.rs @@ -0,0 +1,132 @@ +use bitflags::bitflags; +use windows_kernel_sys::{ + base::{CLIENT_ID, HANDLE, KAPC_STATE, OBJECT_ATTRIBUTES, PEPROCESS}, + ntoskrnl::{ + KeStackAttachProcess, + KeUnstackDetachProcess, + ObDereferenceObject, + ObReferenceObject, + PsGetCurrentProcess, + PsLookupProcessByProcessId, + ZwClose, + ZwOpenProcess, + }, +}; + +use crate::error::{Error, IntoResult}; + +pub type ProcessId = usize; + +#[derive(Clone, Debug)] +pub struct Process { + pub process: PEPROCESS, +} + +impl Process { + pub fn as_ptr(&self) -> PEPROCESS { self.process } + + pub fn current() -> Self { + let process = unsafe { PsGetCurrentProcess() }; + + unsafe { + ObReferenceObject(process as _); + } + + Self { + process, + } + } + + pub fn by_id(process_id: ProcessId) -> Result<Self, Error> { + let mut process = core::ptr::null_mut(); + + unsafe { PsLookupProcessByProcessId(process_id as _, &mut process) }.into_result()?; + + Ok(Self { + process, + }) + } + + pub fn id(&self) -> ProcessId { + let handle = unsafe { windows_kernel_sys::ntoskrnl::PsGetProcessId(self.process) }; + + handle as _ + } + + pub fn attach(&self) -> ProcessAttachment { unsafe { ProcessAttachment::attach(self.process) } } +} + +impl Drop for Process { + fn drop(&mut self) { + unsafe { + ObDereferenceObject(self.process as _); + } + } +} + +pub struct ProcessAttachment { + process: PEPROCESS, + state: KAPC_STATE, +} + +impl ProcessAttachment { + pub unsafe fn attach(process: PEPROCESS) -> Self { + let mut state: KAPC_STATE = core::mem::zeroed(); + + ObReferenceObject(process as _); + KeStackAttachProcess(process, &mut state); + + Self { + process, + state, + } + } +} + +impl Drop for ProcessAttachment { + fn drop(&mut self) { + unsafe { + KeUnstackDetachProcess(&mut self.state); + ObDereferenceObject(self.process as _); + } + } +} + +bitflags! { + pub struct ProcessAccess: u32 { + const ALL_ACCESS = windows_kernel_sys::base::PROCESS_ALL_ACCESS; + } +} + +pub struct ZwProcess { + pub(crate) handle: HANDLE, +} + +impl ZwProcess { + pub fn open(id: ProcessId, access: ProcessAccess) -> Result<Self, Error> { + let mut attrs: OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() }; + attrs.Length = core::mem::size_of::<OBJECT_ATTRIBUTES>() as u32; + + let mut client_id = CLIENT_ID { + UniqueProcess: id as _, + UniqueThread: core::ptr::null_mut(), + }; + + let mut handle = core::ptr::null_mut(); + + unsafe { ZwOpenProcess(&mut handle, access.bits(), &mut attrs, &mut client_id) } + .into_result()?; + + Ok(Self { + handle, + }) + } +} + +impl Drop for ZwProcess { + fn drop(&mut self) { + unsafe { + ZwClose(self.handle); + } + } +} diff --git a/crates/windows-kernel-rs/src/request.rs b/crates/windows-kernel-rs/src/request.rs new file mode 100644 index 0000000..43a6a83 --- /dev/null +++ b/crates/windows-kernel-rs/src/request.rs @@ -0,0 +1,265 @@ +use core::ops::Deref; + +use bitflags::bitflags; +use windows_kernel_sys::{ + base::{ + IO_NO_INCREMENT, + IO_STACK_LOCATION, + IRP, + STATUS_SUCCESS, + _MM_PAGE_PRIORITY as MM_PAGE_PRIORITY, + }, + ntoskrnl::{ + IoCompleteRequest, + IoGetCurrentIrpStackLocation, + MmGetMdlByteCount, + MmGetMdlByteOffset, + MmGetSystemAddressForMdlSafe, + }, +}; + +use crate::{ + error::Error, + ioctl::{ControlCode, RequiredAccess, TransferMethod}, + user_ptr::UserPtr, +}; + +bitflags! { + pub struct IrpFlags: u32 { + const NOCACHE = windows_kernel_sys::base::IRP_NOCACHE; + const PAGING_IO = windows_kernel_sys::base::IRP_PAGING_IO; + const MOUNT_COMPLETION = windows_kernel_sys::base::IRP_MOUNT_COMPLETION; + const SYNCHRONOUS_API = windows_kernel_sys::base::IRP_SYNCHRONOUS_API; + const ASSOCIATED_IRP = windows_kernel_sys::base::IRP_ASSOCIATED_IRP; + const BUFFERED_IO = windows_kernel_sys::base::IRP_BUFFERED_IO; + const DEALLOCATE_BUFFER = windows_kernel_sys::base::IRP_DEALLOCATE_BUFFER; + const INPUT_OPERATION = windows_kernel_sys::base::IRP_INPUT_OPERATION; + const SYNCHRONOUS_PAGING_IO = windows_kernel_sys::base::IRP_SYNCHRONOUS_PAGING_IO; + const CREATE_OPERATION = windows_kernel_sys::base::IRP_CREATE_OPERATION; + const READ_OPERATION = windows_kernel_sys::base::IRP_READ_OPERATION; + const WRITE_OPERATION = windows_kernel_sys::base::IRP_WRITE_OPERATION; + const CLOSE_OPERATION = windows_kernel_sys::base::IRP_CLOSE_OPERATION; + const DEFER_IO_COMPLETION = windows_kernel_sys::base::IRP_DEFER_IO_COMPLETION; + const OB_QUERY_NAME = windows_kernel_sys::base::IRP_OB_QUERY_NAME; + const HOLD_DEVICE_QUEUE = windows_kernel_sys::base::IRP_HOLD_DEVICE_QUEUE; + const UM_DRIVER_INITIATED_IO = windows_kernel_sys::base::IRP_UM_DRIVER_INITIATED_IO; + } +} + +pub struct IoRequest { + irp: *mut IRP, +} + +impl IoRequest { + pub unsafe fn from_raw(irp: *mut IRP) -> Self { + Self { + irp, + } + } + + pub fn irp(&self) -> &IRP { unsafe { &*self.irp } } + + pub fn irp_mut(&self) -> &mut IRP { unsafe { &mut *self.irp } } + + pub fn flags(&self) -> IrpFlags { + IrpFlags::from_bits(self.irp().Flags).unwrap_or(IrpFlags::empty()) + } + + pub fn stack_location(&self) -> &IO_STACK_LOCATION { + unsafe { &*IoGetCurrentIrpStackLocation(self.irp_mut()) } + } + + pub fn major(&self) -> u8 { self.stack_location().MajorFunction } + + pub(crate) fn complete(&self, value: Result<u32, Error>) { + let irp = self.irp_mut(); + + match value { + Ok(value) => { + irp.IoStatus.Information = value as _; + irp.IoStatus.__bindgen_anon_1.Status = STATUS_SUCCESS; + } + Err(error) => { + irp.IoStatus.Information = 0; + irp.IoStatus.__bindgen_anon_1.Status = error.to_ntstatus(); + } + } + + unsafe { + IoCompleteRequest(irp, IO_NO_INCREMENT as _); + } + } +} + +pub struct ReadRequest { + pub(crate) inner: IoRequest, +} + +impl Deref for ReadRequest { + type Target = IoRequest; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl ReadRequest { + pub fn user_ptr(&self) -> UserPtr { + let stack_location = self.stack_location(); + let irp = self.irp(); + + let (ptr, size) = if !irp.MdlAddress.is_null() { + let ptr = unsafe { + MmGetSystemAddressForMdlSafe(irp.MdlAddress, MM_PAGE_PRIORITY::HighPagePriority as _) + }; + + let size = unsafe { MmGetMdlByteCount(irp.MdlAddress) } as usize; + + (ptr, size) + } else if !unsafe { irp.AssociatedIrp.SystemBuffer }.is_null() { + let ptr = unsafe { irp.AssociatedIrp.SystemBuffer }; + let size = unsafe { stack_location.Parameters.Read }.Length as usize; + + (ptr, size) + } else { + (core::ptr::null_mut(), 0) + }; + + unsafe { UserPtr::new_buffered(ptr, 0, size) } + } + + pub fn offset(&self) -> i64 { + let stack_location = self.stack_location(); + let irp = self.irp(); + + if !irp.MdlAddress.is_null() { + (unsafe { MmGetMdlByteOffset(irp.MdlAddress) }) as i64 + } else if !unsafe { irp.AssociatedIrp.SystemBuffer }.is_null() { + unsafe { stack_location.Parameters.Read.ByteOffset.QuadPart } + } else { + 0 + } + } +} + +impl Into<IoRequest> for ReadRequest { + fn into(self) -> IoRequest { self.inner } +} + +pub struct WriteRequest { + pub(crate) inner: IoRequest, +} + +impl Deref for WriteRequest { + type Target = IoRequest; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl WriteRequest { + pub fn user_ptr(&self) -> UserPtr { + let stack_location = self.stack_location(); + let irp = self.irp(); + + let (ptr, size) = if !irp.MdlAddress.is_null() { + let ptr = unsafe { + MmGetSystemAddressForMdlSafe(irp.MdlAddress, MM_PAGE_PRIORITY::HighPagePriority as _) + }; + + let size = unsafe { MmGetMdlByteCount(irp.MdlAddress) } as usize; + + (ptr, size) + } else if !unsafe { irp.AssociatedIrp.SystemBuffer }.is_null() { + let ptr = unsafe { irp.AssociatedIrp.SystemBuffer }; + let size = unsafe { stack_location.Parameters.Write }.Length as usize; + + (ptr, size) + } else { + (core::ptr::null_mut(), 0) + }; + + unsafe { UserPtr::new_buffered(ptr, size, 0) } + } + + pub fn offset(&self) -> i64 { + let stack_location = self.stack_location(); + let irp = self.irp(); + + if !irp.MdlAddress.is_null() { + (unsafe { MmGetMdlByteOffset(irp.MdlAddress) }) as i64 + } else if !unsafe { irp.AssociatedIrp.SystemBuffer }.is_null() { + unsafe { stack_location.Parameters.Write.ByteOffset.QuadPart } + } else { + 0 + } + } +} + +impl Into<IoRequest> for WriteRequest { + fn into(self) -> IoRequest { self.inner } +} + +pub struct IoControlRequest { + pub(crate) inner: IoRequest, +} + +impl Deref for IoControlRequest { + type Target = IoRequest; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl IoControlRequest { + pub fn control_code(&self) -> ControlCode { + let stack_location = self.stack_location(); + + unsafe { + stack_location + .Parameters + .DeviceIoControl + .IoControlCode + .into() + } + } + + pub fn function(&self) -> (RequiredAccess, u32) { + let code = self.control_code(); + + (code.required_access(), code.number()) + } + + pub fn user_ptr(&self) -> UserPtr { + let stack_location = self.stack_location(); + let irp = self.irp(); + + let system_buffer = unsafe { irp.AssociatedIrp.SystemBuffer }; + + let mdl_address = if !irp.MdlAddress.is_null() { + unsafe { + MmGetSystemAddressForMdlSafe(irp.MdlAddress, MM_PAGE_PRIORITY::HighPagePriority as _) + } + } else { + core::ptr::null_mut() + }; + + let input_size = + unsafe { stack_location.Parameters.DeviceIoControl.InputBufferLength } as usize; + let output_size = + unsafe { stack_location.Parameters.DeviceIoControl.OutputBufferLength } as usize; + + match self.control_code().transfer_method() { + TransferMethod::Buffered => unsafe { + UserPtr::new_buffered(system_buffer, input_size, output_size) + }, + TransferMethod::InputDirect => unsafe { + UserPtr::new_direct(mdl_address, system_buffer, output_size, input_size) + }, + TransferMethod::OutputDirect => unsafe { + UserPtr::new_direct(system_buffer, mdl_address, input_size, output_size) + }, + TransferMethod::Neither => unsafe { UserPtr::new_neither() }, + } + } +} + +impl Into<IoRequest> for IoControlRequest { + fn into(self) -> IoRequest { self.inner } +} diff --git a/crates/windows-kernel-rs/src/section.rs b/crates/windows-kernel-rs/src/section.rs new file mode 100644 index 0000000..f80b8b7 --- /dev/null +++ b/crates/windows-kernel-rs/src/section.rs @@ -0,0 +1,163 @@ +use bitflags::bitflags; +use widestring::U16CString; +use windows_kernel_sys::{ + base::{HANDLE, LARGE_INTEGER, OBJECT_ATTRIBUTES}, + ntoskrnl::{ZwClose, ZwMapViewOfSection, ZwOpenSection, ZwUnmapViewOfSection}, +}; + +use crate::{ + error::{Error, IntoResult}, + process::ZwProcess, + string::create_unicode_string, +}; + +bitflags! { + pub struct AllocationFlags: u32 { + const RESERVE = windows_kernel_sys::base::MEM_RESERVE; + const LARGE_PAGES = windows_kernel_sys::base::MEM_LARGE_PAGES; + const TOP_DOWN = windows_kernel_sys::base::MEM_TOP_DOWN; + } +} + +bitflags! { + pub struct ProtectFlags: u32 { + const READ_WRITE = windows_kernel_sys::base::PAGE_READWRITE; + } +} + +bitflags! { + pub struct SectionAccess: u32 { + const EXTEND_SIZE = windows_kernel_sys::base::SECTION_EXTEND_SIZE; + const MAP_EXECUTE = windows_kernel_sys::base::SECTION_MAP_EXECUTE; + const MAP_READ = windows_kernel_sys::base::SECTION_MAP_READ; + const MAP_WRITE = windows_kernel_sys::base::SECTION_MAP_WRITE; + const QUERY = windows_kernel_sys::base::SECTION_QUERY; + const ALL_ACCESS = windows_kernel_sys::base::SECTION_ALL_ACCESS; + } +} + +bitflags! { + pub struct ObjectFlags: u32 { + const CASE_INSENSITIVE = windows_kernel_sys::base::OBJ_CASE_INSENSITIVE; + const KERNEL_HANDLE = windows_kernel_sys::base::OBJ_KERNEL_HANDLE; + } +} + +#[repr(i32)] +pub enum SectionInherit { + ViewShare = windows_kernel_sys::base::_SECTION_INHERIT::ViewShare, + ViewUnmap = windows_kernel_sys::base::_SECTION_INHERIT::ViewUnmap, +} + +pub enum BaseAddress { + Desired(*mut core::ffi::c_void), + ZeroBits(usize), +} + +pub struct Section { + handle: HANDLE, +} + +unsafe impl Send for Section {} +unsafe impl Sync for Section {} + +impl Section { + pub fn open(path: &str, obj_flags: ObjectFlags, access: SectionAccess) -> Result<Self, Error> { + let name = U16CString::from_str(path).unwrap(); + let mut name = create_unicode_string(name.as_slice()); + + let mut attrs = OBJECT_ATTRIBUTES { + Length: core::mem::size_of::<OBJECT_ATTRIBUTES>() as u32, + RootDirectory: core::ptr::null_mut(), + ObjectName: &mut name, + Attributes: obj_flags.bits(), + SecurityDescriptor: core::ptr::null_mut(), + SecurityQualityOfService: core::ptr::null_mut(), + }; + + let mut handle: HANDLE = core::ptr::null_mut(); + + unsafe { ZwOpenSection(&mut handle, access.bits(), &mut attrs) }.into_result()?; + + Ok(Self { + handle, + }) + } + + pub fn map_view( + &mut self, + process: ZwProcess, + base_address: BaseAddress, + commit_size: usize, + offset: Option<u64>, + view_size: usize, + inherit: SectionInherit, + allocation: AllocationFlags, + protection: ProtectFlags, + ) -> Result<SectionView, Error> { + let (mut base_address, zero_bits) = match base_address { + BaseAddress::Desired(ptr) => (ptr, 0), + BaseAddress::ZeroBits(bits) => (core::ptr::null_mut(), bits), + }; + + let mut offset = offset.map(|value| { + let mut offset: LARGE_INTEGER = unsafe { core::mem::zeroed() }; + offset.QuadPart = value as _; + offset + }); + + let mut size: u64 = view_size as _; + + unsafe { + ZwMapViewOfSection( + self.handle, + process.handle, + &mut base_address, + zero_bits as _, + commit_size as _, + match offset { + Some(ref mut offset) => offset as _, + _ => core::ptr::null_mut(), + }, + &mut size, + inherit as _, + allocation.bits(), + protection.bits(), + ) + } + .into_result()?; + + Ok(SectionView { + process, + address: base_address, + }) + } +} + +impl Drop for Section { + fn drop(&mut self) { + unsafe { + ZwClose(self.handle); + } + } +} + +pub struct SectionView { + process: ZwProcess, + address: *mut core::ffi::c_void, +} + +unsafe impl Send for SectionView {} +unsafe impl Sync for SectionView {} + +impl SectionView { + pub fn address(&self) -> *mut core::ffi::c_void { self.address } +} + +impl Drop for SectionView { + fn drop(&mut self) { + unsafe { + ZwUnmapViewOfSection(self.process.handle, self.address); + } + } +} diff --git a/crates/windows-kernel-rs/src/string.rs b/crates/windows-kernel-rs/src/string.rs new file mode 100644 index 0000000..71ad169 --- /dev/null +++ b/crates/windows-kernel-rs/src/string.rs @@ -0,0 +1,17 @@ +use windows_kernel_sys::base::UNICODE_STRING; + +pub fn create_unicode_string(s: &[u16]) -> UNICODE_STRING { + let len = s.len(); + + let n = if len > 0 && s[len - 1] == 0 { + len - 1 + } else { + len + }; + + UNICODE_STRING { + Length: (n * 2) as u16, + MaximumLength: (len * 2) as u16, + Buffer: s.as_ptr() as _, + } +} diff --git a/crates/windows-kernel-rs/src/symbolic_link.rs b/crates/windows-kernel-rs/src/symbolic_link.rs new file mode 100644 index 0000000..8c30f6b --- /dev/null +++ b/crates/windows-kernel-rs/src/symbolic_link.rs @@ -0,0 +1,39 @@ +use widestring::U16CString; +use windows_kernel_sys::ntoskrnl::{IoCreateSymbolicLink, IoDeleteSymbolicLink}; + +use crate::{ + error::{Error, IntoResult}, + string::create_unicode_string, +}; + +pub struct SymbolicLink { + name: U16CString, +} + +impl SymbolicLink { + pub fn new(name: &str, target: &str) -> Result<Self, Error> { + // Convert the name to UTF-16 and then create a UNICODE_STRING. + let name = U16CString::from_str(name).unwrap(); + let mut name_ptr = create_unicode_string(name.as_slice()); + + // Convert the target to UTF-16 and then create a UNICODE_STRING. + let target = U16CString::from_str(target).unwrap(); + let mut target_ptr = create_unicode_string(target.as_slice()); + + unsafe { IoCreateSymbolicLink(&mut name_ptr, &mut target_ptr) }.into_result()?; + + Ok(Self { + name, + }) + } +} + +impl Drop for SymbolicLink { + fn drop(&mut self) { + let mut name_ptr = create_unicode_string(self.name.as_slice()); + + unsafe { + IoDeleteSymbolicLink(&mut name_ptr); + } + } +} diff --git a/crates/windows-kernel-rs/src/sync/fast_mutex.rs b/crates/windows-kernel-rs/src/sync/fast_mutex.rs new file mode 100644 index 0000000..9a82524 --- /dev/null +++ b/crates/windows-kernel-rs/src/sync/fast_mutex.rs @@ -0,0 +1,137 @@ +use alloc::boxed::Box; +use core::{ + cell::UnsafeCell, + ops::{Deref, DerefMut}, +}; + +use windows_kernel_sys::{ + base::FAST_MUTEX, + ntoskrnl::{ + ExAcquireFastMutex, + ExInitializeFastMutex, + ExReleaseFastMutex, + ExTryToAcquireFastMutex, + }, +}; + +/// A mutual exclusion primitive useful for protecting shared data. +/// +/// This mutex will block threads waiting for the lock to become available. The +/// mutex can also be statically initialized or created via a [`new`] +/// constructor. Each mutex has a type parameter which represents the data that +/// it is protecting. The data can only be accessed through the RAII +/// guards returned from [`lock`] and [`try_lock`], which guarantees that the +/// data is only ever accessed when the mutex is locked. +/// +/// [`new`]: FastMutex::new +/// [`lock`]: FastMutex::lock +/// [`try_lock`]: FastMutex::try_lock +pub struct FastMutex<T: ?Sized> { + pub(crate) lock: Box<FAST_MUTEX>, + pub(crate) data: UnsafeCell<T>, +} + +unsafe impl<T> Send for FastMutex<T> {} +unsafe impl<T> Sync for FastMutex<T> {} + +impl<T> FastMutex<T> { + /// Creates a new mutex in an unlocked state ready for use. + pub fn new(data: T) -> Self { + let mut lock: Box<FAST_MUTEX> = Box::new(unsafe { core::mem::zeroed() }); + + unsafe { ExInitializeFastMutex(&mut *lock) }; + + Self { + lock, + data: UnsafeCell::new(data), + } + } + + /// Consumes this `FastMutex`, returning the underlying data. + #[inline] + pub fn into_inner(self) -> T { + let Self { + data, .. + } = self; + data.into_inner() + } + + /// Attempts to acquire this lock. + /// + /// If the lock could not be acquired at this time, then `None` is returned. + /// Otherwise, an RAII guard is returned. The lock will be unlocked when the + /// guard is dropped. + /// + /// This function does not block. + #[inline] + pub fn try_lock(&mut self) -> Option<FastMutexGuard<T>> { + let status = unsafe { ExTryToAcquireFastMutex(&mut *self.lock) } != 0; + + match status { + true => + Some(FastMutexGuard { + lock: &mut self.lock, + data: unsafe { &mut *self.data.get() }, + }), + _ => None, + } + } + + /// Acquires a mutex, blocking the current thread until it is able to do so. + /// + /// This function will block the local thread until it is available to acquire + /// the mutex. Upon returning, the thread is the only thread with the lock + /// held. An RAII guard is returned to allow scoped unlock of the lock. When + /// the guard goes out of scope, the mutex will be unlocked. + /// + /// The underlying function does not allow for recursion. If the thread + /// already holds the lock and tries to lock the mutex again, this function + /// will return `None` instead. + #[inline] + pub fn lock(&mut self) -> Option<FastMutexGuard<T>> { + unsafe { ExAcquireFastMutex(&mut *self.lock) }; + + Some(FastMutexGuard { + lock: &mut self.lock, + data: unsafe { &mut *self.data.get() }, + }) + } +} + +impl<T: ?Sized + Default> Default for FastMutex<T> { + fn default() -> Self { Self::new(T::default()) } +} + +impl<T> From<T> for FastMutex<T> { + fn from(data: T) -> Self { Self::new(data) } +} + +/// An RAII implementation of a "scoped lock" of a mutex. When this structure is +/// dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// [`Deref`] and [`DerefMut`] implementations. +/// +/// This structure is created by the [`lock`] and [`try_lock`] methods on +/// [`FastMutex`]. +/// +/// [`lock`]: FastMutex::lock +/// [`try_lock`]: FastMutex::try_lock +pub struct FastMutexGuard<'a, T: 'a + ?Sized> { + pub(crate) lock: &'a mut FAST_MUTEX, + pub(crate) data: &'a mut T, +} + +impl<'a, T: ?Sized> Drop for FastMutexGuard<'a, T> { + fn drop(&mut self) { unsafe { ExReleaseFastMutex(&mut *self.lock) }; } +} + +impl<'a, T: ?Sized> Deref for FastMutexGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { self.data } +} + +impl<'a, T: ?Sized> DerefMut for FastMutexGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { self.data } +} diff --git a/crates/windows-kernel-rs/src/sync/mod.rs b/crates/windows-kernel-rs/src/sync/mod.rs new file mode 100644 index 0000000..b024c55 --- /dev/null +++ b/crates/windows-kernel-rs/src/sync/mod.rs @@ -0,0 +1,4 @@ +pub mod fast_mutex; +pub mod push_lock; + +pub use self::{fast_mutex::FastMutex as Mutex, push_lock::PushLock as RwLock}; diff --git a/crates/windows-kernel-rs/src/sync/push_lock.rs b/crates/windows-kernel-rs/src/sync/push_lock.rs new file mode 100644 index 0000000..71b016a --- /dev/null +++ b/crates/windows-kernel-rs/src/sync/push_lock.rs @@ -0,0 +1,185 @@ +use alloc::boxed::Box; +use core::{ + cell::UnsafeCell, + ops::{Deref, DerefMut}, +}; + +use windows_kernel_sys::{ + base::EX_PUSH_LOCK, + ntoskrnl::{ + ExAcquirePushLockExclusive, + ExAcquirePushLockShared, + ExInitializePushLock, + ExReleasePushLockExclusive, + ExReleasePushLockShared, + KeEnterCriticalRegion, + KeLeaveCriticalRegion, + }, +}; + +/// A [`PushLock`] is an efficient implementation of a reader-writer lock that +/// can be stored both in paged and non-paged memory. +/// +/// This type of lock allows a number of readers or at most one writer at any +/// point in time. The write portion of this lock typically allows modifications +/// of the underlying data (exclusive access) and the read portion of this lock +/// typically allows for read-only access (shared access). +/// +/// In comparison, a [`FastMutex`] does not distinguish between readers or +/// writers that acquire the lock, therefore blocking any threads waiting for +/// the lock to become available. A [`PushLock`] will allow any number of +/// readers to acquire the lock as long as a writer is not holding the lock. +/// +/// The priority policy is such that a thread trying to acquire the [`PushLock`] +/// for exclusive access will be prioritized over threads trying to acquire the +/// [`PushLock`] for shared access. More specifically, if a thread cannot lock +/// the [`PushLock`] for exclusive access immediately, it will wait for the +/// thread(s) that currently holds the lock to release the lock. If another +/// thread tries to acquire the [`PushLock`] for shared access while a thread is +/// waiting to acquire the lock for exclusive access, it will yield to the +/// thread(s) trying to acquire the [`PushLock`] for exclusive access, even in +/// the event that the [`PushLock`] is acquired for shared access. +/// +/// [`FastMutex`]: crate::fast_mutex::FastMutex +pub struct PushLock<T: ?Sized> { + pub(crate) lock: Box<EX_PUSH_LOCK>, + pub(crate) data: UnsafeCell<T>, +} + +unsafe impl<T> Send for PushLock<T> {} +unsafe impl<T> Sync for PushLock<T> {} + +impl<T> PushLock<T> { + /// Creates new instance of [`PushLock<T>`] that is unlocked. + pub fn new(data: T) -> Self { + let mut lock: Box<EX_PUSH_LOCK> = Box::new(0); + + unsafe { ExInitializePushLock(&mut *lock) }; + + Self { + lock, + data: UnsafeCell::new(data), + } + } + + /// Consumes this [`PushLock`], returning the underlying data. + #[inline] + pub fn into_inner(self) -> T { + let Self { + data, .. + } = self; + data.into_inner() + } + + /// Locks this [`PushLock`] with shared read access, blocking the current + /// thread until it can be acquired. + /// + /// The calling thread will be blocked until there are no more writers which + /// hold the lock. There may be other readers currently inside the lock when + /// this method returns. + /// + /// This function will yield to threads waiting to acquire the [`PushLock`] + /// for exclusive access, even in the event that the [`PushLock`] is + /// currently held by one or more threads for shared access. + /// + /// While the underlying function does allow for recursion, this atomically + /// increments a shared reader counter. Since dropping the RAII guard + /// releases the lock by atomically decrementing this shared counter, it + /// will eventually reach zero once all RAII guards have been dropped. + #[inline] + pub fn read(&mut self) -> Option<PushLockReadGuard<T>> { + unsafe { KeEnterCriticalRegion() }; + + unsafe { ExAcquirePushLockShared(&mut *self.lock) }; + + Some(PushLockReadGuard { + lock: &mut self.lock, + data: unsafe { &mut *self.data.get() }, + }) + } + + /// Locks this [`PushLock`] with exclusive write access, blocking the current + /// thread until it can be acquired. + /// + /// This function will not return while other writers or other readers + /// currently have access to the lock. + /// + /// Returns an RAII guard which will drop the write access of this + /// [`PushLock`] when dropped. + /// + /// This thread will take priority over any threads that are trying to acquire + /// the lock for shared access but that do not currently hold the lock for + /// shared access. + /// + /// The underlying function does not allow for recursion, which ensures + /// correct behavior. + #[inline] + pub fn write(&mut self) -> Option<PushLockWriteGuard<T>> { + unsafe { KeEnterCriticalRegion() }; + + unsafe { ExAcquirePushLockExclusive(&mut *self.lock) }; + + Some(PushLockWriteGuard { + lock: &mut self.lock, + data: unsafe { &mut *self.data.get() }, + }) + } +} + +/// RAII structure used to release the shared read access of a lock when +/// dropped. +/// +/// This structure is created by the [`read`] and [`try_read`] methods on +/// [`PushLock`] +/// +/// [`read`]: PushLock::read +/// [`try_read`]: PushLock::try_read +pub struct PushLockReadGuard<'a, T: 'a + ?Sized> { + pub(crate) lock: &'a mut EX_PUSH_LOCK, + pub(crate) data: &'a T, +} + +impl<'a, T: ?Sized> Drop for PushLockReadGuard<'a, T> { + fn drop(&mut self) { + unsafe { ExReleasePushLockShared(&mut *self.lock) }; + + unsafe { KeLeaveCriticalRegion() }; + } +} + +impl<'a, T: ?Sized> Deref for PushLockReadGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { self.data } +} + +/// RAII structure used to release the exclusive write access of a lock when +/// dropped. +/// +/// This structure is created by the [`write`] and [`try_write`] methods on +/// [`PushLock`] +/// +/// [`write`]: PushLock::write +/// [`try_write`]: PushLock::try_write +pub struct PushLockWriteGuard<'a, T: 'a + ?Sized> { + pub(crate) lock: &'a mut EX_PUSH_LOCK, + pub(crate) data: &'a mut T, +} + +impl<'a, T: ?Sized> Drop for PushLockWriteGuard<'a, T> { + fn drop(&mut self) { + unsafe { ExReleasePushLockExclusive(&mut *self.lock) }; + + unsafe { KeLeaveCriticalRegion() }; + } +} + +impl<'a, T: ?Sized> Deref for PushLockWriteGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { self.data } +} + +impl<'a, T: ?Sized> DerefMut for PushLockWriteGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { self.data } +} diff --git a/crates/windows-kernel-rs/src/user_ptr.rs b/crates/windows-kernel-rs/src/user_ptr.rs new file mode 100644 index 0000000..54dcb58 --- /dev/null +++ b/crates/windows-kernel-rs/src/user_ptr.rs @@ -0,0 +1,172 @@ +use crate::error::Error; + +pub enum UserPtr { + Buffered { + ptr: *mut cty::c_void, + read_size: usize, + write_size: usize, + }, + Direct { + read_ptr: *const cty::c_void, + write_ptr: *mut cty::c_void, + read_size: usize, + write_size: usize, + }, + Neither, +} + +impl UserPtr { + pub unsafe fn new_buffered(ptr: *mut cty::c_void, read_size: usize, write_size: usize) -> Self { + Self::Buffered { + ptr, + read_size, + write_size, + } + } + + pub unsafe fn new_direct( + read_ptr: *const cty::c_void, + write_ptr: *mut cty::c_void, + read_size: usize, + write_size: usize, + ) -> Self { + Self::Direct { + read_ptr, + write_ptr, + read_size, + write_size, + } + } + + pub unsafe fn new_neither() -> Self { Self::Neither } + + pub fn read_size(&self) -> usize { + match self { + Self::Buffered { + read_size, .. + } => *read_size, + Self::Direct { + read_size, .. + } => *read_size, + Self::Neither => 0, + } + } + + pub fn write_size(&self) -> usize { + match self { + Self::Buffered { + write_size, .. + } => *write_size, + Self::Direct { + write_size, .. + } => *write_size, + Self::Neither => 0, + } + } + + pub fn as_slice(&self) -> &[u8] { + let (ptr, size) = match self { + Self::Buffered { + ptr, + read_size, + .. + } => (*ptr as _, *read_size), + Self::Direct { + read_ptr, + read_size, + .. + } => (*read_ptr, *read_size), + Self::Neither => (core::ptr::null(), 0), + }; + + if ptr.is_null() || size == 0 { + &[] + } else { + unsafe { core::slice::from_raw_parts(ptr as *const u8, size) } + } + } + + pub fn as_mut_slice(&mut self) -> &mut [u8] { + let (ptr, size) = match self { + Self::Buffered { + ptr, + write_size, + .. + } => (*ptr, *write_size), + Self::Direct { + write_ptr, + write_size, + .. + } => (*write_ptr, *write_size), + Self::Neither => (core::ptr::null_mut(), 0), + }; + + if ptr.is_null() || size == 0 { + &mut [] + } else { + unsafe { core::slice::from_raw_parts_mut(ptr as *mut u8, size) } + } + } + + pub fn read<T: Copy + Default>(&self) -> Result<T, Error> { + let (ptr, size) = match self { + Self::Buffered { + ptr, + read_size, + .. + } => (*ptr as _, *read_size), + Self::Direct { + read_ptr, + read_size, + .. + } => (*read_ptr, *read_size), + Self::Neither => (core::ptr::null(), 0), + }; + + if ptr.is_null() || size == 0 { + return Err(Error::INVALID_PARAMETER); + } + + if core::mem::size_of::<T>() > size { + return Err(Error::INVALID_USER_BUFFER); + } + + let mut obj = T::default(); + + unsafe { + core::ptr::copy_nonoverlapping(ptr as _, &mut obj, 1); + } + + Ok(obj) + } + + pub fn write<T: Copy>(&mut self, obj: &T) -> Result<(), Error> { + let (ptr, size) = match self { + Self::Buffered { + ptr, + write_size, + .. + } => (*ptr, *write_size), + Self::Direct { + write_ptr, + write_size, + .. + } => (*write_ptr, *write_size), + Self::Neither => (core::ptr::null_mut(), 0), + }; + + if ptr.is_null() || size == 0 { + return Err(Error::INVALID_PARAMETER); + } + + if core::mem::size_of::<T>() > size { + return Err(Error::INVALID_USER_BUFFER); + } + + unsafe { + core::ptr::copy_nonoverlapping(obj, ptr as _, 1); + } + + Ok(()) + } +} diff --git a/crates/windows-kernel-rs/src/version.rs b/crates/windows-kernel-rs/src/version.rs new file mode 100644 index 0000000..3f46a87 --- /dev/null +++ b/crates/windows-kernel-rs/src/version.rs @@ -0,0 +1,35 @@ +//! This module provides utilities to query information about the version of +//! Microsoft Windows. + +use windows_kernel_sys::{base::RTL_OSVERSIONINFOW, ntoskrnl::RtlGetVersion}; + +use crate::error::{Error, IntoResult}; + +/// Represents version information for Microsoft Windows. +pub struct VersionInfo { + version_info: RTL_OSVERSIONINFOW, +} + +impl VersionInfo { + /// Uses [`RtlGetVersion`] to query the version info for Microsoft Windows. + pub fn query() -> Result<Self, Error> { + let mut version_info: RTL_OSVERSIONINFOW = unsafe { core::mem::zeroed() }; + + version_info.dwOSVersionInfoSize = core::mem::size_of::<RTL_OSVERSIONINFOW>() as u32; + + unsafe { RtlGetVersion(&mut version_info) }.into_result()?; + + Ok(Self { + version_info, + }) + } + + /// Retrieves the major version of Microsoft Windows. + pub fn major(&self) -> u32 { self.version_info.dwMajorVersion } + + /// Retrieves the minor version of Microsoft Windows. + pub fn minor(&self) -> u32 { self.version_info.dwMinorVersion } + + /// Retrieves the build number of Microsoft Windows. + pub fn build_number(&self) -> u32 { self.version_info.dwBuildNumber } +} diff --git a/crates/windows-kernel-sys/Cargo.toml b/crates/windows-kernel-sys/Cargo.toml new file mode 100644 index 0000000..dc0819d --- /dev/null +++ b/crates/windows-kernel-sys/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "windows-kernel-sys" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["intrin", "ntoskrnl"] +intrin = [] +ntoskrnl = [] +netio = [] + +[dependencies] +cty = "0.2" + +[build-dependencies] +bindgen = "0.59" +cc = "1.0" +windows-kernel-build = { path = "../windows-kernel-build" } diff --git a/crates/windows-kernel-sys/LICENSE b/crates/windows-kernel-sys/LICENSE new file mode 100644 index 0000000..68bb87c --- /dev/null +++ b/crates/windows-kernel-sys/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 S.J.R. van Schaik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/windows-kernel-sys/build.rs b/crates/windows-kernel-sys/build.rs new file mode 100644 index 0000000..254e2e9 --- /dev/null +++ b/crates/windows-kernel-sys/build.rs @@ -0,0 +1,135 @@ +use std::path::PathBuf; + +use bindgen::callbacks::*; +use windows_kernel_build::DirectoryType; + +#[derive(Debug)] +struct Callbacks; + +impl ParseCallbacks for Callbacks { + fn int_macro(&self, name: &str, _value: i64) -> Option<IntKind> { + Some(match name { + "TRUE" | "FALSE" => IntKind::UChar, + _ => return None, + }) + } +} + +fn generate_base() { + println!("cargo:rerun-if-changed=src/wrapper.h"); + + let include_dir = windows_kernel_build::get_km_dir(DirectoryType::Include).unwrap(); + let out_path = PathBuf::from( + std::env::var_os("OUT_DIR").expect("the environment variable OUT_DIR is undefined"), + ); + + bindgen::Builder::default() + .header("src/wrapper.h") + .use_core() + .derive_debug(false) + .layout_tests(false) + .ctypes_prefix("cty") + .default_enum_style(bindgen::EnumVariation::ModuleConsts) + .clang_arg(format!("-I{}", include_dir.to_str().unwrap())) + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .parse_callbacks(Box::new(Callbacks)) + .ignore_functions() + .generate() + .unwrap() + .write_to_file(out_path.join("base.rs")) + .unwrap(); +} + +#[cfg(feature = "intrin")] +fn generate_intrin() { + println!("cargo:rerun-if-changed=src/wrapper_intrin.c"); + + let include_dir = windows_kernel_build::get_km_dir(DirectoryType::Include).unwrap(); + + cc::Build::new() + .flag("/kernel") + .include(include_dir) + .file("src/wrapper_intrin.c") + .compile("wrapper_intrin"); +} + +#[cfg(not(feature = "intrin"))] +fn generate_intrin() {} + +#[cfg(feature = "ntoskrnl")] +fn generate_ntoskrnl() { + println!("cargo:rerun-if-changed=src/wrapper.h"); + println!("cargo:rerun-if-changed=src/wrapper.c"); + println!("cargo:rustc-link-lib=ntoskrnl"); + + let include_dir = windows_kernel_build::get_km_dir(DirectoryType::Include).unwrap(); + let out_path = PathBuf::from( + std::env::var_os("OUT_DIR").expect("the environment variable OUT_DIR is undefined"), + ); + + bindgen::Builder::default() + .header("src/wrapper.h") + .use_core() + .derive_debug(false) + .layout_tests(false) + .ctypes_prefix("cty") + .default_enum_style(bindgen::EnumVariation::ModuleConsts) + .clang_arg(format!("-I{}", include_dir.to_str().unwrap())) + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .blocklist_type(".*") + .allowlist_function(".*") + .allowlist_recursively(false) + .generate() + .unwrap() + .write_to_file(out_path.join("ntoskrnl.rs")) + .unwrap(); + + cc::Build::new() + .flag("/kernel") + .include(include_dir) + .file("src/wrapper.c") + .compile("wrapper_ntoskrnl"); +} + +#[cfg(not(feature = "ntoskrnl"))] +fn generate_ntoskrnl() {} + +#[cfg(feature = "netio")] +fn generate_netio() { + println!("cargo:rerun-if-changed=src/wrapper_netio.h"); + println!("cargo:rustc-link-lib=netio"); + + let include_dir = windows_kernel_build::get_km_dir(DirectoryType::Include).unwrap(); + let out_path = PathBuf::from( + std::env::var_os("OUT_DIR").expect("the environment variable OUT_DIR is undefined"), + ); + + bindgen::Builder::default() + .header("src/wrapper.h") + .use_core() + .derive_debug(false) + .layout_tests(false) + .ctypes_prefix("cty") + .default_enum_style(bindgen::EnumVariation::ModuleConsts) + .clang_arg(format!("-I{}", include_dir.to_str().unwrap())) + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .blocklist_type(".*") + .allowlist_function(".*") + .allowlist_recursively(false) + .generate() + .unwrap() + .write_to_file(out_path.join("netio.rs")) + .unwrap(); +} + +#[cfg(not(feature = "netio"))] +fn generate_netio() {} + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + generate_base(); + generate_intrin(); + generate_ntoskrnl(); + generate_netio(); +} diff --git a/crates/windows-kernel-sys/rust-toolchain b/crates/windows-kernel-sys/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/crates/windows-kernel-sys/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/crates/windows-kernel-sys/src/base.rs b/crates/windows-kernel-sys/src/base.rs new file mode 100644 index 0000000..073afea --- /dev/null +++ b/crates/windows-kernel-sys/src/base.rs @@ -0,0 +1,39 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +pub use cty::*; + +include!(concat!(env!("OUT_DIR"), "/base.rs")); + +pub const STATUS_SUCCESS: NTSTATUS = 0x00000000; +pub const STATUS_GUARD_PAGE_VIOLATION: NTSTATUS = 0x80000001 as u32 as i32; +pub const STATUS_DATATYPE_MISALIGNMENT: NTSTATUS = 0x80000002 as u32 as i32; +pub const STATUS_BREAKPOINT: NTSTATUS = 0x80000003 as u32 as i32; +pub const STATUS_SINGLE_STEP: NTSTATUS = 0x80000004 as u32 as i32; +pub const STATUS_UNWIND_CONSOLIDATE: NTSTATUS = 0x80000029 as u32 as i32; +pub const STATUS_UNSUCCESSFUL: NTSTATUS = 0xC0000001 as u32 as i32; +pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002 as u32 as i32; +pub const STATUS_ACCESS_VIOLATION: NTSTATUS = 0xC0000005 as u32 as i32; +pub const STATUS_IN_PAGE_ERROR: NTSTATUS = 0xC0000006 as u32 as i32; +pub const STATUS_INVALID_HANDLE: NTSTATUS = 0xC0000008 as u32 as i32; +pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xC000000D as u32 as i32; +pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011 as u32 as i32; +pub const STATUS_NO_MEMORY: NTSTATUS = 0xC0000017 as u32 as i32; +pub const STATUS_ILLEGAL_INSTRUCTION: NTSTATUS = 0xC000001D as u32 as i32; +pub const STATUS_NONCONTINUABLE_EXCEPTION: NTSTATUS = 0xC0000025 as u32 as i32; +pub const STATUS_INVALID_DISPOSITION: NTSTATUS = 0xC0000026 as u32 as i32; +pub const STATUS_ARRAY_BOUNDS_EXCEEDED: NTSTATUS = 0xC000008C as u32 as i32; +pub const STATUS_FLOAT_DENORMAL_OPERAND: NTSTATUS = 0xC000008D as u32 as i32; +pub const STATUS_FLOAT_DIVIDE_BY_ZERO: NTSTATUS = 0xC000008E as u32 as i32; +pub const STATUS_FLOAT_INEXACT_RESULT: NTSTATUS = 0xC000008F as u32 as i32; +pub const STATUS_FLOAT_INVALID_OPERATION: NTSTATUS = 0xC0000090 as u32 as i32; +pub const STATUS_FLOAT_OVERFLOW: NTSTATUS = 0xC0000091 as u32 as i32; +pub const STATUS_FLOAT_STACK_CHECK: NTSTATUS = 0xC0000092 as u32 as i32; +pub const STATUS_FLOAT_UNDERFLOW: NTSTATUS = 0xC0000093 as u32 as i32; +pub const STATUS_INTEGER_DIVIDE_BY_ZERO: NTSTATUS = 0xC0000094 as u32 as i32; +pub const STATUS_INTEGER_OVERFLOW: NTSTATUS = 0xC0000095 as u32 as i32; +pub const STATUS_PRIVILEGED_INSTRUCTION: NTSTATUS = 0xC0000096 as u32 as i32; +pub const STATUS_INSUFFICIENT_RESOURCES: NTSTATUS = 0xC000009A as u32 as i32; +pub const STATUS_INVALID_USER_BUFFER: NTSTATUS = 0xC00000E8 as u32 as i32; +pub const STATUS_STACK_OVERFLOW: NTSTATUS = 0xC00000FD as u32 as i32; diff --git a/crates/windows-kernel-sys/src/intrin.rs b/crates/windows-kernel-sys/src/intrin.rs new file mode 100644 index 0000000..f6ef95f --- /dev/null +++ b/crates/windows-kernel-sys/src/intrin.rs @@ -0,0 +1,16 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use crate::base::*; + +#[link(name = "wrapper_intrin")] +extern "C" { + pub fn read_cr3() -> u64; + pub fn write_cr3(value: u64); + pub fn read_msr(register: u32) -> u64; + pub fn read_msr_safe(register: u32, value: &mut u64) -> NTSTATUS; + pub fn write_msr(register: u32, value: u64); + pub fn write_msr_safe(register: u32, value: u64) -> NTSTATUS; + pub fn invlpg(value: usize); +} diff --git a/crates/windows-kernel-sys/src/lib.rs b/crates/windows-kernel-sys/src/lib.rs new file mode 100644 index 0000000..f41736c --- /dev/null +++ b/crates/windows-kernel-sys/src/lib.rs @@ -0,0 +1,13 @@ +#![no_std] +#![feature(untagged_unions)] + +pub mod base; + +#[cfg(feature = "intrin")] +pub mod intrin; +#[cfg(feature = "netio")] +pub mod netio; +#[cfg(feature = "ntoskrnl")] +pub mod ntoskrnl; + +pub use cty::*; diff --git a/crates/windows-kernel-sys/src/netio.rs b/crates/windows-kernel-sys/src/netio.rs new file mode 100644 index 0000000..0684594 --- /dev/null +++ b/crates/windows-kernel-sys/src/netio.rs @@ -0,0 +1,7 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use crate::base::*; + +include!(concat!(env!("OUT_DIR"), "/netio.rs")); diff --git a/crates/windows-kernel-sys/src/ntoskrnl.rs b/crates/windows-kernel-sys/src/ntoskrnl.rs new file mode 100644 index 0000000..f1daa4e --- /dev/null +++ b/crates/windows-kernel-sys/src/ntoskrnl.rs @@ -0,0 +1,50 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use crate::base::*; + +#[link(name = "wrapper_ntoskrnl")] +extern "C" { + pub fn _ExInitializeFastMutex(mutex: PFAST_MUTEX); + pub fn _ExAcquirePushLockExclusive(push_lock: PEX_PUSH_LOCK); + pub fn _ExReleasePushLockExclusive(push_lock: PEX_PUSH_LOCK); + pub fn _ExAcquirePushLockShared(push_lock: PEX_PUSH_LOCK); + pub fn _ExReleasePushLockShared(push_lock: PEX_PUSH_LOCK); + pub fn _IoGetCurrentIrpStackLocation(irp: PIRP) -> PIO_STACK_LOCATION; + pub fn _IoGetNextIrpStackLocation(irp: PIRP) -> PIO_STACK_LOCATION; + pub fn _IoSetCompletionRoutine( + irp: PIRP, + completion_routine: PIO_COMPLETION_ROUTINE, + context: PVOID, + invoke_on_success: BOOLEAN, + invoke_on_error: BOOLEAN, + invoke_on_cancel: BOOLEAN, + ); + pub fn _IoCompleteRequest(irp: PIRP, priority_boost: CCHAR); + pub fn _MmGetMdlByteCount(mdl: PMDL) -> ULONG; + pub fn _MmGetMdlByteOffset(mdl: PMDL) -> ULONG; + pub fn _MmGetSystemAddressForMdlSafe(mdl: PMDL, priority: ULONG) -> PVOID; + pub fn _ObDereferenceObject(p: *mut cty::c_void); + pub fn _ObReferenceObject(p: *mut cty::c_void); +} + +pub use self::{ + IoGetCurrentProcess as PsGetCurrentProcess, + _ExAcquirePushLockExclusive as ExAcquirePushLockExclusive, + _ExAcquirePushLockShared as ExAcquirePushLockShared, + _ExInitializeFastMutex as ExInitializeFastMutex, + _ExReleasePushLockExclusive as ExReleasePushLockExclusive, + _ExReleasePushLockShared as ExReleasePushLockShared, + _IoCompleteRequest as IoCompleteRequest, + _IoGetCurrentIrpStackLocation as IoGetCurrentIrpStackLocation, + _IoGetNextIrpStackLocation as IoGetNextIrpStackLocation, + _IoSetCompletionRoutine as IoSetCompletionRoutine, + _MmGetMdlByteCount as MmGetMdlByteCount, + _MmGetMdlByteOffset as MmGetMdlByteOffset, + _MmGetSystemAddressForMdlSafe as MmGetSystemAddressForMdlSafe, + _ObDereferenceObject as ObDereferenceObject, + _ObReferenceObject as ObReferenceObject, +}; + +include!(concat!(env!("OUT_DIR"), "/ntoskrnl.rs")); diff --git a/crates/windows-kernel-sys/src/wrapper.c b/crates/windows-kernel-sys/src/wrapper.c new file mode 100644 index 0000000..39c730f --- /dev/null +++ b/crates/windows-kernel-sys/src/wrapper.c @@ -0,0 +1,77 @@ +#include "wrapper.h" + +void _ExInitializeFastMutex( + PFAST_MUTEX fast_mutex +) { + ExInitializeFastMutex(fast_mutex); +} + +void _ExAcquirePushLockExclusive( + PEX_PUSH_LOCK push_lock +) { + ExAcquirePushLockExclusive(push_lock); +} + +void _ExReleasePushLockExclusive( + PEX_PUSH_LOCK push_lock +) { + ExReleasePushLockExclusive(push_lock); +} + +void _ExAcquirePushLockShared( + PEX_PUSH_LOCK push_lock +) { + ExAcquirePushLockShared(push_lock); +} + +void _ExReleasePushLockShared( + PEX_PUSH_LOCK push_lock +) { + ExReleasePushLockShared(push_lock); +} + +PIO_STACK_LOCATION _IoGetCurrentIrpStackLocation(PIRP irp) { + return IoGetCurrentIrpStackLocation(irp); +} + +PIO_STACK_LOCATION _IoGetNextIrpStackLocation(PIRP irp) { + return IoGetNextIrpStackLocation(irp); +} + +void _IoSetCompletionRoutine( + PIRP irp, + PIO_COMPLETION_ROUTINE completion_routine, + PVOID context, + BOOLEAN invoke_on_success, + BOOLEAN invoke_on_error, + BOOLEAN invoke_on_cancel +) { + IoSetCompletionRoutine(irp, completion_routine, context, invoke_on_success, invoke_on_error, invoke_on_cancel); +} + +void _IoCompleteRequest( + PIRP irp, + CCHAR priority_boost +) { + IoCompleteRequest(irp, priority_boost); +} + +ULONG _MmGetMdlByteCount(PMDL mdl) { + return MmGetMdlByteCount(mdl); +} + +ULONG _MmGetMdlByteOffset(PMDL mdl) { + return MmGetMdlByteOffset(mdl); +} + +PVOID _MmGetSystemAddressForMdlSafe(PMDL mdl, ULONG priority) { + return MmGetSystemAddressForMdlSafe(mdl, priority); +} + +void _ObDereferenceObject(PVOID p) { + ObDereferenceObject(p); +} + +void _ObReferenceObject(PVOID p) { + ObReferenceObject(p); +} diff --git a/crates/windows-kernel-sys/src/wrapper.h b/crates/windows-kernel-sys/src/wrapper.h new file mode 100644 index 0000000..1234601 --- /dev/null +++ b/crates/windows-kernel-sys/src/wrapper.h @@ -0,0 +1,63 @@ +#define _AMD64_ + +#include "ntdef.h" +#include "ntstatus.h" + +typedef ULONG_PTR _EX_PUSH_LOCK; +typedef ULONG_PTR EX_PUSH_LOCK; +typedef ULONG_PTR *PEX_PUSH_LOCK; + +typedef union _KGDTENTRY64 +{ + struct + { + unsigned short LimitLow; + unsigned short BaseLow; + union + { + struct + { + unsigned char BaseMiddle; + unsigned char Flags1; + unsigned char Flags2; + unsigned char BaseHigh; + } Bytes; + struct + { + unsigned long BaseMiddle : 8; + unsigned long Type : 5; + unsigned long Dpl : 2; + unsigned long Present : 1; + unsigned long LimitHigh : 4; + unsigned long System : 1; + unsigned long LongMode : 1; + unsigned long DefaultBig : 1; + unsigned long Granularity : 1; + unsigned long BaseHigh : 8; + } Bits; + }; + unsigned long BaseUpper; + unsigned long MustBeZero; + }; + unsigned __int64 Alignment; +} KGDTENTRY64, *PKGDTENTRY64; + +typedef union _KIDTENTRY64 +{ + struct + { + unsigned short OffsetLow; + unsigned short Selector; + unsigned short IstIndex : 3; + unsigned short Reserved0 : 5; + unsigned short Type : 5; + unsigned short Dpl : 2; + unsigned short Present : 1; + unsigned short OffsetMiddle; + unsigned long OffsetHigh; + unsigned long Reserved1; + }; + unsigned __int64 Alignment; +} KIDTENTRY64, *PKIDTENTRY64; + +#include "ntifs.h" diff --git a/crates/windows-kernel-sys/src/wrapper_intrin.c b/crates/windows-kernel-sys/src/wrapper_intrin.c new file mode 100644 index 0000000..5c17cf9 --- /dev/null +++ b/crates/windows-kernel-sys/src/wrapper_intrin.c @@ -0,0 +1,61 @@ +#define _AMD64_ + +#include "wdm.h" +#include "intrin.h" + +unsigned __int64 read_cr3(void) { + return __readcr3(); +} + +void write_cr3(unsigned __int64 Value) { + __writecr3(Value); +} + +unsigned __int64 read_msr( + unsigned long Register +) { + return __readmsr(Register); +} + +NTSTATUS read_msr_safe( + unsigned long Register, + unsigned __int64 *Value +) { + if (!Value) { + return STATUS_INVALID_PARAMETER; + } + + __try { + *Value = __readmsr(Register); + } __except(EXCEPTION_EXECUTE_HANDLER) { + return GetExceptionCode(); + } + + return STATUS_SUCCESS; +} + +void write_msr( + unsigned long Register, + unsigned __int64 Value +) { + __writemsr(Register, Value); +} + +NTSTATUS write_msr_safe( + unsigned long Register, + unsigned __int64 Value +) { + __try { + __writemsr(Register, Value); + } __except(EXCEPTION_EXECUTE_HANDLER) { + return GetExceptionCode(); + } + + return STATUS_SUCCESS; +} + +void invlpg( + void *Address +) { + __invlpg(Address); +} diff --git a/crates/windows-kernel-sys/src/wrapper_netio.h b/crates/windows-kernel-sys/src/wrapper_netio.h new file mode 100644 index 0000000..96c112f --- /dev/null +++ b/crates/windows-kernel-sys/src/wrapper_netio.h @@ -0,0 +1,5 @@ +#define _AMD64_ + +#include "ntdef.h" +#include "wdm.h" +#include "wsk.h" diff --git a/crates/winioctl/Cargo.toml b/crates/winioctl/Cargo.toml new file mode 100644 index 0000000..656c14c --- /dev/null +++ b/crates/winioctl/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "winioctl" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitflags = "1.3" +thiserror = "1.0" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["ioapiset", "winioctl"] } diff --git a/crates/winioctl/LICENSE b/crates/winioctl/LICENSE new file mode 100644 index 0000000..68bb87c --- /dev/null +++ b/crates/winioctl/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 S.J.R. van Schaik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/winioctl/src/error.rs b/crates/winioctl/src/error.rs new file mode 100644 index 0000000..968b166 --- /dev/null +++ b/crates/winioctl/src/error.rs @@ -0,0 +1,7 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error(transparent)] + IoError(#[from] std::io::Error), +} diff --git a/crates/winioctl/src/ioctl.rs b/crates/winioctl/src/ioctl.rs new file mode 100644 index 0000000..d2e6b91 --- /dev/null +++ b/crates/winioctl/src/ioctl.rs @@ -0,0 +1,423 @@ +use bitflags::bitflags; +use winapi::um::{ + winioctl::{ + FILE_ANY_ACCESS, + METHOD_BUFFERED, + METHOD_IN_DIRECT, + METHOD_NEITHER, + METHOD_OUT_DIRECT, + }, + winnt::{FILE_READ_DATA, FILE_WRITE_DATA}, +}; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum DeviceType { + Port8042, + Acpi, + Battery, + Beep, + BusExtender, + Cdrom, + CdromFileSystem, + Changer, + Controller, + DataLink, + Dfs, + DfsFileSystem, + DfsVolume, + Disk, + DiskFileSystem, + Dvd, + FileSystem, + Fips, + FullscreenVideo, + InportPort, + Keyboard, + Ks, + Ksec, + Mailslot, + MassStorage, + MidiIn, + MidiOut, + Modem, + Mouse, + MultiUncProvider, + NamedPipe, + Network, + NetworkBrowser, + NetworkFileSystem, + NetworkRedirector, + Null, + ParallelPort, + PhysicalNetcard, + Printer, + Scanner, + Screen, + Serenum, + SerialPort, + SerialMousePort, + Smartcard, + Smb, + Sound, + Streams, + Tape, + TapeFileSystem, + Termsrv, + Transport, + Unknown, + Vdm, + Video, + VirtualDisk, + WaveIn, + WaveOut, +} + +impl Into<u32> for DeviceType { + fn into(self) -> u32 { + match self { + DeviceType::Port8042 => winapi::um::winioctl::FILE_DEVICE_8042_PORT, + DeviceType::Acpi => winapi::um::winioctl::FILE_DEVICE_ACPI, + DeviceType::Battery => winapi::um::winioctl::FILE_DEVICE_BATTERY, + DeviceType::Beep => winapi::um::winioctl::FILE_DEVICE_BEEP, + DeviceType::BusExtender => winapi::um::winioctl::FILE_DEVICE_BUS_EXTENDER, + DeviceType::Cdrom => winapi::um::winioctl::FILE_DEVICE_CD_ROM, + DeviceType::CdromFileSystem => winapi::um::winioctl::FILE_DEVICE_CD_ROM_FILE_SYSTEM, + DeviceType::Changer => winapi::um::winioctl::FILE_DEVICE_CHANGER, + DeviceType::Controller => winapi::um::winioctl::FILE_DEVICE_CONTROLLER, + DeviceType::DataLink => winapi::um::winioctl::FILE_DEVICE_DATALINK, + DeviceType::Dfs => winapi::um::winioctl::FILE_DEVICE_DFS, + DeviceType::DfsFileSystem => winapi::um::winioctl::FILE_DEVICE_DFS_FILE_SYSTEM, + DeviceType::DfsVolume => winapi::um::winioctl::FILE_DEVICE_DFS_VOLUME, + DeviceType::Disk => winapi::um::winioctl::FILE_DEVICE_DISK, + DeviceType::DiskFileSystem => winapi::um::winioctl::FILE_DEVICE_DISK_FILE_SYSTEM, + DeviceType::Dvd => winapi::um::winioctl::FILE_DEVICE_DVD, + DeviceType::FileSystem => winapi::um::winioctl::FILE_DEVICE_FILE_SYSTEM, + DeviceType::Fips => winapi::um::winioctl::FILE_DEVICE_FIPS, + DeviceType::FullscreenVideo => winapi::um::winioctl::FILE_DEVICE_FULLSCREEN_VIDEO, + DeviceType::InportPort => winapi::um::winioctl::FILE_DEVICE_INPORT_PORT, + DeviceType::Keyboard => winapi::um::winioctl::FILE_DEVICE_KEYBOARD, + DeviceType::Ks => winapi::um::winioctl::FILE_DEVICE_KS, + DeviceType::Ksec => winapi::um::winioctl::FILE_DEVICE_KSEC, + DeviceType::Mailslot => winapi::um::winioctl::FILE_DEVICE_MAILSLOT, + DeviceType::MassStorage => winapi::um::winioctl::FILE_DEVICE_MASS_STORAGE, + DeviceType::MidiIn => winapi::um::winioctl::FILE_DEVICE_MIDI_IN, + DeviceType::MidiOut => winapi::um::winioctl::FILE_DEVICE_MIDI_OUT, + DeviceType::Modem => winapi::um::winioctl::FILE_DEVICE_MODEM, + DeviceType::Mouse => winapi::um::winioctl::FILE_DEVICE_MOUSE, + DeviceType::MultiUncProvider => winapi::um::winioctl::FILE_DEVICE_MULTI_UNC_PROVIDER, + DeviceType::NamedPipe => winapi::um::winioctl::FILE_DEVICE_NAMED_PIPE, + DeviceType::Network => winapi::um::winioctl::FILE_DEVICE_NETWORK, + DeviceType::NetworkBrowser => winapi::um::winioctl::FILE_DEVICE_NETWORK_BROWSER, + DeviceType::NetworkFileSystem => winapi::um::winioctl::FILE_DEVICE_NETWORK_FILE_SYSTEM, + DeviceType::NetworkRedirector => winapi::um::winioctl::FILE_DEVICE_NETWORK_REDIRECTOR, + DeviceType::Null => winapi::um::winioctl::FILE_DEVICE_NULL, + DeviceType::ParallelPort => winapi::um::winioctl::FILE_DEVICE_PARALLEL_PORT, + DeviceType::PhysicalNetcard => winapi::um::winioctl::FILE_DEVICE_PHYSICAL_NETCARD, + DeviceType::Printer => winapi::um::winioctl::FILE_DEVICE_PRINTER, + DeviceType::Scanner => winapi::um::winioctl::FILE_DEVICE_SCANNER, + DeviceType::Screen => winapi::um::winioctl::FILE_DEVICE_SCREEN, + DeviceType::Serenum => winapi::um::winioctl::FILE_DEVICE_SERENUM, + DeviceType::SerialMousePort => winapi::um::winioctl::FILE_DEVICE_SERIAL_MOUSE_PORT, + DeviceType::SerialPort => winapi::um::winioctl::FILE_DEVICE_SERIAL_PORT, + DeviceType::Smartcard => winapi::um::winioctl::FILE_DEVICE_SMARTCARD, + DeviceType::Smb => winapi::um::winioctl::FILE_DEVICE_SMB, + DeviceType::Sound => winapi::um::winioctl::FILE_DEVICE_SOUND, + DeviceType::Streams => winapi::um::winioctl::FILE_DEVICE_STREAMS, + DeviceType::Tape => winapi::um::winioctl::FILE_DEVICE_TAPE, + DeviceType::TapeFileSystem => winapi::um::winioctl::FILE_DEVICE_TAPE_FILE_SYSTEM, + DeviceType::Termsrv => winapi::um::winioctl::FILE_DEVICE_TERMSRV, + DeviceType::Transport => winapi::um::winioctl::FILE_DEVICE_TRANSPORT, + DeviceType::Unknown => winapi::um::winioctl::FILE_DEVICE_UNKNOWN, + DeviceType::Vdm => winapi::um::winioctl::FILE_DEVICE_VDM, + DeviceType::Video => winapi::um::winioctl::FILE_DEVICE_VIDEO, + DeviceType::VirtualDisk => winapi::um::winioctl::FILE_DEVICE_VIRTUAL_DISK, + DeviceType::WaveIn => winapi::um::winioctl::FILE_DEVICE_WAVE_IN, + DeviceType::WaveOut => winapi::um::winioctl::FILE_DEVICE_WAVE_OUT, + } + } +} + +impl From<u32> for DeviceType { + fn from(value: u32) -> Self { + match value { + winapi::um::winioctl::FILE_DEVICE_8042_PORT => DeviceType::Port8042, + winapi::um::winioctl::FILE_DEVICE_ACPI => DeviceType::Acpi, + winapi::um::winioctl::FILE_DEVICE_BATTERY => DeviceType::Battery, + winapi::um::winioctl::FILE_DEVICE_BEEP => DeviceType::Beep, + winapi::um::winioctl::FILE_DEVICE_BUS_EXTENDER => DeviceType::BusExtender, + winapi::um::winioctl::FILE_DEVICE_CD_ROM => DeviceType::Cdrom, + winapi::um::winioctl::FILE_DEVICE_CD_ROM_FILE_SYSTEM => DeviceType::CdromFileSystem, + winapi::um::winioctl::FILE_DEVICE_CHANGER => DeviceType::Changer, + winapi::um::winioctl::FILE_DEVICE_CONTROLLER => DeviceType::Controller, + winapi::um::winioctl::FILE_DEVICE_DATALINK => DeviceType::DataLink, + winapi::um::winioctl::FILE_DEVICE_DFS => DeviceType::Dfs, + winapi::um::winioctl::FILE_DEVICE_DFS_FILE_SYSTEM => DeviceType::DfsFileSystem, + winapi::um::winioctl::FILE_DEVICE_DFS_VOLUME => DeviceType::DfsVolume, + winapi::um::winioctl::FILE_DEVICE_DISK => DeviceType::Disk, + winapi::um::winioctl::FILE_DEVICE_DISK_FILE_SYSTEM => DeviceType::DiskFileSystem, + winapi::um::winioctl::FILE_DEVICE_DVD => DeviceType::Dvd, + winapi::um::winioctl::FILE_DEVICE_FILE_SYSTEM => DeviceType::FileSystem, + winapi::um::winioctl::FILE_DEVICE_FIPS => DeviceType::Fips, + winapi::um::winioctl::FILE_DEVICE_FULLSCREEN_VIDEO => DeviceType::FullscreenVideo, + winapi::um::winioctl::FILE_DEVICE_INPORT_PORT => DeviceType::InportPort, + winapi::um::winioctl::FILE_DEVICE_KEYBOARD => DeviceType::Keyboard, + winapi::um::winioctl::FILE_DEVICE_KS => DeviceType::Ks, + winapi::um::winioctl::FILE_DEVICE_KSEC => DeviceType::Ksec, + winapi::um::winioctl::FILE_DEVICE_MAILSLOT => DeviceType::Mailslot, + winapi::um::winioctl::FILE_DEVICE_MASS_STORAGE => DeviceType::MassStorage, + winapi::um::winioctl::FILE_DEVICE_MIDI_IN => DeviceType::MidiIn, + winapi::um::winioctl::FILE_DEVICE_MIDI_OUT => DeviceType::MidiOut, + winapi::um::winioctl::FILE_DEVICE_MODEM => DeviceType::Modem, + winapi::um::winioctl::FILE_DEVICE_MOUSE => DeviceType::Mouse, + winapi::um::winioctl::FILE_DEVICE_MULTI_UNC_PROVIDER => DeviceType::MultiUncProvider, + winapi::um::winioctl::FILE_DEVICE_NAMED_PIPE => DeviceType::NamedPipe, + winapi::um::winioctl::FILE_DEVICE_NETWORK => DeviceType::Network, + winapi::um::winioctl::FILE_DEVICE_NETWORK_BROWSER => DeviceType::NetworkBrowser, + winapi::um::winioctl::FILE_DEVICE_NETWORK_FILE_SYSTEM => DeviceType::NetworkFileSystem, + winapi::um::winioctl::FILE_DEVICE_NETWORK_REDIRECTOR => DeviceType::NetworkRedirector, + winapi::um::winioctl::FILE_DEVICE_NULL => DeviceType::Null, + winapi::um::winioctl::FILE_DEVICE_PARALLEL_PORT => DeviceType::ParallelPort, + winapi::um::winioctl::FILE_DEVICE_PHYSICAL_NETCARD => DeviceType::PhysicalNetcard, + winapi::um::winioctl::FILE_DEVICE_PRINTER => DeviceType::Printer, + winapi::um::winioctl::FILE_DEVICE_SCANNER => DeviceType::Scanner, + winapi::um::winioctl::FILE_DEVICE_SCREEN => DeviceType::Screen, + winapi::um::winioctl::FILE_DEVICE_SERENUM => DeviceType::Serenum, + winapi::um::winioctl::FILE_DEVICE_SERIAL_MOUSE_PORT => DeviceType::SerialMousePort, + winapi::um::winioctl::FILE_DEVICE_SERIAL_PORT => DeviceType::SerialPort, + winapi::um::winioctl::FILE_DEVICE_SMARTCARD => DeviceType::Smartcard, + winapi::um::winioctl::FILE_DEVICE_SMB => DeviceType::Smb, + winapi::um::winioctl::FILE_DEVICE_SOUND => DeviceType::Sound, + winapi::um::winioctl::FILE_DEVICE_STREAMS => DeviceType::Streams, + winapi::um::winioctl::FILE_DEVICE_TAPE => DeviceType::Tape, + winapi::um::winioctl::FILE_DEVICE_TAPE_FILE_SYSTEM => DeviceType::TapeFileSystem, + winapi::um::winioctl::FILE_DEVICE_TERMSRV => DeviceType::Termsrv, + winapi::um::winioctl::FILE_DEVICE_TRANSPORT => DeviceType::Transport, + winapi::um::winioctl::FILE_DEVICE_UNKNOWN => DeviceType::Unknown, + winapi::um::winioctl::FILE_DEVICE_VDM => DeviceType::Vdm, + winapi::um::winioctl::FILE_DEVICE_VIDEO => DeviceType::Video, + winapi::um::winioctl::FILE_DEVICE_VIRTUAL_DISK => DeviceType::VirtualDisk, + winapi::um::winioctl::FILE_DEVICE_WAVE_IN => DeviceType::WaveIn, + winapi::um::winioctl::FILE_DEVICE_WAVE_OUT => DeviceType::WaveOut, + _ => DeviceType::Unknown, + } + } +} + +bitflags! { + pub struct RequiredAccess: u32 { + const ANY_ACCESS = FILE_ANY_ACCESS; + const READ_DATA = FILE_READ_DATA; + const WRITE_DATA = FILE_WRITE_DATA; + const READ_WRITE_DATA = FILE_READ_DATA | FILE_WRITE_DATA; + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum TransferMethod { + Neither = METHOD_NEITHER, + InputDirect = METHOD_IN_DIRECT, + OutputDirect = METHOD_OUT_DIRECT, + Buffered = METHOD_BUFFERED, +} + +impl From<u32> for TransferMethod { + fn from(value: u32) -> Self { + match value & 0x3 { + METHOD_NEITHER => Self::Neither, + METHOD_IN_DIRECT => Self::InputDirect, + METHOD_OUT_DIRECT => Self::OutputDirect, + METHOD_BUFFERED => Self::Buffered, + _ => unreachable!(), + } + } +} + +impl Into<u32> for TransferMethod { + fn into(self) -> u32 { + match self { + Self::Neither => METHOD_NEITHER, + Self::InputDirect => METHOD_IN_DIRECT, + Self::OutputDirect => METHOD_OUT_DIRECT, + Self::Buffered => METHOD_BUFFERED, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ControlCode( + pub DeviceType, + pub RequiredAccess, + pub u32, + pub TransferMethod, +); + +impl ControlCode { + const ACCESS_BITS: usize = 2; + const ACCESS_MASK: u32 = (1 << Self::ACCESS_BITS) - 1; + const ACCESS_SHIFT: usize = Self::NUM_SHIFT + Self::NUM_BITS; + const METHOD_BITS: usize = 2; + const METHOD_MASK: u32 = (1 << Self::METHOD_BITS) - 1; + const METHOD_SHIFT: usize = 0; + const NUM_BITS: usize = 12; + const NUM_MASK: u32 = (1 << Self::NUM_BITS) - 1; + const NUM_SHIFT: usize = Self::METHOD_SHIFT + Self::METHOD_BITS; + const TYPE_BITS: usize = 16; + const TYPE_MASK: u32 = (1 << Self::TYPE_BITS) - 1; + const TYPE_SHIFT: usize = Self::ACCESS_SHIFT + Self::ACCESS_BITS; +} + +impl From<u32> for ControlCode { + fn from(value: u32) -> Self { + let method = (value >> Self::METHOD_SHIFT) & Self::METHOD_MASK; + let num = (value >> Self::NUM_SHIFT) & Self::NUM_MASK; + let access = (value >> Self::ACCESS_SHIFT) & Self::ACCESS_MASK; + let ty = (value >> Self::TYPE_SHIFT) & Self::TYPE_MASK; + + Self( + ty.into(), + RequiredAccess::from_bits(access).unwrap_or(RequiredAccess::READ_DATA), + num, + method.into(), + ) + } +} + +impl Into<u32> for ControlCode { + fn into(self) -> u32 { + let method = Into::<u32>::into(self.3) << Self::METHOD_SHIFT; + let num = self.2 << Self::NUM_SHIFT; + let access = self.1.bits() << Self::ACCESS_SHIFT; + let ty = Into::<u32>::into(self.0) << Self::TYPE_SHIFT; + + ty | access | num | method + } +} + +#[macro_export] +macro_rules! ioctl_none { + ($(#[$attr:meta])* $name:ident, $dev_ty:expr, $nr:expr) => { + $(#[$attr])* + pub unsafe fn $name(handle: *mut std::ffi::c_void) -> Result<u32, $crate::Error> { + let code = $crate::ControlCode( + $dev_ty, + $crate::RequiredAccess::ANY_ACCESS, + $nr, + $crate::TransferMethod::Neither, + ).into(); + let mut return_value = 0; + + let status = $crate::DeviceIoControl( + handle as _, + code, + std::ptr::null_mut(), + 0, + std::ptr::null_mut(), + 0, + &mut return_value, + std::ptr::null_mut(), + ) != 0; + + match status { + true => Ok(return_value), + _ => Err(std::io::Error::last_os_error())?, + } + } + } +} + +#[macro_export] +macro_rules! ioctl_read { + ($(#[$attr:meta])* $name:ident, $dev_ty:expr, $nr:expr, $ty:ty) => { + $(#[$attr])* + pub unsafe fn $name(handle: *mut std::ffi::c_void, data: *mut $ty) -> Result<u32, $crate::Error> { + let code = $crate::ControlCode( + $dev_ty, + $crate::RequiredAccess::READ_DATA, + $nr, + $crate::TransferMethod::Buffered, + ).into(); + let mut return_value = 0; + + let status = $crate::DeviceIoControl( + handle as _, + code, + data as _, + std::mem::size_of::<$ty>() as _, + data as _, + std::mem::size_of::<$ty>() as _, + &mut return_value, + std::ptr::null_mut(), + ) != 0; + + match status { + true => Ok(return_value), + _ => Err(std::io::Error::last_os_error())?, + } + } + } +} + +#[macro_export] +macro_rules! ioctl_write { + ($(#[$attr:meta])* $name:ident, $dev_ty:expr, $nr:expr, $ty:ty) => { + $(#[$attr])* + pub unsafe fn $name(handle: *mut std::ffi::c_void, data: *const $ty) -> Result<u32, $crate::Error> { + let code = $crate::ControlCode( + $dev_ty, + $crate::RequiredAccess::WRITE_DATA, + $nr, + $crate::TransferMethod::Buffered, + ).into(); + let mut return_value = 0; + + let status = $crate::DeviceIoControl( + handle as _, + code, + data as _, + std::mem::size_of::<$ty>() as _, + std::ptr::null_mut(), + 0, + &mut return_value, + std::ptr::null_mut(), + ) != 0; + + match status { + true => Ok(return_value), + _ => Err(std::io::Error::last_os_error())?, + } + } + } +} + +#[macro_export] +macro_rules! ioctl_readwrite { + ($(#[$attr:meta])* $name:ident, $dev_ty:expr, $nr:expr, $ty:ty) => { + $(#[$attr])* + pub unsafe fn $name(handle: *mut std::ffi::c_void, data: *mut $ty) -> Result<u32, $crate::Error> { + let code = $crate::ControlCode( + $dev_ty, + $crate::RequiredAccess::READ_WRITE_DATA, + $nr, + $crate::TransferMethod::Buffered, + ).into(); + let mut return_value = 0; + + let status = $crate::DeviceIoControl( + handle as _, + code, + data as _, + std::mem::size_of::<$ty>() as _, + data as _, + std::mem::size_of::<$ty>() as _, + &mut return_value, + std::ptr::null_mut(), + ) != 0; + + match status { + true => Ok(return_value), + _ => Err(std::io::Error::last_os_error())?, + } + } + } +} diff --git a/crates/winioctl/src/lib.rs b/crates/winioctl/src/lib.rs new file mode 100644 index 0000000..8cd45b4 --- /dev/null +++ b/crates/winioctl/src/lib.rs @@ -0,0 +1,9 @@ +mod error; +mod ioctl; + +pub use winapi::um::ioapiset::DeviceIoControl; + +pub use crate::{ + error::Error, + ioctl::{ControlCode, DeviceType, RequiredAccess, TransferMethod}, +}; diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..09396e5 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly-2021-12-04" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..cdca26c --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,29 @@ +condense_wildcard_suffixes = true +edition = "2021" +enum_discrim_align_threshold = 20 +# error_on_line_overflow = true +# error_on_unformatted = true +fn_single_line = true +force_multiline_blocks = true +format_code_in_doc_comments = true +format_macro_matchers = true +format_strings = true +imports_layout = "HorizontalVertical" +# license_template_path = ".license_template" +match_arm_blocks = false +imports_granularity = "Crate" +newline_style = "Unix" +normalize_comments = true +normalize_doc_attributes = true +reorder_impl_items = true +group_imports = "StdExternalCrate" +reorder_modules = true +report_fixme = "Always" +# report_todo = "Always" +struct_field_align_threshold = 20 +struct_lit_single_line = false +tab_spaces = 2 +use_field_init_shorthand = true +use_try_shorthand = true +where_single_line = true +wrap_comments = true |