├── .clang-format ├── .github └── workflows │ ├── build.yml │ └── check-format.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── format.sh ├── layout.lds ├── src ├── addr.c ├── arm_helpers.S ├── art.c ├── art.h ├── art_debugfs.c ├── art_debugfs.h ├── asm.c ├── asm.h ├── hvc.c ├── kallsyms.c ├── kallsyms.h ├── kaslr.c ├── kmalloc.c ├── mount.c ├── mount.h ├── msr.c ├── pmem.c ├── smc.c ├── smccc.c ├── smccc.h └── vmem.c └── test.sh /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | AllowShortIfStatementsOnASingleLine: Never 4 | BreakBeforeBinaryOperators: NonAssignment 5 | InsertBraces: true 6 | IncludeBlocks: Regroup 7 | 8 | # Local includes should come before system includes 9 | IncludeCategories: 10 | - Regex: '^".*"' 11 | Priority: 1 12 | - Regex: '^<.*' 13 | Priority: 2 14 | - Regex: '.*' 15 | Priority: 3 16 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request, workflow_dispatch] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | arch: [arm64, x86_64] 11 | kernel: [{ack: 0, version: 5.10.214}, {ack: 0, version: 5.15.153}, {ack: 0, version: 6.1.84}, {ack: 0, version: 6.6.25}, {ack: 1, version: android13-5.10-lts}, {ack: 1, version: android14-5.15-lts}] 12 | env: 13 | ACK: ${{ matrix.kernel.ack }} 14 | ARCH: ${{ matrix.arch }} 15 | ARTIFACTS_URL: https://github.com/gsingh93/linux-exploit-dev-env/releases/download/2024.04.07-ebc24a8 16 | HEADERS_URL: https://github.com/gsingh93/linux-exploit-dev-env/releases/download/linux-headers-2024.04.07-ebc24a8 17 | QEMU_RELEASE_URL: https://github.com/gsingh93/linux-exploit-dev-env/releases/download/qemu-8.2.2-94f421c94011baa837390074449cdd9811441c78 18 | steps: 19 | 20 | - name: Clone Linux Exploit Dev Environment 21 | uses: actions/checkout@v4 22 | with: 23 | repository: gsingh93/linux-exploit-dev-env 24 | 25 | # Don't clone submodules since we need to use the version of 26 | # `art-kernel-toolkit` from the commit currently being tested 27 | - name: Clone build rules for art-kt 28 | uses: actions/checkout@v4 29 | with: 30 | repository: gsingh93/linux-exploit-dev-env-art-kt 31 | path: external/art-kt 32 | 33 | - name: Clone art-kernel-toolkit 34 | uses: actions/checkout@v4 35 | with: 36 | path: external/art-kt/art-kernel-toolkit 37 | 38 | - name: Download kernel artifacts 39 | shell: bash 40 | run: | 41 | set -x 42 | 43 | if [ $ACK -eq 0 ]; then 44 | KERNEL_TYPE=linux 45 | else 46 | KERNEL_TYPE=ack 47 | fi 48 | 49 | if [ $ARCH == x86_64 ]; then 50 | KERNEL_IMAGE_NAME=bzImage 51 | elif [ $ARCH == arm64 ]; then 52 | KERNEL_IMAGE_NAME=Image 53 | fi 54 | 55 | SUFFIX=${KERNEL_TYPE}-${{ matrix.kernel.version }}-${ARCH} 56 | 57 | wget --no-verbose ${ARTIFACTS_URL}/${KERNEL_IMAGE_NAME}-${SUFFIX} 58 | wget --no-verbose ${ARTIFACTS_URL}/alpine-${ARCH}.img 59 | wget --no-verbose ${HEADERS_URL}/linux-headers-${SUFFIX}.deb 60 | 61 | dpkg-deb -R linux-headers-${SUFFIX}.deb linux-headers 62 | 63 | LINUX_OUT="$PWD/linux-headers/usr/src/linux-headers-*" 64 | 65 | # The extra echo is to force glob expansion 66 | echo "LINUX_OUT=$(echo $LINUX_OUT)" >> $GITHUB_ENV 67 | echo "QEMU_KERNEL_IMAGE=$PWD/${KERNEL_IMAGE_NAME}-${SUFFIX}" >> $GITHUB_ENV 68 | echo "ROOTFS=$PWD/alpine-${ARCH}.img" >> $GITHUB_ENV 69 | 70 | - run: ls -lR 71 | 72 | - name: Get cached clang 73 | uses: actions/cache@v4 74 | with: 75 | key: clang 76 | path: toolchain/clang 77 | 78 | - name: Build art-kt 79 | shell: bash 80 | run: | 81 | set -x 82 | 83 | make $PWD/toolchain/clang 84 | 85 | # LINUX_OUT is set in the environment 86 | make art-kt 87 | 88 | - run: ls -lR ${{ env.LINUX_OUT }}/modules_install 89 | 90 | - name: Download QEMU prebuilts 91 | shell: bash 92 | run: | 93 | set -x 94 | 95 | if [ $ARCH == x86_64 ]; then 96 | QEMU_BIN=qemu-system-x86_64 97 | elif [ $ARCH == arm64 ]; then 98 | QEMU_BIN=qemu-system-aarch64 99 | fi 100 | 101 | wget --no-verbose ${QEMU_RELEASE_URL}/${QEMU_BIN} 102 | 103 | chmod +x $QEMU_BIN 104 | 105 | wget --no-verbose https://download.qemu.org/qemu-8.2.2.tar.xz 106 | tar -xf qemu-8.2.2.tar.xz 107 | 108 | echo "QEMU_BIN=$PWD/$QEMU_BIN" >> $GITHUB_ENV 109 | 110 | - name: Install QEMU dependencies 111 | run: sudo apt update && sudo apt install -y libfdt1 112 | 113 | - name: Test art-kt 114 | shell: bash 115 | run: | 116 | set -x 117 | 118 | MODULES_DIR=$LINUX_OUT/modules_install/lib/modules/*/ 119 | 120 | # Expand the glob 121 | MODULES_DIR=$(echo $MODULES_DIR) 122 | 123 | ART_KT_MOD=$(find $MODULES_DIR -name art-kernel-toolkit.ko) 124 | 125 | # Get the directory the module is in i.e. `extra` or `updates` 126 | ART_KT_DIR=$(basename $(dirname $ART_KT_MOD)) 127 | 128 | # `modprobe` won't work without `modules.dep` 129 | echo "${ART_KT_DIR}/art-kernel-toolkit.ko:" > $MODULES_DIR/modules.dep 130 | 131 | # LINUX_OUT, ROOTFS, and QEMU_KERNEL_IMAGE are set in the environment 132 | QEMU_EXTRA_ARGS="-L qemu-8.2.2/pc-bios" make art-kt_test QEMU_BIN=$QEMU_BIN | tee test.log 133 | 134 | grep "All tests passed" test.log || exit 1 135 | -------------------------------------------------------------------------------- /.github/workflows/check-format.yml: -------------------------------------------------------------------------------- 1 | name: Check formatting 2 | on: [pull_request] 3 | jobs: 4 | check-format: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | 9 | - name: Install clang-format 10 | run: | 11 | wget https://apt.llvm.org/llvm.sh 12 | chmod +x llvm.sh 13 | sudo ./llvm.sh 15 14 | sudo apt install clang-format-15 15 | 16 | mkdir ~/bin 17 | ln -s /usr/bin/clang-format-15 ~/bin/clang-format 18 | 19 | - name: Install mdformat 20 | run: | 21 | pipx install mdformat 22 | pipx inject mdformat mdformat-gfm mdformat-frontmatter mdformat-footnote 23 | 24 | - name: Check format 25 | run: PATH=~/bin:$PATH ./format.sh --check 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.o.d 3 | *.ko 4 | *.mod 5 | *.mod.c 6 | *.cmd 7 | .cache 8 | compile_commands.json 9 | modules.order 10 | Module.symvers 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We would love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our Community Guidelines 22 | 23 | This project follows 24 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code Reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests) 32 | for this purpose. 33 | 34 | ### Coding Style 35 | 36 | Install [clang-format](https://clang.llvm.org/docs/ClangFormat.html) and 37 | [mdformat](https://mdformat.readthedocs.io/en/stable/), and then run 38 | `./format.sh` to format all files before submitting a pull request. 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 Google LLC 2 | # 3 | # This program is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 2 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License along 14 | # with this program; if not, write to the Free Software Foundation, Inc., 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 | 17 | ifneq ($(KERNELRELEASE),) 18 | 19 | obj-m += art-kernel-toolkit.o 20 | art-kernel-toolkit-y := src/art.o 21 | art-kernel-toolkit-y += src/addr.o 22 | art-kernel-toolkit-y += src/kallsyms.o 23 | art-kernel-toolkit-y += src/kaslr.o 24 | art-kernel-toolkit-y += src/kmalloc.o 25 | art-kernel-toolkit-y += src/mount.o 26 | art-kernel-toolkit-y += src/pmem.o 27 | art-kernel-toolkit-y += src/vmem.o 28 | art-kernel-toolkit-y += src/art_debugfs.o 29 | 30 | art-kernel-toolkit-$(CONFIG_ARM64) += src/arm_helpers.o 31 | art-kernel-toolkit-$(CONFIG_ARM64) += src/asm.o 32 | art-kernel-toolkit-$(CONFIG_ARM64) += src/msr.o 33 | 34 | art-kernel-toolkit-$(CONFIG_ARM64) += src/hvc.o src/smc.o src/smccc.o 35 | 36 | CWD := $(ROOT_DIR)/$(KERNEL_DIR)/$(M) 37 | 38 | ccflags-y := -Wall -Werror 39 | ccflags-y += -D'pr_fmt(fmt)=KBUILD_MODNAME ": %s(): " fmt, __func__' 40 | 41 | ldflags-y := -T$(CWD)/layout.lds 42 | 43 | # For some reason when building ACK for x86_64, we get some -Wframe-address 44 | # warnings from the ftrace code, so we disable this warning 45 | ccflags-$(CONFIG_X86_64) += $(call cc-disable-warning,frame-address,) 46 | 47 | else 48 | 49 | KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build 50 | M ?= $(CURDIR) 51 | W ?= 1 52 | 53 | all: modules 54 | 55 | modules_install: modules 56 | 57 | modules modules_install clean help: 58 | $(MAKE) -C $(KERNEL_SRC) M=$(M) $(KBUILD_OPTIONS) W=$(W) $(@) 59 | 60 | compile_commands.json: modules 61 | $(KERNEL_SRC)/source/scripts/clang-tools/gen_compile_commands.py -d $(KERNEL_SRC) $(M) 62 | 63 | .PHONY: modules modules_install clean help 64 | 65 | endif 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Red Team Kernel Toolkit 2 | 3 | `art-kernel-toolkit` is a kernel module that can be used to perform actions 4 | requiring kernel privileges from userspace. It supports x86_64 and arm64 Android 5 | Common Kernels and Linux kernels. 6 | 7 | Example use cases: 8 | 9 | - Setting up the right conditions to trigger kernel bugs. You may see a bug that 10 | requires specific conditions to be triggered, such as a particular heap 11 | layout. You can use the `kmalloc` plugin to do this from userspace, so you can 12 | get to reproducing the actual bug faster before investing more time in a full 13 | exploit. 14 | - As a placeholder step in an exploit chain. If you're working on an exploit for 15 | a bug but don't have an information leak yet, use the `vmem` plugin as 16 | placeholder for this step in your exploit chain until you find a real 17 | information leak. 18 | - Testing functionality at higher privilege levels. For example, on ARM systems 19 | only EL1 has the privilege to make HVC/SMC calls that are handled by EL2 and 20 | EL3. You can use the `hvc` and `smc` plugins to make these privileged calls 21 | from userspace. 22 | 23 | Contributions are welcome, see [CONTRIBUTING.md](CONTRIBUTING.md). 24 | 25 | This is not an officially supported Google product. 26 | 27 | ______________________________________________________________________ 28 | 29 | - [Clone and Build](#clone-and-build) 30 | - [Installing](#installing) 31 | - [Usage](#usage) 32 | - [Plugins](#plugins) 33 | - [vmem: Read/Write Virtual Memory](#vmem) 34 | - [pmem: Read/Write Physical Memory](#pmem) 35 | - [addr: Virt to Phys and Phys to Virt Translation](#addr) 36 | - [kaslr: Find KASLR Offset](#kaslr) 37 | - [kallsyms: Lookup Symbol Addresses](#kallsyms) 38 | - [kmalloc: Allocate/Free Memory](#kmalloc) 39 | - [asm: Execute Arbitrary Assembly](#asm) 40 | - [msr: Read/Write MSRs](#msr) 41 | - [smc: Execute SMCs](#smc) 42 | - [hvc: Execute HVCs](#hvc) 43 | - [Security](#security) 44 | 45 | ## Clone and Build 46 | 47 | Clone this repository with 48 | 49 | ```bash 50 | git clone https://github.com/androidoffsec/art-kernel-toolkit 51 | ``` 52 | 53 | Next, clone and build the Linux kernel or the Android Common Kernel that you 54 | want to use `art-kernel-toolkit` with. Then run `make` with the `KERNEL_SRC` 55 | environment variable set to your build output directory: 56 | 57 | ```bash 58 | KERNEL_SRC=/path/to/linux/build make 59 | ``` 60 | 61 | To cross compile for arm64: 62 | 63 | ```bash 64 | KERNEL_SRC=/path/to/linux/build make \ 65 | CC=clang \ 66 | ARCH=arm64 \ 67 | CROSS_COMPILE=aarch64-linux-gnu- 68 | ``` 69 | 70 | You can also generate a `compile_commands.json` for use with `clangd` (make sure 71 | to set the same variables used when building): 72 | 73 | ```bash 74 | KERNEL_SRC=/path/to/linux/build make compile_commands.json \ 75 | CC=clang \ 76 | ARCH=arm64 \ 77 | CROSS_COMPILE=aarch64-linux-gnu- 78 | ``` 79 | 80 | ## Installing 81 | 82 | The easiest way to load the module is with `insmod`: 83 | 84 | ```bash 85 | insmod /path/to/art-kernel-toolkit.ko 86 | ``` 87 | 88 | You can also set the `INSTALL_MOD_PATH` variable and run the `modules_install` 89 | target to install it into the `/lib/modules` folder that will be packaged into 90 | your rootfs: 91 | 92 | ```bash 93 | INSTALL_MOD_PATH=/path/to/rootfs KERNEL_SRC=/path/to/linux/build make 94 | ``` 95 | 96 | Then the module can be loaded from this rootfs with: 97 | 98 | ```bash 99 | modprobe art-kernel-toolkit 100 | ``` 101 | 102 | You can remove the module with: 103 | 104 | ```bash 105 | rmmod art-kernel-toolkit 106 | ``` 107 | 108 | Note that the module will appear as `art_kernel_toolkit` in lsmod: 109 | 110 | ```bash 111 | $ lsmod | grep art 112 | art_kernel_toolkit 40960 0` 113 | ``` 114 | 115 | ### Installing on Android 116 | 117 | For Android, you can push the module to your device and loaded it with the 118 | following commands: 119 | 120 | ```bash 121 | adb root 122 | adb push art-kernel-toolkit.ko /data/local/tmp 123 | adb shell insmod /data/local/tmp/art-kernel-toolkit.ko 124 | ``` 125 | 126 | ## Usage 127 | 128 | The module creates files in `debugfs`, which is mounted under 129 | `/sys/kernel/debug`. If this is not mounted by default, you can mount it 130 | manually with: 131 | 132 | ```bash 133 | mount -t debugfs none /sys/kernel/debug 134 | ``` 135 | 136 | On many physical Android devices, `/d/` is a symlink to /sys/kernel/debug. If 137 | that path does not exist, you can optionally create it with: 138 | 139 | ```bash 140 | ln -s /sys/kernel/debugfs /d 141 | ``` 142 | 143 | You should now see the kernel driver files in `/sys/kernel/debug/art/` and 144 | `/d/art/`. Each folder in this directory is created by a "plugin", documented 145 | below. Reading from or writing to these files will trigger various plugin 146 | actions. 147 | 148 | ### Plugins 149 | 150 | Each plugin defines a set of files in its own directory under `/d/art`. `/d/art` 151 | will usually be omitted in the documentation when referring to plugin files. 152 | Most plugins contain a `help` file in their plugin folder, which contains usage 153 | instructions for the plugin. 154 | 155 | Writeable files take a list of space separated arguments. For example, if a file 156 | is documented as "`foo/file ` (RW)", you can write two arguments to 157 | this file with `echo my_val1 my_val2 > /d/art/foo/file`. Unless otherwise 158 | specified, you can assume that integer arguments can be written in decimal, hex 159 | (with a '0x' prefix), or octal (with a leading '0'). 160 | 161 | Readable files will be documented as having a "return" value which can be read 162 | from the file, i.e. by running `cat /d/art/foo/file`. Most return values will be 163 | in hex. 164 | 165 | #### vmem 166 | 167 | This plugin allows reading/writing from arbitrary kernel virtual memory. 168 | 169 | Files: 170 | 171 | - `vmem/addr ` (RW) 172 | - `addr`: the virtual address to read from or write to 173 | - Returns: the last address written when read 174 | - `vmem/val ` (RW) 175 | - `val`: the 64-bit value to write to the address specified in `vmem/addr` 176 | - Returns: the 64-bit value at the address specified in`vmem/addr` 177 | 178 | ##### Example 179 | 180 | ```bash 181 | # Write address to write to or read from to `addr` 182 | $ echo 0xffffffc009fa2378 > /d/art/vmem/addr 183 | 184 | # Read from `val` to read 64-bit value at address 185 | $ cat /d/art/vmem/val 186 | 0xffffff80038db270 187 | 188 | # Write 64-bit value to `val` to write to address 189 | $ echo 0xdeadbeef > /d/art/vmem/val 190 | 191 | # Confirm write succeeded 192 | $ cat /d/art/vmem/val 193 | 0xdeadbeef 194 | ``` 195 | 196 | #### pmem 197 | 198 | This plugin allows reading/writing from arbitrary physical memory. 199 | 200 | Files: 201 | 202 | - `pmem/addr ` (RW) 203 | - `addr`: the physical address to read from or write to 204 | - Returns: the last address written when read 205 | - `pmem/val ` (RW) 206 | - `val`: the 64-bit value to write to the address specified in `pmem/addr` 207 | - Returns: the 64-bit value at the address specified in `pmem/addr` 208 | - `pmem/bytes ` (RW) 209 | - `byte_str`: a raw string of bytes to write to the address specified in 210 | `pmem/addr` 211 | - Returns: the raw bytes at the address specified in `pmem/addr` 212 | - `pmem/bytes-read-size ` (RW) 213 | - `max_length`: the maximum length to allow for reads from `pmem/bytes` 214 | (defaults to 8). Setting this is optional if you will be manually reading 215 | the exact number of bytes you want from `pmem/bytes`. However, when using 216 | tools like `cat` or even `xxd` with the `-l` argument, this value should be 217 | specified to avoid reading out of bounds. 218 | - Returns: the last value written to `pmem/bytes-read-size` 219 | 220 | ##### Example 221 | 222 | ```bash 223 | # Write address to write to or read from to `addr` 224 | $ echo 0xB62CE0DC > /d/art/pmem/addr 225 | 226 | # Write a value in base 10 to physical address: 227 | $ echo 12345678 > /d/art/pmem/val 228 | 229 | # Write a string to physical address: 230 | $ echo -n 'helloworld' > /d/art/pmem/bytes 231 | 232 | # Write hex bytes to a physical address 233 | $ echo -n '56 67 89 ab cd ef' | xxd -r -p > /d/art/pmem/bytes 234 | 235 | # Read five bytes from a physical address and output as hex: 236 | $ echo 5 > /d/art/pmem/bytes-read-size 237 | $ xxd -p /d/art/pmem/bytes 566789abcd 238 | ``` 239 | 240 | #### addr 241 | 242 | Allows converting virtual address to and from physical addresses. 243 | 244 | Files: 245 | 246 | - `addr/va ` (RW) 247 | - `va`: the virtual address you want to convert to a physical address 248 | - Returns: the virtual address of the last address written to either `addr/va` 249 | or `addr/pa` 250 | - `addr/pa ` (RW) 251 | - `pa`: the physical address you want to convert to a virtual address 252 | - Returns: the physical address of the last address written to `addr/va` or 253 | `addr/pa` 254 | 255 | ##### Example 256 | 257 | ```bash 258 | $ echo 0xffffff800468b400 > /d/art/addr/va 259 | $ cat /d/art/addr/pa 260 | 0x4468b400 261 | 262 | $ echo 0x4468b400 > /d/art/addr/pa 263 | $ cat /d/art/addr/va 264 | 0xffffff800468b400 265 | ``` 266 | 267 | #### kaslr 268 | 269 | Allows finding the KASLR offset. Currently only implemented for arm64. 270 | 271 | Files: 272 | 273 | - `kaslr/offset` (R) 274 | - Returns: the KASLR offset 275 | 276 | ##### Example 277 | 278 | ```bash 279 | $ cat /d/art/kaslr/offset 280 | 0x1be5600000 281 | ``` 282 | 283 | #### kallsyms 284 | 285 | This plugin allows looking up addresses for kernel symbols, allowing you to 286 | determine these addresses even if `kptr_restrict` is set to 2 (which prevents 287 | addresses from being seen in `/proc/kallsyms`). 288 | 289 | Files: 290 | 291 | - `kallsyms/lookup_name ` (RW) 292 | - `sym_name`: name of the symbol you want to lookup 293 | - Returns: the last symbol name written to this file 294 | - `kallsyms/addr` (R) 295 | - Returns: the address of the last symbol written to `kallsyms/lookup_name` 296 | 297 | ##### Example 298 | 299 | ```bash 300 | # Lookup address of __sys_setuid 301 | $ echo __sys_setuid > /d/art/kallsyms/lookup_name 302 | $ cat /d/art/kallsyms/addr 303 | 0xffffffedb417da48 304 | ``` 305 | 306 | #### kmalloc 307 | 308 | This plugin allows calling `kmalloc` and `kfree`. 309 | 310 | Files: 311 | 312 | - `kmalloc/alloc ` (W) 313 | - `size`: size of memory in bytes to allocate 314 | - `kmalloc/free ` (W) 315 | - `addr`: address to call `kfree` on 316 | - `kmalloc/va` (R) 317 | - Returns: the virtual address of the last allocated chunk 318 | - `kmalloc/pa` (R) 319 | - Returns: the physical address of the last allocated chunk 320 | - `kmalloc/pfn` (R) 321 | - Returns: the page frame number of the last allocated chunk 322 | - `kmalloc/size` (R) 323 | - Returns: the size of the last allocated chunk 324 | 325 | ##### Example 326 | 327 | ```bash 328 | # Allocate 1024 bytes 329 | $ echo 0x400 > /d/art/kmalloc/alloc 330 | 331 | $ cat /d/art/kmalloc/size 332 | 0x400 333 | 334 | $ cat /d/art/kmalloc/va 335 | 0xffffff8004048000 336 | 337 | $ cat /d/art/kmalloc/pa 338 | 0x44048000 339 | 340 | $ cat /d/art/kmalloc/pfn 341 | 0x44048 342 | 343 | # Free allocated memory 344 | $ echo $(cat /d/art/kmalloc/va) > /d/art/kmalloc/va 345 | ``` 346 | 347 | #### asm 348 | 349 | Allows executing arbitrary assembly instructions. Only available on arm64. 350 | 351 | Files: 352 | 353 | - `asm/asm ` (W) 354 | - `asm_byte_str`: the raw byte string of compiled assembly code to execute. 355 | You do not need to add a `ret` instruction to your code as it is added for 356 | you, and you do not need to worry about preserving the value of any 357 | registers except for the stack pointer. You should make sure your code does 358 | not corrupt any stack frames in the call stack. The assembly will 359 | immediately be executed after writing to this file. 360 | - `asm/cpumask ` (RW) 361 | - `mask`: a bitmask choosing which CPU to run the code on (defaults to 1, 362 | meaning CPU 0). Exactly one bit of this bitmask must be set, to run the same 363 | code on multiple CPUs, you will need to write to `asm/asm` once per CPU, 364 | changing the mask in between writes. 365 | - Returns: the current CPU mask. 366 | - `asm/x0` to `asm/x28` (R) 367 | - Returns: the value of the corresponding register when the assembly finished 368 | executing. 369 | 370 | ##### Example 371 | 372 | ```bash 373 | # mov x0, 042; mov x9, 42; mov x28, 0x42 374 | $ echo "400480d2490580d25c0880d2" | xxd -r -p > /d/art/asm/asm 375 | 376 | $ cat /d/art/asm/x0 377 | 0x0000000000000022 378 | 379 | $ cat /d/art/asm/x9 380 | 0x000000000000002a 381 | 382 | $ cat /d/art/asm/x28 383 | 0x0000000000000042 384 | ``` 385 | 386 | #### msr 387 | 388 | Allows reading/writing model-specific registers (MSRs). Only available on arm64. 389 | 390 | Files: 391 | 392 | - `msr/msr ` (RW) 393 | - `value`: the value to write to the MSR specified in `msr/regname`. 394 | - Returns: the current value of the MSR specified in `msr/regname` on the CPU 395 | specified in `msr/cpumask` when read. 396 | - `msr/regname ` (RW) 397 | - `regname`: the name of the MSR to read or write. Some common MSR names such 398 | as `sctlr_el1` are defined. For MSRs where the common name is not defined, 399 | use the encoded register form `s____`. Writing to 400 | this file will change the values of `msr/op0`, `msr/op1`, `msr/CRn`, 401 | `msr/CRm`, `msr/op2`. You can also write to those files as an alternative to 402 | writing to this file. 403 | - Returns: the encoded MSR name in the form `s____`. 404 | - `msr/cpumask ` (RW) 405 | - `mask`: a bitmask choosing which CPU to run the code on (defaults to 1, 406 | meaning CPU 0). Exactly one bit of this bitmask must be set when reading MSR 407 | values, although multiple bits may be set when writing MSR values. 408 | - Returns: the current CPU mask. 409 | - `msr/op0 `, `msr/op1 `, `msr/CRn `, `msr/CRm `, 410 | `msr/op2 ` (RW) 411 | - `op0`, `op1`, `CRn`, `CRm`, `op2`: Sets the corresponding component of the 412 | MSR encoding. Writing to these files will change the output of 413 | `msr/regname`. 414 | - Returns: the corresponding component of the encoding of the currently 415 | selected MSR 416 | 417 | Note: The value written to `msr/regname` can be a common MSR name (if defined, 418 | check the source for the full list) or an encoded register name. However, the 419 | parsing for the encoded register name is intentionally not strict. It is case 420 | insensitive, and any non-numeric characters are replaced with spaces. As long as 421 | five space-separate numeric values remain, it will successfully be parsed. See 422 | the examples section for more details. 423 | 424 | ##### Example 425 | 426 | ```bash 427 | # Read SCTLR_EL1 428 | $ echo sctlr_el1 > /d/art/msr/regname 429 | $ cat /d/art/msr/regname 430 | s3_0_c1_c0_0 431 | $ cat /d/art/msr/msr 432 | 0x200000034f4d91d 433 | 434 | # Set cpumask to CPU 0 and CPU 1 435 | $ echo 0x3 > /d/art/msr/cpumask 436 | 437 | # Disable EPAN and SPAN on CPU 0 and CPU 1 438 | $ echo 0x3474d91d > /d/art/msr/msr 439 | 440 | # Set CPU mask back to individual CPUs when reading 441 | $ echo 0x1 > /d/art/msr/cpumask 442 | 443 | # EPAN and SPAN are now unset on CPU 0 and CPU 1 444 | $ cat /d/art/msr/msr 445 | 0x3474d91d 446 | 447 | $ echo 0x2 > /d/art/msr/cpumask 448 | $ cat /d/art/msr/msr 449 | 0x3474d91d 450 | 451 | # SCTLR_EL1 is unchanged on CPU 2 452 | $ echo 0x4 > /d/art/msr/cpumask 453 | $ cat /d/art/msr/msr 454 | 0x200000034f4d91d 455 | ``` 456 | 457 | You can set individual components of the register encoding instead of setting 458 | `msr/regname`: 459 | 460 | ```bash 461 | $ echo 3 > /d/art/msr/op0 462 | $ echo 1 > /d/art/msr/op1 463 | $ echo 0 > /d/art/msr/CRn 464 | $ echo 0 > /d/art/msr/CRm 465 | $ echo 4 > /d/art/msr/op2 466 | $ cat /d/art/msr/regname 467 | s3_1_c0_c0_4 468 | ``` 469 | 470 | You can write an encoded MSR name to `msr/regname`, taking advantage of the 471 | loose parsing. All of the following commands are equivalent: 472 | 473 | ```bash 474 | $ echo s3_1_c0_c0_4 > /d/art/msr/regname 475 | $ echo 3_1_0_0_4 > /d/art/msr/regname 476 | $ echo 3 1 0 0 4 > /d/art/msr/regname 477 | ``` 478 | 479 | #### smc 480 | 481 | Allows making SMC (supervisor) calls from userspace. Only available on arm64. 482 | 483 | Files: 484 | 485 | - `smc/cmd [x0] [x1] [x2] [x3] [x4] [x5] [x6] [x7]` (RW) 486 | - `x0` to `x7`: the values to set for the registers before executing an `smc` 487 | instruction. If a register is not specified, it is assumed to be zero 488 | - Returns: the last command string written to `smc/cmd` 489 | - `smc/result` (R) 490 | - Returns: four space-separated hex integers, representing the values of `x0` 491 | to `x4` after the SMC has completed 492 | 493 | ##### Example 494 | 495 | ```bash 496 | # Execute SMCCC_VERSION with some unused arguments in different numeric formats 497 | # Supports up to 8 arguments including SMC ID 498 | $ echo 0x80000000 0xdeadbeef 0777 42 > /d/art/smc/cmd 499 | 500 | # Result is SMC Version 1.2, unused arguments are returned as is (in hex) 501 | $ cat /d/art/smc/result 502 | 0x10002 0xdeadbeef 0x1ff 0x2a 503 | ``` 504 | 505 | #### hvc 506 | 507 | Similar to the `smc` plugin, but for HVC (hypervisor) calls. Only available on 508 | arm64. 509 | 510 | Files: 511 | 512 | - `hvc/cmd [x0] [x1] [x2] [x3] [x4] [x5] [x6] [x7]` (RW) 513 | - `x0` to `x7`: the values to set for the registers before executing an `hvc` 514 | instruction. If a register is not specified, it is assumed to be zero 515 | - Returns: the last command string written to `hvc/cmd` 516 | - `hvc/result` (R) 517 | - Returns: four space-separated hex integers, representing the values of `x0` 518 | to `x4` after the SMC has completed 519 | 520 | ## Security 521 | 522 | Installing this kernel module gives userspace applications kernel privileges. It 523 | is intended to only be used in virtual machines or test devices. Because of 524 | this, there is no additional security risk added by a buggy implementation of a 525 | plugin. For example, if a plugin contains a buffer overflow feel free to submit 526 | a PR fixing it, but do not open an issue, request a CVE, or submit the issue to 527 | any bug bounty programs. 528 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | usage() { 7 | echo "Usage: $0 [-c|--check]" 8 | echo " -c, --check check format instead of formatting the code" 9 | exit 1 10 | } 11 | 12 | if [[ $# -gt 1 ]]; then 13 | usage 14 | fi 15 | 16 | CHECK=0 17 | 18 | while [[ $# -gt 0 ]]; do 19 | case $1 in 20 | -c | --check) 21 | CHECK=1 22 | shift 23 | ;; 24 | *) 25 | usage 26 | ;; 27 | esac 28 | done 29 | 30 | CLANG_ARGS="-i" 31 | MDFORMAT_ARGS="--wrap 80" 32 | 33 | set -o xtrace 34 | 35 | if [[ $CHECK == 1 ]]; then 36 | CLANG_ARGS="--dry-run -Werror" 37 | MDFORMAT_ARGS="--check" 38 | fi 39 | 40 | find src -name "*.c" -o -name "*.h" | xargs clang-format $CLANG_ARGS 41 | mdformat $MDFORMAT_ARGS *.md 42 | -------------------------------------------------------------------------------- /layout.lds: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | .art.plugins : { 4 | __art_plugins_start = . ; 5 | *(.art.plugins) 6 | __art_plugins_end = . ; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/addr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art.h" 20 | 21 | #include 22 | #include 23 | 24 | uint64_t vaddr; 25 | uint64_t paddr; 26 | 27 | static int addr_va_read_op(void *data, uint64_t *value) { 28 | *value = vaddr; 29 | return 0; 30 | } 31 | static int addr_va_write_op(void *data, uint64_t value) { 32 | vaddr = value; 33 | paddr = virt_to_phys((void *)vaddr); 34 | return 0; 35 | } 36 | 37 | static int addr_pa_read_op(void *data, uint64_t *value) { 38 | *value = paddr; 39 | return 0; 40 | } 41 | 42 | static int addr_pa_write_op(void *data, uint64_t value) { 43 | paddr = value; 44 | vaddr = (uint64_t)phys_to_virt(paddr); 45 | return 0; 46 | } 47 | 48 | DEFINE_DEBUGFS_ATTRIBUTE(addr_va_fops, addr_va_read_op, addr_va_write_op, 49 | "0x%llx\n"); 50 | 51 | DEFINE_DEBUGFS_ATTRIBUTE(addr_pa_fops, addr_pa_read_op, addr_pa_write_op, 52 | "0x%llx\n"); 53 | 54 | static int addr_init(struct dentry *parent) { 55 | debugfs_create_file("va", 0666, parent, NULL, &addr_va_fops); 56 | debugfs_create_file("pa", 0666, parent, NULL, &addr_pa_fops); 57 | 58 | return 0; 59 | } 60 | 61 | static char *const HELP = 62 | "$ echo 0xffffff800468b400 > /d/art/addr/va\n" 63 | "$ cat /d/art/addr/pa\n" 64 | "0x4468b400\n" 65 | "\n" 66 | "$ echo 0x4468b400 > /d/art/addr/pa\n" 67 | "$ cat /d/art/addr/va\n" 68 | "0xffffff800468b400\n"; 69 | 70 | REGISTER_ART_PLUGIN(addr, HELP, addr_init, NULL); 71 | -------------------------------------------------------------------------------- /src/arm_helpers.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | .global exec_code 20 | .type exec_code, %function 21 | 22 | // x0 contains the address to jump to and x1 contains the address of 23 | // the `struct arm64_regs` 24 | exec_code: 25 | // Preserve callee-saved registers 26 | stp x29, x30, [sp, #-16]! 27 | stp x19, x20, [sp, #-16]! 28 | stp x21, x22, [sp, #-16]! 29 | stp x23, x24, [sp, #-16]! 30 | stp x25, x26, [sp, #-16]! 31 | stp x27, x28, [sp, #-16]! 32 | 33 | // The code we jump to may not necessarily follow proper calling convention, 34 | // so we need to save x1 and restore it after the call 35 | str x1, [sp, #-16]! 36 | 37 | // Call the code at address in x0 38 | blr x0 39 | 40 | // Restore the saved x1 into x29, as we don't want to clobber the x1 that 41 | // was set when calling the user's assembly code 42 | ldr x29, [sp], #16 43 | 44 | // Populate the `struct arm64_regs` with the state of the registers 45 | stp x0, x1, [x29, #0] 46 | stp x2, x3, [x29, #16] 47 | stp x4, x5, [x29, #32] 48 | stp x6, x7, [x29, #48] 49 | stp x8, x9, [x29, #64] 50 | stp x10, x11, [x29, #80] 51 | stp x12, x13, [x29, #96] 52 | stp x14, x15, [x29, #112] 53 | stp x16, x17, [x29, #128] 54 | stp x18, x19, [x29, #144] 55 | stp x20, x21, [x29, #160] 56 | stp x22, x23, [x29, #176] 57 | stp x24, x25, [x29, #192] 58 | stp x26, x27, [x29, #208] 59 | str x28, [x29, #224] 60 | 61 | // Restore callee-saved registers 62 | ldp x27, x28, [sp], #16 63 | ldp x25, x26, [sp], #16 64 | ldp x23, x24, [sp], #16 65 | ldp x21, x22, [sp], #16 66 | ldp x19, x20, [sp], #16 67 | ldp x29, x30, [sp], #16 68 | 69 | ret 70 | -------------------------------------------------------------------------------- /src/art.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art.h" 20 | 21 | #include "art_debugfs.h" 22 | #include "mount.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | static struct dentry *art_debugfs_dir = NULL; 29 | 30 | extern struct art_plugin __art_plugins_start[]; 31 | extern struct art_plugin __art_plugins_end[]; 32 | 33 | static int do_art_plugins_init(void) { 34 | int err = 0; 35 | struct art_plugin *p; 36 | 37 | for (p = __art_plugins_start; p != __art_plugins_end; p++) { 38 | struct dentry *debugfs_dir = debugfs_create_dir(p->name, art_debugfs_dir); 39 | 40 | if (p->help_string != NULL) { 41 | art_debugfs_create_string("help", 0444, debugfs_dir, p->help_string); 42 | } 43 | err = p->init(debugfs_dir); 44 | if (err) { 45 | return err; 46 | } 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | static void do_art_plugins_exit(void) { 53 | struct art_plugin *p; 54 | 55 | for (p = __art_plugins_start; p != __art_plugins_end; p++) { 56 | if (p->exit) { 57 | p->exit(); 58 | } 59 | } 60 | } 61 | 62 | static int __init art_driver_init(void) { 63 | int ret; 64 | 65 | art_debugfs_dir = debugfs_create_dir("art", NULL); 66 | if (IS_ERR(art_debugfs_dir)) { 67 | return PTR_ERR(art_debugfs_dir); 68 | } 69 | 70 | ret = do_art_plugins_init(); 71 | if (ret) { 72 | return ret; 73 | } 74 | 75 | // This must be called after `do_art_plugins_init` as this relies on the 76 | // kallsyms plugin 77 | ret = mount_init(); 78 | if (ret) { 79 | return ret; 80 | } 81 | 82 | // Note that we intentionally don't return the error code here. Mounting 83 | // debugfs automatically is non-critical functionality, and in case this 84 | // function breaks with a kernel upgrade, we still want the rest of the module 85 | // to be usable as long as the user can manually mount debugfs 86 | ret = mount("none", "/sys/kernel/debug", "debugfs"); 87 | if (ret < 0) { 88 | if (ret == -EBUSY) { 89 | pr_warn("debugfs is already mounted at /sys/kernel/debug\n"); 90 | } else { 91 | pr_err("failed to automatically mount debugfs: %d\n", ret); 92 | } 93 | } 94 | return 0; 95 | } 96 | 97 | static void __exit art_driver_exit(void) { 98 | do_art_plugins_exit(); 99 | 100 | debugfs_remove_recursive(art_debugfs_dir); 101 | } 102 | 103 | MODULE_DESCRIPTION("Android Red Team Kernel Toolkit"); 104 | MODULE_LICENSE("GPL v2"); 105 | MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); 106 | module_init(art_driver_init); 107 | module_exit(art_driver_exit); 108 | -------------------------------------------------------------------------------- /src/art.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef ART_H 20 | #define ART_H 21 | 22 | #include 23 | 24 | typedef int (*art_plugin_init_func)(struct dentry *); 25 | typedef void (*art_plugin_exit_func)(void); 26 | 27 | struct art_plugin { 28 | char *name; 29 | char *help_string; 30 | art_plugin_init_func init; 31 | art_plugin_exit_func exit; 32 | }; 33 | 34 | #define REGISTER_ART_PLUGIN(_name, _help, _init, _exit) \ 35 | struct art_plugin __art_plugin_##_name __section(".art.plugins") = { \ 36 | .name = #_name, .help_string = _help, .init = _init, .exit = _exit} 37 | 38 | #endif /* ART_H */ 39 | -------------------------------------------------------------------------------- /src/art_debugfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art_debugfs.h" 20 | 21 | #include 22 | #include 23 | 24 | static ssize_t art_debugfs_string_read(struct file *file, char __user *user_buf, 25 | size_t count, loff_t *ppos) { 26 | const char *value = file->f_inode->i_private; 27 | return simple_read_from_buffer(user_buf, count, ppos, value, strlen(value)); 28 | } 29 | 30 | static const struct file_operations art_debugfs_fops_string = { 31 | .read = art_debugfs_string_read}; 32 | 33 | struct dentry *art_debugfs_create_string(const char *name, umode_t mode, 34 | struct dentry *parent, 35 | const char *value) { 36 | struct dentry *dentry; 37 | char *buf; 38 | 39 | buf = kstrdup(value, GFP_KERNEL); 40 | if (!buf) { 41 | return NULL; 42 | } 43 | 44 | dentry = 45 | debugfs_create_file(name, mode, parent, buf, &art_debugfs_fops_string); 46 | if (!dentry) { 47 | kfree(buf); 48 | return NULL; 49 | } 50 | 51 | return dentry; 52 | } 53 | -------------------------------------------------------------------------------- /src/art_debugfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef ART_DEBUGFS_H 20 | #define ART_DEBUGFS_H 21 | 22 | #include 23 | 24 | struct dentry *art_debugfs_create_string(const char *name, umode_t mode, 25 | struct dentry *parent, 26 | const char *value); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/asm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "asm.h" 20 | 21 | #include "art.h" 22 | #include "kallsyms.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | extern void exec_code(uintptr_t code_addr, struct arm64_regs *regs); 33 | 34 | static struct arm64_regs regs; 35 | static cpumask_t cpu_mask = CPU_MASK_CPU0; 36 | 37 | typedef int (*set_memory_x_t)(unsigned long addr, int numpages); 38 | static set_memory_x_t __art_set_memory_x; 39 | 40 | static __nocfi int art_set_memory_x(unsigned long addr, int numpages) { 41 | BUG_ON(!__art_set_memory_x); 42 | return __art_set_memory_x(addr, numpages); 43 | } 44 | 45 | struct exec_code_info { 46 | uintptr_t code_addr; 47 | struct arm64_regs *regs; 48 | }; 49 | 50 | static void exec_code_smp_call_func(void *info) { 51 | struct exec_code_info *exec_info = (struct exec_code_info *)info; 52 | uintptr_t code_addr = exec_info->code_addr; 53 | struct arm64_regs *regs = exec_info->regs; 54 | 55 | pr_info("Jumping to shellcode at %lx on CPU %d\n", code_addr, 56 | smp_processor_id()); 57 | exec_code(code_addr, regs); 58 | } 59 | 60 | int exec_asm(uint8_t *buf, size_t len, struct arm64_regs *regs, 61 | const cpumask_t *cpu_mask) { 62 | int res; 63 | uint32_t ret_ins = 0xd65f03c0; 64 | struct exec_code_info info; 65 | void (*exec_page)(void); 66 | 67 | size_t alloc_size = PAGE_ALIGN(len); 68 | size_t num_pages = alloc_size / PAGE_SIZE; 69 | 70 | // We can't use kmalloc with `set_memory_x` 71 | exec_page = vmalloc(alloc_size); 72 | if (!exec_page) { 73 | pr_err("Failed to allocate memory\n"); 74 | return -ENOMEM; 75 | } 76 | pr_debug("exec_page: %lx, page_count: %zu", (uintptr_t)exec_page, num_pages); 77 | 78 | memcpy(exec_page, buf, len); 79 | memcpy(exec_page + len, &ret_ins, sizeof(ret_ins)); 80 | 81 | res = art_set_memory_x((unsigned long)exec_page, num_pages); 82 | if (res) { 83 | pr_err("Failed to set memory as executable\n"); 84 | goto err; 85 | } 86 | 87 | info.code_addr = (uintptr_t)exec_page; 88 | info.regs = regs; 89 | on_each_cpu_mask(cpu_mask, exec_code_smp_call_func, &info, true); 90 | 91 | err: 92 | vfree(exec_page); 93 | return res; 94 | } 95 | 96 | static ssize_t asm_write_op(struct file *fp, const char __user *user_buffer, 97 | size_t count, loff_t *position) { 98 | int res; 99 | int num_bytes; 100 | uint8_t *asm_code = kzalloc(count, GFP_KERNEL); 101 | 102 | if (cpumask_weight(&cpu_mask) != 1) { 103 | pr_err("Exactly one CPU must be selected\n"); 104 | return -EINVAL; 105 | } 106 | 107 | num_bytes = 108 | simple_write_to_buffer(asm_code, count, position, user_buffer, count); 109 | if (num_bytes < 0) { 110 | return num_bytes; 111 | } 112 | 113 | res = exec_asm(asm_code, count, ®s, &cpu_mask); 114 | if (res < 0) { 115 | return res; 116 | } 117 | 118 | return num_bytes; 119 | } 120 | 121 | struct file_operations asm_fops = {.write = asm_write_op}; 122 | 123 | static int asm_init(struct dentry *parent) { 124 | int i; 125 | 126 | __art_set_memory_x = (set_memory_x_t)art_kallsyms_lookup_name("set_memory_x"); 127 | if (!__art_set_memory_x) { 128 | pr_err("could not resolve set_memory_x function address\n"); 129 | return -1; 130 | } 131 | 132 | debugfs_create_file("asm", 0222, parent, NULL, &asm_fops); 133 | debugfs_create_ulong("cpumask", 0666, parent, &cpumask_bits(&cpu_mask)[0]); 134 | 135 | // Create a read-only debugfs file for each register in the `regs` struct 136 | for (i = 0; i <= 28; i++) { 137 | char name[4]; 138 | snprintf(name, sizeof(name), "x%d", i); 139 | debugfs_create_x64(name, 0444, parent, (uint64_t *)®s + i); 140 | } 141 | 142 | return 0; 143 | } 144 | 145 | static char *const HELP = 146 | "# mov x0, 042; mov x9, 42; mov x28, 0x42\n" 147 | "$ echo 400480d2490580d25c0880d2 | xxd -r -p > /d/art/asm/asm\n" 148 | "$ cat /d/art/asm/x0\n" 149 | "0x0000000000000022\n" 150 | "$ cat /d/art/asm/x9\n" 151 | "0x000000000000002a\n" 152 | "$ cat /d/art/asm/x28\n" 153 | "0x0000000000000042\n"; 154 | 155 | REGISTER_ART_PLUGIN(asm, HELP, asm_init, NULL); 156 | -------------------------------------------------------------------------------- /src/asm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef ASM_H 20 | #define ASM_H 21 | 22 | #include 23 | #include 24 | 25 | // We only store registers up to x28, the others are not useful for us 26 | struct arm64_regs { 27 | uint64_t x0; 28 | uint64_t x1; 29 | uint64_t x2; 30 | uint64_t x3; 31 | uint64_t x4; 32 | uint64_t x5; 33 | uint64_t x6; 34 | uint64_t x7; 35 | uint64_t x8; 36 | uint64_t x9; 37 | uint64_t x10; 38 | uint64_t x11; 39 | uint64_t x12; 40 | uint64_t x13; 41 | uint64_t x14; 42 | uint64_t x15; 43 | uint64_t x16; 44 | uint64_t x17; 45 | uint64_t x18; 46 | uint64_t x19; 47 | uint64_t x20; 48 | uint64_t x21; 49 | uint64_t x22; 50 | uint64_t x23; 51 | uint64_t x24; 52 | uint64_t x25; 53 | uint64_t x26; 54 | uint64_t x27; 55 | uint64_t x28; 56 | } __attribute__((packed)); 57 | 58 | int exec_asm(uint8_t *buf, size_t len, struct arm64_regs *regs, 59 | const cpumask_t *mask); 60 | 61 | #endif // ASM_H 62 | -------------------------------------------------------------------------------- /src/hvc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art.h" 20 | #include "smccc.h" 21 | 22 | static struct dentry *hvc_dentry; 23 | 24 | static int hvc_init(struct dentry *parent) { 25 | hvc_dentry = parent; 26 | return smccc_init(SMCCC_HVC, parent); 27 | } 28 | 29 | static void hvc_exit(void) { smccc_exit(hvc_dentry); } 30 | 31 | REGISTER_ART_PLUGIN(hvc, NULL, hvc_init, hvc_exit); 32 | -------------------------------------------------------------------------------- /src/kallsyms.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "kallsyms.h" 20 | 21 | #include "art.h" 22 | #include "art_debugfs.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); 35 | static kallsyms_lookup_name_t __art_kallsyms_lookup_name; 36 | 37 | static char lookup_name[NAME_MAX + 1]; 38 | static uint64_t lookup_addr; 39 | 40 | __nocfi unsigned long art_kallsyms_lookup_name(const char *name) { 41 | BUG_ON(!__art_kallsyms_lookup_name); 42 | return __art_kallsyms_lookup_name(name); 43 | } 44 | EXPORT_SYMBOL(art_kallsyms_lookup_name); 45 | 46 | #ifdef CONFIG_KPROBES 47 | static int find_kallsyms_lookup_name(void) { 48 | int ret = 0; 49 | struct kprobe kp = {.symbol_name = "kallsyms_lookup_name"}; 50 | 51 | ret = register_kprobe(&kp); 52 | if (ret) { 53 | return ret; 54 | } 55 | 56 | __art_kallsyms_lookup_name = (kallsyms_lookup_name_t)kp.addr; 57 | pr_info("found kallsyms_lookup_name at 0x%lx\n", 58 | (unsigned long)__art_kallsyms_lookup_name); 59 | 60 | unregister_kprobe(&kp); 61 | return ret; 62 | } 63 | #else 64 | 65 | #include 66 | 67 | static int find_kallsyms_lookup_name(void) { 68 | unsigned long addr = kimage_vaddr; 69 | unsigned long end_addr = kimage_vaddr + SZ_32M; 70 | const uint8_t insn_size = AARCH64_INSN_SIZE; 71 | 72 | char *target_sym = "kallsyms_lookup_name"; 73 | char *lookup_sym = kzalloc(NAME_MAX, GFP_KERNEL); 74 | 75 | pr_info("CONFIG_KPROBES is not set, falling back to manual search for %s", 76 | target_sym); 77 | 78 | if (!lookup_sym) { 79 | return -ENOMEM; 80 | } 81 | 82 | pr_info("starting search for %s at 0x%lx\n", target_sym, addr); 83 | while (addr < end_addr) { 84 | sprint_symbol(lookup_sym, addr); 85 | 86 | // sprint_symbol output will look like 'kallsyms_lookup_name+0x0/0x124', so 87 | // we must use strncmp here instead of strcmp to only compare the prefix 88 | if (strncmp(lookup_sym, target_sym, strlen(target_sym)) == 0) { 89 | __art_kallsyms_lookup_name = (kallsyms_lookup_name_t)addr; 90 | pr_info("found %s at 0x%lx\n", target_sym, addr); 91 | break; 92 | } 93 | 94 | addr += insn_size; 95 | } 96 | 97 | kfree(lookup_sym); 98 | 99 | if (__art_kallsyms_lookup_name == NULL) { 100 | return -EINVAL; 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | #endif /* CONFIG_KPROBES */ 107 | 108 | static ssize_t kallsyms_lookup_name_read(struct file *fp, 109 | char __user *user_buffer, size_t count, 110 | loff_t *position) { 111 | ssize_t res; 112 | 113 | res = simple_read_from_buffer(user_buffer, count, position, lookup_name, 114 | strlen(lookup_name)); 115 | 116 | return res; 117 | } 118 | 119 | static ssize_t kallsyms_lookup_name_write(struct file *fp, 120 | const char __user *user_buffer, 121 | size_t count, loff_t *position) { 122 | int res; 123 | res = simple_write_to_buffer(lookup_name, NAME_MAX, position, user_buffer, 124 | count); 125 | if (res <= 0) { 126 | lookup_addr = 0; 127 | return res; 128 | } 129 | 130 | // Set the newline to a null byte 131 | lookup_name[res - 1] = '\0'; 132 | 133 | lookup_addr = art_kallsyms_lookup_name(lookup_name); 134 | return res; 135 | } 136 | 137 | static const struct file_operations lookup_name_fops = { 138 | .read = kallsyms_lookup_name_read, .write = kallsyms_lookup_name_write}; 139 | 140 | static int kallsyms_init(struct dentry *parent) { 141 | int res = find_kallsyms_lookup_name(); 142 | if (res) { 143 | pr_warn("failed to find kallsyms_lookup_name\n"); 144 | return res; 145 | } 146 | 147 | debugfs_create_file("lookup_name", 0666, parent, NULL, &lookup_name_fops); 148 | debugfs_create_x64("addr", 0444, parent, &lookup_addr); 149 | 150 | return res; 151 | } 152 | 153 | static char *const HELP = 154 | "$ echo __sys_setuid > /d/art/kallsyms/lookup_name\n" 155 | "$ cat /d/art/kallsyms/addr\n" 156 | "0xffffffedb417da48\n"; 157 | 158 | REGISTER_ART_PLUGIN(kallsyms, HELP, kallsyms_init, NULL); 159 | -------------------------------------------------------------------------------- /src/kallsyms.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef KALLSYMS_H 20 | #define KALLSYMS_H 21 | 22 | unsigned long art_kallsyms_lookup_name(const char *name); 23 | 24 | #endif /* KALLSYMS_H */ 25 | -------------------------------------------------------------------------------- /src/kaslr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art.h" 20 | 21 | #include 22 | 23 | static int kaslr_offset_read_op(void *data, uint64_t *value) { 24 | #ifdef CONFIG_ARM64 25 | *value = kaslr_offset(); 26 | return 0; 27 | #else 28 | // TODO: Implement this for x86 29 | return -ENOSYS; 30 | #endif /* CONFIG_ARM64 */ 31 | } 32 | 33 | DEFINE_DEBUGFS_ATTRIBUTE(kaslr_offset_fops, kaslr_offset_read_op, NULL, 34 | "0x%llx\n"); 35 | 36 | static int art_kaslr_init(struct dentry *parent) { 37 | debugfs_create_file("offset", 0444, parent, NULL, &kaslr_offset_fops); 38 | 39 | return 0; 40 | } 41 | 42 | static char *const HELP = 43 | "$ cat /d/art/kaslr/offset\n" 44 | "0x1be5600000\n"; 45 | 46 | REGISTER_ART_PLUGIN(kaslr, HELP, art_kaslr_init, NULL); 47 | -------------------------------------------------------------------------------- /src/kmalloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art.h" 20 | 21 | #include 22 | #include 23 | 24 | static void *page; 25 | static uint64_t alloc_size; 26 | 27 | static int kmalloc_alloc_write_op(void *data, uint64_t value) { 28 | alloc_size = value; 29 | page = kmalloc(alloc_size, GFP_KERNEL); 30 | 31 | if (page == NULL) { 32 | pr_err("allocation failed"); 33 | return -1; 34 | } 35 | 36 | return 0; 37 | } 38 | 39 | DEFINE_DEBUGFS_ATTRIBUTE(kmalloc_alloc_fops, NULL, kmalloc_alloc_write_op, 40 | "0x%llx\n"); 41 | 42 | static int kmalloc_free_write_op(void *data, uint64_t value) { 43 | kfree((void *)value); 44 | return 0; 45 | } 46 | 47 | DEFINE_DEBUGFS_ATTRIBUTE(kmalloc_free_fops, NULL, kmalloc_free_write_op, 48 | "0x%llx\n"); 49 | 50 | static int kmalloc_va_read_op(void *data, uint64_t *value) { 51 | if (page == NULL) { 52 | pr_err("no page has been allocated"); 53 | return -1; 54 | } 55 | 56 | *value = (uint64_t)page; 57 | 58 | return 0; 59 | } 60 | 61 | DEFINE_DEBUGFS_ATTRIBUTE(kmalloc_va_fops, kmalloc_va_read_op, NULL, "0x%llx\n"); 62 | 63 | static int kmalloc_pa_read_op(void *data, uint64_t *value) { 64 | if (page == NULL) { 65 | pr_err("no page has been allocated"); 66 | return -1; 67 | } 68 | 69 | *value = (uint64_t)__pa(page); 70 | 71 | return 0; 72 | } 73 | 74 | DEFINE_DEBUGFS_ATTRIBUTE(kmalloc_pa_fops, kmalloc_pa_read_op, NULL, "0x%llx\n"); 75 | 76 | static int kmalloc_pfn_read_op(void *data, uint64_t *value) { 77 | if (page == NULL) { 78 | pr_err("no page has been allocated"); 79 | return -1; 80 | } 81 | 82 | *value = (uint64_t)__pa(page) >> 12; 83 | 84 | return 0; 85 | } 86 | 87 | DEFINE_DEBUGFS_ATTRIBUTE(kmalloc_pfn_fops, kmalloc_pfn_read_op, NULL, 88 | "0x%llx\n"); 89 | 90 | static int kmalloc_size_read_op(void *data, uint64_t *value) { 91 | if (page == NULL) { 92 | pr_err("no page has been allocated"); 93 | return -1; 94 | } 95 | 96 | *value = (uint64_t)alloc_size; 97 | 98 | return 0; 99 | } 100 | 101 | DEFINE_DEBUGFS_ATTRIBUTE(kmalloc_size_fops, kmalloc_size_read_op, NULL, 102 | "0x%llx\n"); 103 | 104 | static int kmalloc_init(struct dentry *parent) { 105 | debugfs_create_file("alloc", 0222, parent, NULL, &kmalloc_alloc_fops); 106 | debugfs_create_file("free", 0222, parent, NULL, &kmalloc_free_fops); 107 | 108 | debugfs_create_file("va", 0444, parent, NULL, &kmalloc_va_fops); 109 | debugfs_create_file("pa", 0444, parent, NULL, &kmalloc_pa_fops); 110 | debugfs_create_file("pfn", 0444, parent, NULL, &kmalloc_pfn_fops); 111 | debugfs_create_file("size", 0444, parent, NULL, &kmalloc_size_fops); 112 | 113 | return 0; 114 | } 115 | 116 | static char *const HELP = 117 | "# Allocate 1024 bytes\n" 118 | "$ echo 0x400 > /d/art/kmalloc/alloc\n" 119 | "\n" 120 | "$ cat /d/art/kmalloc/size\n" 121 | "0x400\n" 122 | "\n" 123 | "$ cat /d/art/kmalloc/va\n" 124 | "0xffffff8004048000\n" 125 | "\n" 126 | "$ cat /d/art/kmalloc/pa\n" 127 | "0x44048000\n" 128 | "\n" 129 | "$ cat /d/art/kmalloc/pfn\n" 130 | "0x44048\n" 131 | "\n" 132 | "# Free allocated memory\n" 133 | "$ echo $(cat /d/art/kmalloc/va) > /d/art/kmalloc/va\n"; 134 | 135 | REGISTER_ART_PLUGIN(kmalloc, HELP, kmalloc_init, NULL); 136 | -------------------------------------------------------------------------------- /src/mount.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "mount.h" 20 | 21 | #include "kallsyms.h" 22 | 23 | #include 24 | #include 25 | 26 | typedef int (*path_mount_t)(const char *dev_name, struct path *path, 27 | const char *type_page, unsigned long flags, 28 | void *data_page); 29 | 30 | static path_mount_t __art_path_mount; 31 | 32 | static __nocfi unsigned long art_path_mount(const char *dev_name, 33 | struct path *path, 34 | const char *type_page, 35 | unsigned long flags, 36 | void *data_page) { 37 | BUG_ON(!__art_path_mount); 38 | return __art_path_mount(dev_name, path, type_page, flags, data_page); 39 | } 40 | 41 | int mount(const char *dev_name, const char *pathname, const char *type) { 42 | int ret; 43 | struct path path; 44 | 45 | ret = kern_path(pathname, 0, &path); 46 | if (ret) { 47 | return ret; 48 | } 49 | 50 | ret = art_path_mount(dev_name, &path, type, 0, NULL); 51 | if (ret) { 52 | goto out; 53 | } 54 | 55 | pr_info("successfully mounted %s\n", pathname); 56 | 57 | out: 58 | path_put(&path); 59 | return ret; 60 | } 61 | 62 | int mount_init(void) { 63 | // There doesn't seem to be any exported API for kernel modules to mount 64 | // filesystems, so we have to find the unexported `path_mount` method to do 65 | // this 66 | __art_path_mount = (path_mount_t)art_kallsyms_lookup_name("path_mount"); 67 | if (!__art_path_mount) { 68 | pr_err("could not resolve path_mount function address\n"); 69 | return -1; 70 | } 71 | 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /src/mount.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef MOUNT_H 20 | #define MOUNT_H 21 | 22 | int mount_init(void); 23 | int mount(const char *dev_name, const char *pathname, const char *type); 24 | 25 | #endif /* MOUNT_H */ 26 | -------------------------------------------------------------------------------- /src/msr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art.h" 20 | #include "asm.h" 21 | #include "linux/printk.h" 22 | 23 | #include 24 | #include 25 | 26 | static cpumask_t cpu_mask = CPU_MASK_CPU0; 27 | 28 | struct sysreg { 29 | const char *name; 30 | uint32_t op0; 31 | uint32_t op1; 32 | uint32_t CRn; 33 | uint32_t CRm; 34 | uint32_t op2; 35 | }; 36 | 37 | static struct sysreg sysreg = {.op0 = 2}; 38 | 39 | static struct sysreg sysregs[] = { 40 | {"sctlr_el1", 3, 0, 1, 0, 0}, 41 | }; 42 | 43 | static uint32_t msr_mrs_encode(uint32_t ins) { 44 | uint32_t o0 = sysreg.op0 - 2; 45 | 46 | ins |= o0 << 19; 47 | ins |= sysreg.op1 << 16; 48 | ins |= sysreg.CRn << 12; 49 | ins |= sysreg.CRm << 8; 50 | ins |= sysreg.op2 << 5; 51 | 52 | return ins; 53 | } 54 | 55 | static int msr_read_op(void *data, uint64_t *value) { 56 | int res; 57 | struct arm64_regs regs = {0}; 58 | 59 | // MRS x0, 60 | uint32_t mrs_ins = msr_mrs_encode(0xd5300000); 61 | 62 | if (cpumask_weight(&cpu_mask) != 1) { 63 | pr_err("Exactly one CPU must be selected\n"); 64 | return -EINVAL; 65 | } 66 | 67 | res = exec_asm((uint8_t *)&mrs_ins, sizeof(mrs_ins), ®s, &cpu_mask); 68 | if (res < 0) { 69 | return res; 70 | } 71 | 72 | *value = regs.x0; 73 | 74 | return res; 75 | } 76 | 77 | static int msr_write_op(void *data, uint64_t value) { 78 | // Unused, but required by `exec_asm` 79 | struct arm64_regs regs = {0}; 80 | 81 | uint32_t insns[] = { 82 | // LDR x0, $pc+16' 83 | 0x58000080, 84 | // MSR , x0 85 | msr_mrs_encode(0xd5100000), 86 | // RET 87 | 0xd65f03c0, 88 | // padding 89 | 0x00000000, 90 | // 91 | value & 0xFFFFFFFF, 92 | value >> 32, 93 | }; 94 | 95 | return exec_asm((uint8_t *)&insns, sizeof(insns), ®s, &cpu_mask); 96 | } 97 | 98 | DEFINE_DEBUGFS_ATTRIBUTE(msr_fops, msr_read_op, msr_write_op, "0x%llx\n"); 99 | 100 | static int parse_regname(char *regname) { 101 | uint32_t tmp[5]; 102 | int i; 103 | char *s = regname; 104 | 105 | // Convert `regname` to lowercase, and terminate string at newline 106 | while (*s) { 107 | if (*s == '\n') { 108 | *s = '\0'; 109 | break; 110 | } 111 | *s = tolower(*s); 112 | s++; 113 | } 114 | 115 | for (i = 0; i < ARRAY_SIZE(sysregs); i++) { 116 | pr_debug("Comparing %s to %s\n", regname, sysregs[i].name); 117 | if (strcmp(regname, sysregs[i].name) == 0) { 118 | sysreg.op0 = sysregs[i].op0; 119 | sysreg.op1 = sysregs[i].op1; 120 | sysreg.CRn = sysregs[i].CRn; 121 | sysreg.CRm = sysregs[i].CRm; 122 | sysreg.op2 = sysregs[i].op2; 123 | return 0; 124 | } 125 | } 126 | 127 | // Replace non-numeric characters with spaces 128 | s = regname; 129 | while (*s) { 130 | if (*s < '0' || *s > '9') { 131 | *s = ' '; 132 | } 133 | s++; 134 | } 135 | 136 | if (sscanf(regname, "%u %u %u %u %u", &tmp[0], &tmp[1], &tmp[2], &tmp[3], 137 | &tmp[4]) 138 | != 5) { 139 | return -EINVAL; 140 | } 141 | 142 | sysreg.op0 = tmp[0]; 143 | sysreg.op1 = tmp[1]; 144 | sysreg.CRn = tmp[2]; 145 | sysreg.CRm = tmp[3]; 146 | sysreg.op2 = tmp[4]; 147 | 148 | return 0; 149 | } 150 | 151 | static ssize_t regname_write_op(struct file *fp, const char __user *user_buffer, 152 | size_t count, loff_t *position) { 153 | char regname[256] = {0}; 154 | int bytes_written; 155 | int res; 156 | 157 | bytes_written = simple_write_to_buffer(regname, sizeof(regname), position, 158 | user_buffer, count); 159 | if (bytes_written < 0) { 160 | return bytes_written; 161 | } 162 | 163 | res = parse_regname(regname); 164 | if (res < 0) { 165 | return res; 166 | } 167 | 168 | return bytes_written; 169 | } 170 | 171 | static ssize_t regname_read_op(struct file *fp, char __user *user_buffer, 172 | size_t count, loff_t *position) { 173 | char regname[256] = {0}; 174 | int available; 175 | 176 | available = 177 | snprintf(regname, sizeof(regname), "s%u_%u_c%u_c%u_%u\n", sysreg.op0, 178 | sysreg.op1, sysreg.CRn, sysreg.CRm, sysreg.op2); 179 | 180 | return simple_read_from_buffer(user_buffer, count, position, regname, 181 | available); 182 | } 183 | 184 | struct file_operations regname_fops = { 185 | .read = regname_read_op, 186 | .write = regname_write_op, 187 | }; 188 | 189 | static int msr_init(struct dentry *parent) { 190 | debugfs_create_file("msr", 0666, parent, NULL, &msr_fops); 191 | debugfs_create_ulong("cpumask", 0666, parent, &cpumask_bits(&cpu_mask)[0]); 192 | debugfs_create_file("regname", 0666, parent, NULL, ®name_fops); 193 | debugfs_create_u32("op0", 0666, parent, &sysreg.op0); 194 | debugfs_create_u32("op1", 0666, parent, &sysreg.op1); 195 | debugfs_create_u32("CRn", 0666, parent, &sysreg.CRn); 196 | debugfs_create_u32("CRm", 0666, parent, &sysreg.CRm); 197 | debugfs_create_u32("op2", 0666, parent, &sysreg.op2); 198 | 199 | return 0; 200 | } 201 | 202 | static char *const HELP = 203 | "# Read SCTLR_EL1\n" 204 | "$ echo sctlr_el1 > /d/art/msr/regname\n" 205 | "$ cat /d/art/msr/regname\n" 206 | "s3_0_c1_c0_0\n" 207 | "$ cat /d/art/msr/msr\n" 208 | "0x200000034f4d91d\n" 209 | "\n" 210 | "# Set cpumask to CPU 0 and CPU 1\n" 211 | "$ echo 0x3 > /d/art/msr/cpumask\n" 212 | "\n" 213 | "# Disable EPAN and SPAN on CPU 0 and CPU 1\n" 214 | "$ echo 0x3474d91d > /d/art/msr/msr\n" 215 | "\n" 216 | "# Set CPU mask back to individual CPUs when reading\n" 217 | "$ echo 0x1 > /d/art/msr/cpumask\n" 218 | "\n" 219 | "# EPAN and SPAN are now unset on CPU 0 and CPU 1\n" 220 | "$ cat /d/art/msr/msr\n" 221 | "0x3474d91d\n" 222 | "\n" 223 | "$ echo 0x2 > /d/art/msr/cpumask\n" 224 | "$ cat /d/art/msr/msr\n" 225 | "0x3474d91d\n" 226 | "\n" 227 | "# SCTLR_EL1 is unchanged on CPU 2\n" 228 | "$ echo 0x4 > /d/art/msr/cpumask\n" 229 | "$ cat /d/art/msr/msr\n" 230 | "0x200000034f4d91d"; 231 | 232 | REGISTER_ART_PLUGIN(msr, HELP, msr_init, NULL); 233 | -------------------------------------------------------------------------------- /src/pmem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art.h" 20 | #include "art_debugfs.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | static uint64_t addr; 29 | static uint64_t bytes_read_size = 8; 30 | 31 | static bool is_ram(uint64_t addr) { 32 | #ifdef CONFIG_ARM64 33 | 34 | #if KERNEL_VERSION(5, 14, 0) <= LINUX_VERSION_CODE 35 | return pfn_is_map_memory(__phys_to_pfn(addr)); 36 | #else 37 | return pfn_valid(__phys_to_pfn(addr)); 38 | #endif /* KERNEL_VERSION(5, 14, 0) */ 39 | 40 | #else 41 | return false; 42 | #endif /* CONFIG_ARM64 */ 43 | } 44 | 45 | static void *pmem_kmap(struct page *page) { 46 | #if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE 47 | return kmap_local_page(page); 48 | #else 49 | return kmap(page); 50 | #endif /* KERNEL_VERSION(5, 11, 0) */ 51 | } 52 | 53 | static void pmem_kunmap(void *phys_addr) { 54 | #if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE 55 | kunmap_local(phys_addr); 56 | #else 57 | kunmap(phys_addr); 58 | #endif /* KERNEL_VERSION(5, 11, 0) */ 59 | } 60 | 61 | // x86_64 doesn't define this macro but arm64 does, so we define it here if it's 62 | // not defined 63 | #ifndef phys_to_page 64 | #define phys_to_page(phys) pfn_to_page(__phys_to_pfn(phys)) 65 | #endif 66 | 67 | static int copy_phys_ram(char *buffer, uint64_t phys_addr, size_t len, 68 | bool to_buffer) { 69 | ssize_t ret = -EINVAL; 70 | 71 | if (phys_addr == 0 || len == 0) { 72 | return ret; 73 | } 74 | 75 | ret = len; 76 | while (len) { 77 | off_t page_offset; 78 | size_t bytes_to_copy; 79 | char *kaddr; 80 | void *mapped_page; 81 | 82 | mapped_page = pmem_kmap(phys_to_page(phys_addr)); 83 | if (mapped_page == NULL) { 84 | ret = -EFAULT; 85 | break; 86 | } 87 | 88 | page_offset = phys_addr & ~PAGE_MASK; 89 | kaddr = mapped_page + page_offset; 90 | bytes_to_copy = min(len, (size_t)PAGE_SIZE - page_offset); 91 | 92 | if (to_buffer) { 93 | memcpy(buffer, kaddr, bytes_to_copy); 94 | } else { 95 | memcpy(kaddr, buffer, bytes_to_copy); 96 | } 97 | 98 | pmem_kunmap(mapped_page); 99 | 100 | phys_addr += bytes_to_copy; 101 | buffer += bytes_to_copy; 102 | len -= bytes_to_copy; 103 | } 104 | 105 | return ret; 106 | } 107 | 108 | static int copy_from_phys_ioremap(char *dst_buffer, uint64_t phys_addr, 109 | size_t len) { 110 | int ret = -EINVAL; 111 | void *io = ioremap(phys_addr, len); 112 | if (io) { 113 | int i; 114 | for (i = 0; i < len; i++) { 115 | dst_buffer[i] = ioread8(io + i); 116 | } 117 | if (i == len) { 118 | ret = len; 119 | } 120 | iounmap(io); 121 | } 122 | 123 | return ret; 124 | } 125 | 126 | static int copy_to_phys_ioremap(const char *src_buffer, uint64_t phys_addr, 127 | size_t len) { 128 | int ret = -EINVAL; 129 | void *io = ioremap(phys_addr, len); 130 | if (io) { 131 | int i; 132 | for (i = 0; i < len; i++) { 133 | iowrite8(src_buffer[i], io + i); 134 | } 135 | if (i == len) { 136 | ret = len; 137 | } 138 | iounmap(io); 139 | } 140 | return ret; 141 | } 142 | 143 | static int read_phys(char *buffer, uint64_t addr, size_t len) { 144 | int ret = -EINVAL; 145 | 146 | if (is_ram(addr)) { 147 | ret = copy_phys_ram(buffer, addr, len, true); 148 | } else { 149 | ret = copy_from_phys_ioremap(buffer, addr, len); 150 | } 151 | 152 | return ret; 153 | } 154 | 155 | static int write_phys(const char *buffer, uint64_t addr, size_t len) { 156 | int ret = -EINVAL; 157 | 158 | if (is_ram(addr)) { 159 | ret = copy_phys_ram((char *)buffer, addr, len, false); 160 | } else { 161 | ret = copy_to_phys_ioremap(buffer, addr, len); 162 | } 163 | 164 | return ret; 165 | } 166 | 167 | static int copy_from_phys_to_user(char __user *buffer, loff_t offset, 168 | size_t len) { 169 | int ret = -EINVAL; 170 | uint64_t target_addr = addr + offset; 171 | char *tmp_buf = kzalloc(len, GFP_KERNEL); 172 | 173 | ret = read_phys(tmp_buf, target_addr, len); 174 | 175 | if (ret < 0) { 176 | goto err; 177 | } 178 | if (copy_to_user(buffer, tmp_buf, ret)) { 179 | ret = -EFAULT; 180 | goto err; 181 | } 182 | 183 | err: 184 | kfree(tmp_buf); 185 | 186 | return ret; 187 | } 188 | 189 | static int copy_from_user_to_phys(const char __user *buffer, loff_t offset, 190 | size_t len) { 191 | int ret = -EINVAL; 192 | uint64_t target_addr = addr + offset; 193 | char *tmp_buf = kzalloc(len, GFP_KERNEL); 194 | 195 | if (copy_from_user(tmp_buf, buffer, len)) { 196 | ret = -EFAULT; 197 | goto err; 198 | } 199 | 200 | ret = write_phys(tmp_buf, target_addr, len); 201 | 202 | err: 203 | kfree(tmp_buf); 204 | 205 | return ret; 206 | } 207 | 208 | static int pmem_val_write_op(void *data, uint64_t value) { 209 | int ret = write_phys((char *)&value, addr, sizeof(value)); 210 | if (ret < 0) { 211 | return ret; 212 | } 213 | 214 | return 0; 215 | } 216 | 217 | static int pmem_val_read_op(void *data, uint64_t *value) { 218 | int ret = read_phys((char *)value, addr, sizeof(*value)); 219 | if (ret < 0) { 220 | return ret; 221 | } 222 | 223 | return 0; 224 | } 225 | 226 | DEFINE_DEBUGFS_ATTRIBUTE(pmem_val_fops, pmem_val_read_op, pmem_val_write_op, 227 | "0x%llx\n"); 228 | 229 | static ssize_t pmem_bytes_read(struct file *filp, char __user *buffer, 230 | size_t len, loff_t *offset) { 231 | size_t remaining, read_size; 232 | ssize_t ret; 233 | 234 | if (*offset >= bytes_read_size) { 235 | // No more data to read 236 | return 0; 237 | } 238 | 239 | remaining = bytes_read_size - *offset; 240 | read_size = min(len, remaining); 241 | 242 | ret = copy_from_phys_to_user(buffer, *offset, read_size); 243 | if (ret >= 0) { 244 | *offset += ret; 245 | } 246 | 247 | return ret; 248 | } 249 | 250 | static ssize_t pmem_bytes_write(struct file *filp, const char __user *buffer, 251 | size_t len, loff_t *offset) { 252 | return copy_from_user_to_phys(buffer, *offset, len); 253 | } 254 | 255 | static const struct file_operations pmem_bytes_fops = { 256 | .read = pmem_bytes_read, 257 | .write = pmem_bytes_write, 258 | }; 259 | 260 | static char *const HELP = 261 | "# Write 32-bit value in base 10 to addr:\n" 262 | "$ echo 0xB62CE0DC > /d/art/pmem/addr\n" 263 | "$ echo 12345678 > /d/art/pmem/val\n" 264 | "\n" 265 | "# Write string to addr:\n" 266 | "$ echo 0xB62CE0DC > /d/art/pmem/addr\n" 267 | "$ echo -n 'helloworld' > /d/art/pmem/bytes\n" 268 | "\n" 269 | "# Write hex value to addr:\n" 270 | "$ echo 0xB62CE0DC > /d/art/pmem/addr\n" 271 | "$ echo -n '56 67 89 ab cd ef' | xxd -r -p | dd of=/d/art/pmem/bytes\n" 272 | "\n" 273 | "# Read 5 hex values from addr:\n" 274 | "$ echo 0x5 > /d/art/pmem/bytes-read-size\n" 275 | "$ xxd -p /d/art/pmem/bytes\n"; 276 | 277 | static int pmem_init(struct dentry *parent) { 278 | debugfs_create_x64("addr", 0666, parent, &addr); 279 | debugfs_create_x64("bytes-read-size", 0666, parent, &bytes_read_size); 280 | debugfs_create_file("val", 0666, parent, NULL, &pmem_val_fops); 281 | debugfs_create_file("bytes", 0666, parent, NULL, &pmem_bytes_fops); 282 | return 0; 283 | } 284 | 285 | REGISTER_ART_PLUGIN(pmem, HELP, pmem_init, NULL); 286 | -------------------------------------------------------------------------------- /src/smc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art.h" 20 | #include "smccc.h" 21 | 22 | static struct dentry *smc_dentry; 23 | 24 | static int smc_init(struct dentry *parent) { 25 | smc_dentry = parent; 26 | return smccc_init(SMCCC_SMC, parent); 27 | } 28 | 29 | static void smc_exit(void) { smccc_exit(smc_dentry); } 30 | 31 | static char *const HELP = 32 | "# Execute SMCCC_VERSION with some unused arguments in different numeric " 33 | "formats (supports up to 8 arguments including SMC ID)\n" 34 | "$ echo 0x80000000 0xdeadbeef 0777 42 > /d/art/smc/cmd\n" 35 | "\n" 36 | "# Result is SMC Version 1.2, unused arguments are returned as is (in " 37 | "hex)\n" 38 | "$ cat /d/art/smc/result\n" 39 | "0x10002 0xdeadbeef 0x1ff 0x2a\n"; 40 | 41 | REGISTER_ART_PLUGIN(smc, HELP, smc_init, smc_exit); 42 | -------------------------------------------------------------------------------- /src/smccc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "smccc.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define SMCCC_BUF_SIZE 128 32 | #define MAX_SMCCC_REGS 8 33 | 34 | struct smccc_private_data { 35 | spinlock_t lock; 36 | enum smccc_type type; 37 | char cmd_buf[SMCCC_BUF_SIZE]; 38 | char result_buf[SMCCC_BUF_SIZE]; 39 | }; 40 | 41 | #define STRSCPY_TO_SMCCC(smccc_data_obj, smccc_buf, src_buf, count) \ 42 | strscpy_with_lock(smccc_data_obj->smccc_buf, src_buf, count, \ 43 | &smccc_data_obj->lock) 44 | #define STRSCPY_FROM_SMCCC(smccc_data_obj, smccc_buf, dst_buf, count) \ 45 | strscpy_with_lock(dst_buf, smccc_data_obj->smccc_buf, count, \ 46 | &smccc_data_obj->lock) 47 | 48 | #define STRSCPY_TO_SMCCC_CMD(smccc_data_obj, src_buf, count) \ 49 | STRSCPY_TO_SMCCC(smccc_data_obj, cmd_buf, src_buf, count) 50 | #define STRSCPY_TO_SMCCC_RESULT(smccc_data_obj, src_buf, count) \ 51 | STRSCPY_TO_SMCCC(smccc_data_obj, result_buf, src_buf, count) 52 | #define STRSCPY_FROM_SMCCC_CMD(smccc_data_obj, dst_buf, count) \ 53 | STRSCPY_FROM_SMCCC(smccc_data_obj, cmd_buf, dst_buf, count) 54 | #define STRSCPY_FROM_SMCCC_RESULT(smccc_data_obj, dst_buf, count) \ 55 | STRSCPY_FROM_SMCCC(smccc_data_obj, result_buf, dst_buf, count) 56 | 57 | static ssize_t strscpy_with_lock(char *dst, char *src, size_t count, 58 | spinlock_t *lock) { 59 | unsigned long flags; 60 | ssize_t res; 61 | 62 | spin_lock_irqsave(lock, flags); 63 | res = strscpy(dst, src, count); 64 | spin_unlock_irqrestore(lock, flags); 65 | 66 | return res; 67 | } 68 | 69 | static int parse_smccc_args(char *cmd_buf, 70 | uint64_t smccc_regs[MAX_SMCCC_REGS]) { 71 | char *arg; 72 | size_t num_args = 0; 73 | int ret = -EINVAL; 74 | char *buf = kstrdup(cmd_buf, GFP_KERNEL); 75 | 76 | while ((arg = strsep(&buf, " ")) != NULL) { 77 | if (num_args >= MAX_SMCCC_REGS) { 78 | pr_err("number of SMCCC arguments exceeds maximum of %d\n", 79 | MAX_SMCCC_REGS); 80 | ret = -EINVAL; 81 | goto out; 82 | } 83 | 84 | ret = kstrtoull(arg, 0, &smccc_regs[num_args]); 85 | if (ret) { 86 | goto out; 87 | } 88 | 89 | num_args++; 90 | } 91 | 92 | ret = num_args; 93 | 94 | out: 95 | kfree(buf); 96 | return ret; 97 | } 98 | 99 | static int smccc_cmd_execute(struct smccc_private_data *smccc_data) { 100 | int res; 101 | uint64_t smccc_args[MAX_SMCCC_REGS] = {0}; 102 | char cmd_buf[SMCCC_BUF_SIZE]; 103 | char tmp_buf[SMCCC_BUF_SIZE]; 104 | struct arm_smccc_res smccc_res = {0, 0, 0, 0}; 105 | 106 | res = STRSCPY_FROM_SMCCC_CMD(smccc_data, cmd_buf, sizeof(cmd_buf)); 107 | if (res < 0) { 108 | return res; 109 | } 110 | 111 | res = parse_smccc_args(cmd_buf, smccc_args); 112 | if (res < 0) { 113 | pr_err("failed to parse SMCCC command buffer\n"); 114 | return res; 115 | } 116 | 117 | switch (smccc_data->type) { 118 | case SMCCC_HVC: 119 | arm_smccc_1_1_hvc(smccc_args[0], smccc_args[1], smccc_args[2], 120 | smccc_args[3], smccc_args[4], smccc_args[5], 121 | smccc_args[6], smccc_args[7], &smccc_res); 122 | break; 123 | case SMCCC_SMC: 124 | arm_smccc_1_1_smc(smccc_args[0], smccc_args[1], smccc_args[2], 125 | smccc_args[3], smccc_args[4], smccc_args[5], 126 | smccc_args[6], smccc_args[7], &smccc_res); 127 | break; 128 | default: 129 | pr_err("invalid SMCCC type: %d\n", smccc_data->type); 130 | BUG(); 131 | } 132 | 133 | snprintf(tmp_buf, SMCCC_BUF_SIZE - 1, "0x%lx 0x%lx 0x%lx 0x%lx\n", 134 | smccc_res.a0, smccc_res.a1, smccc_res.a2, smccc_res.a3); 135 | 136 | res = STRSCPY_TO_SMCCC_RESULT(smccc_data, tmp_buf, sizeof(tmp_buf)); 137 | if (res < 0) { 138 | return res; 139 | } 140 | 141 | pr_notice("result: %s\n", tmp_buf); 142 | return 0; 143 | } 144 | 145 | static ssize_t smccc_cmd_write(struct file *fp, const char __user *user_buffer, 146 | size_t count, loff_t *position) { 147 | int res; 148 | int num_written; 149 | char tmp_buf[SMCCC_BUF_SIZE]; 150 | 151 | struct smccc_private_data *smccc_data = 152 | (struct smccc_private_data *)file_inode(fp)->i_private; 153 | 154 | num_written = simple_write_to_buffer(tmp_buf, SMCCC_BUF_SIZE - 1, position, 155 | user_buffer, count); 156 | 157 | if (num_written < 0) { 158 | return num_written; 159 | } 160 | 161 | tmp_buf[num_written] = '\0'; 162 | STRSCPY_TO_SMCCC_CMD(smccc_data, tmp_buf, sizeof(tmp_buf)); 163 | res = smccc_cmd_execute(smccc_data); 164 | if (res < 0) { 165 | return res; 166 | } 167 | 168 | return num_written; 169 | } 170 | 171 | static ssize_t smccc_cmd_read(struct file *fp, char __user *user_buffer, 172 | size_t count, loff_t *position) { 173 | char tmp_buf[SMCCC_BUF_SIZE]; 174 | size_t buf_len; 175 | 176 | struct smccc_private_data *smccc_data = 177 | (struct smccc_private_data *)file_inode(fp)->i_private; 178 | 179 | buf_len = STRSCPY_FROM_SMCCC_CMD(smccc_data, tmp_buf, sizeof(tmp_buf)); 180 | if (buf_len < 0) { 181 | return buf_len; 182 | } 183 | 184 | return simple_read_from_buffer(user_buffer, count, position, tmp_buf, 185 | buf_len); 186 | } 187 | 188 | static const struct file_operations smccc_cmd_fops = { 189 | .read = smccc_cmd_read, 190 | .write = smccc_cmd_write, 191 | }; 192 | 193 | static ssize_t smccc_result_read(struct file *fp, char __user *user_buffer, 194 | size_t count, loff_t *position) { 195 | char tmp_buf[SMCCC_BUF_SIZE]; 196 | size_t buf_len; 197 | 198 | struct smccc_private_data *smccc_data = 199 | (struct smccc_private_data *)file_inode(fp)->i_private; 200 | 201 | buf_len = STRSCPY_FROM_SMCCC_RESULT(smccc_data, tmp_buf, sizeof(tmp_buf)); 202 | if (buf_len < 0) { 203 | return buf_len; 204 | } 205 | 206 | return simple_read_from_buffer(user_buffer, count, position, tmp_buf, 207 | buf_len); 208 | } 209 | 210 | static const struct file_operations smccc_result_fops = { 211 | .read = smccc_result_read, 212 | }; 213 | 214 | int smccc_init(enum smccc_type type, struct dentry *parent) { 215 | struct smccc_private_data *smccc_data = 216 | kzalloc(sizeof(struct smccc_private_data), GFP_KERNEL); 217 | 218 | if (smccc_data == NULL) { 219 | return -ENOMEM; 220 | } 221 | 222 | spin_lock_init(&smccc_data->lock); 223 | smccc_data->type = type; 224 | 225 | debugfs_create_file("cmd", 0666, parent, (void *)smccc_data, &smccc_cmd_fops); 226 | debugfs_create_file("result", 0444, parent, (void *)smccc_data, 227 | &smccc_result_fops); 228 | 229 | parent->d_inode->i_private = smccc_data; 230 | 231 | return 0; 232 | } 233 | 234 | void smccc_exit(struct dentry *parent) { 235 | struct smccc_private_data *smccc_data = parent->d_inode->i_private; 236 | kfree(smccc_data); 237 | } 238 | -------------------------------------------------------------------------------- /src/smccc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #ifndef SMCCC_H 20 | #define SMCCC_H 21 | 22 | #include 23 | 24 | enum smccc_type { 25 | SMCCC_HVC, 26 | SMCCC_SMC, 27 | }; 28 | 29 | int smccc_init(enum smccc_type type, struct dentry *parent); 30 | void smccc_exit(struct dentry *parent); 31 | 32 | #endif /* SMCCC_H */ 33 | -------------------------------------------------------------------------------- /src/vmem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | */ 18 | 19 | #include "art.h" 20 | #include "art_debugfs.h" 21 | 22 | #include 23 | 24 | static uint64_t addr; 25 | 26 | static int vmem_val_write_op(void *data, uint64_t value) { 27 | *(uint64_t *)addr = value; 28 | return 0; 29 | } 30 | 31 | static int vmem_val_read_op(void *data, uint64_t *value) { 32 | *value = *(uint64_t *)addr; 33 | return 0; 34 | } 35 | 36 | DEFINE_DEBUGFS_ATTRIBUTE(vmem_val_fops, vmem_val_read_op, vmem_val_write_op, 37 | "0x%llx\n"); 38 | 39 | static int vmem_init(struct dentry *parent) { 40 | debugfs_create_x64("addr", 0666, parent, &addr); 41 | debugfs_create_file("val", 0666, parent, NULL, &vmem_val_fops); 42 | 43 | return 0; 44 | } 45 | 46 | static char *const HELP = 47 | "# Write address to write to or read from to `addr`\n" 48 | "$ echo ffffffc009fa2378 > /d/art/vmem/addr\n" 49 | "\n" 50 | "# Read from `val` to read 64-bit hex value at address\n" 51 | "$ cat /d/art/vmem/val\n" 52 | "0xffffff80038db270\n" 53 | "\n" 54 | "# Write 64-bit hex value to `val` to write to address\n" 55 | "$ echo 0xdeadbeef > /d/art/vmem/val\n" 56 | "\n" 57 | "# Confirm write succeeded" 58 | "$ cat /d/art/vmem/val\n" 59 | "0xdeadbeef\n"; 60 | 61 | REGISTER_ART_PLUGIN(vmem, HELP, vmem_init, NULL); 62 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (C) 2024 Google LLC 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License along 16 | # with this program; if not, write to the Free Software Foundation, Inc., 17 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | 19 | MODULE_DIR=/sys/kernel/debug/art 20 | 21 | RED='\033[0;31m' 22 | GREEN='\033[0;32m' 23 | YELLOW='\033[0;33m' 24 | NC='\033[0m' 25 | 26 | fail() { 27 | echo -e "${RED}Test failed${NC}" 28 | exit 1 29 | } 30 | 31 | log() { 32 | echo -e "${YELLOW}[+] $1${NC}" 33 | } 34 | 35 | assert_eq() { 36 | local plugin_path="$1" 37 | local expected_val="$2" 38 | 39 | actual_val=$(cat "${MODULE_DIR}/${plugin_path}") 40 | echo -n "Asserting ${plugin_path} value ${actual_val} equals expected value of ${expected_val}... " 41 | [ "$actual_val" == "$expected_val" ] || fail 42 | echo -e "${GREEN}Success${NC}" 43 | } 44 | 45 | to_uppercase() { 46 | echo $1 | tr "a-z" "A-Z" 47 | } 48 | 49 | to_lowercase() { 50 | echo $1 | tr "A-A" "a-z" 51 | } 52 | 53 | divide_hex() { 54 | local arg1="$1" 55 | local arg2="$2" 56 | 57 | # Convert arguments to uppercase and strip any leading 0x prefix in order to 58 | # use `bc` 59 | arg1="$(to_uppercase $arg1)" 60 | arg1="${arg1#0X}" 61 | 62 | arg2="$(to_uppercase $arg2)" 63 | arg2="${arg2#0X}" 64 | 65 | # Compute result of division, the output is in base 10 66 | local result=$(echo "obase=10; ibase=16; $arg1 / $arg2" | bc) 67 | 68 | # Output result in lowercase with leading 0x prefix 69 | printf "0x%x\n" $result 70 | } 71 | 72 | KERNEL_VERSION=$(uname -r | cut -d'-' -f1) 73 | 74 | AARCH64=0 75 | if [[ $(uname -m) == aarch64 ]]; then 76 | AARCH64=1 77 | fi 78 | 79 | ACK=0 80 | if grep "lede.ack=1" /proc/cmdline > /dev/null 2>&1 ; then 81 | ACK=1 82 | fi 83 | 84 | QEMU=0 85 | if grep "art-kt-qemu-test" /proc/cmdline > /dev/null 2>&1 ; then 86 | QEMU=1 87 | fi 88 | 89 | log "Test whether module is loaded" 90 | lsmod | grep art_kernel_toolkit || fail 91 | 92 | log "Test whether module directory exists" 93 | test -d "${MODULE_DIR}" || fail 94 | 95 | log "Test kmalloc alloc" 96 | echo 0x400 > "${MODULE_DIR}/kmalloc/alloc" 97 | assert_eq kmalloc/size 0x400 98 | 99 | VA=$(cat "${MODULE_DIR}/kmalloc/va") 100 | # echo $VA | grep 0xffff || fail 101 | 102 | log "Verifying PFN and physical address" 103 | PAGE_SIZE=0x1000 104 | PA=$(cat "${MODULE_DIR}/kmalloc/pa") 105 | assert_eq kmalloc/pfn $(divide_hex "$PA" $PAGE_SIZE) 106 | 107 | log "Test addr" 108 | echo $VA > "${MODULE_DIR}/addr/va" 109 | assert_eq addr/pa $PA 110 | echo $PA > "${MODULE_DIR}/addr/pa" 111 | assert_eq addr/va $VA 112 | 113 | log "Test kaslr" 114 | cat "${MODULE_DIR}/kaslr/offset" 115 | 116 | log "Test vmem" 117 | echo $VA > "${MODULE_DIR}/vmem/addr" 118 | assert_eq vmem/addr $VA 119 | echo 0xdeadbeefbabecafe > "${MODULE_DIR}/vmem/val" 120 | assert_eq vmem/val 0xdeadbeefbabecafe 121 | 122 | log "Test pmem (ram)" 123 | echo $PA > "${MODULE_DIR}/pmem/addr" 124 | # assert_eq pmem/addr $PA 125 | echo 0xbabecafedeadbeef > "${MODULE_DIR}/vmem/val" 126 | assert_eq pmem/val 0xbabecafedeadbeef 127 | 128 | # The "pmem read (mmio)" test reads an ARM specific register, so only run it on 129 | # ARM. Only run on QEMU since the register address might change on other systems 130 | if [[ $AARCH64 == 1 ]] && [[ $QEMU == 1 ]]; then 131 | log "Test pmem read (mmio)" 132 | # GIC_DIST base of 0x08000000 plus offset of 8 133 | GICD_IIDR_ADDR=0x0000000008000008 134 | echo $GICD_IIDR_ADDR > "${MODULE_DIR}/pmem/addr" 135 | assert_eq pmem/addr $GICD_IIDR_ADDR 136 | assert_eq pmem/val 0x43b # JEP 106 code for ARM 137 | else 138 | log "Skipping pmem read (mmio) test" 139 | fi 140 | 141 | log "Test pmem bytes" 142 | echo $PA > "${MODULE_DIR}/pmem/addr" 143 | echo -n '56 67 89 ab cd ef' | xxd -r -p > /d/art/pmem/bytes 144 | 145 | echo 6 > "${MODULE_DIR}/pmem/bytes-read-size" 146 | actual_val=$(xxd -p /d/art/pmem/bytes) 147 | expected_val=566789abcdef 148 | echo -n "Asserting $actual_val equals $expected_val... " 149 | [ "$actual_val" == "$expected_val" ] || fail 150 | echo -e "${GREEN}Success${NC}" 151 | 152 | log "Test kallsyms lookup" 153 | SYM_NAME=__sys_setuid 154 | echo $SYM_NAME > "${MODULE_DIR}/kallsyms/lookup_name" 155 | assert_eq kallsyms/lookup_name $SYM_NAME 156 | setuid_addr=$(cat "${MODULE_DIR}/kallsyms/addr") 157 | echo $setuid_addr | grep ffff || fail 158 | 159 | log "Test kmalloc free" 160 | echo $VA > "${MODULE_DIR}/kmalloc/free" 161 | 162 | # Only run SMC tests for aarch64. Don't run this when running QEMU as there may 163 | # not be any EL3 handler for it 164 | if [[ $AARCH64 == 1 ]] && [[ $QEMU == 0 ]]; then 165 | log "Test SMC" 166 | # Execute SMCCC_VERSION with some unused arguments in different formats 167 | echo 0x80000000 0xdeadbeef 0777 42 > "${MODULE_DIR}/smc/cmd" 168 | # Result is SMC Version 1.2, unused arguments are returned as is 169 | assert_eq smc/result "0x10002 0xdeadbeef 0x1ff 0x2a" 170 | else 171 | log "Skipping SMC test" 172 | fi 173 | 174 | if [[ $AARCH64 == 1 ]]; then 175 | log "Test asm" 176 | 177 | assert_eq asm/x0 0x0000000000000000 178 | assert_eq asm/x9 0x0000000000000000 179 | assert_eq asm/x28 0x0000000000000000 180 | 181 | # mov x0, 042; mov x9, 42; mov x28, 0x42 182 | ASM_CODE="400480d2490580d25c0880d2" 183 | 184 | # For some reason if the `xxd > /d/art/asm/asm` write fails, the exit code is 185 | # still zero. This doesn't happen for `cat`, so we write to a temporary file and 186 | # `cat` that into the `asm` file 187 | echo $ASM_CODE | xxd -r -p > /tmp/asm_code 188 | 189 | cat /tmp/asm_code > "${MODULE_DIR}/asm/asm" 190 | 191 | assert_eq asm/x0 0x0000000000000022 192 | assert_eq asm/x9 0x000000000000002a 193 | assert_eq asm/x28 0x0000000000000042 194 | 195 | # Verify zero and multi-CPU asm writes fails 196 | echo 0 > "${MODULE_DIR}/asm/cpumask" 197 | cat /tmp/asm_code > "${MODULE_DIR}/asm/asm" && fail 198 | 199 | echo 0x3 > "${MODULE_DIR}/asm/cpumask" 200 | cat /tmp/asm_code > "${MODULE_DIR}/asm/asm" && fail 201 | 202 | log "Test msr" 203 | 204 | # Set register by writing in individual fields 205 | echo 3 > "${MODULE_DIR}/msr/op0" 206 | echo 1 > "${MODULE_DIR}/msr/op1" 207 | echo 1 > "${MODULE_DIR}/msr/CRn" 208 | echo 1 > "${MODULE_DIR}/msr/CRm" 209 | echo 1 > "${MODULE_DIR}/msr/op2" 210 | assert_eq msr/regname s3_1_c1_c1_1 211 | 212 | # Set register by writing uppercase and lowercase common names 213 | echo sctlr_el1 > "${MODULE_DIR}/msr/regname" 214 | assert_eq msr/regname s3_0_c1_c0_0 215 | 216 | assert_eq msr/op0 3 217 | assert_eq msr/op1 0 218 | assert_eq msr/CRn 1 219 | assert_eq msr/CRm 0 220 | assert_eq msr/op2 0 221 | 222 | echo SCTLR_EL1 > "${MODULE_DIR}/msr/regname" 223 | assert_eq msr/regname s3_0_c1_c0_0 224 | 225 | # For some reason, EPAN is not enabled for android13-5.10-lts kernels 226 | if [[ $ACK == 1 ]] && [[ $KERNEL_VERSION == 5.10.* ]]; then 227 | # Enabled flags: UCI SPAN TSCXT NTWE UCT DZE I SED SA0 SA C M 228 | SCTLR_EL1_VAL=0x34f4d91d 229 | else 230 | # Enabled flags: EPAN UCI SPAN TSCXT NTWE UCT DZE I SED SA0 SA C M 231 | SCTLR_EL1_VAL=0x200000034f4d91d 232 | fi 233 | 234 | # Verify register value 235 | assert_eq msr/msr $SCTLR_EL1_VAL 236 | 237 | # Verify zero and multi-CPU reads fails 238 | echo 0 > "${MODULE_DIR}/msr/cpumask" 239 | cat "${MODULE_DIR}/msr/msr" && fail 240 | 241 | echo 0x3 > "${MODULE_DIR}/msr/cpumask" 242 | cat "${MODULE_DIR}/msr/msr" && fail 243 | 244 | # Set cpumask back to its original value 245 | echo 0x1 > "${MODULE_DIR}/msr/cpumask" 246 | 247 | # Disable EPAN and SPAN 248 | echo 0x3474d91d > "${MODULE_DIR}/msr/msr" 249 | assert_eq msr/msr 0x3474d91d 250 | 251 | # The original value should still be set on a different CPU 252 | echo 0x2 > "${MODULE_DIR}/msr/cpumask" 253 | assert_eq msr/msr $SCTLR_EL1_VAL 254 | else 255 | log "Skipping asm test" 256 | log "Skipping msr test" 257 | fi 258 | 259 | echo -e "${GREEN}[+] All tests passed ${NC}" 260 | --------------------------------------------------------------------------------