├── .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 |
--------------------------------------------------------------------------------