├── .gitignore ├── LICENSE.txt ├── Makefile ├── README.md ├── UPDATES.md ├── clang-format-linux-linpmem.config ├── demo └── test.c ├── figures └── linpmem_with_eye.png ├── src ├── linpmem.c ├── linpmem.h ├── page_table.h ├── precompiler.h ├── pte_mmap.c └── pte_mmap.h └── userspace_interface └── linpmem_shared.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o* 2 | *.ko* 3 | *.mod* 4 | *.cmd* 5 | *.json* 6 | *.saved 7 | Module* 8 | Makefile.* 9 | demo/ 10 | !demo/test.c 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 6 | 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | Preamble 10 | The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. 11 | 12 | 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 this service 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. 13 | 14 | To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. 15 | 16 | For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. 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. 17 | 18 | We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. 19 | 20 | Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. 21 | 22 | Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. 23 | 24 | The precise terms and conditions for copying, distribution and modification follow. 25 | 26 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 27 | 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". 28 | 29 | Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 30 | 31 | 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. 32 | 33 | You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 34 | 35 | 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: 36 | 37 | a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. 38 | b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. 39 | c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) 40 | These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. 41 | 42 | Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. 43 | 44 | In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 45 | 46 | 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: 47 | 48 | a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 49 | b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, 50 | c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) 51 | The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. 52 | 53 | If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 54 | 55 | 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 56 | 57 | 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 58 | 59 | 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 60 | 61 | 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. 62 | 63 | If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. 64 | 65 | It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. 66 | 67 | This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 68 | 69 | 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 70 | 71 | 9. The Free Software Foundation may publish revised and/or new versions of the 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. 72 | 73 | Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 74 | 75 | 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. 76 | 77 | NO WARRANTY 78 | 79 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. 80 | 81 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE 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. 82 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MNAME = linpmem 2 | 3 | obj-m += $(MNAME).o 4 | linpmem-objs += src/linpmem.o src/pte_mmap.o 5 | 6 | MDIR ?= $(shell pwd) 7 | KDIR ?= /lib/modules/$(shell uname -r)/build 8 | 9 | .PHONY: all clean modules 10 | 11 | all: clean modules 12 | 13 | modules: 14 | make -C $(KDIR) M=$(MDIR) modules 15 | 16 | clean: 17 | make -C $(KDIR) M=$(MDIR) clean 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linpmem -- a physical memory acquisition tool for Linux 2 | 3 | ![alt text](figures/linpmem_with_eye.png "LinPmem -- a physical memory acquisition tool (For Linux)") 4 | 5 | ##### Linpmem is a Linux x64-only tool for reading physical memory. 6 | 7 | Like its Windows counterpart, [Winpmem](https://github.com/Velocidex/WinPmem), this is not a traditional memory dumper. Linpmem offers an API for reading from *any* physical address, including *reserved memory* and *memory holes*, but it can also be used for normal memory dumping. Furthermore, the driver offers a variety of access modes to read physical memory, such as byte, word, dword, qword, and buffer access mode, where buffer access mode is appropriate in most standard cases. If reading requires an aligned byte/word/dword/qword read, Linpmem will do precisely that. 8 | 9 | Currently, the Linpmem features: 10 | 11 | 1. Read from physical address (access mode byte, word, dword, qword, or buffer) 12 | 2. CR3 info service (specify target process by pid) 13 | 3. Virtual to physical address translation service 14 | 15 | Cache Control is to be added in future for support of the specialized read access modes. 16 | 17 | ## Summary table 18 | 19 | * [Building The Kernel Driver](#building-the-kernel-driver) 20 | * [Loading The Driver](#loading-the-driver) 21 | * [Usage](#usage) 22 | * [Demo Code](#demo-code) 23 | * [CLI tool](#command-line-interface-tool) 24 | * [Library](#libraries) 25 | * [Memdumping tool](#memdumping-tool) 26 | * [Library](#libraries) 27 | * [Tested Linux Distributions](#tested-linux-distributions) 28 | * [Handling Secure Boot](#handling-secure-boot) 29 | * [Known Issues](#known-issues) 30 | * [Under Work](#under-work) 31 | * [Future Work](#future-work) 32 | * [Acknowledgements](#acknowledgements) 33 | 34 | 35 | ## Building the kernel driver 36 | 37 | *At least for now*, you must compile the Linpmem driver yourself. A method to load a precompiled Linpmem driver on other Linux systems is currently under work, but not finished yet. That said, compiling the Linpmem driver is not difficult, basically it's executing 'make'. 38 | 39 | ### Step 1 - getting the right headers 40 | 41 | You need `make` and a C compiler. (We recommend gcc, but clang should work as well). 42 | 43 | Make sure that you have the `linux-headers` installed (using whatever package manager your target linux distro has). The exact package name may vary on your distribution. 44 | A quick (distro-independent) way to check if you have the package installed: 45 | 46 | ``` 47 | ls -l /usr/lib/modules/`uname -r`/ 48 | ``` 49 | 50 | That's it, you can proceed to step 2. 51 | 52 | **Foreign system:** *Currently*, if you want to compile the driver for _another_ system, e.g., because you want to create a memory dump but can't compile on the target, you have to download the header package directly from the package repositories of that system's Linux distribution. Double-check that the package version *exactly* matches the release and kernel version running on the foreign system. In case the other system is using a self-compiled kernel you have to obtain a copy of that kernel's build directory. Then, place the location of either directory in the `KDIR` environment variable. 53 | 54 | ``` 55 | export KDIR=path/to/extracted/header/package/or/kernel/root 56 | ``` 57 | 58 | ### Step 2 - make 59 | 60 | Compiling the driver is simple, just type: 61 | 62 | ``` 63 | make 64 | ``` 65 | 66 | This should produce `linpmem.ko` in the current working directory. 67 | 68 | You might want to check `precompiler.h` before and chose whether to compile for release or debug (e.g., with debug printing). There aren't much other precompiler settings right now. 69 | 70 | ## Loading The Driver 71 | 72 | The linpmem.ko module can be loaded by using `insmod path-to-linpmem.ko`, and unloaded with `rmmod path-to-linpmem.ko`. (This will load the driver only for this uptime.) If you compiled for debug, also take a look at dmesg. 73 | 74 | After loading, for talking to the driver, you need to create the device: 75 | 76 | ``` 77 | mknod /dev/linpmem c 42 0 78 | ``` 79 | 80 | If you can't talk to the driver, potentially check in dmesg log to verify that '42' was indeed the registered major: 81 | 82 | ``` 83 | [12827.900168] linpmem: registered chrdev with major 42 84 | ``` 85 | 86 | Though usually the kernel would try to really assign this number. 87 | 88 | You can use `chown` on the device to give it to your user, if you do not want to have a root console open all the time. (Or just keep using it in a root console.) 89 | 90 | * Watch dmesg output. Please report errors if you see any! 91 | * Warning: if there is a dmesg error print from Linpmem telling to reboot, better do it immediately. 92 | * Warning: this is an early version. 93 | 94 | ## Usage 95 | 96 | ### Demo Code 97 | 98 | There is an example code demonstrating and explaining (in detail) how to interact with the driver. The user-space API reference can furthermore be found in `./userspace_interface/linpmem_shared.h`. 99 | 100 | 1. cd demo 101 | 2. gcc -o test test.c 102 | 3. (sudo) ./test // <= you need sudo if you did not use chown on the device. 103 | 104 | This code is important, if you want to understand how to directly interact with the driver instead of using a [library](#libraries). It can also be used as a short function test. 105 | 106 | ### Command Line Interface Tool 107 | 108 | There is an (optional) basic command line interface tool to Linpmem, the *pmem CLI tool*. It can be found here: [https://github.com/vobst/linpmem-cli](https://github.com/vobst/linpmem-cli). Aside from the source code, there is also a precompiled CLI tool as well as the precompiled static library and headers that can be found [here](https://github.com/vobst/linpmem-cli/releases/) (signed). Note: this is a preliminary version, be sure to check for updates, as many additions and enhancements will follow soon. 109 | 110 | The pmem CLI tool can be used for testing the various functions of Linpmem in a (relatively) safe and convenient manner. Linpmem can also be loaded by this tool instead of using insmod/rmmod, with some extra options in future. This also has the advantage that pmem auto-creates the right device for you for immediate use. It is extremely portable and runs on any Linux system (and, in fact, has been tested even on a Linux 2.6). 111 | 112 | ``` 113 | $ ./pmem -h 114 | Command-line client for the linpmem driver 115 | 116 | Usage: pmem [OPTIONS] [COMMAND] 117 | 118 | Commands: 119 | insmod Load the linpmem driver 120 | help Print this message or the help of the given subcommand(s) 121 | 122 | Options: 123 | -a, --address
Address for physical read operations 124 | -v, --virt-address Translate address in target process' address space (default: current process) 125 | -s, --size Size of buffer read operations 126 | -m, --mode Access mode for read operations [possible values: byte, word, dword, qword, buffer] 127 | -p, --pid Target process for cr3 info and virtual-to-physical translations 128 | --cr3 Query cr3 value of target process (default: current process) 129 | --verbose Display debug output 130 | -h, --help Print help (see more with '--help') 131 | -V, --version Print version 132 | ``` 133 | 134 | If you want to compile the cli tool yourself, change to its directory and follow the instructions in the (cli) Readme to build it. Otherwise, just download the prebuilt program, it should work on *any* Linux. To load the kernel driver with the cli tool: 135 | 136 | ``` 137 | # pmem insmod path/to/linpmem.ko 138 | ``` 139 | 140 | The advantage of using the pmem tool to load the driver is that you do not have to create the device file yourself, and it will offer (on next releases) to choose who owns the linpmem device. 141 | 142 | ### Libraries 143 | 144 | The [pmem command line interface](#command-line-interface-tool) is only a thin wrapper around a small Rust library that exposes an API for interfacing with the driver. More advanced users can also use this library. The library is automatically compiled (as static portable library) along with the pmem cli tool when compiling from [https://github.com/vobst/linpmem-cli](https://github.com/vobst/linpmem-cli), but also included (precompiled) [here](https://github.com/vobst/linpmem-cli/releases/) (signed). Note: this is a preliminary version, more to follow soon. 145 | 146 | If you do not want to use the usermode library and prefer to interface with the driver directly on your own, you can find its user-space API/interface and documentation in `./userspace_interface/linpmem_shared.h`. We also provide example code in `demo/test.c` that explains how to use the driver directly. 147 | 148 | ### Memdumping tool 149 | 150 | Not implemented yet. 151 | 152 | 153 | ## Tested Linux Distributions 154 | 155 | * Debian, self-compiled 6.4.X, Qemu/KVM, not paravirtualized. 156 | * PTI: off/on 157 | * Debian 12, Qemu/KVM, fully paravirtualized. 158 | * PTI: on 159 | * Ubuntu server, Qemu/KVM, not paravirtualized. 160 | * PTI: on 161 | * Fedora 38, Qemu/KVM, fully paravirtualized. 162 | * PTI: on 163 | * Baremetal Linux test, AMI BIOS: Linux 6.4.4 164 | * PTI: on 165 | * Baremetal Linux test, HP: Linux 6.4.4 166 | * PTI: on 167 | * Baremetal, Arch[-hardened], Dell BIOS, Linux 6.4.X 168 | * Baremetal, Debian, 6.1.X 169 | * Baremetal, Ubuntu 20.04 with Secure Boot on. Works, but [sign](#handling-secure-boot) driver first. 170 | * Baremetal, Ubuntu 22.04, Linux 6.2.X 171 | 172 | ## Handling Secure Boot 173 | 174 | If the system reports the following error message when loading the module, it might be because of secure boot: 175 | ``` 176 | $ sudo insmod linpmem.ko 177 | insmod: ERROR: could not insert module linpmem.ko: Operation not permitted 178 | ``` 179 | There are different ways to still load the module. The obvious one is to disable secure boot in your UEFI settings. 180 | 181 | If your distribution supports it, a more elegant solution would be to sign the module before using it. 182 | This can be done using the following steps (tested on Ubuntu 20.04). 183 | 1. Install mokutil: 184 | ``` 185 | $ sudo apt install mokutil 186 | ``` 187 | 2. Create the singing key material: 188 | ``` 189 | $ openssl req -new -newkey rsa:4096 -keyout mok-signing.key -out mok-signing.crt -outform DER -days 365 -nodes -subj "/CN=Some descriptive name/" 190 | ``` 191 | Make sure to adjust the options to your needs. Especially, consider the key length (-newkey), the validity (-days), the option to set a key pass phrase (-nodes; leave it out, if you want to set a pass phrase), and the common name to include into the certificate (-subj). 192 | 3. Register the new MOK: 193 | ``` 194 | $ sudo mokutil --import mok-signing.crt 195 | ``` 196 | You will be asked for a password, which is required in the following step. Consider using a password, which you can type on a US keyboard layout. 197 | 4. Reboot the system. 198 | It will enter a MOK enrollment menu. Follow the instructions to enroll your new key. 199 | 5. Sign the module 200 | Once the MOK is enrolled, you can sign your module. 201 | ``` 202 | $ /usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 path/to/mok-singing/MOK.key path/to//MOK.cert path/to/linpmem.ko 203 | ``` 204 | After that, you should be able to load the module. 205 | 206 | Note that from a forensic-readiness perspective, you should prepare a signed module **before** you need it, as the system will reboot twice during the process described above, destroying most of your volatile data in memory. 207 | 208 | ## Known Issues 209 | 210 | * Huge page read is not implemented. Linpmem recognizes a huge page and rejects the read, for now. 211 | * Reading from mapped io and DMA space will be done with CPU caching enabled. 212 | * No locks are taken during the page table walk. This might lead to funny results when concurrent modifications are going on. This is a general and (mostly unsolvable) problem of live RAM reading, without halting the entire OS to full stop. 213 | * Secure Boot (Ubuntu): please [sign](#handling-secure-boot) your driver prior to using. 214 | * Any CPU-powered memory encryption, e.g., AMD SME, Intel SGX/TDX, ... 215 | * Pluton chips? 216 | 217 | (Please report potential issues if you encounter anything.) 218 | 219 | ## Under work 220 | 221 | * Loading precompiled driver on any Linux. 222 | * Processor cache control. Example: for uncached reading of mapped I/O and DMA space. 223 | 224 | ## Future work 225 | 226 | * Arm/Mips support. (far future work) 227 | * Legacy kernels (such as 2.6), unix-based kernels 228 | 229 | ## Acknowledgements 230 | 231 | [Linpmem](https://github.com/Velocidex/Linpmem), as well as [Winpmem](https://github.com/Velocidex/WinPmem), would not exist without the work of our predecessors of the (now retired) REKALL project: https://github.com/google/rekall. 232 | 233 | * We would like to thank Mike Cohen and Johannes Stüttgen for their pioneer work and open source contribution on PTE remapping, a technique which is still in use 10 years later. 234 | 235 | Our open source contributors: 236 | 237 | * Viviane Zwanger 238 | * Valentin Obst 239 | -------------------------------------------------------------------------------- /UPDATES.md: -------------------------------------------------------------------------------- 1 | # Updates and changes 2 | 3 | 11. May 2024 4 | 5 | There was a serious issue in Linpmem where in the middle of PTE remapping, Linpmem could get scheduled off the CPU processor and later being re-scheduled on another CPU core along with another CPU cache. This definitely made trouble on Linux in the sense of reading wrong data. This has been fixed. (see commit log). 6 | 7 | 1. Sept 2023: 8 | 9 | * Many safety enhancements. Most important, this driver is now thread-safe. It can be called simultaneously from multiple processes, while returning the correct results to each. Also, better deadlock prevention and generally safety and sanity checks. 10 | * Now not only can a foreign CR3 be specified in VTOP, but you can also rely on the CR3 info service from Linpmem to acquire said CR3 from any running foreign process. 11 | * There is a basic Linpmem shell (Linpmem-cli) you can use now. It implements all basic functionalities from Linpmem and can also be used as library. 12 | 13 | 14 | 25. July 2023: 15 | 16 | * Enabled using a foreign CR3 in the VTOP translation service. Leave it to zero for default CR3. (See linpmem_shared.h, struct `LINPMEM_VTOP_INFO`, field `associated_cr3`). Use at your own risk! 17 | * Foreign CR3's can currently be acquired by inserting a thread into another process in gentlemen agreement, and calling Linpmem for CR3 query from there. There is no extra service. 18 | * Made precompiler switches consistent. Commenting a precompiler switch always disables it. 19 | 20 | 25. July 2023: 21 | 22 | * Initial commit 23 | -------------------------------------------------------------------------------- /clang-format-linux-linpmem.config: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 11. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | --- 12 | AccessModifierOffset: -4 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveAssignments: false 15 | AlignConsecutiveDeclarations: false 16 | AlignEscapedNewlines: Left 17 | AlignOperands: true 18 | AlignTrailingComments: false 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | AllowShortBlocksOnASingleLine: false 21 | AllowShortCaseLabelsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: None 23 | AllowShortIfStatementsOnASingleLine: false 24 | AllowShortLoopsOnASingleLine: false 25 | AlwaysBreakAfterDefinitionReturnType: None 26 | AlwaysBreakAfterReturnType: None 27 | AlwaysBreakBeforeMultilineStrings: false 28 | AlwaysBreakTemplateDeclarations: false 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterClass: false 33 | AfterControlStatement: false 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: true 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | AfterExternBlock: false 41 | BeforeCatch: false 42 | BeforeElse: false 43 | IndentBraces: false 44 | SplitEmptyFunction: true 45 | SplitEmptyRecord: true 46 | SplitEmptyNamespace: true 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Custom 49 | BreakBeforeInheritanceComma: false 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializersBeforeComma: false 52 | BreakConstructorInitializers: BeforeComma 53 | BreakAfterJavaFieldAnnotations: false 54 | BreakStringLiterals: false 55 | ColumnLimit: 80 56 | CommentPragmas: '^ IWYU pragma:' 57 | CompactNamespaces: false 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 59 | ConstructorInitializerIndentWidth: 4 60 | ContinuationIndentWidth: 4 61 | Cpp11BracedListStyle: false 62 | DerivePointerAlignment: false 63 | DisableFormat: false 64 | ExperimentalAutoDetectBinPacking: false 65 | FixNamespaceComments: false 66 | 67 | # Taken from: 68 | # git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ tools/ \ 69 | # | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ 70 | # | LC_ALL=C sort -u 71 | ForEachMacros: 72 | - '__ata_qc_for_each' 73 | - '__bio_for_each_bvec' 74 | - '__bio_for_each_segment' 75 | - '__evlist__for_each_entry' 76 | - '__evlist__for_each_entry_continue' 77 | - '__evlist__for_each_entry_from' 78 | - '__evlist__for_each_entry_reverse' 79 | - '__evlist__for_each_entry_safe' 80 | - '__for_each_mem_range' 81 | - '__for_each_mem_range_rev' 82 | - '__for_each_thread' 83 | - '__hlist_for_each_rcu' 84 | - '__map__for_each_symbol_by_name' 85 | - '__perf_evlist__for_each_entry' 86 | - '__perf_evlist__for_each_entry_reverse' 87 | - '__perf_evlist__for_each_entry_safe' 88 | - '__rq_for_each_bio' 89 | - '__shost_for_each_device' 90 | - 'apei_estatus_for_each_section' 91 | - 'ata_for_each_dev' 92 | - 'ata_for_each_link' 93 | - 'ata_qc_for_each' 94 | - 'ata_qc_for_each_raw' 95 | - 'ata_qc_for_each_with_internal' 96 | - 'ax25_for_each' 97 | - 'ax25_uid_for_each' 98 | - 'bio_for_each_bvec' 99 | - 'bio_for_each_bvec_all' 100 | - 'bio_for_each_folio_all' 101 | - 'bio_for_each_integrity_vec' 102 | - 'bio_for_each_segment' 103 | - 'bio_for_each_segment_all' 104 | - 'bio_list_for_each' 105 | - 'bip_for_each_vec' 106 | - 'bond_for_each_slave' 107 | - 'bond_for_each_slave_rcu' 108 | - 'bpf__perf_for_each_map' 109 | - 'bpf__perf_for_each_map_named' 110 | - 'bpf_for_each_spilled_reg' 111 | - 'bpf_object__for_each_map' 112 | - 'bpf_object__for_each_program' 113 | - 'bpf_object__for_each_safe' 114 | - 'bpf_perf_object__for_each' 115 | - 'btree_for_each_safe128' 116 | - 'btree_for_each_safe32' 117 | - 'btree_for_each_safe64' 118 | - 'btree_for_each_safel' 119 | - 'card_for_each_dev' 120 | - 'cgroup_taskset_for_each' 121 | - 'cgroup_taskset_for_each_leader' 122 | - 'cpufreq_for_each_efficient_entry_idx' 123 | - 'cpufreq_for_each_entry' 124 | - 'cpufreq_for_each_entry_idx' 125 | - 'cpufreq_for_each_valid_entry' 126 | - 'cpufreq_for_each_valid_entry_idx' 127 | - 'css_for_each_child' 128 | - 'css_for_each_descendant_post' 129 | - 'css_for_each_descendant_pre' 130 | - 'damon_for_each_region' 131 | - 'damon_for_each_region_safe' 132 | - 'damon_for_each_scheme' 133 | - 'damon_for_each_scheme_safe' 134 | - 'damon_for_each_target' 135 | - 'damon_for_each_target_safe' 136 | - 'data__for_each_file' 137 | - 'data__for_each_file_new' 138 | - 'data__for_each_file_start' 139 | - 'device_for_each_child_node' 140 | - 'displayid_iter_for_each' 141 | - 'dma_fence_array_for_each' 142 | - 'dma_fence_chain_for_each' 143 | - 'dma_fence_unwrap_for_each' 144 | - 'dma_resv_for_each_fence' 145 | - 'dma_resv_for_each_fence_unlocked' 146 | - 'do_for_each_ftrace_op' 147 | - 'drm_atomic_crtc_for_each_plane' 148 | - 'drm_atomic_crtc_state_for_each_plane' 149 | - 'drm_atomic_crtc_state_for_each_plane_state' 150 | - 'drm_atomic_for_each_plane_damage' 151 | - 'drm_client_for_each_connector_iter' 152 | - 'drm_client_for_each_modeset' 153 | - 'drm_connector_for_each_possible_encoder' 154 | - 'drm_for_each_bridge_in_chain' 155 | - 'drm_for_each_connector_iter' 156 | - 'drm_for_each_crtc' 157 | - 'drm_for_each_crtc_reverse' 158 | - 'drm_for_each_encoder' 159 | - 'drm_for_each_encoder_mask' 160 | - 'drm_for_each_fb' 161 | - 'drm_for_each_legacy_plane' 162 | - 'drm_for_each_plane' 163 | - 'drm_for_each_plane_mask' 164 | - 'drm_for_each_privobj' 165 | - 'drm_mm_for_each_hole' 166 | - 'drm_mm_for_each_node' 167 | - 'drm_mm_for_each_node_in_range' 168 | - 'drm_mm_for_each_node_safe' 169 | - 'dsa_switch_for_each_available_port' 170 | - 'dsa_switch_for_each_cpu_port' 171 | - 'dsa_switch_for_each_port' 172 | - 'dsa_switch_for_each_port_continue_reverse' 173 | - 'dsa_switch_for_each_port_safe' 174 | - 'dsa_switch_for_each_user_port' 175 | - 'dsa_tree_for_each_user_port' 176 | - 'dso__for_each_symbol' 177 | - 'dsos__for_each_with_build_id' 178 | - 'elf_hash_for_each_possible' 179 | - 'elf_section__for_each_rel' 180 | - 'elf_section__for_each_rela' 181 | - 'elf_symtab__for_each_symbol' 182 | - 'evlist__for_each_cpu' 183 | - 'evlist__for_each_entry' 184 | - 'evlist__for_each_entry_continue' 185 | - 'evlist__for_each_entry_from' 186 | - 'evlist__for_each_entry_reverse' 187 | - 'evlist__for_each_entry_safe' 188 | - 'flow_action_for_each' 189 | - 'for_each_acpi_dev_match' 190 | - 'for_each_active_dev_scope' 191 | - 'for_each_active_drhd_unit' 192 | - 'for_each_active_iommu' 193 | - 'for_each_active_route' 194 | - 'for_each_aggr_pgid' 195 | - 'for_each_available_child_of_node' 196 | - 'for_each_bench' 197 | - 'for_each_bio' 198 | - 'for_each_board_func_rsrc' 199 | - 'for_each_btf_ext_rec' 200 | - 'for_each_btf_ext_sec' 201 | - 'for_each_bvec' 202 | - 'for_each_card_auxs' 203 | - 'for_each_card_auxs_safe' 204 | - 'for_each_card_components' 205 | - 'for_each_card_dapms' 206 | - 'for_each_card_pre_auxs' 207 | - 'for_each_card_prelinks' 208 | - 'for_each_card_rtds' 209 | - 'for_each_card_rtds_safe' 210 | - 'for_each_card_widgets' 211 | - 'for_each_card_widgets_safe' 212 | - 'for_each_cgroup_storage_type' 213 | - 'for_each_child_of_node' 214 | - 'for_each_clear_bit' 215 | - 'for_each_clear_bit_from' 216 | - 'for_each_clear_bitrange' 217 | - 'for_each_clear_bitrange_from' 218 | - 'for_each_cmd' 219 | - 'for_each_cmsghdr' 220 | - 'for_each_collection' 221 | - 'for_each_comp_order' 222 | - 'for_each_compatible_node' 223 | - 'for_each_component_dais' 224 | - 'for_each_component_dais_safe' 225 | - 'for_each_console' 226 | - 'for_each_console_srcu' 227 | - 'for_each_cpu' 228 | - 'for_each_cpu_and' 229 | - 'for_each_cpu_wrap' 230 | - 'for_each_dapm_widgets' 231 | - 'for_each_dedup_cand' 232 | - 'for_each_dev_addr' 233 | - 'for_each_dev_scope' 234 | - 'for_each_dma_cap_mask' 235 | - 'for_each_dpcm_be' 236 | - 'for_each_dpcm_be_rollback' 237 | - 'for_each_dpcm_be_safe' 238 | - 'for_each_dpcm_fe' 239 | - 'for_each_drhd_unit' 240 | - 'for_each_dss_dev' 241 | - 'for_each_efi_memory_desc' 242 | - 'for_each_efi_memory_desc_in_map' 243 | - 'for_each_element' 244 | - 'for_each_element_extid' 245 | - 'for_each_element_id' 246 | - 'for_each_endpoint_of_node' 247 | - 'for_each_event' 248 | - 'for_each_event_tps' 249 | - 'for_each_evictable_lru' 250 | - 'for_each_fib6_node_rt_rcu' 251 | - 'for_each_fib6_walker_rt' 252 | - 'for_each_free_mem_pfn_range_in_zone' 253 | - 'for_each_free_mem_pfn_range_in_zone_from' 254 | - 'for_each_free_mem_range' 255 | - 'for_each_free_mem_range_reverse' 256 | - 'for_each_func_rsrc' 257 | - 'for_each_group_evsel' 258 | - 'for_each_group_member' 259 | - 'for_each_hstate' 260 | - 'for_each_if' 261 | - 'for_each_inject_fn' 262 | - 'for_each_insn' 263 | - 'for_each_insn_prefix' 264 | - 'for_each_intid' 265 | - 'for_each_iommu' 266 | - 'for_each_ip_tunnel_rcu' 267 | - 'for_each_irq_nr' 268 | - 'for_each_lang' 269 | - 'for_each_link_codecs' 270 | - 'for_each_link_cpus' 271 | - 'for_each_link_platforms' 272 | - 'for_each_lru' 273 | - 'for_each_matching_node' 274 | - 'for_each_matching_node_and_match' 275 | - 'for_each_mem_pfn_range' 276 | - 'for_each_mem_range' 277 | - 'for_each_mem_range_rev' 278 | - 'for_each_mem_region' 279 | - 'for_each_member' 280 | - 'for_each_memory' 281 | - 'for_each_migratetype_order' 282 | - 'for_each_missing_reg' 283 | - 'for_each_net' 284 | - 'for_each_net_continue_reverse' 285 | - 'for_each_net_rcu' 286 | - 'for_each_netdev' 287 | - 'for_each_netdev_continue' 288 | - 'for_each_netdev_continue_rcu' 289 | - 'for_each_netdev_continue_reverse' 290 | - 'for_each_netdev_feature' 291 | - 'for_each_netdev_in_bond_rcu' 292 | - 'for_each_netdev_rcu' 293 | - 'for_each_netdev_reverse' 294 | - 'for_each_netdev_safe' 295 | - 'for_each_new_connector_in_state' 296 | - 'for_each_new_crtc_in_state' 297 | - 'for_each_new_mst_mgr_in_state' 298 | - 'for_each_new_plane_in_state' 299 | - 'for_each_new_plane_in_state_reverse' 300 | - 'for_each_new_private_obj_in_state' 301 | - 'for_each_new_reg' 302 | - 'for_each_node' 303 | - 'for_each_node_by_name' 304 | - 'for_each_node_by_type' 305 | - 'for_each_node_mask' 306 | - 'for_each_node_state' 307 | - 'for_each_node_with_cpus' 308 | - 'for_each_node_with_property' 309 | - 'for_each_nonreserved_multicast_dest_pgid' 310 | - 'for_each_of_allnodes' 311 | - 'for_each_of_allnodes_from' 312 | - 'for_each_of_cpu_node' 313 | - 'for_each_of_pci_range' 314 | - 'for_each_old_connector_in_state' 315 | - 'for_each_old_crtc_in_state' 316 | - 'for_each_old_mst_mgr_in_state' 317 | - 'for_each_old_plane_in_state' 318 | - 'for_each_old_private_obj_in_state' 319 | - 'for_each_oldnew_connector_in_state' 320 | - 'for_each_oldnew_crtc_in_state' 321 | - 'for_each_oldnew_mst_mgr_in_state' 322 | - 'for_each_oldnew_plane_in_state' 323 | - 'for_each_oldnew_plane_in_state_reverse' 324 | - 'for_each_oldnew_private_obj_in_state' 325 | - 'for_each_online_cpu' 326 | - 'for_each_online_node' 327 | - 'for_each_online_pgdat' 328 | - 'for_each_path' 329 | - 'for_each_pci_bridge' 330 | - 'for_each_pci_dev' 331 | - 'for_each_pcm_streams' 332 | - 'for_each_physmem_range' 333 | - 'for_each_populated_zone' 334 | - 'for_each_possible_cpu' 335 | - 'for_each_present_cpu' 336 | - 'for_each_prime_number' 337 | - 'for_each_prime_number_from' 338 | - 'for_each_probe_cache_entry' 339 | - 'for_each_process' 340 | - 'for_each_process_thread' 341 | - 'for_each_prop_codec_conf' 342 | - 'for_each_prop_dai_codec' 343 | - 'for_each_prop_dai_cpu' 344 | - 'for_each_prop_dlc_codecs' 345 | - 'for_each_prop_dlc_cpus' 346 | - 'for_each_prop_dlc_platforms' 347 | - 'for_each_property_of_node' 348 | - 'for_each_reg' 349 | - 'for_each_reg_filtered' 350 | - 'for_each_registered_fb' 351 | - 'for_each_requested_gpio' 352 | - 'for_each_requested_gpio_in_range' 353 | - 'for_each_reserved_mem_range' 354 | - 'for_each_reserved_mem_region' 355 | - 'for_each_rtd_codec_dais' 356 | - 'for_each_rtd_components' 357 | - 'for_each_rtd_cpu_dais' 358 | - 'for_each_rtd_dais' 359 | - 'for_each_script' 360 | - 'for_each_sec' 361 | - 'for_each_set_bit' 362 | - 'for_each_set_bit_from' 363 | - 'for_each_set_bitrange' 364 | - 'for_each_set_bitrange_from' 365 | - 'for_each_set_clump8' 366 | - 'for_each_sg' 367 | - 'for_each_sg_dma_page' 368 | - 'for_each_sg_page' 369 | - 'for_each_sgtable_dma_page' 370 | - 'for_each_sgtable_dma_sg' 371 | - 'for_each_sgtable_page' 372 | - 'for_each_sgtable_sg' 373 | - 'for_each_shell_test' 374 | - 'for_each_sibling_event' 375 | - 'for_each_subelement' 376 | - 'for_each_subelement_extid' 377 | - 'for_each_subelement_id' 378 | - 'for_each_sublist' 379 | - 'for_each_subsystem' 380 | - 'for_each_supported_activate_fn' 381 | - 'for_each_supported_inject_fn' 382 | - 'for_each_test' 383 | - 'for_each_thread' 384 | - 'for_each_token' 385 | - 'for_each_unicast_dest_pgid' 386 | - 'for_each_vsi' 387 | - 'for_each_wakeup_source' 388 | - 'for_each_zone' 389 | - 'for_each_zone_zonelist' 390 | - 'for_each_zone_zonelist_nodemask' 391 | - 'func_for_each_insn' 392 | - 'fwnode_for_each_available_child_node' 393 | - 'fwnode_for_each_child_node' 394 | - 'fwnode_graph_for_each_endpoint' 395 | - 'gadget_for_each_ep' 396 | - 'genradix_for_each' 397 | - 'genradix_for_each_from' 398 | - 'hash_for_each' 399 | - 'hash_for_each_possible' 400 | - 'hash_for_each_possible_rcu' 401 | - 'hash_for_each_possible_rcu_notrace' 402 | - 'hash_for_each_possible_safe' 403 | - 'hash_for_each_rcu' 404 | - 'hash_for_each_safe' 405 | - 'hashmap__for_each_entry' 406 | - 'hashmap__for_each_entry_safe' 407 | - 'hashmap__for_each_key_entry' 408 | - 'hashmap__for_each_key_entry_safe' 409 | - 'hctx_for_each_ctx' 410 | - 'hists__for_each_format' 411 | - 'hists__for_each_sort_list' 412 | - 'hlist_bl_for_each_entry' 413 | - 'hlist_bl_for_each_entry_rcu' 414 | - 'hlist_bl_for_each_entry_safe' 415 | - 'hlist_for_each' 416 | - 'hlist_for_each_entry' 417 | - 'hlist_for_each_entry_continue' 418 | - 'hlist_for_each_entry_continue_rcu' 419 | - 'hlist_for_each_entry_continue_rcu_bh' 420 | - 'hlist_for_each_entry_from' 421 | - 'hlist_for_each_entry_from_rcu' 422 | - 'hlist_for_each_entry_rcu' 423 | - 'hlist_for_each_entry_rcu_bh' 424 | - 'hlist_for_each_entry_rcu_notrace' 425 | - 'hlist_for_each_entry_safe' 426 | - 'hlist_for_each_entry_srcu' 427 | - 'hlist_for_each_safe' 428 | - 'hlist_nulls_for_each_entry' 429 | - 'hlist_nulls_for_each_entry_from' 430 | - 'hlist_nulls_for_each_entry_rcu' 431 | - 'hlist_nulls_for_each_entry_safe' 432 | - 'i3c_bus_for_each_i2cdev' 433 | - 'i3c_bus_for_each_i3cdev' 434 | - 'idr_for_each_entry' 435 | - 'idr_for_each_entry_continue' 436 | - 'idr_for_each_entry_continue_ul' 437 | - 'idr_for_each_entry_ul' 438 | - 'in_dev_for_each_ifa_rcu' 439 | - 'in_dev_for_each_ifa_rtnl' 440 | - 'inet_bind_bucket_for_each' 441 | - 'inet_lhash2_for_each_icsk' 442 | - 'inet_lhash2_for_each_icsk_continue' 443 | - 'inet_lhash2_for_each_icsk_rcu' 444 | - 'interval_tree_for_each_double_span' 445 | - 'interval_tree_for_each_span' 446 | - 'intlist__for_each_entry' 447 | - 'intlist__for_each_entry_safe' 448 | - 'iopt_for_each_contig_area' 449 | - 'kcore_copy__for_each_phdr' 450 | - 'key_for_each' 451 | - 'key_for_each_safe' 452 | - 'klp_for_each_func' 453 | - 'klp_for_each_func_safe' 454 | - 'klp_for_each_func_static' 455 | - 'klp_for_each_object' 456 | - 'klp_for_each_object_safe' 457 | - 'klp_for_each_object_static' 458 | - 'kunit_suite_for_each_test_case' 459 | - 'kvm_for_each_memslot' 460 | - 'kvm_for_each_memslot_in_gfn_range' 461 | - 'kvm_for_each_vcpu' 462 | - 'libbpf_nla_for_each_attr' 463 | - 'list_for_each' 464 | - 'list_for_each_codec' 465 | - 'list_for_each_codec_safe' 466 | - 'list_for_each_continue' 467 | - 'list_for_each_entry' 468 | - 'list_for_each_entry_continue' 469 | - 'list_for_each_entry_continue_rcu' 470 | - 'list_for_each_entry_continue_reverse' 471 | - 'list_for_each_entry_from' 472 | - 'list_for_each_entry_from_rcu' 473 | - 'list_for_each_entry_from_reverse' 474 | - 'list_for_each_entry_lockless' 475 | - 'list_for_each_entry_rcu' 476 | - 'list_for_each_entry_reverse' 477 | - 'list_for_each_entry_safe' 478 | - 'list_for_each_entry_safe_continue' 479 | - 'list_for_each_entry_safe_from' 480 | - 'list_for_each_entry_safe_reverse' 481 | - 'list_for_each_entry_srcu' 482 | - 'list_for_each_from' 483 | - 'list_for_each_prev' 484 | - 'list_for_each_prev_safe' 485 | - 'list_for_each_safe' 486 | - 'llist_for_each' 487 | - 'llist_for_each_entry' 488 | - 'llist_for_each_entry_safe' 489 | - 'llist_for_each_safe' 490 | - 'map__for_each_symbol' 491 | - 'map__for_each_symbol_by_name' 492 | - 'map_for_each_event' 493 | - 'map_for_each_metric' 494 | - 'maps__for_each_entry' 495 | - 'maps__for_each_entry_safe' 496 | - 'mci_for_each_dimm' 497 | - 'media_device_for_each_entity' 498 | - 'media_device_for_each_intf' 499 | - 'media_device_for_each_link' 500 | - 'media_device_for_each_pad' 501 | - 'msi_for_each_desc' 502 | - 'nanddev_io_for_each_page' 503 | - 'netdev_for_each_lower_dev' 504 | - 'netdev_for_each_lower_private' 505 | - 'netdev_for_each_lower_private_rcu' 506 | - 'netdev_for_each_mc_addr' 507 | - 'netdev_for_each_uc_addr' 508 | - 'netdev_for_each_upper_dev_rcu' 509 | - 'netdev_hw_addr_list_for_each' 510 | - 'nft_rule_for_each_expr' 511 | - 'nla_for_each_attr' 512 | - 'nla_for_each_nested' 513 | - 'nlmsg_for_each_attr' 514 | - 'nlmsg_for_each_msg' 515 | - 'nr_neigh_for_each' 516 | - 'nr_neigh_for_each_safe' 517 | - 'nr_node_for_each' 518 | - 'nr_node_for_each_safe' 519 | - 'of_for_each_phandle' 520 | - 'of_property_for_each_string' 521 | - 'of_property_for_each_u32' 522 | - 'pci_bus_for_each_resource' 523 | - 'pci_dev_for_each_resource' 524 | - 'pcl_for_each_chunk' 525 | - 'pcl_for_each_segment' 526 | - 'pcm_for_each_format' 527 | - 'perf_config_items__for_each_entry' 528 | - 'perf_config_sections__for_each_entry' 529 | - 'perf_config_set__for_each_entry' 530 | - 'perf_cpu_map__for_each_cpu' 531 | - 'perf_evlist__for_each_entry' 532 | - 'perf_evlist__for_each_entry_reverse' 533 | - 'perf_evlist__for_each_entry_safe' 534 | - 'perf_evlist__for_each_evsel' 535 | - 'perf_evlist__for_each_mmap' 536 | - 'perf_hpp_list__for_each_format' 537 | - 'perf_hpp_list__for_each_format_safe' 538 | - 'perf_hpp_list__for_each_sort_list' 539 | - 'perf_hpp_list__for_each_sort_list_safe' 540 | - 'perf_pmu__for_each_hybrid_pmu' 541 | - 'ping_portaddr_for_each_entry' 542 | - 'ping_portaddr_for_each_entry_rcu' 543 | - 'plist_for_each' 544 | - 'plist_for_each_continue' 545 | - 'plist_for_each_entry' 546 | - 'plist_for_each_entry_continue' 547 | - 'plist_for_each_entry_safe' 548 | - 'plist_for_each_safe' 549 | - 'pnp_for_each_card' 550 | - 'pnp_for_each_dev' 551 | - 'protocol_for_each_card' 552 | - 'protocol_for_each_dev' 553 | - 'queue_for_each_hw_ctx' 554 | - 'radix_tree_for_each_slot' 555 | - 'radix_tree_for_each_tagged' 556 | - 'rb_for_each' 557 | - 'rbtree_postorder_for_each_entry_safe' 558 | - 'rdma_for_each_block' 559 | - 'rdma_for_each_port' 560 | - 'rdma_umem_for_each_dma_block' 561 | - 'resort_rb__for_each_entry' 562 | - 'resource_list_for_each_entry' 563 | - 'resource_list_for_each_entry_safe' 564 | - 'rhl_for_each_entry_rcu' 565 | - 'rhl_for_each_rcu' 566 | - 'rht_for_each' 567 | - 'rht_for_each_entry' 568 | - 'rht_for_each_entry_from' 569 | - 'rht_for_each_entry_rcu' 570 | - 'rht_for_each_entry_rcu_from' 571 | - 'rht_for_each_entry_safe' 572 | - 'rht_for_each_from' 573 | - 'rht_for_each_rcu' 574 | - 'rht_for_each_rcu_from' 575 | - 'rq_for_each_bvec' 576 | - 'rq_for_each_segment' 577 | - 'rq_list_for_each' 578 | - 'rq_list_for_each_safe' 579 | - 'scsi_for_each_prot_sg' 580 | - 'scsi_for_each_sg' 581 | - 'sctp_for_each_hentry' 582 | - 'sctp_skb_for_each' 583 | - 'sec_for_each_insn' 584 | - 'sec_for_each_insn_continue' 585 | - 'sec_for_each_insn_from' 586 | - 'shdma_for_each_chan' 587 | - 'shost_for_each_device' 588 | - 'sk_for_each' 589 | - 'sk_for_each_bound' 590 | - 'sk_for_each_entry_offset_rcu' 591 | - 'sk_for_each_from' 592 | - 'sk_for_each_rcu' 593 | - 'sk_for_each_safe' 594 | - 'sk_nulls_for_each' 595 | - 'sk_nulls_for_each_from' 596 | - 'sk_nulls_for_each_rcu' 597 | - 'snd_array_for_each' 598 | - 'snd_pcm_group_for_each_entry' 599 | - 'snd_soc_dapm_widget_for_each_path' 600 | - 'snd_soc_dapm_widget_for_each_path_safe' 601 | - 'snd_soc_dapm_widget_for_each_sink_path' 602 | - 'snd_soc_dapm_widget_for_each_source_path' 603 | - 'strlist__for_each_entry' 604 | - 'strlist__for_each_entry_safe' 605 | - 'sym_for_each_insn' 606 | - 'sym_for_each_insn_continue_reverse' 607 | - 'symbols__for_each_entry' 608 | - 'tb_property_for_each' 609 | - 'tcf_act_for_each_action' 610 | - 'tcf_exts_for_each_action' 611 | - 'udp_portaddr_for_each_entry' 612 | - 'udp_portaddr_for_each_entry_rcu' 613 | - 'usb_hub_for_each_child' 614 | - 'v4l2_device_for_each_subdev' 615 | - 'v4l2_m2m_for_each_dst_buf' 616 | - 'v4l2_m2m_for_each_dst_buf_safe' 617 | - 'v4l2_m2m_for_each_src_buf' 618 | - 'v4l2_m2m_for_each_src_buf_safe' 619 | - 'virtio_device_for_each_vq' 620 | - 'while_for_each_ftrace_op' 621 | - 'xa_for_each' 622 | - 'xa_for_each_marked' 623 | - 'xa_for_each_range' 624 | - 'xa_for_each_start' 625 | - 'xas_for_each' 626 | - 'xas_for_each_conflict' 627 | - 'xas_for_each_marked' 628 | - 'xbc_array_for_each_value' 629 | - 'xbc_for_each_key_value' 630 | - 'xbc_node_for_each_array_value' 631 | - 'xbc_node_for_each_child' 632 | - 'xbc_node_for_each_key_value' 633 | - 'xbc_node_for_each_subkey' 634 | - 'zorro_for_each_dev' 635 | 636 | IncludeBlocks: Preserve 637 | IncludeCategories: 638 | - Regex: '.*' 639 | Priority: 1 640 | IncludeIsMainRegex: '(Test)?$' 641 | IndentCaseLabels: false 642 | IndentGotoLabels: false 643 | IndentPPDirectives: None 644 | IndentWidth: 4 645 | IndentWrappedFunctionNames: false 646 | JavaScriptQuotes: Leave 647 | JavaScriptWrapImports: true 648 | KeepEmptyLinesAtTheStartOfBlocks: false 649 | MacroBlockBegin: '' 650 | MacroBlockEnd: '' 651 | MaxEmptyLinesToKeep: 1 652 | NamespaceIndentation: None 653 | ObjCBinPackProtocolList: Auto 654 | ObjCBlockIndentWidth: 4 655 | ObjCSpaceAfterProperty: true 656 | ObjCSpaceBeforeProtocolList: true 657 | 658 | # Taken from git's rules 659 | PenaltyBreakAssignment: 10 660 | PenaltyBreakBeforeFirstCallParameter: 30 661 | PenaltyBreakComment: 10 662 | PenaltyBreakFirstLessLess: 0 663 | PenaltyBreakString: 10 664 | PenaltyExcessCharacter: 100 665 | PenaltyReturnTypeOnItsOwnLine: 60 666 | 667 | PointerAlignment: Right 668 | ReflowComments: false 669 | SortIncludes: false 670 | SortUsingDeclarations: false 671 | SpaceAfterCStyleCast: false 672 | SpaceAfterTemplateKeyword: true 673 | SpaceBeforeAssignmentOperators: true 674 | SpaceBeforeCtorInitializerColon: true 675 | SpaceBeforeInheritanceColon: true 676 | SpaceBeforeParens: ControlStatementsExceptForEachMacros 677 | SpaceBeforeRangeBasedForLoopColon: true 678 | SpaceInEmptyParentheses: false 679 | SpacesBeforeTrailingComments: 1 680 | SpacesInAngles: false 681 | SpacesInContainerLiterals: false 682 | SpacesInCStyleCastParentheses: false 683 | SpacesInParentheses: false 684 | SpacesInSquareBrackets: false 685 | Standard: Cpp03 686 | TabWidth: 4 687 | UseTab: Never 688 | ... 689 | -------------------------------------------------------------------------------- /demo/test.c: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2023 Viviane Zwanger 2 | * SPDX-License-Identifier: GPL-2.0-only 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "../userspace_interface/linpmem_shared.h" 15 | 16 | 17 | // ### Explanation: 18 | // 19 | // This contains demo usage for all ioctls of Linpmem: 20 | // * reading CR3 21 | // * reading from a physical address 22 | // * qword read 23 | // * buffer read 24 | // * using the VTOP translation service 25 | // 26 | // All tests are void functions and already inserted in main(). 27 | // Recommended: only try one at a time. 28 | 29 | // Compiling: gcc -o test test.c 30 | // Usage: 31 | // sudo ./test 32 | 33 | 34 | // ### Read CR3. 35 | void do_CR3_test(int dev) 36 | { 37 | LINPMEM_CR3_INFO cr3info = {0}; 38 | 39 | cr3info.target_process = 0; // <= pid of process, must be living. 40 | cr3info.result_cr3 = 0; 41 | 42 | ioctl(dev, IOCTL_LINPMEM_QUERY_CR3, &cr3info); 43 | 44 | printf("CR3 is: %llx\n", cr3info.result_cr3); 45 | 46 | } 47 | 48 | // ### Read from a physical address. 49 | // If you, by chance, use qemu/kvm, you can resort to use this 50 | // hardcoded DSDT address as handy physical test address. 51 | #define QEMU_HARDCODED_DSDT (0x7FFE0040) 52 | 53 | // qword (8 byte) physical read 54 | void do_physread_test_qwordread(int dev) 55 | { 56 | LINPMEM_DATA_TRANSFER dataTransfer = {0}; 57 | 58 | dataTransfer.phys_address = QEMU_HARDCODED_DSDT; // <= specify what you want here. 59 | dataTransfer.access_type = PHYS_QWORD_READ; 60 | 61 | // Testing here with the 8 byte read on the DSDT address of QEMU/KVM. 62 | // (Which gives us "DSDT" + size of DSDT, which actually makes sense). 63 | // On bare metal, you can also use the VTOP translation service of Linpmem to 64 | // get a physical address from a virtual address. 65 | // 66 | // Example: a hello world string in your program. 67 | // Then use the returned physical address to test the physical read...! 68 | // Note: this is already done in test do_vtop_query_with_proof_read(dev). 69 | 70 | ioctl(dev, IOCTL_LINPMEM_READ_PHYSADDR, &dataTransfer); 71 | 72 | if (dataTransfer.out_value) 73 | { 74 | printf("Got: '%llx'\n", dataTransfer.out_value); 75 | } 76 | else 77 | { 78 | printf("The 8 byte read failed!\n"); 79 | } 80 | 81 | } 82 | 83 | // Buffer read from physical address. 84 | void do_physread_test_bufferread(int dev) 85 | { 86 | unsigned char * charptr = NULL; 87 | LINPMEM_DATA_TRANSFER dataTransfer = {0}; 88 | uint64_t i = 0; 89 | 90 | dataTransfer.phys_address = QEMU_HARDCODED_DSDT; 91 | dataTransfer.access_type = PHYS_BUFFER_READ; 92 | dataTransfer.readbuffer = malloc(0x200); 93 | if (!dataTransfer.readbuffer) 94 | { 95 | printf("Malloc didn't not allocate buffer.\n"); // I seriously doubt this ever happens. 96 | return; 97 | } 98 | dataTransfer.readbuffer_size = 0x200; 99 | 100 | // try read 0x200 bytes from DSDT. 101 | 102 | ioctl(dev, IOCTL_LINPMEM_READ_PHYSADDR, &dataTransfer); 103 | 104 | if (dataTransfer.readbuffer_size) // returns either 0x200 or 0. 105 | { 106 | printf("Read 0x%llx bytes.\n", dataTransfer.readbuffer_size); 107 | charptr = (unsigned char *) dataTransfer.readbuffer; 108 | 109 | for (i=0;i 2 | * SPDX-License-Identifier: GPL-2.0-only 3 | */ 4 | 5 | #include "precompiler.h" 6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "pte_mmap.h" 25 | #include "page_table.h" 26 | #include "linpmem.h" 27 | 28 | unsigned int major = 42; 29 | 30 | DEVICE_EXTENSION g_device_extension = { 0 }; 31 | 32 | static int pmem_open(struct inode *device_file, struct file *instance) 33 | { 34 | pr_debug("open\n"); 35 | 36 | return 0; 37 | } 38 | 39 | static int pmem_close(struct inode *device_file, struct file *instance) 40 | { 41 | pr_debug("close\n"); 42 | 43 | return 0; 44 | } 45 | 46 | /* pte_mmap_read - read up to count bytes from `phys_addr` using rogue PTE 47 | * @pte_data: management data 48 | * @phys_addr: physical address to read from 49 | * @buf: the buffer to read data into (user-space pointer in buffer read mode) 50 | * @count: requested amount of bytes to read, size of buf 51 | * @access_mode: how to access the memory 52 | * 53 | * note: reads can not cross page boundaries 54 | * note: non-buffer-mode accesses must be properly aligned 55 | * 56 | * Returns number of bytes read into `buf` 57 | */ 58 | static uint64_t pte_mmap_read(PPTE_METHOD_DATA pte_data, uint64_t phys_addr, 59 | void *buf, uint64_t count, 60 | PHYS_ACCESS_MODE access_mode) 61 | { 62 | PTE_STATUS pte_status; 63 | uint64_t page_offset; 64 | uint64_t to_read; 65 | uint64_t pfn; 66 | PTE new_pte; 67 | uint64_t bytes_read = 0; 68 | 69 | if (!pte_data) { 70 | pr_err("BUG: pte_data == NULL"); 71 | return 0; 72 | } 73 | new_pte = pte_data->original_pte; 74 | 75 | page_offset = offset_in_page(phys_addr); 76 | to_read = min(PAGE_SIZE - page_offset, count); 77 | 78 | pfn = __phys_to_pfn(phys_addr); 79 | if (!pfn_valid(pfn)) { 80 | pr_notice_ratelimited("invalid pfn"); 81 | return 0; 82 | } 83 | 84 | new_pte.page_frame = pfn; 85 | pte_status = pte_remap_rogue_page_locked(pte_data, new_pte); 86 | if (pte_status != PTE_SUCCESS) { 87 | return 0; 88 | } 89 | 90 | switch (access_mode) { 91 | case PHYS_BYTE_READ: 92 | *((uint8_t *)buf) = ((uint8_t *)(((uint64_t)pte_data->rogue_va.value) + 93 | page_offset))[0]; 94 | break; 95 | case PHYS_WORD_READ: 96 | if (!IS_ALIGNED(page_offset, __alignof__(uint16_t))) 97 | goto out_unlock; 98 | *((uint16_t *)buf) = (( 99 | uint16_t *)(((uint64_t)pte_data->rogue_va.value) + page_offset))[0]; 100 | break; 101 | case PHYS_DWORD_READ: 102 | if (!IS_ALIGNED(page_offset, __alignof__(uint32_t))) 103 | goto out_unlock; 104 | *((uint32_t *)buf) = (( 105 | uint32_t *)(((uint64_t)pte_data->rogue_va.value) + page_offset))[0]; 106 | 107 | break; 108 | case PHYS_QWORD_READ: 109 | if (!IS_ALIGNED(page_offset, __alignof__(uint64_t))) 110 | goto out_unlock; 111 | *((uint64_t *)buf) = (( 112 | uint64_t *)(((uint64_t)pte_data->rogue_va.value) + page_offset))[0]; 113 | 114 | break; 115 | case PHYS_BUFFER_READ: 116 | pr_debug( 117 | "%s: copying %llu bytes from rogue page to user address %llx\n", 118 | __func__, to_read, (uint64_t)buf); 119 | // we don't want any size checks inserted here, just in case 120 | if (_copy_to_user( 121 | buf, 122 | (void *)(((uint64_t)pte_data->rogue_va.value) + page_offset), 123 | to_read)) { 124 | pr_notice_ratelimited("%s: copying rogue page to user failed\n", 125 | __func__); 126 | goto out_unlock; 127 | } 128 | break; 129 | } 130 | 131 | bytes_read = to_read; 132 | 133 | out_unlock: 134 | mutex_unlock(&g_rogue_page_mutex); 135 | 136 | return bytes_read; 137 | } 138 | 139 | /* r_cr3_pa_pid - get the physical address of the top-level page tables of task 140 | * @upid: user space pid 141 | * 142 | * Currently, Linpmem will NOT try to probe and lock the process in question. 143 | * From security perspective, the process might exit at any time. 144 | * Make sure that the process still exists while asking for its CR3! 145 | * 146 | * Returns physical address of pgd 147 | */ 148 | static CR3 r_cr3_pa_pid(pid_t upid) 149 | { 150 | CR3 cr3_pa; 151 | struct pid *pid; 152 | struct task_struct *task; 153 | struct mm_struct *mm; 154 | 155 | cr3_pa.value = 0; 156 | 157 | pid = find_get_pid(upid); 158 | if (!pid) 159 | goto out; 160 | 161 | task = get_pid_task(pid, PIDTYPE_PID); 162 | if (!task) 163 | goto out_pid; 164 | 165 | mm = get_task_mm(task); 166 | if (!mm) 167 | goto out_task; 168 | 169 | cr3_pa.value = (uint64_t)virt_to_phys((void *)mm->pgd); 170 | 171 | pr_debug("Task with upid %d has pdg@0x%llx\n", upid, cr3_pa.value); 172 | if (!is_kernel_pgtable(cr3_pa.value)) { 173 | pr_notice("PGD stored in mm is not kernel\n"); 174 | cr3_pa.value &= ~PTI_USER_PGTABLE_MASK; 175 | } 176 | 177 | mmput(mm); 178 | out_task: 179 | put_task_struct(task); 180 | out_pid: 181 | put_pid(pid); 182 | out: 183 | return cr3_pa; 184 | } 185 | 186 | static long do_ioctl_query_cr3(PLINPMEM_CR3_INFO __user userbuffer) 187 | { 188 | long ret = 0; 189 | LINPMEM_CR3_INFO cr3_info; 190 | CR3 cr3_pa; 191 | 192 | if (copy_from_user(&cr3_info, userbuffer, sizeof(LINPMEM_CR3_INFO))) { 193 | pr_notice_ratelimited("IOCTL: copying LINPMEM_CR3_INFO from user!\n"); 194 | ret = -EFAULT; 195 | goto out; 196 | } 197 | 198 | if (cr3_info.target_process) { 199 | cr3_pa = r_cr3_pa_pid((pid_t)cr3_info.target_process); 200 | if (!cr3_pa.value) { 201 | ret = -ESRCH; 202 | goto out; 203 | } 204 | } else { 205 | cr3_pa = r_cr3_pa(); 206 | } 207 | 208 | // CR3 can return as zero in very rare and anomalous circumstances. 209 | if (!pfn_valid(__phys_to_pfn(cr3_pa.value))) { 210 | pr_err( 211 | "User requested cr3 read is invalid! This should NOT happen. You can't use Linpmem for physical reading on this OS.\n"); 212 | ret = -EIO; 213 | goto out; 214 | } 215 | 216 | cr3_info.result_cr3 = cr3_pa.value; 217 | 218 | if (copy_to_user(userbuffer, &cr3_info, sizeof(LINPMEM_CR3_INFO))) { 219 | pr_notice_ratelimited("IOCTL: copying LINPMEM_CR3_INFO to user!\n"); 220 | ret = -EFAULT; 221 | goto out; 222 | } 223 | 224 | out: 225 | return ret; 226 | } 227 | 228 | static long do_ioctl_vtop(PLINPMEM_VTOP_INFO __user userbuffer) 229 | { 230 | LINPMEM_VTOP_INFO vtop_info; 231 | VIRT_ADDR in_va = { 0 }; 232 | uint64_t page_offset = 0; 233 | volatile PPTE ppte; 234 | PTE_STATUS pte_status; 235 | long ret = 0; 236 | 237 | if (copy_from_user(&vtop_info, userbuffer, sizeof(LINPMEM_VTOP_INFO))) { 238 | pr_notice_ratelimited("%s: copy-in LINPMEM_VTOP_INFO from user!\n", 239 | __func__); 240 | ret = -EFAULT; 241 | goto out; 242 | } 243 | 244 | pr_debug("%s: translation wanted for: VA %llx, associated CR3: %llx.\n", 245 | __func__, vtop_info.virt_address, vtop_info.associated_cr3); 246 | 247 | if (!vtop_info.virt_address) { 248 | pr_notice_ratelimited("%s: no virtual address specified for vtop.\n", 249 | __func__); 250 | ret = -EINVAL; 251 | goto out; 252 | } 253 | 254 | in_va.value = vtop_info.virt_address; 255 | page_offset = in_va.offset; 256 | in_va.value -= page_offset; 257 | 258 | pte_status = virt_find_pte(in_va, &ppte, vtop_info.associated_cr3); 259 | if (pte_status != PTE_SUCCESS) { 260 | pr_info_ratelimited( 261 | "%s: No translation possible: no present page for %llx. Sorry.\n", 262 | __func__, in_va.value); 263 | vtop_info.phys_address = 0; 264 | vtop_info.ppte = NULL; 265 | ret = -EIO; 266 | goto out_usercopy; 267 | } 268 | 269 | if (ppte->present) { 270 | if (!ppte->large_page) { 271 | // Normal calculation. 4096 page size. 272 | vtop_info.phys_address = (PFN_PHYS(ppte->page_frame)) + page_offset; 273 | } else { 274 | // Large page calculation. 2MiB size. 275 | vtop_info.phys_address = 276 | (PFN_PHYS(ppte->page_frame + in_va.pt_index)) + page_offset; 277 | } 278 | vtop_info.ppte = (void *)ppte; 279 | } else { 280 | pr_info_ratelimited( 281 | "%s: No translation possible: Present bit not set in PTE.\n", 282 | __func__); 283 | vtop_info.phys_address = 0; 284 | vtop_info.ppte = NULL; 285 | goto out_usercopy; 286 | } 287 | 288 | pr_debug( 289 | "%s: vtop translation success. Physical address: %llx. PTE address: %llx\n", 290 | __func__, (long long unsigned int)vtop_info.phys_address, 291 | (long long unsigned int)vtop_info.ppte); 292 | 293 | dprint_pte_contents(ppte); 294 | 295 | out_usercopy: 296 | if (copy_to_user((PLINPMEM_VTOP_INFO)userbuffer, &vtop_info, 297 | sizeof(LINPMEM_VTOP_INFO))) { 298 | pr_notice_ratelimited("%s: copying LINPMEM_VTOP_INFO back to user!\n", 299 | __func__); 300 | ret = -EFAULT; 301 | goto out; 302 | } 303 | 304 | out: 305 | return ret; 306 | } 307 | 308 | static long do_ioctl_read(PLINPMEM_DATA_TRANSFER __user userbuffer) 309 | { 310 | LINPMEM_DATA_TRANSFER data_transfer; 311 | uint64_t tmp = 0; 312 | void *buf = &tmp; 313 | PHYS_ACCESS_MODE access_mode = 0; 314 | uint64_t count; 315 | long ret = 0; 316 | uint64_t bytes_read = 0; 317 | 318 | if (copy_from_user(&data_transfer, userbuffer, 319 | sizeof(LINPMEM_DATA_TRANSFER))) { 320 | pr_notice_ratelimited("%s: copying LINPMEM_DATA_TRANSFER from user!\n", 321 | __func__); 322 | ret = -EFAULT; 323 | goto out; 324 | } 325 | 326 | switch (data_transfer.access_type) { 327 | case PHYS_BYTE_READ: 328 | count = 1; 329 | access_mode = PHYS_BYTE_READ; 330 | break; 331 | case PHYS_WORD_READ: 332 | count = 2; 333 | access_mode = PHYS_WORD_READ; 334 | break; 335 | case PHYS_DWORD_READ: 336 | count = 4; 337 | access_mode = PHYS_DWORD_READ; 338 | break; 339 | case PHYS_QWORD_READ: 340 | count = 8; 341 | access_mode = PHYS_QWORD_READ; 342 | break; 343 | case PHYS_BUFFER_READ: 344 | count = data_transfer.readbuffer_size; 345 | 346 | if (count == 0 || count > PAGE_SIZE) { 347 | pr_notice_ratelimited( 348 | "%s: BUFFER_READ: invalid read size specified\n", __func__); 349 | ret = -EINVAL; 350 | goto out; 351 | } 352 | 353 | if (!data_transfer.readbuffer) { 354 | pr_notice_ratelimited( 355 | "%s: BUFFER_READ: provided usermode buffer is null\n", 356 | __func__); 357 | ret = -EINVAL; 358 | goto out; 359 | } 360 | 361 | access_mode = PHYS_BUFFER_READ; 362 | 363 | buf = data_transfer.readbuffer; 364 | 365 | break; 366 | default: 367 | pr_notice_ratelimited("%s: unknown access type %08x set!\n", __func__, 368 | data_transfer.access_type); 369 | ret = -EINVAL; 370 | goto out; 371 | } // end of switch (data_transfer.access_type) 372 | 373 | pr_debug("%s: Reading up to %llu bytes from %llx.\n", __func__, count, 374 | (long long unsigned int)data_transfer.phys_address); 375 | 376 | bytes_read = pte_mmap_read(&g_device_extension.pte_data, 377 | data_transfer.phys_address, buf, count, 378 | access_mode); 379 | 380 | pr_debug("%s: Read %llu bytes from %llx.\n", __func__, bytes_read, 381 | (long long unsigned int)data_transfer.phys_address); 382 | 383 | data_transfer.out_value = bytes_read == count ? tmp : 0; 384 | 385 | if (access_mode != PHYS_BUFFER_READ && bytes_read != count) { 386 | ret = -EIO; 387 | } 388 | 389 | if (access_mode == PHYS_BUFFER_READ) { 390 | if (bytes_read <= count) { 391 | data_transfer.readbuffer_size = bytes_read; 392 | } else { 393 | data_transfer.readbuffer_size = 0; 394 | ret = -EIO; 395 | } 396 | } 397 | 398 | if (copy_to_user(userbuffer, &data_transfer, 399 | sizeof(LINPMEM_DATA_TRANSFER))) { 400 | pr_notice_ratelimited( 401 | "%s: copying LINPMEM_DATA_TRANSFER back to user!\n", __func__); 402 | ret = -EFAULT; 403 | goto out; 404 | } 405 | 406 | out: 407 | return ret; 408 | } 409 | 410 | static long int pmem_ioctl(struct file *file, unsigned int ioctl, 411 | unsigned long userbuffer) 412 | { 413 | long ret = 0; 414 | 415 | switch (ioctl) { 416 | case IOCTL_LINPMEM_READ_PHYSADDR: 417 | ret = do_ioctl_read((PLINPMEM_DATA_TRANSFER)userbuffer); 418 | break; 419 | case IOCTL_LINPMEM_VTOP_TRANSLATION_SERVICE: 420 | ret = do_ioctl_vtop((PLINPMEM_VTOP_INFO)userbuffer); 421 | break; 422 | case IOCTL_LINPMEM_QUERY_CR3: 423 | ret = do_ioctl_query_cr3((PLINPMEM_CR3_INFO)userbuffer); 424 | break; 425 | default: 426 | pr_err_ratelimited("%s: unknown IOCTL %08x\n", __func__, ioctl); 427 | ret = -ENOSYS; 428 | } 429 | 430 | return ret; 431 | } 432 | 433 | const static struct file_operations pmem_fops = { .owner = THIS_MODULE, 434 | .open = pmem_open, 435 | .release = pmem_close, 436 | .unlocked_ioctl = 437 | pmem_ioctl }; 438 | 439 | /* init_check_compatibility - checks necessary conditions for driver loading 440 | * 441 | * This function function checks that some necessary conditions for loading 442 | * the driver are satisfied. Decides whether to bail out, to adapt strategies, 443 | * or that some features will not be available. 444 | * 445 | * Returns 0 if it is ok to continue loading the driver, or -1 if we must bail 446 | * out. 447 | */ 448 | static int init_check_compatibility(void) 449 | { 450 | int ret = 0; 451 | 452 | if (boot_cpu_has(X86_FEATURE_SEV)) { 453 | pr_debug("SEV: active. BAIL OUT\n"); 454 | ret = -1; 455 | goto out; 456 | } else { 457 | pr_debug("SEV: not active. OK\n"); 458 | } 459 | 460 | if (boot_cpu_has(X86_FEATURE_SME)) { 461 | pr_debug("SME: active. BAIL OUT\n"); 462 | ret = -1; 463 | goto out; 464 | } else { 465 | pr_debug("SME: not active. OK\n"); 466 | } 467 | 468 | // pgtable_l5_enabled() =- cpu_feature_enabled(X86_FEATURE_LA57) 469 | if (cpu_feature_enabled(X86_FEATURE_LA57)) { 470 | pr_debug("5-level paging: active. BAIL OUT\n"); 471 | ret = -1; 472 | goto out; 473 | } else { 474 | pr_debug("5-level paging: not active. OK\n"); 475 | } 476 | 477 | out: 478 | return ret; 479 | } 480 | 481 | static int __init pmem_init(void) 482 | { 483 | int ret = 0; 484 | 485 | pr_info("init start\n"); 486 | 487 | ret = init_check_compatibility(); 488 | if (ret) { 489 | pr_err("check_compatibility->%d\n", ret); 490 | return ret; 491 | } 492 | 493 | ret = register_chrdev(major, KBUILD_MODNAME, &pmem_fops); 494 | if (ret) { 495 | pr_err("register_chrdev->%d\n", ret); 496 | return ret; 497 | } else { 498 | pr_info("registered chrdev with major %d\n", major); 499 | } 500 | 501 | ret = setup_pte_method(&g_device_extension.pte_data); 502 | if (ret) { 503 | pr_emerg("rogue page setup failed terribly - pls reboot\n"); 504 | goto out_chrdev; 505 | } 506 | 507 | pr_info("startup successfull\n"); 508 | 509 | return 0; 510 | 511 | out_chrdev: 512 | unregister_chrdev(major, KBUILD_MODNAME); 513 | 514 | return ret; 515 | } 516 | 517 | static void __exit pmem_exit(void) 518 | { 519 | // Undo the sacrifice. 520 | if (g_device_extension.pte_data.pte_method_is_ready_to_use) { 521 | restore_pte_method(&g_device_extension.pte_data); 522 | } 523 | 524 | // Everything should be as it should be, except if we lost control. 525 | // If there is a programming error, or a tiny little thing which we did not guard against, 526 | // this might lead to a full loss of control over the rogue page. 527 | // We won't know until we go looking. 528 | // So, peek first character carefully. Expected: 'S'. 529 | if (g_rogue_page[0] != 'S') { 530 | pr_emerg("The rogue page is out of control. Reboot. now.\n"); 531 | goto out; 532 | } 533 | 534 | // Turns out fine. 535 | pr_debug("Identifier string on sacrifice page: %s, %llx\n", g_rogue_page, 536 | (long long unsigned int)&g_rogue_page); 537 | 538 | pr_info("Goodbye, Kernel\n"); 539 | 540 | out: 541 | unregister_chrdev(major, KBUILD_MODNAME); 542 | } 543 | 544 | module_init(pmem_init); 545 | module_exit(pmem_exit); 546 | 547 | MODULE_LICENSE("GPL"); 548 | MODULE_AUTHOR("Viviane Zwanger/Valentin Obst"); 549 | MODULE_DESCRIPTION("Updated Pmem driver for Linux"); 550 | MODULE_INFO(version, LINPMEM_DRIVER_VERSION); 551 | 552 | #pragma GCC diagnostic push 553 | #pragma GCC diagnostic ignored "-Wdate-time" 554 | MODULE_INFO(compilation_timestamp, __TIMESTAMP__); 555 | #pragma GCC diagnostic pop 556 | 557 | module_param(major, uint, 00); 558 | MODULE_PARM_DESC(major, 559 | "The major number that the driver will use (default is 42)"); 560 | -------------------------------------------------------------------------------- /src/linpmem.h: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2023 Viviane Zwanger, Valentin Obst 2 | * SPDX-License-Identifier: GPL-2.0-only 3 | */ 4 | 5 | #ifndef _LINPMEM_H_ 6 | #define _LINPMEM_H_ 7 | 8 | #include "pte_mmap.h" 9 | #include "../userspace_interface/linpmem_shared.h" 10 | 11 | /* Our Device Extension Structure. 12 | * pte_data Our management data for the rogue page. 13 | * Contains volatile PPTE of rogue_pte. 14 | * READ ONLY after init method! 15 | * *pte_data.rogue_pte and *pte_data.rogue_va are protected by 16 | * g_rogue_page_lock. Do not read/write eiter without holding it. 17 | */ 18 | typedef struct { 19 | PTE_METHOD_DATA pte_data; 20 | } DEVICE_EXTENSION, *PDEVICE_EXTENSION; 21 | 22 | extern DEVICE_EXTENSION g_device_extension; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/page_table.h: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2023 Viviane Zwanger, Valentin Obst 2 | * SPDX-License-Identifier: GPL-2.0-only 3 | */ 4 | 5 | #ifndef __PAGE_TABLE_H__ 6 | #define __PAGE_TABLE_H__ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "pte_mmap.h" 13 | 14 | // defined in entry/calling.h which cannot be included 15 | #define PTI_USER_PGTABLE_BIT PAGE_SHIFT 16 | #define PTI_USER_PGTABLE_MASK (1UL << PTI_USER_PGTABLE_BIT) 17 | #define PTI_USER_PCID_BIT X86_CR3_PTI_PCID_USER_BIT 18 | #define PTI_USER_PCID_MASK (1UL << PTI_USER_PCID_BIT) 19 | #define PTI_USER_PGTABLE_AND_PCID_MASK \ 20 | (PTI_USER_PCID_MASK | PTI_USER_PGTABLE_MASK) 21 | 22 | /* is_kernel_pgtable - checks if a phys. addr. is suitable for kernel page table 23 | * @cr3_pa: address to check 24 | * 25 | * Returns true iff the address can store a kernel page table 26 | */ 27 | static inline bool is_kernel_pgtable(uint64_t cr3_pa) 28 | { 29 | if (!boot_cpu_has(X86_FEATURE_PTI)) 30 | return true; 31 | return (cr3_pa & PTI_USER_PGTABLE_MASK) == 0; 32 | } 33 | 34 | /* r_cr3_pa - get phys. address of the current task's (kernel) root page table 35 | * 36 | * In theory, the top-level page table should not change while the task is alive. 37 | * However, the process might exit at any time and this routine 38 | * currently does neither probe nor lock the process. It's currently 39 | * up to the user to provide sane commands. 40 | * 41 | * note: all the pr_debug are not compiled into release builds, which makes it 42 | * OK to define it as inline 43 | * 44 | * Returns address of current pgd 45 | */ 46 | static inline CR3 r_cr3_pa(void) 47 | { 48 | CR3 cr3; 49 | 50 | cr3.value = __native_read_cr3(); 51 | 52 | pr_debug("Kernel CR3: %llx\n", cr3.value); 53 | pr_debug("Kernel CR3 (pa): %llx\n", cr3.value & CR3_ADDR_MASK); 54 | 55 | if (cpu_feature_enabled(X86_FEATURE_PCID)) 56 | pr_debug("Kernel PCID: %lx\n", (unsigned long)cr3.pcid); 57 | 58 | if (boot_cpu_has(X86_FEATURE_PTI)) { 59 | pr_debug("User CR3 (pa): %llx\n", 60 | (cr3.value & CR3_ADDR_MASK) | PTI_USER_PGTABLE_MASK); 61 | if (cpu_feature_enabled(X86_FEATURE_PCID)) 62 | pr_debug("User PCID: %lx\n", cr3.pcid | PTI_USER_PCID_MASK); 63 | } 64 | 65 | cr3.value &= CR3_ADDR_MASK; 66 | 67 | return cr3; 68 | } 69 | 70 | #endif // __PAGE_TABLE_H__ 71 | -------------------------------------------------------------------------------- /src/precompiler.h: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2023 Viviane Zwanger, Valentin Obst 2 | * SPDX-License-Identifier: GPL-2.0-only 3 | */ 4 | 5 | #ifndef _PRECOMPILER_H_ 6 | #define _PRECOMPILER_H_ 7 | 8 | // Comment this Statement to compile normally for 'release'. 9 | // Uncomment for debug compilation with verbose debug printing. 10 | #define DEBUG 11 | 12 | #define DRV_NAME KBUILD_MODNAME 13 | #define LINPMEM_DRIVER_VERSION "0.9.1" 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/pte_mmap.c: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Velocidex Innovations 2 | // Copyright 2014 - 2017 Google Inc. 3 | // Copyright 2012 Google Inc. All Rights Reserved. 4 | // Author: Viviane Zwanger, Valentin Obst 5 | // derived from Rekall/WinPmem by Mike Cohen and Johannes Stüttgen. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #include "precompiler.h" 20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "linpmem.h" 27 | #include "page_table.h" 28 | #include "pte_mmap.h" 29 | 30 | DEFINE_MUTEX(g_rogue_page_mutex); 31 | 32 | char g_rogue_page[PAGE_SIZE] 33 | __attribute__((aligned(PAGE_SIZE))) = { "SacrificePhysicalPage=1;" }; 34 | 35 | // Edit the page tables to relink a virtual address to a specific physical page. 36 | // 37 | // Argument 1: a PTE data struct, filled with information about the rogue page to be used. 38 | // Argument 2: the physical address to re-map to. 39 | // 40 | // Returns: 41 | // PTE_SUCCESS (with g_rogue_page_lock) 42 | // PTE_ERROR (without g_rogue_page_lock) 43 | // 44 | PTE_STATUS pte_remap_rogue_page_locked(PPTE_METHOD_DATA pte_data, PTE new_pte) 45 | { 46 | if (!pte_data || !pte_data->rogue_va.pointer) 47 | return PTE_ERROR; 48 | 49 | pr_debug("Remapping va %llx to %llx\n", 50 | (long long unsigned int)pte_data->rogue_va.pointer, 51 | __pfn_to_phys(new_pte.page_frame)); 52 | 53 | mutex_lock(&g_rogue_page_mutex); 54 | 55 | // It is *critical* there is no interruption while doing PTE remapping. 56 | // Alternatively we could allow interruption and being re-scheduled in the plain middle 57 | // of messing with the PTEs, but then we need to make sure we get the same CPU core (with its private cache) when being re-scheduled. 58 | // On Linux, it seems a more viable option to simply use cli/sti, which works well. 59 | // The cli region is kept very small, it covers the PTE remap action and the flush command. 60 | 61 | // cli 62 | pmem_x64cli(); 63 | 64 | // Change the pte to point to the new offset. 65 | WRITE_ONCE((*pte_data->rogue_pte).value, new_pte.value); 66 | 67 | // Flush the old pte from the tlbs (maybe incomplete, see comment) 68 | tlb_flush((uint64_t)pte_data->rogue_va.pointer); 69 | 70 | // sti 71 | pmem_x64sti(); 72 | 73 | return PTE_SUCCESS; 74 | } 75 | 76 | // Traverses the page tables to find the pte for a given virtual address. 77 | // 78 | // Args: 79 | // _In_ VIRT_ADDR vaddr: The virtual address to resolve the pte for. 80 | // _Out_ PPTE * pPTE: A pointer to receive the PTE virtual address. 81 | // ... if found. 82 | // _In_Optional uint64_t foreign_CR3. Another CR3 (not yours) can be used instead. Hopefully you know that it's valid! 83 | // 84 | // Returns: 85 | // PTE_SUCCESS or PTE_ERROR 86 | // 87 | // Remarks: Supports only virtual addresses that are *not* using huge pages. It will return PTE_ERROR upon finding use of a huge page. 88 | // Large pages are supported. 89 | // 90 | // 91 | PTE_STATUS virt_find_pte(VIRT_ADDR vaddr, volatile PPTE *pppte, 92 | uint64_t foreign_cr3_pa) 93 | { 94 | CR3 cr3; 95 | PPML4E pml4; 96 | PPML4E pml4e; 97 | PPDPTE pdpt; 98 | PPDPTE pdpte; 99 | PPDE pd; 100 | PPDE pde; 101 | PPTE pt; 102 | PPTE final_ppte = 0; 103 | PTE_STATUS status = PTE_ERROR; 104 | uint64_t cur_pa; 105 | 106 | if (!vaddr.pointer || !pppte) 107 | goto error; 108 | 109 | // Initialize _Out_ variable with zero. This guarantees there is no arbitrary value on it if we later take the error path. 110 | *pppte = 0; 111 | 112 | pr_debug("Resolving PTE for address: %llx.\n", vaddr.value); 113 | pr_debug( 114 | "Printing ambiguous names: WinDbg terminus(first)/normal terminus(second).\n"); 115 | 116 | // Get CR3 to get to the PML4 117 | if (foreign_cr3_pa == 0) { 118 | cr3 = r_cr3_pa(); 119 | } else if (pfn_valid(__phys_to_pfn(foreign_cr3_pa))) { 120 | cr3.value = foreign_cr3_pa; 121 | } else { 122 | pr_notice_ratelimited( 123 | "A custom CR3 was specified for vtop, but it is clearly wrong and invalid. Caller: please check your code.\n"); 124 | goto error; 125 | } 126 | 127 | pr_debug("CR3 pa is %llx.\n", cr3.value); 128 | 129 | // I don't know how this could fail, but... 130 | if (!cr3.value) 131 | goto error; 132 | 133 | // Resolve the PML4 134 | cur_pa = cr3.value; 135 | pml4 = phys_to_virt(cur_pa); 136 | 137 | pr_debug("Kernel PX/PML4 base is at %llx physical, and %llx virtual.\n", 138 | (long long unsigned int)cr3.value, (long long unsigned int)pml4); 139 | 140 | if (!pml4) 141 | goto error; 142 | 143 | // Resolve the PDPT 144 | pml4e = pml4 + vaddr.pml4_index; 145 | 146 | if (!pml4e->present) { 147 | pr_notice_ratelimited("Address %llx has no valid mapping in PML4\n", 148 | vaddr.value); 149 | dprint_pte_contents((PPTE)pml4e); 150 | goto error; 151 | } 152 | 153 | pr_debug("PXE/PML4[%llx] (at %llx): %llx\n", 154 | (long long unsigned int)vaddr.pml4_index, 155 | (long long unsigned int)pml4e, 156 | (long long unsigned int)pml4e->value); 157 | 158 | cur_pa = PFN_PHYS(pml4e->pdpt_p); 159 | pdpt = phys_to_virt(cur_pa); 160 | 161 | pr_debug("Points to PP/PDPT base: %llx.\n", (long long unsigned int)pdpt); 162 | 163 | if (!pdpt) 164 | goto error; 165 | 166 | // Resolve the PDT 167 | pdpte = pdpt + vaddr.pdpt_index; 168 | 169 | if (!pdpte->present) { 170 | pr_notice_ratelimited("Address %llx has no valid mapping in PDPT\n", 171 | vaddr.value); 172 | dprint_pte_contents((PPTE)pdpte); 173 | goto error; 174 | } 175 | 176 | if (pdpte->large_page) { 177 | pr_notice_ratelimited("Address %llx belongs to a 1GB huge page\n", 178 | vaddr.value); 179 | dprint_pte_contents((PPTE)pdpte); 180 | goto error; 181 | } 182 | 183 | pr_debug("PPE/PDPT[%llx] (at %llx): %llx.\n", 184 | (long long unsigned int)vaddr.pdpt_index, 185 | (long long unsigned int)pdpte, pdpte->value); 186 | 187 | cur_pa = PFN_PHYS(pdpte->pd_p); 188 | pd = phys_to_virt(cur_pa); 189 | 190 | pr_debug("Points to PD base: %llx.\n", (long long unsigned int)pd); 191 | 192 | if (!pd) 193 | goto error; 194 | 195 | // Resolve the PT 196 | pde = pd + vaddr.pd_index; 197 | 198 | if (!pde->present) { 199 | pr_notice_ratelimited("Address %llx has no valid mapping in PD\n", 200 | vaddr.value); 201 | dprint_pte_contents((PPTE)pde); 202 | goto error; 203 | } 204 | 205 | if (pde->large_page) { 206 | // this is basically like a PTE, just like one tier level above. Though not 100%. 207 | final_ppte = (PPTE)pde; 208 | *pppte = final_ppte; 209 | 210 | pr_debug("Final 'PTE' --large page PDE-- (at %llx) : %llx.\n", 211 | (long long unsigned int)final_ppte, 212 | (long long unsigned int)final_ppte->value); 213 | 214 | return PTE_SUCCESS; 215 | } 216 | 217 | pr_debug("PDE/PD[%llx] (at %llx): %llx.\n", 218 | (long long unsigned int)vaddr.pd_index, 219 | (long long unsigned int)pde, (long long unsigned int)pde->value); 220 | 221 | cur_pa = PFN_PHYS(pde->pt_p); 222 | pt = phys_to_virt(cur_pa); 223 | 224 | pr_debug("Points to PT base: %llx.\n", (long long unsigned int)pt); 225 | 226 | if (!pt) 227 | goto error; 228 | 229 | // Get the PTE and Page Frame 230 | final_ppte = pt + vaddr.pt_index; 231 | 232 | if (!final_ppte) 233 | goto error; 234 | 235 | if (!final_ppte->present) { 236 | pr_notice_ratelimited("Address %llx has no valid mapping in PT\n", 237 | vaddr.value); 238 | dprint_pte_contents(final_ppte); 239 | goto error; 240 | } 241 | 242 | pr_debug("final PTE [%llx] (at %llx): %llx.\n", 243 | (long long unsigned int)vaddr.pt_index, 244 | (long long unsigned int)final_ppte, 245 | (long long unsigned int)final_ppte->value); 246 | 247 | *pppte = final_ppte; 248 | 249 | // Everything went well, set PTE_SUCCESS 250 | status = PTE_SUCCESS; 251 | 252 | error: 253 | return status; 254 | } 255 | 256 | int setup_pte_method(PPTE_METHOD_DATA pte_data) 257 | { 258 | PTE_STATUS pte_status; 259 | 260 | pte_data->pte_method_is_ready_to_use = false; 261 | 262 | if (!PAGE_ALIGNED(g_rogue_page)) { 263 | pr_warn( 264 | "Setup of PTE method failed: rogue map is not pagesize aligned. This is a programming error!\n"); 265 | return -1; 266 | } 267 | pte_data->rogue_va.pointer = g_rogue_page; 268 | 269 | // We only need one PTE for the rogue page, and just remap the PFN. 270 | // A part of the driver's body is sacrificed for this. 271 | // However, during rest of the life time, this part of the driver 272 | // must be considered "missing", basically to be treated as a black hole. 273 | pte_status = virt_find_pte(pte_data->rogue_va, &pte_data->rogue_pte, 0); 274 | if (pte_status != PTE_SUCCESS) { 275 | pr_warn( 276 | "Setup of PTE method failed: virt_find_pte failed. This method will not be available!\n"); 277 | return -1; 278 | } 279 | 280 | // Backup original rogue page (full pte) 281 | pte_data->original_pte.value = pte_data->rogue_pte->value; 282 | 283 | if (!pte_data->original_pte.page_frame) // <= shouldn't we put pfn_valid here instead? 284 | // not going to fail until there is some voodoo VSM magic going on. But there are a few anomalous systems. 285 | { 286 | pr_warn( 287 | "Setup of PTE method failed: no rogue page pfn?!?. This method will not be available!\n"); 288 | return -1; 289 | } 290 | 291 | pte_data->pte_method_is_ready_to_use = true; 292 | 293 | return 0; 294 | } 295 | 296 | void restore_pte_method(PPTE_METHOD_DATA pte_data) 297 | { 298 | PTE_STATUS pte_status; 299 | 300 | // If pte method IS ALREADY false, then do nothing 301 | // (This might for example happen in DriverEntry in the error path.) 302 | if (!pte_data->pte_method_is_ready_to_use) 303 | return; 304 | 305 | // If there is null stored don't even try to restore. null is wrong. 306 | if (!pte_data->original_pte.page_frame) 307 | { 308 | pr_crit( 309 | "Restoring the sacrificed section failed horribly. The backup value was null! Please reboot soon.\n"); 310 | return; 311 | } 312 | 313 | pte_status = pte_remap_rogue_page_locked(pte_data, pte_data->original_pte); 314 | if (pte_status != PTE_SUCCESS) { 315 | pr_crit("PTE remapping error in restore function.\n"); 316 | return; 317 | } 318 | 319 | if (g_rogue_page[0] == 'S') 320 | pr_info("Sacrifice section successfully restored: %s.\n", g_rogue_page); 321 | else 322 | pr_crit("Uh-oh, restoring failed. Consider rebooting. (Right now.)\n"); 323 | 324 | mutex_unlock(&g_rogue_page_mutex); 325 | 326 | return; 327 | } 328 | -------------------------------------------------------------------------------- /src/pte_mmap.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Velocidex Innovations 2 | // Copyright 2014 - 2017 Google Inc. 3 | // Copyright 2012 Google Inc. All Rights Reserved. 4 | // Author: Viviane Zwanger, Valentin Obst 5 | // derived from Rekall/WinPmem by Mike Cohen and Johannes Stüttgen. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #ifndef _PTE_MMAP_H_ 20 | #define _PTE_MMAP_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef PAGE_SIZE 27 | #define PAGE_SIZE (4096) 28 | #endif 29 | 30 | // not used / not needed. 31 | #ifndef LARGE_PAGE_SIZE 32 | #define LARGE_PAGE_SIZE (2097152) 33 | #endif 34 | 35 | #ifndef PAGE_MASK 36 | #define PAGE_MASK (~(PAGE_SIZE - 1)) 37 | #endif 38 | 39 | /* Protects PTE of rogue page. Only modify the value after acquiring this 40 | * mutex. Only read from the rogue page while holding this mutex. 41 | */ 42 | extern struct mutex g_rogue_page_mutex; 43 | extern char g_rogue_page[]; 44 | 45 | #pragma pack(push, 1) 46 | typedef union { 47 | uint64_t value; 48 | void *pointer; 49 | struct { 50 | uint64_t offset : 12; 51 | uint64_t pt_index : 9; 52 | uint64_t pd_index : 9; 53 | uint64_t pdpt_index : 9; 54 | uint64_t pml4_index : 9; 55 | uint64_t reserved : 16; 56 | }; 57 | } VIRT_ADDR, *PVIRT_ADDR; 58 | 59 | typedef union { 60 | uint64_t value; 61 | struct { 62 | // CR4.PCIDE is set 63 | struct { 64 | uint64_t pcid : 12; 65 | }; 66 | // non-PAE, non-PCID 67 | struct { 68 | uint64_t ignored_1 : 3; 69 | uint64_t write_through : 1; 70 | uint64_t cache_disable : 1; 71 | uint64_t ignored_2 : 7; 72 | }; 73 | uint64_t pml4_p : 40; 74 | uint64_t reserved : 12; 75 | }; 76 | } CR3, *PCR3; 77 | 78 | typedef union { 79 | uint64_t value; 80 | struct { 81 | uint64_t present : 1; 82 | uint64_t rw : 1; 83 | uint64_t user : 1; 84 | uint64_t write_through : 1; 85 | uint64_t cache_disable : 1; 86 | uint64_t accessed : 1; 87 | uint64_t ignored_1 : 1; 88 | uint64_t reserved_1 : 1; 89 | uint64_t ignored_2 : 4; 90 | uint64_t pdpt_p : 40; 91 | uint64_t ignored_3 : 11; 92 | uint64_t xd : 1; 93 | }; 94 | } PML4E, *PPML4E; 95 | 96 | typedef union { 97 | uint64_t value; 98 | struct { 99 | uint64_t present : 1; 100 | uint64_t rw : 1; 101 | uint64_t user : 1; 102 | uint64_t write_through : 1; 103 | uint64_t cache_disable : 1; 104 | uint64_t accessed : 1; 105 | uint64_t dirty : 1; 106 | uint64_t large_page : 1; 107 | uint64_t ignored_2 : 4; 108 | uint64_t pd_p : 40; 109 | uint64_t ignored_3 : 11; 110 | uint64_t xd : 1; 111 | }; 112 | } PDPTE, *PPDPTE; 113 | 114 | typedef union { 115 | uint64_t value; 116 | struct { 117 | uint64_t present : 1; 118 | uint64_t rw : 1; 119 | uint64_t user : 1; 120 | uint64_t write_through : 1; 121 | uint64_t cache_disable : 1; 122 | uint64_t accessed : 1; 123 | uint64_t dirty : 1; 124 | uint64_t large_page : 1; 125 | uint64_t ignored_2 : 4; 126 | uint64_t pt_p : 40; 127 | uint64_t ignored_3 : 11; 128 | uint64_t xd : 1; 129 | }; 130 | } PDE, *PPDE; 131 | 132 | typedef union { 133 | uint64_t value; 134 | struct { 135 | uint64_t present : 1; 136 | uint64_t rw : 1; 137 | uint64_t user : 1; 138 | uint64_t write_through : 1; 139 | uint64_t cache_disable : 1; 140 | uint64_t accessed : 1; 141 | uint64_t dirty : 1; 142 | uint64_t large_page : 1; // PAT/PS 143 | uint64_t global : 1; 144 | uint64_t ignored_1 : 3; 145 | uint64_t page_frame : 40; 146 | uint64_t ignored_3 : 11; 147 | uint64_t xd : 1; 148 | }; 149 | } PTE, *PPTE; 150 | #pragma pack(pop) 151 | 152 | /* Operating system independent error checking. */ 153 | typedef enum { 154 | PTE_SUCCESS = 0, 155 | PTE_ERROR, 156 | PTE_ERROR_HUGE_PAGE, 157 | PTE_ERROR_RO_PTE 158 | } PTE_STATUS; 159 | 160 | typedef struct { 161 | bool pte_method_is_ready_to_use; 162 | VIRT_ADDR rogue_va; 163 | volatile PPTE rogue_pte; 164 | PTE original_pte; 165 | } PTE_METHOD_DATA, *PPTE_METHOD_DATA; 166 | 167 | /* Parse a 64 bit page table entry and print it. */ 168 | static void inline dprint_pte_contents(volatile PPTE ppte) 169 | { 170 | pr_debug( 171 | "Page information: %#016llx\n" 172 | "\tpresent: %llx\n" 173 | "\trw: %llx\n" 174 | "\tuser: %llx\n" 175 | "\twrite_through:%llx\n" 176 | "\tcache_disable:%llx\n" 177 | "\taccessed: %llx\n" 178 | "\tdirty: %llx\n" 179 | "\tpat/ps: %llx\n" 180 | "\tglobal: %llx\n" 181 | "\txd: %llx\n" 182 | "\tpfn: %010llx", 183 | (long long unsigned int)ppte, (long long unsigned int)ppte->present, 184 | (long long unsigned int)ppte->rw, (long long unsigned int)ppte->user, 185 | (long long unsigned int)ppte->write_through, 186 | (long long unsigned int)ppte->cache_disable, 187 | (long long unsigned int)ppte->accessed, 188 | (long long unsigned int)ppte->dirty, 189 | (long long unsigned int)ppte->large_page, 190 | (long long unsigned int)ppte->global, (long long unsigned int)ppte->xd, 191 | (long long unsigned int)ppte->page_frame); 192 | } 193 | 194 | /* tlb_flush - flush a single TLB entry 195 | * @addr: virtual address for which to clear the PTE entry 196 | * 197 | * INVLPG is unfortunately not sufficient if PTI is on, see comment of 198 | * flush_tlb_one_kernel. In short, other PCIDs might still have a stale TLB 199 | * entry after this operation. Therefore, always flush the TLB before using the 200 | * rogue page. 201 | * INVLPG is an architecturally serializing instruction, thus, no barriers or 202 | * fences are needed. Furthermore, using the "memory" clobber effectively 203 | * forms a read/write memory barrier for the compiler. Thus, no further need to 204 | * prevent compiler reordering. 205 | */ 206 | static inline void tlb_flush(uint64_t addr) 207 | { 208 | asm volatile("invlpg (%0)" ::"r"(addr) : "memory"); 209 | } 210 | 211 | 212 | // Winpmem (x64 platform) uses cli/sti. 213 | 214 | static inline void pmem_x64cli(void) 215 | { 216 | asm volatile("cli" ::: "memory"); 217 | } 218 | 219 | static inline void pmem_x64sti(void) 220 | { 221 | asm volatile("sti" ::: "memory"); 222 | } 223 | 224 | PTE_STATUS pte_remap_rogue_page_locked(PPTE_METHOD_DATA pte_data, PTE new_pte); 225 | 226 | PTE_STATUS virt_find_pte(VIRT_ADDR vaddr, volatile PPTE *pPTE, 227 | uint64_t foreign_CR3); 228 | 229 | int setup_pte_method(PPTE_METHOD_DATA pPtedata); 230 | 231 | void restore_pte_method(PPTE_METHOD_DATA pPtedata); 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /userspace_interface/linpmem_shared.h: -------------------------------------------------------------------------------- 1 | // ############################################################################ 2 | // # Linpmem userspace interface # 3 | // ############################################################################ 4 | 5 | /* For you, the usermode program writer, this is the really important header 6 | * file. You will find here all needed for doing a proper invocation. 7 | * Therefore, most documentation and explanation is put here. 8 | * Include this header file into your usermode program. 9 | * 10 | * Contains: 11 | * > struct definitions 12 | * > ioctl definitions 13 | * > documentation on how to use them 14 | */ 15 | 16 | #ifndef _LINPMEM_SHARED_H_ 17 | #define _LINPMEM_SHARED_H_ 18 | 19 | #ifndef __KERNEL__ 20 | #include 21 | #include 22 | #else 23 | #include 24 | #include 25 | #endif 26 | 27 | #define LINPMEM_DEVICE_NAME "linpmem" 28 | 29 | // ############################################################################ 30 | // # Structs # 31 | // ############################################################################ 32 | 33 | /* The driver has one job: reading from whatever address you want. 34 | * 35 | * You can read from reserved space, even memory holes. Everything is up to 36 | * your responsibility; e.g., try not to hit the I/O space accidentally! 37 | * In addition to this, the driver offers a translation service to 38 | * translate virtual addresses into physical addresses. 39 | * And since the CR3 is important, you can also query CR3. 40 | * 41 | * === Please zero out your structs before using this driver. === 42 | */ 43 | 44 | /* Access mode types used (in LINPMEM_DATA_TRANSFER struct) when reading from 45 | * a physical address. Tells the driver if it should read-access in bytes, 46 | * words, dwords, qwords, or buffer access. 47 | */ 48 | typedef enum _PHYS_ACCESS_MODE { 49 | PHYS_BYTE_READ = 1, 50 | PHYS_WORD_READ = 2, 51 | PHYS_DWORD_READ = 4, 52 | PHYS_QWORD_READ = 8, 53 | PHYS_BUFFER_READ = 9 54 | } PHYS_ACCESS_MODE; 55 | 56 | /* LINPMEM_DATA_TRANSFER (for physical reading, the main capability): 57 | * You must provide a physical address, and then choose whether you want 58 | * a true integer read (1/2/4/8 byte), or a buffer read. 59 | * 60 | * Use the access type to specify the access mode. 61 | * The 1/2/4/8 byte read is returned on out_value. You could try this 62 | * for accessing mapped io or dma space, if you know the semantics. 63 | * The buffer read is returned on your own provided buffer, which you 64 | * must allocate. Convenient reading. 65 | */ 66 | typedef struct _LINPMEM_DATA_TRANSFER { 67 | // (_IN_) The physical address you want to read from. Mandatory. 68 | uint64_t phys_address; 69 | 70 | // (_OUT_) The read value. On return, this will contain either the 71 | // read byte, word, dword or qword; or zero on error. 72 | uint64_t out_value; 73 | // If you hit the page boundary by your n-byte integer read, you will 74 | // get zero. 75 | // Example: you want to read from: 0x123ffe. 76 | // You specify a 2 byte read. 77 | // This will fail. With 0xffe, you are too near the page 78 | // boundary. 79 | 80 | // (_INOUT_) For buffer access mode. The usermode program must provide 81 | // the buffer! 82 | void *readbuffer; 83 | 84 | // (_INOUT_) The usermode buffer size, as told by the usermode 85 | // program. 86 | uint64_t readbuffer_size; 87 | // On return --PHYS_BUFFER_READ only--, this contains the number of 88 | // bytes that could be read. 89 | // Ideally, this number is identical to original input size, but 90 | // it will be less when a page boundary is encountered. 91 | // Example: you want to read from: 0x123aaa. 92 | // You want to read: 0xf00 bytes. 93 | // Maximum the driver will (currently) read: 94 | // 0x1000 - 0xaaa = 0x556 bytes. 95 | // 96 | // In future there might be an option to force-ignore page boundary. 97 | // (In other words, you can provide a buffer as large as you want.) 98 | // However, reading from a physical address you got from translating a 99 | // virtual address and *then* ignoring the page boundary is most 100 | // certainly not what you want! 101 | // On the other side, being able to force ignore page boundary for 102 | // reading from contiguous memory (such as acpi tables, for instance) 103 | // might really come in handy. 104 | // A reserved field might be used in future for a 105 | // "forceIgnorePageBoundary" flag. 106 | 107 | // (_IN_) access mode types: byte, word, dword, qword, buffer 108 | uint8_t access_type; 109 | // List of possible access types: see PHYS_ACCESS_MODE enum (above). 110 | // Remarks: 111 | // > If 0, your request gets rejected, because you apparently forgot 112 | // to set an access type. ;-) 113 | // > If access type doesn't match any known, your request gets 114 | // rejected, too. 115 | 116 | // Unused. 117 | uint8_t write_access; 118 | 119 | // Every good struct has minimum one! 120 | uint8_t reserved1; 121 | uint8_t reserved2; 122 | } LINPMEM_DATA_TRANSFER, *PLINPMEM_DATA_TRANSFER; 123 | 124 | /* LINPMEM_VTOP_INFO: Use this struct for an ioctl invocation of 125 | * type "IOCTL_LINPMEM_VTOP_TRANSLATION_SERVICE" to the driver. 126 | * vtop returns the physical address from a virtual address. 127 | */ 128 | typedef struct _LINPMEM_VTOP_INFO { 129 | // (_IN_) The virtual address in question. 130 | uint64_t virt_address; 131 | 132 | // (_IN_OPT_) Optional: specify a custom CR3 (of a foreign process) to 133 | // be used in the translation service. 134 | uint64_t associated_cr3; 135 | // Leave this to zero if you do not want a foreign CR3 to be used! 136 | // You can specify a foreign CR3 that belongs to another (valid) 137 | // process context. 138 | // E.g., you are Alice, and you want Bob's CR3 to be used, to read 139 | // something from Bob's userspace. As Alice, previously to using vtop, 140 | // you would have done a CR3 query invocation, so that you have Bob's 141 | // CR3 by now. Also, Bob must still live, of course. :-D 142 | // Beware, this value is used if nonzero! 143 | 144 | // (_OUT_) returns the physical address you wanted. 145 | uint64_t phys_address; 146 | 147 | // (_OUT_) returns the PTE virtual address, too. 148 | void *ppte; 149 | } LINPMEM_VTOP_INFO, *PLINPMEM_VTOP_INFO; 150 | 151 | /* LINPMEM_CR3_INFO: Use this struct for an ioctl invocation of type 152 | * "IOCTL_LINPMEM_QUERY_CR3" to the driver. 153 | */ 154 | typedef struct _LINPMEM_CR3_INFO { 155 | // (_IN_) A (foreign) process (pid_t) from which you want the CR3. 156 | uint64_t target_process; 157 | 158 | // (_OUT_) returned CR3 value. 159 | uint64_t result_cr3; 160 | } LINPMEM_CR3_INFO, *PLINPMEM_CR3_INFO; 161 | 162 | // ############################################################################ 163 | // # Possible Linpmem invocations # 164 | // ############################################################################ 165 | 166 | // read bytes from physical address. 167 | #define IOCTL_LINPMEM_READ_PHYSADDR _IOWR('a', 'a', LINPMEM_DATA_TRANSFER) 168 | 169 | // The classical vtop operation: translates virtual address to physical 170 | // address. Optionally, a foreign CR3 can be specified to translate a 171 | // virtual address from *another* usermode process to a physical page. 172 | #define IOCTL_LINPMEM_VTOP_TRANSLATION_SERVICE \ 173 | _IOWR('a', 'b', LINPMEM_VTOP_INFO) 174 | 175 | // A service to return the CR3 of a foreign process (e.g., for use in vtop). 176 | #define IOCTL_LINPMEM_QUERY_CR3 _IOWR('a', 'c', LINPMEM_CR3_INFO) 177 | 178 | #endif 179 | --------------------------------------------------------------------------------