├── .github └── workflows │ └── build.yaml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── capabilities.c ├── cpu_stealer.c ├── disk.c ├── dump.c ├── ebpf └── mem.ebpf.c ├── lemon.c ├── lemon.h ├── mem.c └── net.c /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | [push, pull_request] 5 | 6 | jobs: 7 | build: 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [ubuntu-24.04-arm, ubuntu-24.04] 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v3 16 | 17 | - name: Install dependencies from apt 18 | run: | 19 | sudo apt update 20 | sudo apt install -y make llvm libbpf-dev libcap-dev linux-tools-generic 21 | 22 | - name: Compile with Make 23 | run: | 24 | make CORE=1 static 25 | 26 | - name: Upload x64 binary 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: lemon.x86_64 30 | path: "lemon.x86_64" 31 | continue-on-error: true 32 | 33 | - name: Upload ARM64 binary 34 | uses: actions/upload-artifact@v4 35 | with: 36 | name: lemon.aarch64 37 | path: "lemon.aarch64" 38 | continue-on-error: true 39 | 40 | run: 41 | needs: build 42 | runs-on: ${{ matrix.os }} 43 | strategy: 44 | matrix: 45 | os: [ubuntu-24.04-arm, ubuntu-24.04] 46 | steps: 47 | - name: Download artifacts 48 | uses: actions/download-artifact@v4 49 | - name: Run dump to disk 50 | run: | 51 | sudo chmod +x ./lemon.$(uname -m)/lemon.$(uname -m) 52 | sudo ./lemon.$(uname -m)/lemon.$(uname -m) -d /dev/null 53 | # TODO build and run on NO CORE 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vmlinux.h 2 | -------------------------------------------------------------------------------- /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 | # Optional build flags (can be overridden via command line) 2 | STATIC ?= # Default is not static 3 | CORE ?= 1 # Default to CORE=1, can be disabled via CORE=0 4 | 5 | # System and architecture detection 6 | ARCH := $(shell uname -m) 7 | BPFTOOL := bpftool 8 | 9 | # Detect architecture for eBPF 10 | ifeq ($(ARCH), x86_64) 11 | TARGET_ARCH := __TARGET_ARCH_x86 12 | else ifeq ($(ARCH), aarch64) 13 | TARGET_ARCH := __TARGET_ARCH_arm64 14 | else 15 | $(error Unsupported architecture: $(ARCH)) 16 | endif 17 | 18 | # Define compiler and flags 19 | CLANG := clang 20 | CFLAGS := -Wall -O2 -D$(TARGET_ARCH) 21 | LDFLAGS := -lbpf -lelf -lz -lzstd -lcap 22 | 23 | # Conditional flags 24 | ifeq ($(CORE), 1) 25 | CFLAGS += -DCORE 26 | BPF_CORE_FLAG := -DCORE 27 | endif 28 | 29 | ifeq ($(STATIC), 1) 30 | LDFLAGS += -static 31 | endif 32 | 33 | # Files 34 | LOADER_SRCS := lemon.c cpu_stealer.c mem.c dump.c disk.c net.c capabilities.c 35 | LOADER_BIN := lemon.$(ARCH) 36 | BPF_SRC := ebpf/mem.ebpf.c 37 | BPF_OBJ := ebpf/mem.ebpf.o 38 | BPF_SKEL := ebpf/mem.ebpf.skel.h 39 | 40 | # Default target: If CORE is enabled, make vmlinux first, then compile eBPF and loader 41 | all: clean $(if $(filter 1,$(CORE)), vmlinux) $(BPF_OBJ) $(LOADER_BIN) 42 | 43 | # Build eBPF object and generate skeleton 44 | $(BPF_OBJ): $(BPF_SRC) 45 | $(CLANG) -target bpf -D$(TARGET_ARCH) $(BPF_CORE_FLAG) -I/usr/include/linux -I/usr/include/$(ARCH)-linux-gnu \ 46 | -Wall -O2 -g -c $< -o $@ 47 | llvm-strip -g $(BPF_OBJ) 48 | $(BPFTOOL) gen skeleton $(BPF_OBJ) > $(BPF_SKEL) 49 | 50 | # Build the loader (compiled before eBPF program) 51 | $(LOADER_BIN): $(LOADER_SRCS) 52 | $(CLANG) $(CFLAGS) $^ -o $@ $(LDFLAGS) 53 | objcopy --strip-all --keep-symbol=read_kernel_memory $@ $@_strip 54 | mv $@_strip $@ 55 | 56 | # Dump vmlinux BTF as C header (only if CORE is enabled) 57 | vmlinux: 58 | $(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h 59 | 60 | # Static target for convenience 61 | static: 62 | $(MAKE) STATIC=1 63 | 64 | # Clean 65 | clean: 66 | rm -f $(LOADER_BIN) $(BPF_OBJ) $(BPF_SKEL) vmlinux.h 67 | 68 | .PHONY: all static clean vmlinux 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LEMON - An eBPF Memory Dump Tool for x64 and ARM64 Linux and Android 2 | 3 | LEMON is a Linux and Android memory dump tool that utilizes eBPF to capture the entire physical memory of a system and save it in LiME format, compatible with forensic tools such as Volatility 3. 4 | 5 | LEMON is available as a precompiled static binary for x64 and ARM64, leveraging a CO-RE (Compile Once, Run Everywhere) eBPF program. This allows analysts to dump system memory without compiling anything on the target machine, checking for specific compatibility with installed libraries and kernel versions, and without requiring kernel headers. It is particularly useful in scenarios where loading kernel modules is not possible (e.g., due to Secure Boot) or when `{/proc, /dev}/kcore` is unavailable. 6 | 7 | ## Usage 8 | 9 | Copy the `lemon` binary to the target machine and initiate a memory dump on disk with: 10 | 11 | ```sh 12 | ./lemon.ARCH -d memory_on_disk.dump 13 | ``` 14 | 15 | For a network dump instead use: 16 | 17 | ```sh 18 | ./lemon.ARCH -n TARGET_IP -p TARGET_PORT 19 | ``` 20 | while on the target machine 21 | ```sh 22 | nc -l -p TARGET_PORT > memory_by_net.dump 23 | ``` 24 | 25 | This generates a `memory.dump` file in LiME format, containing all physical memory pages. Since running eBPF programs typically requires root privileges, LEMON must be executed as `root` or with an appropriate `sudo` configuration. 26 | 27 | ## Build 28 | 29 | Precompiled static binaries are available in this repository (check the Github actions tab). Analysts can also compile LEMON themselves, either dynamically or statically. The dynamic version requires the presence of `libbpf`, `libz`, `libelf`, and `libzstd` on the target machine, whereas the static version has no external dependencies. Note that the build machine **MUST** have the same CPU architecture as the target. 30 | 31 | ### Dependencies 32 | 33 | To build LEMON, install the necessary dependencies on the analyst's machine. The following command sets up all required packages on an Ubuntu 24.04 system: 34 | 35 | ```sh 36 | sudo apt install -y git make clang llvm libbpf-dev libcap-dev linux-tools-generic 37 | ``` 38 | 39 | Other distributions provide equivalent packages, which at minimum allow compiling the dynamic version via the system package manager. 40 | 41 | ### Build Procedure 42 | 43 | 1. **Clone the repository:** 44 | 45 | ```sh 46 | git clone git@github.com:eurecom-s3/lemon.git && cd lemon 47 | ``` 48 | 49 | 2. **Generate a valid **`vmlinux.h`** file (only for CO-RE builds):** 50 | 51 | Copy a valid `vmlinux.h` file into `lemon/` or generate one with: 52 | 53 | ```sh 54 | make vmlinux 55 | ``` 56 | 57 | 3. **Compile:** 58 | 59 | - Dynamic binary (set CORE=0 for non CO-RE binaries): 60 | ```sh 61 | make CORE=1 62 | ``` 63 | - Static binary: 64 | ```sh 65 | make CORE=1 static 66 | ``` 67 | 68 | ## Limitations 69 | 70 | - The kernel must support eBPF (obviously!). 71 | - Kernel lockdown must not be in confidentiality mode (or must allow `bpf_probe_read_kernel()`). 72 | 73 | ## TODO 74 | 75 | - [X] Support non CO-RE kernels ([this library](https://github.com/eunomia-bpf/bpf-compatible) might help) 76 | - [X] Insert checks on kernel versions and ```CONFIG_``` kernel options to extend support 77 | - [X] Implement network dump (TCP) 78 | - [X] Implement dump with reduced granule if page fail to be read 79 | - [X] Introduce support for kernels that do not have uprobes 80 | - [ ] Support other CPU architectures (x32, ARM32, MIPS, PowerPC, POWER, RISC-V) 81 | - [ ] Use of `_stext` in x64 to bypass missing `CONFIG_KALLSYMS_ALL` 82 | - [ ] Bruteforce scanning (?) for page containing same data of `_stext` page in ARM64 to bypass missing `CONFIG_KALLSYMS_ALL` 83 | - [ ] Implement network dump (UDP) 84 | 85 | ## Notes 86 | 87 | ### eBPF cronology 88 | - Introduction of eBPF: 3.15 89 | - Introduction of Array maps 3.19 90 | - Introduction of kProbe/uProbe support 4.1 91 | - Introduction of tracepoint support (syscalls tracing) 4.7 92 | - Introduction of XDP 4.8 93 | - Android 9 support eBPF: 4.9 94 | - Introduction of BTF 4.18 95 | - Introduction mmap() support for array maps 5.5 96 | - !!! Introduction of read_kernel() 5.5 <==== Minimum Lemon target version 97 | - Introduction of ring_buffer 5.8 98 | - Android 13 support BTF 5.15 99 | - Introduction of SYSCALL program type 5.15 100 | - Introduction of kallsyms() in ebpf 5.16 101 | -------------------------------------------------------------------------------- /capabilities.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "lemon.h" 6 | 7 | /* Minimal capabilities required by LEMON: 8 | * 9 | * CAP_BPF -> needed to load the eBPF components (on older kernels is included in CAP_SYS_ADMIN) 10 | * CAP_PERFMON -> needed to change RLIMIT_MEMLOCK, from libbpf, bypassable?, (on older kernel is included in CAP_SYS_ADMIN) 11 | * CAP_SYSLOG -> read addresses from /proc/kallsyms 12 | * 13 | * Capabilities needed in some cases 14 | * 15 | * CAP_SYS_ADMIN -> needed to change /proc/sys/kernel/kptr_restrict from 2 to 0 (not needed if set to 1 or 0), needed to access 16 | * /proc/iomem if CONFIG_KALLSYMS_ALL is not active and so iomem_resources is not available, needed on old kernels 17 | * CAP_DAC_OVERRIDE -> needed to create a brand new dump file in the case the directory is not owned by the user running LEMON 18 | */ 19 | 20 | 21 | /* 22 | * check_capability() - Checks if the current process has a specific effective capability 23 | * @cap: The capability to check (e.g., CAP_SYS_PTRACE, CAP_NET_ADMIN) 24 | * 25 | * Retrieves the current process's capabilities using libcap, checks whether the given 26 | * capability is present in the effective set, and returns the result. 27 | * Returns 1 if the capability is set, 0 if not set, and a negative errno value on error. 28 | */ 29 | int check_capability(const cap_value_t cap) { 30 | cap_t caps; 31 | cap_flag_value_t cap_flag; 32 | int ret = 0; 33 | 34 | /* Get process capabilities */ 35 | caps = cap_get_proc(); 36 | if (caps == NULL) { 37 | perror("Fail to get current capabilities"); 38 | return -errno; 39 | } 40 | 41 | 42 | /* Get effective capabilities */ 43 | if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &cap_flag) == -1) { 44 | perror("Fail to get effective capabilities"); 45 | ret = -errno; 46 | goto cleanup; 47 | } 48 | 49 | cleanup: 50 | if((ret = cap_free(caps))) { 51 | perror("Fail to free capabilities struct"); 52 | return -errno; 53 | }; 54 | 55 | if(!ret) ret = (cap_flag == CAP_SET); 56 | return ret; 57 | } 58 | -------------------------------------------------------------------------------- /cpu_stealer.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "lemon.h" 15 | 16 | static int nprocs = 0; 17 | static pthread_t *threads = NULL; 18 | static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; 19 | 20 | typedef struct { 21 | int cpu; 22 | int priority; 23 | } thread_params; 24 | 25 | /* 26 | * Wrapper around sched_setscheduler that sets the scheduler to SCHED_FIFO, 27 | * sets the priority to the desired one and prints on error. 28 | * 29 | * @param priority: The priority to set for the current process. 30 | * @return 0 on success, -1 on failure. 31 | */ 32 | static int set_priority(const int priority) { 33 | const struct sched_param sparam = { 34 | .sched_priority = priority 35 | }; 36 | 37 | if(sched_setscheduler(0, SCHED_FIFO, &sparam) == -1) { 38 | perror("Failed to set realtime scheduler class and priority"); 39 | return -1; 40 | } 41 | return 0; 42 | } 43 | 44 | /* 45 | * @brief Entry point for each CPU stealer thread. 46 | * 47 | * Each thread tries to acquire a global mutex in a busy-loop using 48 | * pthread_mutex_trylock(). Once it succeeds, it immediately unlocks it and exits. 49 | * The purpose is to keep these threads active and scheduled on CPU cores. 50 | * 51 | * @param arg: thread_params* The priority and CPU to set for the thread. 52 | */ 53 | static void* thread_function(void *arg) { 54 | const thread_params *tp = (const thread_params*) arg; 55 | cpu_set_t cpuset; 56 | int ret = EBUSY; 57 | 58 | set_priority(tp->priority); 59 | 60 | /* Set the CPU affinity for this thread */ 61 | CPU_ZERO(&cpuset); 62 | CPU_SET(tp->cpu, &cpuset); 63 | if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) == -1) { 64 | perror("Failed to pin CPU core for cpu stealer thread"); 65 | } 66 | 67 | /* Infinite loop waiting fo the global mutex to be unlocked */ 68 | while (ret == EBUSY) { 69 | ret = pthread_mutex_trylock(&mut); 70 | } 71 | 72 | /* Unlock the mutex if it was succesfully acquired */ 73 | if (!ret) { 74 | pthread_mutex_unlock(&mut); 75 | } 76 | 77 | free((void*)tp); 78 | return NULL; 79 | } 80 | 81 | /* 82 | * @brief Wait for a specific number of CPU stealer threads to terminate. 83 | * 84 | * Unlocks the global mutex to let all threads exit their busy loops. 85 | * 86 | * @param n: Number of threads to join. 87 | */ 88 | static int join_n_cpu_stealers(int n) { 89 | if(!threads) return -1; 90 | 91 | /* Unlock the global mutex */ 92 | if(pthread_mutex_unlock(&mut)) { 93 | WARN("Fail to unlock mutex\n"); 94 | return -1; 95 | } 96 | 97 | /* Join all the processes */ 98 | for (int i = 0; i < n; i++) { 99 | const int ret = pthread_join(threads[i], NULL); 100 | if (ret) return -1; 101 | } 102 | 103 | free(threads); 104 | threads = NULL; 105 | return 0; 106 | } 107 | 108 | /* 109 | * @brief Create and run nprocs threads to occupy CPU cores. 110 | * 111 | * Allocates and launches nprocs threads that will spin on a locked mutex. 112 | * This prevents the OS from scheduling other tasks on those cores. 113 | * If thread creation fails midway, joins already created threads to clean up. 114 | * 115 | * @param priority: The scheduling priority to set for the stealers threads. 116 | */ 117 | static int launch_cpu_stealers(const int priority) { 118 | int ret = 0; 119 | 120 | assert(threads == NULL); 121 | 122 | /* Allocate thread structs */ 123 | nprocs = get_nprocs(); 124 | threads = (pthread_t *)malloc((nprocs) * sizeof(pthread_t)); 125 | if(!threads) { 126 | perror("Failed to allocate pthread_t structs"); 127 | return errno; 128 | } 129 | 130 | /* Lock the global mutex */ 131 | if((ret = pthread_mutex_lock(&mut))) { 132 | fprintf(stderr, "Fail to lock mutex\n"); 133 | return ret; 134 | } 135 | 136 | /* Try to create nproc - 1 threads (the remaining one is the dumper)*/ 137 | for (int i = 0; i < nprocs; i++) { 138 | thread_params *tp = (thread_params*) malloc(sizeof(thread_params)); 139 | tp->cpu = i; 140 | tp->priority = priority; 141 | ret = pthread_create(&threads[i], NULL, thread_function, (void*) tp); 142 | if (ret) { 143 | /* Try to join the already created threads and silently ignore errors */ 144 | join_n_cpu_stealers(i); 145 | return ret; 146 | } 147 | } 148 | 149 | return 0; 150 | } 151 | 152 | /* 153 | * @brief Set current process priority to highest real-time priority and launch CPU stealers. 154 | * 155 | * This function attempts to increase the scheduling priority of the calling process 156 | * by assigning it to the `SCHED_FIFO` real-time scheduling policy with the maximum 157 | * allowable priority. 158 | * 159 | * Allocates and launches nprocs threads that will spin on a locked mutex. 160 | * This prevents the OS from scheduling other tasks on those cores. 161 | * If thread creation fails midway, joins already created threads to clean up. 162 | * 163 | * @return 0 on success, or -1 on failure. 164 | */ 165 | int increase_priority_and_launch_stealers() { 166 | const int max_sched = sched_get_priority_max(SCHED_FIFO); 167 | if(max_sched == -1) { 168 | perror("Failed to obtain max priority value for realtime process class"); 169 | return -1; 170 | } 171 | 172 | if(set_priority(max_sched) == -1) { 173 | return -1; 174 | } 175 | 176 | launch_cpu_stealers(max_sched - 1); 177 | 178 | return 0; 179 | } 180 | 181 | /* 182 | * @brief Join all CPU stealer threads. 183 | * 184 | * Wrapper around join_n_cpu_stealers() that unlocks all of them. 185 | */ 186 | int join_cpu_stealers() { 187 | return join_n_cpu_stealers(nprocs); 188 | } 189 | -------------------------------------------------------------------------------- /disk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "lemon.h" 8 | 9 | extern int dump(const struct options *restrict opts, const struct ram_regions *restrict ram_regions, int (*write_f)(void *restrict, const void *restrict, const unsigned long), void *restrict args); 10 | extern int check_capability(const cap_value_t cap); 11 | 12 | /* 13 | * write_on_disk() - Writes a memory chunks to the disk file descriptor 14 | * @args: Pointer to the file descriptor 15 | * @data: Pointer to the buffer to be written 16 | * @size: Number of bytes to write 17 | */ 18 | static int write_on_disk(void *restrict args, const void *restrict data, const unsigned long size) { 19 | unsigned long r = 0; 20 | unsigned long total = 0; 21 | 22 | while(total < size) { 23 | r = write(*((int *)args), data + total, size - total); 24 | if(r == -1) { 25 | if(errno == EINTR) continue; 26 | perror("Fail to write on dump file"); 27 | return errno; 28 | } 29 | 30 | total += r; 31 | } 32 | 33 | return 0; 34 | } 35 | 36 | /* 37 | * dump_on_disk() - Writes a memory dump to a file on disk 38 | * @opts: Dumping options, including output file path 39 | * @ram_regions: List of RAM regions to be dumped 40 | * 41 | * Opens the specified file for writing, then performs the memory dump using the 42 | * generic dump function. Ensures data is flushed and file descriptor is closed. 43 | */ 44 | int dump_on_disk(const struct options *restrict opts, const struct ram_regions *restrict ram_regions) { 45 | int fd; 46 | int ret = 0; 47 | 48 | /* Check CAP_DAC_OVERRIDE, creation of the file can fail without it */ 49 | if(check_capability(CAP_DAC_OVERRIDE) <= 0) { 50 | WARN("LEMON does not have CAP_DAC_OVERRIDE, it may fail in dump file creation\n"); 51 | } 52 | 53 | /* Open dump file in write mode */ 54 | fd = open(opts->path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 55 | if(fd < 0) { 56 | perror("Failed to open dump file for writing"); 57 | return errno; 58 | } 59 | 60 | /* Dump! */ 61 | ret = dump(opts, ram_regions, write_on_disk, (void *)&fd); 62 | 63 | if(fsync(fd)) { 64 | switch (errno) { 65 | case EINVAL: 66 | /* fd is bound to a special file (e.g., a pipe, FIFO, or 67 | * socket) which does not support synchronization. 68 | */ 69 | break; 70 | default: 71 | perror("Failed to finalize writes on dump file"); 72 | ret = errno; 73 | } 74 | } 75 | 76 | if(close(fd)) { 77 | perror("Failed to close the dump file"); 78 | ret = errno; 79 | } 80 | 81 | return ret; 82 | } 83 | -------------------------------------------------------------------------------- /dump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "lemon.h" 7 | 8 | extern int read_kernel_memory(const uintptr_t addr, const size_t size, unsigned char **restrict data); 9 | extern uintptr_t phys_to_virt(const uintptr_t phy_addr); 10 | 11 | 12 | /* 13 | * dump_region() - Reads a physical memory region and writes it to a destination in chunks. 14 | * @region_start: Start of the physical memory region to be dumped. 15 | * @region_end: End of the physical memory region to be dumped. 16 | * @granule: Preferred chunk size to use when reading memory. 17 | * @write_f: Callback function used to write the read memory somewhere (e.g., file, socket). 18 | * @args: Argument passed to the write function (e.g., file descriptor). 19 | * @fatal: If true, aborts the dump on any read error; otherwise, tries to recover or zero-fill. 20 | * 21 | * This function attempts to read memory between region_start and region_end in chunks 22 | * defined by `granule`. If a read fails and `fatal` is false, it retries with the smallest 23 | * allowed granule (system page size). If that also fails, it writes zero-filled data instead. 24 | */ 25 | static int dump_region(const uintptr_t region_start, const uintptr_t region_end, unsigned int granule, int (*write_f)(void *restrict, const void *restrict, const unsigned long), void *restrict args, bool fatal) { 26 | int ret = 0; 27 | const unsigned int min_granule = getpagesize(); 28 | size_t chunk_size; 29 | uintptr_t chunk_start, chunk_end; 30 | unsigned char *read_data = NULL; 31 | 32 | chunk_start = region_start; 33 | while (chunk_start <= region_end) { 34 | /* Read memory region in chunks of maximum granule bytes */ 35 | chunk_end = (region_end - chunk_start + 1 > granule) ? chunk_start + granule - 1 : region_end; 36 | chunk_size = chunk_end - chunk_start + 1; 37 | 38 | if ((ret = read_kernel_memory(phys_to_virt(chunk_start), chunk_size, &read_data))) { 39 | fprintf(stderr, "Error reading kernel physical memory. Physical address: 0x%lx, size: 0x%zx. Error code: %d Maybe KFENCE area ?\n", chunk_start, chunk_size, ret); 40 | 41 | /* Error reading memory, abort the dump or try with minimum granule of the system */ 42 | if(fatal) return ret; 43 | 44 | if(granule != min_granule) { 45 | fprintf(stderr, "Try to read it using the minimum page size available\n"); 46 | if((ret = dump_region(chunk_start, chunk_end, min_granule, write_f, args, fatal))) return ret; 47 | goto next_iter; 48 | } 49 | 50 | else memset(read_data, 0x00, chunk_size); /* We are already at the minimum granule, replace with 0x00 */ 51 | } 52 | 53 | /* Save the chunk */ 54 | if ((ret = write_f(args, read_data, chunk_size)) < 0) { 55 | fprintf(stderr, "Error saving dump data\n"); 56 | return ret; 57 | } 58 | 59 | /* Continue to next chunk */ 60 | next_iter: 61 | chunk_start = chunk_end + 1; 62 | } 63 | 64 | return ret; 65 | } 66 | 67 | /* 68 | * dump() - Dumps the contents of system RAM using eBPF-assisted memory reading 69 | * @opts: Dumping options 70 | * @ram_regions: List of RAM regions to be dumped 71 | * @write_f: Callback function used to write data to the output 72 | * @args: User-provided context passed to the write callback 73 | * 74 | * Iterates through each system RAM region, writes a LiME header, reads the memory region 75 | * in chunks using eBPF, and writes the contents to the specified output. 76 | * On read failures, either aborts or fills the chunk with 0xFF, based on fatal mode. 77 | */ 78 | int dump(const struct options *restrict opts, const struct ram_regions *restrict ram_regions, int (*write_f)(void *restrict, const void *restrict, const unsigned long), void *restrict args) { 79 | int ret = 0; 80 | 81 | /* Loop through the system RAM ranges, read the memory ranges and write them on file */ 82 | for (size_t i = 0; i < ram_regions->num_regions; i++) 83 | { 84 | const uintptr_t region_pstart = ram_regions->regions[i].start; 85 | const uintptr_t region_pend = ram_regions->regions[i].end; 86 | 87 | printf("Dumping Range: 0x%lx-0x%lx\n", region_pstart, region_pend); 88 | 89 | /* Write the LiMe header for that RAM region to the file (only if not RAW format)*/ 90 | if(!opts->raw) { 91 | const lime_header header = { 92 | .magic = 0x4C694D45, 93 | .version = 1, 94 | .s_addr = region_pstart, 95 | .e_addr = region_pend, 96 | .reserved = {0}, 97 | }; 98 | 99 | if ((ret = write_f(args, &header, sizeof(lime_header))) < 0) { 100 | fprintf(stderr, "Error saving LiME header\n"); 101 | return ret; 102 | } 103 | } 104 | 105 | /* Dump the memory range */ 106 | if((ret = dump_region(region_pstart, region_pend, HUGE_PAGE_SIZE, write_f, args, opts->fatal))) return ret; 107 | } 108 | 109 | return ret; 110 | } 111 | -------------------------------------------------------------------------------- /ebpf/mem.ebpf.c: -------------------------------------------------------------------------------- 1 | #ifdef CORE 2 | #include "../vmlinux.h" 3 | #include 4 | 5 | #ifndef ETH_P_IP 6 | #define ETH_P_IP 0x0800 7 | #endif 8 | 9 | #ifndef IPPROTO_UDP 10 | #define IPPROTO_UDP 17 11 | #endif 12 | #else 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "../lemon.h" 26 | 27 | /* Mapping used to pass the memory content to userspace */ 28 | struct { 29 | __uint(type, BPF_MAP_TYPE_ARRAY); 30 | __type(key, int); 31 | __type(value, struct read_mem_result); 32 | __uint(max_entries, 1); 33 | __uint(map_flags, BPF_F_MMAPABLE | BPF_F_NUMA_NODE); 34 | } read_mem_array_map SEC(".maps"); 35 | 36 | /* VA bits for ARM64 37 | * 38 | * Try to get the va bits from the kernel config. 39 | * Otherwise we try to compute the actual va bits (runtime) in userspace and inject it here on eBPF 40 | * program open. 41 | */ 42 | #ifdef __TARGET_ARCH_arm64 43 | extern unsigned long CONFIG_ARM64_VA_BITS __kconfig __weak; 44 | unsigned long runtime_va_bits SEC(".data"); 45 | #endif 46 | 47 | /* 48 | * read_memory() - Read kernel memory and save the content in the eBPF map 49 | * 50 | * Attempts to read a specified chunk of kernel memory starting from a given address, 51 | * validating the request against architecture-specific constraints and dump size limits. 52 | * The memory contents are copied into a BPF map for retrieval from userspace. 53 | * Returns 0 on success or parameter validation failure, and -1 if the BPF map is unavailable. 54 | * Return also a specific error code in the map. 55 | */ 56 | static int inline read_memory(__u64 address, const __u64 dump_size) { 57 | /* Get the map in which save the memory content to pass to userspace */ 58 | int key = 0; 59 | struct read_mem_result *read_mem_result = bpf_map_lookup_elem(&read_mem_array_map, &key); 60 | if (!read_mem_result) { 61 | return -1; // We cannot catch this error... 62 | } 63 | 64 | /* Validate dump size */ 65 | if(dump_size > HUGE_PAGE_SIZE) { 66 | read_mem_result->ret_code = -EINVAL; 67 | return 0; 68 | } 69 | 70 | /* ARM64 phys to virt offset depends also on virtual addresses number of bits */ 71 | #ifdef __TARGET_ARCH_arm64 72 | if (CONFIG_ARM64_VA_BITS != 0) { 73 | address |= 0xffffffffffffffff << CONFIG_ARM64_VA_BITS; 74 | } else { 75 | address |= 0xffffffffffffffff << runtime_va_bits; 76 | } 77 | #endif 78 | 79 | /* Ensure parameters are sanitized (some checks are needed to bypass eBPF type checking) */ 80 | #ifdef __TARGET_ARCH_x86 81 | if (address < 0 || address < 0xff00000000000000){ 82 | #elif __TARGET_ARCH_arm64 83 | if (address < 0 || address < 0xfff0000000000000){ 84 | #else 85 | if(true){ 86 | #endif 87 | 88 | read_mem_result->ret_code = -EINVAL; 89 | return 0; 90 | } 91 | 92 | if (dump_size < 0 || dump_size > HUGE_PAGE_SIZE) { 93 | read_mem_result->ret_code = -EINVAL; 94 | return 0; 95 | } 96 | 97 | /* Read the kernel memory */ 98 | #ifdef CORE 99 | read_mem_result->ret_code = bpf_core_read((void *)(&read_mem_result->buf), (__u32)dump_size, (void *)address); 100 | #else 101 | read_mem_result->ret_code = bpf_probe_read_kernel((void *)(&read_mem_result->buf), (__u32)dump_size, (void *)address); 102 | #endif 103 | 104 | return 0; 105 | } 106 | 107 | /* 108 | * read_kernel_memory_uprobe() - Read kernel memory using a Uprobe trigger 109 | * 110 | * Uprobe handler for extracting kernel memory from userspace-triggered instrumentation. 111 | * Retrieves the target address and dump size from the probed function’s arguments, 112 | */ 113 | SEC("uprobe//proc/self/exe:read_kernel_memory") 114 | int read_kernel_memory_uprobe(struct pt_regs *ctx) 115 | { 116 | /* Extract the first two arguments of the function */ 117 | #ifdef CORE 118 | __u64 address = (__u64)(PT_REGS_PARM1_CORE(ctx)); 119 | __u64 dump_size = (__u64)(PT_REGS_PARM2_CORE(ctx)); 120 | #else 121 | __u64 address = (__u64)(PT_REGS_PARM1(ctx)); 122 | __u64 dump_size = (__u64)(PT_REGS_PARM2(ctx)); 123 | #endif 124 | 125 | /* Read memory! */ 126 | return read_memory(address, dump_size); 127 | } 128 | 129 | #define TRIGGER_PACKET_PORT 9999 130 | #define TRIGGER_PACKET_ADDR 0x7f000001 /* 127.0.0.1 */ 131 | 132 | /* 133 | * read_kernel_memory_xdp() - XDP program to trigger a kernel memory read 134 | * @ctx: Pointer to the XDP context containing packet metadata 135 | * 136 | * Parses a UDP packet containing address and size parameters used to 137 | * perform a kernel memory read. Expects UDP packets to 127.0.0.1:9999. 138 | */ 139 | SEC("xdp") 140 | int read_kernel_memory_xdp(struct xdp_md* ctx) { 141 | void* data = (void*)(long)ctx->data; 142 | void* data_end = (void*)(long)ctx->data_end; 143 | 144 | /* Validate Ethernet header */ 145 | struct ethhdr *eth = data; 146 | if ((void*)(eth + 1) > data_end) { 147 | return XDP_DROP; 148 | } 149 | 150 | /* Check if this is an IP packet */ 151 | if (eth->h_proto != bpf_htons(ETH_P_IP)) { 152 | return XDP_PASS; 153 | } 154 | 155 | /* Validate and parse IP header */ 156 | struct iphdr *ip = (struct iphdr*)(eth + 1); 157 | if ((void*)(ip + 1) > data_end) { 158 | return XDP_DROP; 159 | } 160 | 161 | /* Check if this is a UDP packet */ 162 | if (ip->protocol != IPPROTO_UDP) { 163 | return XDP_PASS; 164 | } 165 | 166 | /* Check if source/dest is loopback */ 167 | if (ip->saddr != bpf_htonl(TRIGGER_PACKET_ADDR) || ip->daddr != bpf_htonl(TRIGGER_PACKET_ADDR)) { 168 | return XDP_PASS; 169 | } 170 | 171 | /* Validate IP header length */ 172 | if (ip->ihl < 5) { 173 | return XDP_DROP; 174 | } 175 | 176 | /* Validate UDP header */ 177 | struct udphdr *udp = (struct udphdr*)((char*)ip + (ip->ihl * 4)); 178 | if ((void*)(udp + 1) > data_end) { 179 | return XDP_DROP; 180 | } 181 | 182 | /* Check destination port */ 183 | if (udp->dest != bpf_htons(TRIGGER_PACKET_PORT)) { 184 | return XDP_PASS; 185 | } 186 | 187 | /* Validate payload */ 188 | struct read_mem_args *args = (struct read_mem_args*)(udp + 1); 189 | if ((void*)(args + 1) > data_end) { 190 | return XDP_DROP; 191 | } 192 | 193 | __u64 address = args->addr; 194 | __u64 dump_size = args->size; 195 | 196 | /* Read memory! */ 197 | if (read_memory(address, dump_size)) { 198 | return XDP_DROP; 199 | } 200 | 201 | return XDP_PASS; 202 | } 203 | 204 | char _license[] SEC("license") = "GPL"; 205 | -------------------------------------------------------------------------------- /lemon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "lemon.h" 11 | 12 | extern int load_ebpf_mem_progs(void); 13 | extern int init_translation(struct ram_regions *restrict ram_regions); 14 | extern int dump_on_disk(const struct options *restrict opts, const struct ram_regions *restrict ram_regions); 15 | extern int dump_on_net(const struct options *restrict opts, const struct ram_regions *restrict ram_regions); 16 | extern int increase_priority_and_launch_stealers(void); 17 | extern int join_cpu_stealers(void); 18 | extern int check_capability(const cap_value_t cap); 19 | extern int toggle_kptr(void); 20 | extern void cleanup_mem_ebpf(void); 21 | 22 | /* Constants needed for argparse */ 23 | static const struct argp_option options[] = { 24 | {"disk", 'd', "PATH", 0, "Dump on disk", 0}, 25 | {"network", 'n', "ADDRESS", 0, "Dump through the network", 1}, 26 | {"port", 'p', "PORT", 0, "Specify port number", 1}, 27 | {"udp", 'u', 0, 0, "Use UDP instead of TCP", 1}, 28 | {"realtime", 'r', 0, 0, "Use realtime priority", 2}, 29 | {"fatal", 'f', 0, 0, "Interrupt the dump in case of memory read error", 2}, 30 | {"raw", 'w', 0, 0, "Produce a RAW dump instead of a LiME one", 2}, 31 | {0} 32 | }; 33 | static const char doc[] = "Lemon - An eBPF Memory Dump Tool for x64 and ARM64 Linux and Android"; 34 | 35 | /* 36 | * parse_opt() - Argument parser callback for argp 37 | * @key: Option key 38 | * @arg: Option argument string 39 | * @state: Argp parser state 40 | * 41 | * Parses command-line arguments into the options struct. Validates IP address and port, 42 | * enforces mutual exclusivity between disk and network modes, and ensures required options 43 | * are present based on the selected mode. 44 | * Returns 0 on success or ARGP_ERR_UNKNOWN for unrecognized options. 45 | */ 46 | static error_t parse_opt(int key, char *arg, struct argp_state *state) { 47 | struct options *opts = state->input; 48 | struct in_addr addr; 49 | 50 | switch (key) { 51 | case 'n': 52 | if (inet_pton(AF_INET, arg, &addr) != 1) { 53 | argp_error(state, "Invalid IP address format"); 54 | } 55 | opts->address = addr.s_addr; 56 | opts->network_mode = true; 57 | break; 58 | case 'p': 59 | opts->port = atoi(arg); 60 | if (opts->port <= 0 || opts->port > 65535) { 61 | argp_error(state, "Port must be between 1 and 65535"); 62 | } 63 | break; 64 | case 'd': 65 | opts->disk_mode = true; 66 | opts->path = arg; 67 | break; 68 | case 'r': 69 | opts->realtime = true; 70 | break; 71 | case 'f': 72 | opts->fatal = true; 73 | break; 74 | case 'u': 75 | opts->udp = true; 76 | fprintf(stderr, "To be implemented...\n"); 77 | exit(EXIT_FAILURE); 78 | case 'w': 79 | opts->raw = true; 80 | break; 81 | case ARGP_KEY_END: 82 | /* Validate mutual exclusivity of disk vs net dump */ 83 | if (opts->network_mode && opts->disk_mode) { 84 | argp_error(state, "Disk and network mode are mutually exclusive"); 85 | } 86 | 87 | /* Ensure at least one mode is specified */ 88 | if (!opts->network_mode && !opts->disk_mode) { 89 | argp_error(state, "Either network mode or disk mode must be specified"); 90 | } 91 | break; 92 | default: 93 | return ARGP_ERR_UNKNOWN; 94 | } 95 | return 0; 96 | } 97 | 98 | /* 99 | * check_kernel_version() - Checks if the running Linux kernel version support all the feature requested 100 | * Return: 101 | * 1 if kernel version is valid 102 | * 0 if kernel version does not support all the features 103 | * < 0 on failure 104 | */ 105 | static int check_kernel_version() { 106 | struct utsname buffer; 107 | int major = 0, minor = 0, patch = 0; 108 | 109 | if (uname(&buffer) != 0) { 110 | perror("Fail to get Linux kernel version"); 111 | return -errno; 112 | } 113 | sscanf(buffer.release, "%d.%d.%d", &major, &minor, &patch); 114 | if(errno) { 115 | perror("Fail to parse Linux version"); 116 | return -errno; 117 | } 118 | 119 | return (major > MIN_MAJOR_LINUX) || ((major == MIN_MAJOR_LINUX) && (minor >= MIN_MINOR_LINUX)); 120 | } 121 | 122 | int main(int argc, char **argv) { 123 | struct ram_regions ram_regions; 124 | struct options opts = {0}; 125 | struct argp argp = {options, parse_opt, "", doc}; 126 | int ret = EXIT_SUCCESS; 127 | 128 | /* Check if is running as root */ 129 | if(getuid() != 0) { 130 | WARN("LEMON is not running as root."); 131 | } 132 | 133 | /* Check Linux version */ 134 | if(check_kernel_version() != 1) { 135 | WARN("Detected Linux version is not supported by LEMON. Minimum required version: %d.%d", MIN_MAJOR_LINUX, MIN_MINOR_LINUX); 136 | } 137 | 138 | /* Check if can load eBPF programs */ 139 | if((check_capability(CAP_BPF) <= 0) && (check_capability(CAP_SYS_ADMIN) <= 0)) { 140 | WARN("LEMON does not have CAP_BPF nor CAP_SYS_ADMIN to load the eBPF component"); 141 | } 142 | 143 | /* Check for eBPF support */ 144 | bpf_prog_load(BPF_PROG_TYPE_UNSPEC, NULL, NULL, NULL, 0, NULL); 145 | if(errno == ENOSYS) { 146 | fprintf(stderr, "eBPF not supported by this kernel"); 147 | return EXIT_FAILURE; 148 | } 149 | 150 | #ifdef CORE 151 | /* Check for eBPF CORE support */ 152 | struct btf *vmlinux_btf = btf__load_vmlinux_btf(); 153 | if (!vmlinux_btf) { 154 | fprintf(stderr, "eBPF CO-RE not supported by this kernel. Try to use no CO-RE version."); 155 | return EXIT_FAILURE; 156 | } 157 | btf__free(vmlinux_btf); 158 | #endif 159 | 160 | /* Parse the arguments */ 161 | opts.port = DEFAULT_PORT; 162 | argp_parse(&argp, argc, argv, 0, 0, &opts); 163 | 164 | /* Increase process priority and lauch stealers */ 165 | if(opts.realtime) { 166 | ret = increase_priority_and_launch_stealers(); 167 | if(ret) { 168 | WARN("Failed to increase process priority and launch CPU stealers"); 169 | } 170 | } 171 | 172 | /* Load eBPF progs that read memory */ 173 | if((ret = load_ebpf_mem_progs())) return ret; 174 | 175 | /* Disable kptr_restrict if needed */ 176 | if((ret = toggle_kptr())) return ret; 177 | 178 | /* Determine the memory dumpable regions */ 179 | if((ret = init_translation(&ram_regions))) goto cleanup; 180 | 181 | /* Dump on a file */ 182 | if(opts.disk_mode) { 183 | if((ret = dump_on_disk(&opts, &ram_regions))) goto cleanup; 184 | } 185 | 186 | /* Dump using TCP packets */ 187 | else if(opts.network_mode) { 188 | if((ret = dump_on_net(&opts, &ram_regions))) goto cleanup; 189 | } 190 | 191 | /* Cleanup: close BPF object */ 192 | cleanup: 193 | cleanup_mem_ebpf(); 194 | join_cpu_stealers(); 195 | 196 | /* Restore kptr_restrict if needed */ 197 | if((ret = toggle_kptr())) return ret; 198 | 199 | return ret; // TODO rework all the ret values handling 200 | } 201 | -------------------------------------------------------------------------------- /lemon.h: -------------------------------------------------------------------------------- 1 | #ifndef LEMON_H 2 | #define LEMON_H 3 | 4 | #include 5 | #include 6 | 7 | #define MIN_MAJOR_LINUX 5 /* Minimium kernel version supported */ 8 | #define MIN_MINOR_LINUX 5 9 | 10 | #define HUGE_PAGE_SIZE 2 * 1024 * 1024 /* Same for huge pages */ 11 | #define DEFAULT_PORT 2304 /* Default port used for networt dump */ 12 | #define UDP_MAX_PAYLOAD 1024 /* Maximum payload for UDP socket */ 13 | 14 | #define WARN(msg, ...) fprintf(stderr, "WARNING: " msg "\n", ##__VA_ARGS__) 15 | 16 | struct options { 17 | /* Modes */ 18 | bool disk_mode; 19 | bool network_mode; 20 | 21 | /* Disk options */ 22 | char *path; 23 | 24 | /* Network options */ 25 | unsigned long address; 26 | unsigned short port; 27 | bool udp; 28 | 29 | /* Options */ 30 | bool realtime; 31 | bool fatal; 32 | bool raw; 33 | }; 34 | 35 | struct mem_range { 36 | unsigned long long start; 37 | unsigned long long end; 38 | }; 39 | 40 | struct ram_regions { 41 | struct mem_range *regions; 42 | unsigned int num_regions; 43 | }; 44 | 45 | typedef struct __attribute__((packed)) { 46 | unsigned int magic; 47 | unsigned int version; 48 | unsigned long long s_addr; 49 | unsigned long long e_addr; 50 | unsigned char reserved[8]; 51 | } lime_header; 52 | 53 | struct read_mem_result { 54 | int ret_code; 55 | unsigned char buf[HUGE_PAGE_SIZE]; 56 | }; 57 | 58 | struct read_mem_args { 59 | unsigned long long addr; 60 | unsigned long size; 61 | }; 62 | 63 | #endif /* LEMON_H */ 64 | -------------------------------------------------------------------------------- /mem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "lemon.h" 10 | #include "ebpf/mem.ebpf.skel.h" 11 | 12 | /* Kernel definition of a memory region (from include/linux/ioport.h) 13 | * !!! WARNING !!! In theory this struct can change in different kernel versions 14 | * However last time changes was in Linux 4.6 15 | */ 16 | struct resource { 17 | unsigned long long start; 18 | unsigned long long end; 19 | const char *name; 20 | unsigned long flags; 21 | unsigned long desc; 22 | struct resource *parent, *sibling, *child; 23 | }; 24 | 25 | #define IORESOURCE_MEM 0x00000200 26 | #define IORESOURCE_SYSRAM 0x01000000 27 | #define IORESOURCE_BUSY 0x80000000 28 | #define IORESOURCE_SYSTEM_RAM (IORESOURCE_MEM|IORESOURCE_SYSRAM) 29 | #define SYSTEM_RAM_FLAGS (IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY) 30 | 31 | extern int check_capability(const cap_value_t cap); 32 | 33 | /* eBPF memory read program skeleton */ 34 | struct mem_ebpf *mem_ebpf_skel; 35 | 36 | /* XDP and UDP trigger resources */ 37 | int udp_sockfd = -1; 38 | const char *loopback_interface = "lo"; 39 | struct bpf_link *bpf_prog_link = NULL; 40 | 41 | /* File descriptor and mmap() pointer associated to the eBPF map */ 42 | int read_mem_result_fd; 43 | struct read_mem_result *read_mem_result; 44 | 45 | /* Offset used to perform physical to virtual address translation in x86 and ARM64 */ 46 | #ifdef __TARGET_ARCH_x86 47 | static uintptr_t v2p_offset; 48 | #elif __TARGET_ARCH_arm64 49 | static int64_t v2p_offset; 50 | #endif 51 | 52 | /* Address of root of struct resources list (physical memory regions list) */ 53 | static uintptr_t iomem_resource; 54 | 55 | #if defined(__TARGET_ARCH_arm64) 56 | /* 57 | * @brief Check if memory mapping respects the given address 58 | * @param addr: The address to check 59 | * 60 | * Attempts to mmap a 1-byte region at the specified address. If the mmap operation is successful 61 | * and the address is valid (greater than or equal to the specified address) the function returns 62 | * true. Otherwise, it returns false. 63 | * 64 | * @return: true if the mmap succeeds at addr, false otherwise. 65 | */ 66 | static bool is_mmap_respecting_address(void *addr) { 67 | const size_t size = 1; 68 | void *mapped_addr = mmap(addr, size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 69 | 70 | if (mapped_addr == MAP_FAILED) { 71 | return false; 72 | } 73 | 74 | if (munmap(mapped_addr, size) == -1) { 75 | perror("Failed to munmap"); 76 | return false; 77 | } 78 | 79 | /* Check if the mapped address is the desired address, also greater is ok */ 80 | if (mapped_addr >= addr) { 81 | return true; 82 | } else { 83 | return false; 84 | } 85 | } 86 | 87 | /* 88 | * @brief Determine the actual virtual address bits for ARM64 89 | * 90 | * Determines the number of virtual address bits used by the system on ARM64 91 | * by checking the mmap behavior for various address values defined in arch/arm64/Kconfig. 92 | * The function first checks the most common virtual address bit settings (48 and 52), 93 | * then falls back to testing other possible values (47, 42, 39, 36) if necessary. 94 | * @return Number of virtual address bits used (e.g., 48, 52). 95 | */ 96 | static unsigned long arm64_vabits_actual() { 97 | unsigned long vabits = 0; 98 | 99 | /* VA_BITS = 48 is probably the most common check it first */ 100 | if (is_mmap_respecting_address((void*)(1ul << (48 - 1)))) { 101 | if (is_mmap_respecting_address((void*)(1ul << (52 - 1)))) { 102 | vabits = 52; 103 | } else { 104 | vabits = 48; 105 | } 106 | } else { 107 | /* Remaining cases */ 108 | const unsigned long va_bits[] = {47, 42, 39, 36}; 109 | for(int i = 0; i < 4; ++i) { 110 | if (is_mmap_respecting_address((void*)(1ul << (va_bits[i] - 1)))) { 111 | vabits = va_bits[i]; 112 | break; 113 | } 114 | } 115 | } 116 | 117 | return vabits; 118 | } 119 | #endif // __TARGET_ARCH_arm64 120 | 121 | /* 122 | * init_mmap() - Initializes a shared memory mapping for reading memory results from eBPF 123 | * 124 | * Retrieves the file descriptor for the BPF map and creates a shared memory mapping 125 | * to allow user space to access the memory read results. 126 | */ 127 | static int init_mmap() { 128 | 129 | read_mem_result_fd = bpf_map__fd(mem_ebpf_skel->maps.read_mem_array_map); 130 | if(read_mem_result_fd < 0) 131 | return read_mem_result_fd; 132 | 133 | read_mem_result = (struct read_mem_result *)mmap(NULL, sizeof(struct read_mem_result), PROT_READ | PROT_WRITE, MAP_SHARED, read_mem_result_fd, 0); 134 | if (read_mem_result == MAP_FAILED) { 135 | return errno; 136 | } 137 | 138 | return 0; 139 | } 140 | 141 | /* 142 | * init_udp_socket() - Create and configure UDP socket for sending trigger packets 143 | * 144 | * Creates a UDP socket for sending XDP trigger packets to the loopback interface. 145 | * Returns 0 on success, negative errno value on failure. 146 | */ 147 | static int init_udp_socket() { 148 | struct sockaddr_in local_addr; 149 | 150 | /* Create UDP socket */ 151 | udp_sockfd = socket(AF_INET, SOCK_DGRAM, 0); 152 | if (udp_sockfd < 0) { 153 | perror("Failed to create UDP socket for XDP trigger"); 154 | return -errno; 155 | } 156 | 157 | /* Setup local address structure for binding*/ 158 | memset(&local_addr, 0, sizeof(local_addr)); 159 | local_addr.sin_family = AF_INET; 160 | local_addr.sin_addr.s_addr = INADDR_ANY; 161 | local_addr.sin_port = 0; 162 | 163 | return 0; 164 | } 165 | 166 | /* 167 | * load_ebpf_mem_progs() - Initialize and attach eBPF programs for memory access 168 | * 169 | * Opens, loads, attaches the eBPF programs, and sets up shared memory. 170 | * Returns 0 on success or a negative error code on failure. 171 | */ 172 | int load_ebpf_mem_progs() { 173 | int ret; 174 | 175 | /* Check if we have sufficient capabilities to set RLIMIT_MEMLOCK (required by libbpf...)*/ 176 | if((check_capability(CAP_PERFMON) <= 0) && (check_capability(CAP_SYS_ADMIN) <= 0)) { 177 | WARN("LEMON does not have CAP_PERFMON needed to modify RLIMIT_MEMLOCK"); 178 | } 179 | 180 | /* Open the BPF object file */ 181 | mem_ebpf_skel = mem_ebpf__open(); 182 | if(!mem_ebpf_skel) { 183 | perror("Failed to open BPF skeleton"); 184 | return -errno; 185 | } 186 | 187 | /* ARM64 phys to virt translation requires two values, one of the two (CONFIG_ARM64_VA_BITS) 188 | * might not be available from eBPF so we try to compute it at runtime here and we pass it to 189 | * eBPF. 190 | */ 191 | #if defined(__TARGET_ARCH_arm64) 192 | unsigned long vabits = arm64_vabits_actual(); 193 | if (vabits == 0) { 194 | WARN("Failed to determine runtime virtual address bits, defaulting to 48"); 195 | vabits = 48; 196 | } 197 | mem_ebpf_skel->data->runtime_va_bits = vabits; 198 | #endif 199 | 200 | /* Load the BPF objectes */ 201 | if (mem_ebpf__load(mem_ebpf_skel)) { 202 | perror("Failed to load BPF object"); 203 | return -errno; 204 | } 205 | 206 | /* Attach the uprobe to the 'read_kernel_memory' function in the current executable */ 207 | bpf_prog_link = bpf_program__attach(mem_ebpf_skel->progs.read_kernel_memory_uprobe); 208 | if (!bpf_prog_link) { 209 | fprintf(stderr, "Failed to attach eBPF Uprobe program, use XDP fallback...\n"); 210 | 211 | /* Get loopback interface index by name "lo" (usually 1) */ 212 | int ifindex = if_nametoindex(loopback_interface); 213 | if (ifindex <= 0) { 214 | perror("Failed to get interface index"); 215 | return -errno; 216 | } 217 | 218 | /* Attach XDP program to the interface */ 219 | bpf_prog_link = bpf_program__attach_xdp(mem_ebpf_skel->progs.read_kernel_memory_xdp, ifindex); 220 | if (!bpf_prog_link) { 221 | fprintf(stderr, "Failed to attach XDP program to interface %s...\n", loopback_interface); 222 | return -errno; 223 | } 224 | 225 | /* Create socket for sending trigger packets */ 226 | if ((ret = init_udp_socket())) { 227 | return ret; 228 | } 229 | } 230 | 231 | /* Create the mmap */ 232 | if((ret = init_mmap())) { 233 | return ret; 234 | } 235 | 236 | return 0; 237 | } 238 | 239 | /* 240 | * cleanup_mem_ebpf() - Unmaps the shared memory region used to access map and free eBPF resources. 241 | */ 242 | void cleanup_mem_ebpf() { 243 | if(mem_ebpf_skel) { 244 | if(read_mem_result) munmap(read_mem_result, sizeof(struct read_mem_result)); 245 | mem_ebpf__destroy(mem_ebpf_skel); 246 | } 247 | 248 | /* Destroy bpf_link if it exists*/ 249 | if (bpf_prog_link) { 250 | bpf_link__destroy(bpf_prog_link); 251 | bpf_prog_link = NULL; 252 | } 253 | 254 | /* Close UDP socket if it's open */ 255 | if (udp_sockfd > 0) { 256 | close(udp_sockfd); 257 | udp_sockfd = -1; 258 | } 259 | } 260 | 261 | /* 262 | * phys_to_virt() - Convert a physical address to a virtual address using direct mapping 263 | * @phy_addr: Physical address to translate 264 | * 265 | * Performs architecture-specific translation using kernel direct mapping. 266 | * Currently supports x86_64 and ARM64 only. 267 | */ 268 | uintptr_t phys_to_virt(const uintptr_t phy_addr) { 269 | #ifdef __TARGET_ARCH_x86 270 | return phy_addr + v2p_offset; 271 | #elif __TARGET_ARCH_arm64 272 | return phy_addr - v2p_offset; 273 | #else 274 | return phy_addr; 275 | #endif 276 | } 277 | 278 | /* 279 | * send_udp_trigger_packet() - Send UDP packets to trigger XDP program 280 | * @addr: Virtual address of the memory region to read 281 | * @size: Size of the memory region to read 282 | */ 283 | static int send_udp_trigger_packet(const uintptr_t addr, const size_t size) { 284 | ssize_t sent_bytes; 285 | struct sockaddr_in dest_addr; 286 | struct read_mem_args args; 287 | 288 | /* Setup memory read arguments in payload */ 289 | args.addr = addr; 290 | args.size = size; 291 | 292 | /* Setup destination address (loopback) */ 293 | memset(&dest_addr, 0, sizeof(dest_addr)); 294 | dest_addr.sin_family = AF_INET; 295 | dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 296 | dest_addr.sin_port = htons(9999); 297 | 298 | /* Send the UDP packet */ 299 | sent_bytes = sendto(udp_sockfd, &args, sizeof(args), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 300 | 301 | if (sent_bytes < 0) { 302 | perror("Failed to send UDP trigger packet"); 303 | return -errno; 304 | } 305 | 306 | /* Check partial send*/ 307 | if (sent_bytes != sizeof(args)) { 308 | fprintf(stderr, "Incomplete packet send: %zd of %zu bytes\n", sent_bytes, sizeof(args)); 309 | return -EIO; 310 | } 311 | 312 | return 0; 313 | } 314 | 315 | /* 316 | * read_kernel_memory() - Trigger eBPF UProbe or XDP to read kernel virtual memory 317 | * @addr: Virtual address of the memory region to read 318 | * @size: Size of the memory region to read 319 | * @data: Pointer to store the output data 320 | * 321 | * This function triggers an eBPF UProbe or XDP to read the specified memory region in kernel space. 322 | * The function is marked with `noinline` and `optnone` to ensure the code is not optimized or inlined by the compiler. 323 | */ 324 | int __attribute__((noinline, optnone)) read_kernel_memory(const uintptr_t addr, const size_t size, __u8 **restrict data) { 325 | /* If the Uprobe support is not active in kernel, use XDP to read the memory*/ 326 | if(udp_sockfd > 0) { 327 | int ret; 328 | 329 | /* Send UDP trigger packet for XDP*/ 330 | ret = send_udp_trigger_packet(addr, size); 331 | if (ret < 0) { 332 | read_mem_result->ret_code = ret; 333 | return ret; 334 | } 335 | } 336 | 337 | *data = read_mem_result->buf; 338 | return read_mem_result->ret_code; 339 | } 340 | 341 | /* 342 | * parse_kallsyms_line() - Extracts the address of a specific kernel symbol from a text line 343 | * @line: A line of text, typically from /proc/kallsyms or System.map 344 | * @symbol: The name of the symbol to search for 345 | * @symbol_addr: Pointer to store the resolved symbol address 346 | * 347 | * Scans the line for the symbol name and extracts its address using sscanf. 348 | * Returns 1 on success, 0 on not looked for element or a negative error code from read_kernel_memory(). 349 | */ 350 | static int inline parse_kallsyms_line(const char *restrict line, const char *restrict symbol, uintptr_t *restrict current_symb_addr) { 351 | char current_symb_name[256]; 352 | 353 | /* Read the address and check if the it is the symbol that we look for */ 354 | if ((sscanf(line, "%lx %*c %255s\n", current_symb_addr, current_symb_name) != 2) || strncmp(current_symb_name, symbol, strlen(symbol))) 355 | return 0; 356 | 357 | /* Check that address is not 0 */ 358 | return current_symb_addr != 0; 359 | } 360 | 361 | /* 362 | * parse_kallsyms() - Parse /proc/kallsyms extracting needed symbols 363 | * 364 | * Opens /proc/kallsyms, searches for the appropriate symbol (e.g., "page_offset_base" or 365 | * "memstart_addr" and "iomem_resource") based on architecture, and retrieves the physical-to-virtual address 366 | * translation offset and the pointer to the tree of physical memory regions. 367 | * Returns 0 on success, or an error code on failure. 368 | */ 369 | static int parse_kallsyms() { 370 | FILE *fp; 371 | char line[256]; 372 | __u8 *data = NULL; 373 | uintptr_t current_symb_addr = 0; 374 | int err; 375 | 376 | /* Make sure we can use the same read for signed and unsigned offsets (arm/intel) */ 377 | _Static_assert(sizeof(uintptr_t) == sizeof(int64_t), "sizeof(uintptr_t) != sizeof(int64_t)"); 378 | 379 | /* Check for capabilities */ 380 | if((check_capability(CAP_SYSLOG) <= 0)) { 381 | fprintf(stderr, "LEMON does not have CAP_SYSLOG to read addresses from /proc/kallsyms\n"); 382 | return EPERM; 383 | } 384 | 385 | /* Symbol to be located in /proc/kallsyms */ 386 | iomem_resource = 0; 387 | v2p_offset = 0; 388 | 389 | #ifdef __TARGET_ARCH_x86 390 | char *v2p_symbol = "page_offset_base"; 391 | #elif __TARGET_ARCH_arm64 392 | char *v2p_symbol = "memstart_addr"; 393 | #endif 394 | 395 | /* Open the kallsyms file and look for symbols in it*/ 396 | fp = fopen("/proc/kallsyms", "r"); 397 | if (!fp) 398 | { 399 | perror("Failed to open /proc/kallsyms"); 400 | return errno; 401 | } 402 | 403 | /* Look for all the symbols */ 404 | while (fgets(line, sizeof(line), fp)) { 405 | 406 | /* Check if all the symbols are already found */ 407 | if(iomem_resource && v2p_offset) break; 408 | 409 | /* Look for symbols */ 410 | if(!iomem_resource && parse_kallsyms_line(line, "iomem_resource", ¤t_symb_addr)) { 411 | iomem_resource = current_symb_addr; 412 | continue; 413 | } 414 | 415 | if(!v2p_offset && parse_kallsyms_line(line, v2p_symbol, ¤t_symb_addr)) { 416 | 417 | /* Read it to obtain the offset */ 418 | if((err = read_kernel_memory(current_symb_addr, sizeof(uintptr_t), &data))) break; 419 | #ifdef __TARGET_ARCH_x86 420 | v2p_offset = *((uintptr_t *)data); 421 | #elif __TARGET_ARCH_arm64 422 | v2p_offset = *((int64_t *)data); 423 | #endif 424 | continue; 425 | } 426 | } 427 | 428 | if(fclose(fp)) { 429 | perror("Fail to close /proc/kallsyms"); 430 | return errno; 431 | } 432 | 433 | /* Check if all the virtual to phisical offset is found */ 434 | if (!v2p_offset) 435 | { 436 | fprintf(stderr, "Symbol %s not found in /proc/kallsyms\n", v2p_symbol); 437 | return EIO; 438 | } 439 | 440 | return 0; 441 | } 442 | 443 | /* 444 | * get_iomem_regions_user() - Parse /proc/iomem to extract "System RAM" regions 445 | * @ram_regions: Pointer to store the extracted RAM regions 446 | * 447 | * Opens /proc/iomem, searches for "System RAM" regions, and populates the provided 448 | * ram_regions struct with the start and end addresses of each region. The function 449 | * reallocates memory as needed to accommodate additional regions. 450 | * Returns 0 on success, or an error code on failure. 451 | */ 452 | static int get_iomem_regions_user(struct ram_regions *restrict ram_regions) { 453 | FILE *fp; 454 | char line[256]; 455 | int slot_availables; 456 | uintptr_t start, end; 457 | int cap_ret; 458 | 459 | /* Check if we have CAP_SYS_ADMIN capability */ 460 | if((cap_ret = check_capability(CAP_SYS_ADMIN)) <= 0) { 461 | fprintf(stderr, "LEMON does not have CAP_SYS_ADMIN to read /proc/iomem\n"); 462 | return cap_ret; 463 | } 464 | 465 | /* Open the /proc/iomem and parse only "System RAM" regions */ 466 | fp = fopen("/proc/iomem", "r"); 467 | if (!fp) 468 | { 469 | perror("Failed to open /proc/iomem"); 470 | return errno; 471 | } 472 | 473 | /* Initial RAM regions allocations */ 474 | ram_regions->num_regions = 0; 475 | slot_availables = 8; 476 | 477 | ram_regions->regions = (struct mem_range *)malloc(slot_availables * sizeof(struct mem_range)); 478 | if (!ram_regions->regions) 479 | { 480 | perror("Failed to allocate memory for RAM ranges"); 481 | fclose(fp); 482 | return errno; 483 | } 484 | 485 | /* Look only for "System RAM" regions */ 486 | while (fgets(line, sizeof(line), fp)) 487 | { 488 | if (strstr(line, "System RAM") && sscanf(line, "%lx-%lx", &start, &end) == 2) 489 | { 490 | /* If the array is full, reallocate to increase its size */ 491 | if (ram_regions->num_regions >= slot_availables) 492 | { 493 | slot_availables *= 2; 494 | ram_regions->regions = 495 | (struct mem_range *)realloc(ram_regions->regions, slot_availables * sizeof(struct mem_range)); 496 | if (!ram_regions->regions) 497 | { 498 | perror("Failed to reallocate memory for RAM ranges"); 499 | fclose(fp); 500 | return errno; 501 | } 502 | } 503 | 504 | /* Save region start and end */ 505 | (ram_regions->regions)[ram_regions->num_regions].start = start; 506 | (ram_regions->regions)[ram_regions->num_regions].end = end; 507 | (ram_regions->num_regions)++; 508 | 509 | } 510 | } 511 | if(fclose(fp)) { 512 | perror("Fail to close /proc/iomem"); 513 | return errno; 514 | } 515 | 516 | return 0; 517 | } 518 | 519 | /* 520 | * get_iomem_regions_kernel() - Parse struct resources directly in kernel to extract "System RAM" regions 521 | * @ram_regions: Pointer to store the extracted RAM regions 522 | * 523 | * Read struct resources from kernel, and populates the provided 524 | * ram_regions struct with the start and end addresses of each region. The function 525 | * reallocates memory as needed to accommodate additional regions. 526 | * Returns 0 on success, or an error code on failure. 527 | */ 528 | static int get_iomem_regions_kernel(struct ram_regions *restrict ram_regions) { 529 | int slot_availables; 530 | __u8 *data = NULL; 531 | struct resource *res, *next_res; 532 | int err; 533 | 534 | /* Initial RAM regions allocations */ 535 | ram_regions->num_regions = 0; 536 | slot_availables = 8; 537 | 538 | ram_regions->regions = (struct mem_range *)malloc(slot_availables * sizeof(struct mem_range)); 539 | if (!ram_regions->regions) 540 | { 541 | perror("Failed to allocate memory for RAM ranges"); 542 | return errno; 543 | } 544 | 545 | /* We follow the implementation of LiME considering only sibling leafs (level 1 only). 546 | * Is it possible to have "System RAM" regions inside non System RAM regions? I don't think so. 547 | */ 548 | 549 | /* Obtain the address child of the root struct */ 550 | if((err = read_kernel_memory(iomem_resource, sizeof(struct resource), &data))) { 551 | fprintf(stderr, "Error reading root struct resource"); 552 | return err; 553 | } 554 | res = ((struct resource *)data); 555 | next_res = res->child; 556 | 557 | /* Walk the sibling list */ 558 | while(next_res) { 559 | if((err = read_kernel_memory((uintptr_t)next_res, sizeof(struct resource), &data))) { 560 | fprintf(stderr, "Error reading sibling struct"); 561 | return err; 562 | } 563 | res = ((struct resource *)data); 564 | 565 | /* Check if it is a "System RAM" region using flags instead of string (reduce memory copying from kernel to user) */ 566 | if(res->name && ((res->flags & SYSTEM_RAM_FLAGS) == SYSTEM_RAM_FLAGS)) { 567 | /* If the array is full, reallocate to increase its size */ 568 | if (ram_regions->num_regions >= slot_availables) 569 | { 570 | slot_availables *= 2; 571 | ram_regions->regions = 572 | (struct mem_range *)realloc(ram_regions->regions, slot_availables * sizeof(struct mem_range)); 573 | if (!ram_regions->regions) 574 | { 575 | perror("Failed to reallocate memory for RAM ranges"); 576 | return errno; 577 | } 578 | } 579 | 580 | /* Save region start and end */ 581 | (ram_regions->regions)[ram_regions->num_regions].start = res->start; 582 | (ram_regions->regions)[ram_regions->num_regions].end = res->end; 583 | (ram_regions->num_regions)++; 584 | } 585 | 586 | /* Prepare for next iteration */ 587 | next_res = res->sibling; 588 | } 589 | return 0; 590 | } 591 | 592 | /* 593 | * toggle_kptr() - Toggle the kernel.kptr_restrict sysctl setting 594 | * 595 | * Reads and toggles /proc/sys/kernel/kptr_restrict between 0 and its original value (only if needed). 596 | * Caches the original value on first call. Returns 0 on success, or an error code on failure. 597 | */ 598 | int toggle_kptr(void) { 599 | static int orig_kptr_status = - 1; 600 | 601 | struct stat stat_tmp; 602 | FILE *kptr_fd; 603 | int current_kptr_status, new_kptr_status, cap_ret, err = 0; 604 | 605 | /* If kptr_restrict does not exists (?) do nothing */ 606 | if(stat("/proc/sys/kernel/kptr_restrict", &stat_tmp)) { 607 | perror("/proc/sys/kernel/kptr_restrict not found"); 608 | return 0; 609 | } 610 | 611 | /* Open the file */ 612 | if(!(kptr_fd = fopen("/proc/sys/kernel/kptr_restrict", "r"))) { 613 | perror("Failed to open /proc/sys/kernel/kptr_restrict"); 614 | return errno; 615 | } 616 | 617 | /* Read current kptr_status */ 618 | if(fscanf(kptr_fd, "%d", ¤t_kptr_status) == EOF) { 619 | perror("Fail to read /proc/sys/kernel/kptr_restrict"); 620 | err = errno; 621 | goto cleanup; 622 | } 623 | 624 | /* Save the original value */ 625 | if(orig_kptr_status == -1) { 626 | orig_kptr_status = current_kptr_status; 627 | } 628 | 629 | /* If the original kptr_value is 0 do nothing */ 630 | if(!orig_kptr_status) goto cleanup; 631 | 632 | /* If the value is 1 and we have CAP_SYSLOG is not necessary to toggle it (neigter CAP_SYS_ADMIN!) :) */ 633 | if((orig_kptr_status == 1) && (check_capability(CAP_SYSLOG) > 0)) goto cleanup; 634 | 635 | /* Check CAP_SYS_ADMIN to modify kptr_restrict */ 636 | if((cap_ret = check_capability(CAP_SYS_ADMIN)) <= 0) { 637 | fprintf(stderr, "LEMON does not have CAP_SYS_ADMIN to modify /proc/sys/kernel/kptr_restrict policy\n"); 638 | err = cap_ret; 639 | goto cleanup; 640 | } 641 | 642 | /* Reopen the file in RW mode */ 643 | if(!(kptr_fd = freopen(NULL, "r+", kptr_fd))) { 644 | perror("Failed to open /proc/sys/kernel/kptr_restrict in RW mode"); 645 | err = errno; 646 | goto cleanup; 647 | } 648 | 649 | /* Toggle the kptr_restrict value*/ 650 | new_kptr_status = (current_kptr_status > 0) ? 0 : orig_kptr_status; 651 | if(fprintf(kptr_fd, "%d", new_kptr_status) < 0) { 652 | err = EIO; 653 | goto cleanup; 654 | } 655 | 656 | cleanup: 657 | if(kptr_fd) { 658 | if(fclose(kptr_fd)) { 659 | perror("Fail to close /proc/sys/kernel/kptr_restrict"); 660 | return errno; 661 | } 662 | } 663 | 664 | return err; 665 | } 666 | 667 | /* 668 | * init_translation() - Initialize phys-to-virt translation and extract System RAM regions 669 | * @ram_regions: Output pointer for storing valid memory regions 670 | * 671 | * Initializes the physical-to-virtual address mapping and retrieves System RAM virtual address ranges 672 | * from kernel or /proc/iomem. 673 | * Returns 0 on success or an error code on failure. 674 | */ 675 | int init_translation(struct ram_regions *restrict ram_regions) { 676 | int err; 677 | 678 | /* Parse kallsyms looking for symbols needed to initialize translatation system */ 679 | if((err = parse_kallsyms())) return err; 680 | 681 | /* If the iomem_resource symbol is available access to it through eBPF bypassing CAP_SYS_ADMIN 682 | * Otherwise use /proc/iomem which requires CAP_SYS_ADMIN. 683 | */ 684 | 685 | if(iomem_resource) { 686 | return get_iomem_regions_kernel(ram_regions); 687 | } 688 | else 689 | return get_iomem_regions_user(ram_regions); 690 | } 691 | -------------------------------------------------------------------------------- /net.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "lemon.h" 6 | 7 | extern int dump(const struct options *restrict opts, const struct ram_regions *restrict ram_regions, int (*write_f)(void *restrict, const void *restrict, const unsigned long), void *restrict args); 8 | 9 | /* Arguments passed to write_on_socket() */ 10 | struct net_args { 11 | bool udp; 12 | int sockfd; 13 | }; 14 | 15 | /* 16 | * write_on_socket() - Sends data over a TCP socket. 17 | * @args: pointer to an integer file descriptor (the socket) 18 | * @data: pointer to the buffer to send 19 | * @size: number of bytes to send 20 | * 21 | * Returns 0 on success. 22 | */ 23 | int write_on_socket(void *restrict args, const void *restrict data, const unsigned long size) { 24 | unsigned long r; 25 | unsigned long total; 26 | struct net_args *net_args = (struct net_args *)args; 27 | 28 | total = r = 0; 29 | while(total < size) { 30 | r = write(net_args->sockfd, data + total, size - total); 31 | if(r == -1) { 32 | if(errno == EINTR) continue; 33 | perror("Fail to write on socket"); 34 | return errno; 35 | } 36 | 37 | total += r; 38 | } 39 | 40 | return 0; 41 | } 42 | 43 | /* 44 | * dump_on_net() - Sends the memory dump over the network. 45 | * @opts: user-provided options, including destination address 46 | * @ram_regions: memory regions to dump 47 | * 48 | * On success, it returns 0. On failure, a negative value or errno is returned. 49 | */ 50 | int dump_on_net(const struct options *restrict opts, const struct ram_regions *restrict ram_regions) { 51 | int sockfd; 52 | struct sockaddr_in dest_addr; 53 | struct net_args net_args; 54 | int ret; 55 | 56 | /* Create socket */ 57 | sockfd = socket(AF_INET, opts->udp ? SOCK_DGRAM : SOCK_STREAM, 0); 58 | if (sockfd < 0) { 59 | perror("Fail to open network socket"); 60 | return errno; 61 | } 62 | 63 | /* Setup destination address */ 64 | dest_addr.sin_family = AF_INET; 65 | dest_addr.sin_port = htons(opts->port); 66 | dest_addr.sin_addr.s_addr = opts->address; 67 | 68 | /* Connect to the destination */ 69 | if ((ret = connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr))) < 0) { 70 | perror("Fail to connect to remote host"); 71 | return errno; 72 | } 73 | 74 | /* Setup arguments for write_on_socket */ 75 | net_args.sockfd = sockfd; 76 | net_args.udp = opts->udp; 77 | 78 | /* Dump! */ 79 | ret = dump(opts, ram_regions, write_on_socket, (void *)&net_args); 80 | 81 | if(sockfd) { 82 | if(close(sockfd)) { perror("Fail to close the connection"); ret = errno; } 83 | } 84 | 85 | return ret; 86 | } 87 | --------------------------------------------------------------------------------